commit bf65e372029deb236eca4fab0d8d46cace0a3ed6 Author: Yi-Ting Shih Date: Mon Oct 5 04:41:24 2026 +0800 init: HW1 diff --git a/part1/Makefile b/part1/Makefile new file mode 100644 index 0000000..46298fa --- /dev/null +++ b/part1/Makefile @@ -0,0 +1,20 @@ +CXX := g++ +CXXFLAGS := -O3 -std=c++17 -Wall + +ifneq ($(VECTOR_WIDTH),) + CXXFLAGS += -DVECTOR_WIDTH=$(VECTOR_WIDTH) +endif + +all: myexp + +logger.o: logger.cpp logger.h PPintrin.h PPintrin.cpp def.h + $(CXX) $(CXXFLAGS) -c logger.cpp + +PPintrin.o: PPintrin.cpp PPintrin.h logger.cpp logger.h def.h + $(CXX) $(CXXFLAGS) -c PPintrin.cpp + +myexp: PPintrin.o logger.o main.cpp serialOP.cpp vectorOP.cpp + $(CXX) $(CXXFLAGS) logger.o PPintrin.o main.cpp serialOP.cpp vectorOP.cpp -o myexp + +clean: + $(RM) *.o *.s myexp *~ diff --git a/part1/PPintrin.cpp b/part1/PPintrin.cpp new file mode 100644 index 0000000..311a8ee --- /dev/null +++ b/part1/PPintrin.cpp @@ -0,0 +1,304 @@ +#include "PPintrin.h" +#include "logger.h" + +//****************** +//* Implementation * +//****************** + +__pp_mask _pp_init_ones(int first) +{ + __pp_mask mask; + for (int i = 0; i < VECTOR_WIDTH; i++) + { + mask.value[i] = (i < first) ? true : false; + } + return mask; +} + +__pp_mask _pp_mask_not(__pp_mask &maska) +{ + __pp_mask resultMask; + for (int i = 0; i < VECTOR_WIDTH; i++) + { + resultMask.value[i] = !maska.value[i]; + } + PPLogger.addLog("masknot", _pp_init_ones(), VECTOR_WIDTH); + return resultMask; +} + +__pp_mask _pp_mask_or(__pp_mask &maska, __pp_mask &maskb) +{ + __pp_mask resultMask; + for (int i = 0; i < VECTOR_WIDTH; i++) + { + resultMask.value[i] = maska.value[i] | maskb.value[i]; + } + PPLogger.addLog("maskor", _pp_init_ones(), VECTOR_WIDTH); + return resultMask; +} + +__pp_mask _pp_mask_and(__pp_mask &maska, __pp_mask &maskb) +{ + __pp_mask resultMask; + for (int i = 0; i < VECTOR_WIDTH; i++) + { + resultMask.value[i] = maska.value[i] && maskb.value[i]; + } + PPLogger.addLog("maskand", _pp_init_ones(), VECTOR_WIDTH); + return resultMask; +} + +int _pp_cntbits(__pp_mask &maska) +{ + int count = 0; + for (int i = 0; i < VECTOR_WIDTH; i++) + { + if (maska.value[i]) + count++; + } + PPLogger.addLog("cntbits", _pp_init_ones(), VECTOR_WIDTH); + return count; +} + +template +void _pp_vset(__pp_vec &vecResult, T value, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + vecResult.value[i] = mask.value[i] ? value : vecResult.value[i]; + } + PPLogger.addLog("vset", mask, VECTOR_WIDTH); +} + +template void _pp_vset(__pp_vec_float &vecResult, float value, __pp_mask &mask); +template void _pp_vset(__pp_vec_int &vecResult, int value, __pp_mask &mask); + +void _pp_vset_float(__pp_vec_float &vecResult, float value, __pp_mask &mask) { _pp_vset(vecResult, value, mask); } +void _pp_vset_int(__pp_vec_int &vecResult, int value, __pp_mask &mask) { _pp_vset(vecResult, value, mask); } + +__pp_vec_float _pp_vset_float(float value) +{ + __pp_vec_float vecResult; + __pp_mask mask = _pp_init_ones(); + _pp_vset_float(vecResult, value, mask); + return vecResult; +} +__pp_vec_int _pp_vset_int(int value) +{ + __pp_vec_int vecResult; + __pp_mask mask = _pp_init_ones(); + _pp_vset_int(vecResult, value, mask); + return vecResult; +} + +template +void _pp_vmove(__pp_vec &dest, __pp_vec &src, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + dest.value[i] = mask.value[i] ? src.value[i] : dest.value[i]; + } + PPLogger.addLog("vmove", mask, VECTOR_WIDTH); +} + +template void _pp_vmove(__pp_vec_float &dest, __pp_vec_float &src, __pp_mask &mask); +template void _pp_vmove(__pp_vec_int &dest, __pp_vec_int &src, __pp_mask &mask); + +void _pp_vmove_float(__pp_vec_float &dest, __pp_vec_float &src, __pp_mask &mask) { _pp_vmove(dest, src, mask); } +void _pp_vmove_int(__pp_vec_int &dest, __pp_vec_int &src, __pp_mask &mask) { _pp_vmove(dest, src, mask); } + +template +void _pp_vload(__pp_vec &dest, T *src, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + dest.value[i] = mask.value[i] ? src[i] : dest.value[i]; + } + PPLogger.addLog("vload", mask, VECTOR_WIDTH); +} + +template void _pp_vload(__pp_vec_float &dest, float *src, __pp_mask &mask); +template void _pp_vload(__pp_vec_int &dest, int *src, __pp_mask &mask); + +void _pp_vload_float(__pp_vec_float &dest, float *src, __pp_mask &mask) { _pp_vload(dest, src, mask); } +void _pp_vload_int(__pp_vec_int &dest, int *src, __pp_mask &mask) { _pp_vload(dest, src, mask); } + +template +void _pp_vstore(T *dest, __pp_vec &src, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + dest[i] = mask.value[i] ? src.value[i] : dest[i]; + } + PPLogger.addLog("vstore", mask, VECTOR_WIDTH); +} + +template void _pp_vstore(float *dest, __pp_vec_float &src, __pp_mask &mask); +template void _pp_vstore(int *dest, __pp_vec_int &src, __pp_mask &mask); + +void _pp_vstore_float(float *dest, __pp_vec_float &src, __pp_mask &mask) { _pp_vstore(dest, src, mask); } +void _pp_vstore_int(int *dest, __pp_vec_int &src, __pp_mask &mask) { _pp_vstore(dest, src, mask); } + +template +void _pp_vadd(__pp_vec &vecResult, __pp_vec &veca, __pp_vec &vecb, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + vecResult.value[i] = mask.value[i] ? (veca.value[i] + vecb.value[i]) : vecResult.value[i]; + } + PPLogger.addLog("vadd", mask, VECTOR_WIDTH); +} + +template void _pp_vadd(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +template void _pp_vadd(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +void _pp_vadd_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask) { _pp_vadd(vecResult, veca, vecb, mask); } +void _pp_vadd_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask) { _pp_vadd(vecResult, veca, vecb, mask); } + +template +void _pp_vsub(__pp_vec &vecResult, __pp_vec &veca, __pp_vec &vecb, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + vecResult.value[i] = mask.value[i] ? (veca.value[i] - vecb.value[i]) : vecResult.value[i]; + } + PPLogger.addLog("vsub", mask, VECTOR_WIDTH); +} + +template void _pp_vsub(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +template void _pp_vsub(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +void _pp_vsub_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask) { _pp_vsub(vecResult, veca, vecb, mask); } +void _pp_vsub_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask) { _pp_vsub(vecResult, veca, vecb, mask); } + +template +void _pp_vmult(__pp_vec &vecResult, __pp_vec &veca, __pp_vec &vecb, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + vecResult.value[i] = mask.value[i] ? (veca.value[i] * vecb.value[i]) : vecResult.value[i]; + } + PPLogger.addLog("vmult", mask, VECTOR_WIDTH); +} + +template void _pp_vmult(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +template void _pp_vmult(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +void _pp_vmult_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask) { _pp_vmult(vecResult, veca, vecb, mask); } +void _pp_vmult_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask) { _pp_vmult(vecResult, veca, vecb, mask); } + +template +void _pp_vdiv(__pp_vec &vecResult, __pp_vec &veca, __pp_vec &vecb, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + vecResult.value[i] = mask.value[i] ? (veca.value[i] / vecb.value[i]) : vecResult.value[i]; + } + PPLogger.addLog("vdiv", mask, VECTOR_WIDTH); +} + +template void _pp_vdiv(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +template void _pp_vdiv(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +void _pp_vdiv_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask) { _pp_vdiv(vecResult, veca, vecb, mask); } +void _pp_vdiv_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask) { _pp_vdiv(vecResult, veca, vecb, mask); } + +template +void _pp_vabs(__pp_vec &vecResult, __pp_vec &veca, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + vecResult.value[i] = mask.value[i] ? (abs(veca.value[i])) : vecResult.value[i]; + } + PPLogger.addLog("vabs", mask, VECTOR_WIDTH); +} + +template void _pp_vabs(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_mask &mask); +template void _pp_vabs(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_mask &mask); + +void _pp_vabs_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_mask &mask) { _pp_vabs(vecResult, veca, mask); } +void _pp_vabs_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_mask &mask) { _pp_vabs(vecResult, veca, mask); } + +template +void _pp_vgt(__pp_mask &maskResult, __pp_vec &veca, __pp_vec &vecb, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + maskResult.value[i] = mask.value[i] ? (veca.value[i] > vecb.value[i]) : maskResult.value[i]; + } + PPLogger.addLog("vgt", mask, VECTOR_WIDTH); +} + +template void _pp_vgt(__pp_mask &maskResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +template void _pp_vgt(__pp_mask &maskResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +void _pp_vgt_float(__pp_mask &maskResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask) { _pp_vgt(maskResult, veca, vecb, mask); } +void _pp_vgt_int(__pp_mask &maskResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask) { _pp_vgt(maskResult, veca, vecb, mask); } + +template +void _pp_vlt(__pp_mask &maskResult, __pp_vec &veca, __pp_vec &vecb, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + maskResult.value[i] = mask.value[i] ? (veca.value[i] < vecb.value[i]) : maskResult.value[i]; + } + PPLogger.addLog("vlt", mask, VECTOR_WIDTH); +} + +template void _pp_vlt(__pp_mask &maskResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +template void _pp_vlt(__pp_mask &maskResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +void _pp_vlt_float(__pp_mask &maskResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask) { _pp_vlt(maskResult, veca, vecb, mask); } +void _pp_vlt_int(__pp_mask &maskResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask) { _pp_vlt(maskResult, veca, vecb, mask); } + +template +void _pp_veq(__pp_mask &maskResult, __pp_vec &veca, __pp_vec &vecb, __pp_mask &mask) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + maskResult.value[i] = mask.value[i] ? (veca.value[i] == vecb.value[i]) : maskResult.value[i]; + } + PPLogger.addLog("veq", mask, VECTOR_WIDTH); +} + +template void _pp_veq(__pp_mask &maskResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +template void _pp_veq(__pp_mask &maskResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +void _pp_veq_float(__pp_mask &maskResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask) { _pp_veq(maskResult, veca, vecb, mask); } +void _pp_veq_int(__pp_mask &maskResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask) { _pp_veq(maskResult, veca, vecb, mask); } + +template +void _pp_hadd(__pp_vec &vecResult, __pp_vec &vec) +{ + for (int i = 0; i < VECTOR_WIDTH / 2; i++) + { + T result = vec.value[2 * i] + vec.value[2 * i + 1]; + vecResult.value[2 * i] = result; + vecResult.value[2 * i + 1] = result; + } + PPLogger.addLog("hadd", _pp_init_ones(), VECTOR_WIDTH); +} + +template void _pp_hadd(__pp_vec_float &vecResult, __pp_vec_float &vec); + +void _pp_hadd_float(__pp_vec_float &vecResult, __pp_vec_float &vec) { _pp_hadd(vecResult, vec); } + +template +void _pp_interleave(__pp_vec &vecResult, __pp_vec vec) +{ + for (int i = 0; i < VECTOR_WIDTH; i++) + { + int index = i < VECTOR_WIDTH / 2 ? (2 * i) : (2 * (i - VECTOR_WIDTH / 2) + 1); + vecResult.value[i] = vec.value[index]; + } + PPLogger.addLog("interleave", _pp_init_ones(), VECTOR_WIDTH); +} + +template void _pp_interleave(__pp_vec_float &vecResult, __pp_vec_float vec); + +void _pp_interleave_float(__pp_vec_float &vecResult, __pp_vec_float vec) { _pp_interleave(vecResult, vec); } + +void addUserLog(const char *logStr) +{ + PPLogger.addLog(logStr, _pp_init_ones(), 0); +} diff --git a/part1/PPintrin.h b/part1/PPintrin.h new file mode 100644 index 0000000..f029a87 --- /dev/null +++ b/part1/PPintrin.h @@ -0,0 +1,123 @@ +#ifndef PPINTRIN_H_ +#define PPINTRIN_H_ + +#include +#include +#include "logger.h" +#include "def.h" +//******************* +//* Type Definition * +//******************* + +extern Logger PPLogger; + +template +struct __pp_vec { + T value[VECTOR_WIDTH]; +}; + +// Declare a mask with __pp_mask +struct __pp_mask : __pp_vec {}; + +// Declare a floating point vector register with __pp_vec_float +#define __pp_vec_float __pp_vec + +// Declare an integer vector register with __pp_vec_int +#define __pp_vec_int __pp_vec + +//*********************** +//* Function Definition * +//*********************** + +// Return a mask initialized to 1 in the first N lanes and 0 in the others +__pp_mask _pp_init_ones(int first = VECTOR_WIDTH); + +// Return the inverse of maska +__pp_mask _pp_mask_not(__pp_mask &maska); + +// Return (maska | maskb) +__pp_mask _pp_mask_or(__pp_mask &maska, __pp_mask &maskb); + +// Return (maska & maskb) +__pp_mask _pp_mask_and(__pp_mask &maska, __pp_mask &maskb); + +// Count the number of 1s in maska +int _pp_cntbits(__pp_mask &maska); + +// Set register to value if vector lane is active +// otherwise keep the old value +void _pp_vset_float(__pp_vec_float &vecResult, float value, __pp_mask &mask); +void _pp_vset_int(__pp_vec_int &vecResult, int value, __pp_mask &mask); +// For user's convenience, returns a vector register with all lanes initialized to value +__pp_vec_float _pp_vset_float(float value); +__pp_vec_int _pp_vset_int(int value); + +// Copy values from vector register src to vector register dest if vector lane active +// otherwise keep the old value +void _pp_vmove_float(__pp_vec_float &dest, __pp_vec_float &src, __pp_mask &mask); +void _pp_vmove_int(__pp_vec_int &dest, __pp_vec_int &src, __pp_mask &mask); + +// Load values from array src to vector register dest if vector lane active +// otherwise keep the old value +void _pp_vload_float(__pp_vec_float &dest, float* src, __pp_mask &mask); +void _pp_vload_int(__pp_vec_int &dest, int* src, __pp_mask &mask); + +// Store values from vector register src to array dest if vector lane active +// otherwise keep the old value +void _pp_vstore_float(float* dest, __pp_vec_float &src, __pp_mask &mask); +void _pp_vstore_int(int* dest, __pp_vec_int &src, __pp_mask &mask); + +// Return calculation of (veca + vecb) if vector lane active +// otherwise keep the old value +void _pp_vadd_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +void _pp_vadd_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +// Return calculation of (veca - vecb) if vector lane active +// otherwise keep the old value +void _pp_vsub_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +void _pp_vsub_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +// Return calculation of (veca * vecb) if vector lane active +// otherwise keep the old value +void _pp_vmult_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +void _pp_vmult_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +// Return calculation of (veca / vecb) if vector lane active +// otherwise keep the old value +void _pp_vdiv_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +void _pp_vdiv_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + + +// Return calculation of absolute value abs(veca) if vector lane active +// otherwise keep the old value +void _pp_vabs_float(__pp_vec_float &vecResult, __pp_vec_float &veca, __pp_mask &mask); +void _pp_vabs_int(__pp_vec_int &vecResult, __pp_vec_int &veca, __pp_mask &mask); + +// Return a mask of (veca > vecb) if vector lane active +// otherwise keep the old value +void _pp_vgt_float(__pp_mask &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +void _pp_vgt_int(__pp_mask &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +// Return a mask of (veca < vecb) if vector lane active +// otherwise keep the old value +void _pp_vlt_float(__pp_mask &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +void _pp_vlt_int(__pp_mask &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +// Return a mask of (veca == vecb) if vector lane active +// otherwise keep the old value +void _pp_veq_float(__pp_mask &vecResult, __pp_vec_float &veca, __pp_vec_float &vecb, __pp_mask &mask); +void _pp_veq_int(__pp_mask &vecResult, __pp_vec_int &veca, __pp_vec_int &vecb, __pp_mask &mask); + +// Adds up adjacent pairs of elements, so +// [0 1 2 3] -> [0+1 0+1 2+3 2+3] +void _pp_hadd_float(__pp_vec_float &vecResult, __pp_vec_float &vec); + +// Performs an even-odd interleaving where all even-indexed elements move to front half +// of the array and odd-indexed to the back half, so +// [0 1 2 3 4 5 6 7] -> [0 2 4 6 1 3 5 7] +void _pp_interleave_float(__pp_vec_float &vecResult, __pp_vec_float vec); + +// Add a customized log to help debugging +void addUserLog(const char * logStr); + +#endif diff --git a/part1/PPintrin.o b/part1/PPintrin.o new file mode 100644 index 0000000..33c3ac7 Binary files /dev/null and b/part1/PPintrin.o differ diff --git a/part1/def.h b/part1/def.h new file mode 100644 index 0000000..a617554 --- /dev/null +++ b/part1/def.h @@ -0,0 +1,5 @@ +// Define vector unit width here +#ifndef VECTOR_WIDTH +#define VECTOR_WIDTH 4 +#endif +#define EXP_MAX 10 diff --git a/part1/logger.cpp b/part1/logger.cpp new file mode 100644 index 0000000..c74d795 --- /dev/null +++ b/part1/logger.cpp @@ -0,0 +1,66 @@ +#include "logger.h" +#include "PPintrin.h" + +void Logger::addLog(const char *instruction, __pp_mask mask, int N) +{ + Log newLog; + strcpy(newLog.instruction, instruction); + newLog.mask = 0; + for (int i = 0; i < N; i++) + { + if (mask.value[i]) + { + newLog.mask |= (((unsigned long long)1) << i); + stats.utilized_lane++; + } + } + stats.total_lane += N; + stats.total_instructions += (N > 0); + log.push_back(newLog); +} + +void Logger::printStats() +{ + printf("****************** Printing Vector Unit Statistics *******************\n"); + printf("Vector Width: %d\n", VECTOR_WIDTH); + printf("Total Vector Instructions: %lld\n", stats.total_instructions); + printf("Vector Utilization: %.1f%%\n", (double)stats.utilized_lane / stats.total_lane * 100); + printf("Utilized Vector Lanes: %lld\n", stats.utilized_lane); + printf("Total Vector Lanes: %lld\n", stats.total_lane); +} + +void Logger::printLog() +{ + printf("***************** Printing Vector Unit Execution Log *****************\n"); + printf(" Instruction | Vector Lane Occupancy ('*' for active, '_' for inactive)\n"); + printf("------------- --------------------------------------------------------\n"); + for (int i = 0; i < log.size(); i++) + { + printf("%12s | ", log[i].instruction); + for (int j = 0; j < VECTOR_WIDTH; j++) + { + if (log[i].mask & (((unsigned long long)1) << j)) + { + printf("*"); + } + else + { + printf("_"); + } + } + printf("\n"); + } +} + +void Logger::refresh() +{ + stats.total_instructions = 0; + stats.total_lane = 0; + stats.utilized_lane = 0; + fflush(stdout); +}; + +unsigned long long Logger::getTotalInstrs() +{ + return stats.total_instructions; +} diff --git a/part1/logger.h b/part1/logger.h new file mode 100644 index 0000000..2dc2634 --- /dev/null +++ b/part1/logger.h @@ -0,0 +1,37 @@ +#ifndef LOGGER_H_ +#define LOGGER_H_ + +#include +#include +#include +using namespace std; + +#define MAX_INST_LEN 32 + +struct __pp_mask; + +struct Log { + char instruction[MAX_INST_LEN]; + unsigned long long mask; // support vector width up to 64 +}; + +struct Statistics { + unsigned long long utilized_lane; + unsigned long long total_lane; + unsigned long long total_instructions; +}; + +class Logger { + private: + vector log; + Statistics stats; + + public: + void addLog(const char * instruction, __pp_mask mask, int N = 0); + void printStats(); + void printLog(); + void refresh(); + unsigned long long getTotalInstrs(); +}; + +#endif diff --git a/part1/logger.o b/part1/logger.o new file mode 100644 index 0000000..62a42b9 Binary files /dev/null and b/part1/logger.o differ diff --git a/part1/main.cpp b/part1/main.cpp new file mode 100644 index 0000000..e1c5ac3 --- /dev/null +++ b/part1/main.cpp @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include "logger.h" +#include +#include "def.h" +using namespace std; + +Logger PPLogger; + +void usage(const char *progname); +void initValue(float *values, int *exponents, float *output, float *gold, unsigned int N); +void absSerial(float *values, float *output, int N); +void absVector(float *values, float *output, int N); +void clampedExpSerial(float *values, int *exponents, float *output, int N); +void clampedExpVector(float *values, int *exponents, float *output, int N); +float arraySumSerial(float *values, int N); +float arraySumVector(float *values, int N); +bool verifyResult(float *values, int *exponents, float *output, float *gold, int N); + +int main(int argc, char *argv[]) +{ + int N = 16; + bool printLog = false; + + // parse commandline options //////////////////////////////////////////// + int opt; + static struct option long_options[] = { + {"size", 1, 0, 's'}, + {"log", 0, 0, 'l'}, + {"help", 0, 0, '?'}, + {0, 0, 0, 0}}; + + while ((opt = getopt_long(argc, argv, "s:l?", long_options, NULL)) != EOF) + { + + switch (opt) + { + case 's': + N = atoi(optarg); + if (N <= 0) + { + printf("Error: Workload size is set to %d (<0).\n", N); + return -1; + } + break; + case 'l': + printLog = true; + break; + case '?': + default: + usage(argv[0]); + return 1; + } + } + + float *values = new float[N + VECTOR_WIDTH]; + int *exponents = new int[N + VECTOR_WIDTH]; + float *output = new float[N + VECTOR_WIDTH]; + float *gold = new float[N + VECTOR_WIDTH]; + initValue(values, exponents, output, gold, N); + + clampedExpSerial(values, exponents, gold, N); + clampedExpVector(values, exponents, output, N); + + //absSerial(values, gold, N); + //absVector(values, output, N); + + printf("\e[1;31mCLAMPED EXPONENT\e[0m (required) \n"); + bool clampedCorrect = verifyResult(values, exponents, output, gold, N); + if (printLog) + PPLogger.printLog(); + PPLogger.printStats(); + + printf("************************ Result Verification *************************\n"); + if (!clampedCorrect) + { + printf("@@@ ClampedExp Failed!!!\n"); + } + else if (PPLogger.getTotalInstrs() == 0) + { + printf("Not using fake intrinsics in ClampedExp\n"); + } + else + { + printf("ClampedExp Passed!!!\n"); + } + + PPLogger.refresh(); + + printf("\n\e[1;31mARRAY SUM\e[0m (bonus) \n"); + if (N % VECTOR_WIDTH == 0) + { + float sumGold = arraySumSerial(values, N); + float sumOutput = arraySumVector(values, N); + + if (printLog) + PPLogger.printLog(); + PPLogger.printStats(); + + printf("************************ Result Verification *************************\n"); + + float epsilon = 0.1; + bool sumCorrect = abs(sumGold - sumOutput) < epsilon * 2; + if (!sumCorrect) + { + printf("Expected %f, got %f\n.", sumGold, sumOutput); + printf("@@@ ArraySum Failed!!!\n"); + } + else if (PPLogger.getTotalInstrs() == 0) + { + printf("Not using fake intrinsics in ArraySum\n"); + } + else + { + printf("ArraySum Passed!!!\n"); + } + } + else + { + printf("Must have N %% VECTOR_WIDTH == 0 for this problem (VECTOR_WIDTH is %d)\n", VECTOR_WIDTH); + } + + delete[] values; + delete[] exponents; + delete[] output; + delete[] gold; + + return 0; +} + +void usage(const char *progname) +{ + printf("Usage: %s [options]\n", progname); + printf("Program Options:\n"); + printf(" -s --size Use workload size N (Default = 16)\n"); + printf(" -l --log Print vector unit execution log\n"); + printf(" -? --help This message\n"); +} + +void initValue(float *values, int *exponents, float *output, float *gold, unsigned int N) +{ + + for (unsigned int i = 0; i < N + VECTOR_WIDTH; i++) + { + // random input values + values[i] = -1.f + 4.f * static_cast(rand()) / RAND_MAX; + exponents[i] = rand() % EXP_MAX; + output[i] = 0.f; + gold[i] = 0.f; + } +} + +bool verifyResult(float *values, int *exponents, float *output, float *gold, int N) +{ + int incorrect = -1; + float epsilon = 0.00001; + for (int i = 0; i < N + VECTOR_WIDTH; i++) + { + if (abs(output[i] - gold[i]) > epsilon) + { + incorrect = i; + break; + } + } + + if (incorrect != -1) + { + if (incorrect >= N) + printf("You have written to out of bound value!\n"); + printf("Wrong calculation at value[%d]!\n", incorrect); + printf("value = "); + for (int i = 0; i < N; i++) + { + printf("% f ", values[i]); + } + printf("\n"); + + printf("exp = "); + for (int i = 0; i < N; i++) + { + printf("% 9d ", exponents[i]); + } + printf("\n"); + + printf("output = "); + for (int i = 0; i < N; i++) + { + printf("% f ", output[i]); + } + printf("\n"); + + printf("gold = "); + for (int i = 0; i < N; i++) + { + printf("% f ", gold[i]); + } + printf("\n"); + return false; + } + printf("Results matched with answer!\n"); + return true; +} diff --git a/part1/myexp b/part1/myexp new file mode 100755 index 0000000..ea305ae Binary files /dev/null and b/part1/myexp differ diff --git a/part1/serialOP.cpp b/part1/serialOP.cpp new file mode 100644 index 0000000..363fa8e --- /dev/null +++ b/part1/serialOP.cpp @@ -0,0 +1,61 @@ +// computes the absolute value of all elements in the input array +// values, stores result in output +void absSerial(float *values, float *output, int N) +{ + for (int i = 0; i < N; i++) + { + float x = values[i]; + if (x < 0) + { + output[i] = -x; + } + else + { + output[i] = x; + } + } +} + +// accepts an array of values and an array of exponents +// +// For each element, compute values[i]^exponents[i] and clamp value to +// 9.999. Store result in output. +void clampedExpSerial(float *values, int *exponents, float *output, int N) +{ + for (int i = 0; i < N; i++) + { + float x = values[i]; + int y = exponents[i]; + if (y == 0) + { + output[i] = 1.f; + } + else + { + float result = x; + int count = y - 1; + while (count > 0) + { + result *= x; + count--; + } + if (result > 9.999999f) + { + result = 9.999999f; + } + output[i] = result; + } + } +} + +// returns the sum of all elements in values +float arraySumSerial(float *values, int N) +{ + float sum = 0; + for (int i = 0; i < N; i++) + { + sum += values[i]; + } + + return sum; +} \ No newline at end of file diff --git a/part1/vectorOP.cpp b/part1/vectorOP.cpp new file mode 100644 index 0000000..e3f943a --- /dev/null +++ b/part1/vectorOP.cpp @@ -0,0 +1,72 @@ +#include "PPintrin.h" + +// implementation of absSerial(), but it is vectorized using PP intrinsics +void absVector(float *values, float *output, int N) +{ + __pp_vec_float x; + __pp_vec_float result; + __pp_vec_float zero = _pp_vset_float(0.f); + __pp_mask maskAll, maskIsNegative, maskIsNotNegative; + + // Note: Take a careful look at this loop indexing. This example + // code is not guaranteed to work when (N % VECTOR_WIDTH) != 0. + // Why is that the case? + for (int i = 0; i < N; i += VECTOR_WIDTH) + { + + // All ones + maskAll = _pp_init_ones(); + + // All zeros + maskIsNegative = _pp_init_ones(0); + + // Load vector of values from contiguous memory addresses + _pp_vload_float(x, values + i, maskAll); // x = values[i]; + + // Set mask according to predicate + _pp_vlt_float(maskIsNegative, x, zero, maskAll); // if (x < 0) { + + // Execute instruction using mask ("if" clause) + _pp_vsub_float(result, zero, x, maskIsNegative); // output[i] = -x; + + // Inverse maskIsNegative to generate "else" mask + maskIsNotNegative = _pp_mask_not(maskIsNegative); // } else { + + // Execute instruction ("else" clause) + _pp_vload_float(result, values + i, maskIsNotNegative); // output[i] = x; } + + // Write results back to memory + _pp_vstore_float(output + i, result, maskAll); + } +} + +void clampedExpVector(float *values, int *exponents, float *output, int N) +{ + // + // PP STUDENTS TODO: Implement your vectorized version of + // clampedExpSerial() here. + // + // Your solution should work for any value of + // N and VECTOR_WIDTH, not just when VECTOR_WIDTH divides N + // + for (int i = 0; i < N; i += VECTOR_WIDTH) + { + } +} + +// returns the sum of all elements in values +// You can assume N is a multiple of VECTOR_WIDTH +// You can assume VECTOR_WIDTH is a power of 2 +float arraySumVector(float *values, int N) +{ + + // + // PP STUDENTS TODO: Implement your vectorized version of arraySumSerial here + // + + for (int i = 0; i < N; i += VECTOR_WIDTH) + { + } + + return 0.0; +} \ No newline at end of file diff --git a/part2/Makefile b/part2/Makefile new file mode 100644 index 0000000..7b4a698 --- /dev/null +++ b/part2/Makefile @@ -0,0 +1,53 @@ +TARGET := test_auto_vectorize + +OBJS := main.o test1.o test2.o test3.o + +CC := clang + +CFLAGS := -O3 -std=c11 -Wall -D_POSIX_C_SOURCE=200809L + +ifeq ($(ASSEMBLE),1) + CFLAGS += -S +endif +ifeq ($(VECTORIZE),1) + CFLAGS += -Rpass=loop-vectorize -Rpass-missed=loop-vectorize -Rpass-analysis=loop-vectorize + SUFFIX := .vec +else + CFLAGS += -fno-vectorize + SUFFIX := .novec +endif +ifeq ($(RESTRICT),1) + SUFFIX := $(SUFFIX).restr +endif +ifeq ($(ALIGN),1) + SUFFIX := $(SUFFIX).align +endif +ifeq ($(AVX2),1) + CFLAGS += -mavx2 + SUFFIX := $(SUFFIX).avx2 +endif +ifeq ($(FASTMATH),1) + CFLAGS += -ffast-math + SUFFIX := $(SUFFIX).fmath +endif + +all: $(TARGET) + +%.o: %.c test.h +ifeq ($(ASSEMBLE),1) + mkdir -p "./assembly" + $(CC) $(CFLAGS) -c $< -o assembly/$(basename $<)$(SUFFIX).s +else + $(CC) $(CFLAGS) -c $< +endif + +$(TARGET): $(OBJS) +ifneq ($(ASSEMBLE),1) + $(CC) $(CFLAGS) $(OBJS) -o $@ +endif + +clean: + $(RM) *.o *.s $(TARGET) *~ + +cleanall: + $(RM) -r *.o *.s $(TARGET) *~ assembly diff --git a/part2/fasttime.h b/part2/fasttime.h new file mode 100644 index 0000000..f12c856 --- /dev/null +++ b/part2/fasttime.h @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2014 MIT License by 6.172 Staff + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + **/ + +#ifndef INCLUDED_FASTTIME_DOT_H +#define INCLUDED_FASTTIME_DOT_H + +#define _POSIX_C_SOURCE 200809L + +#include + +#ifdef __MACH__ +#include // mach_absolute_time + +typedef uint64_t fasttime_t; + + +// Return the current time. +static inline fasttime_t gettime(void) { + return mach_absolute_time(); +} + +// Return the time different between the start and the end, as a float +// in units of seconds. This function does not need to be fast. +// Implementation notes: See +// https://developer.apple.com/library/mac/qa/qa1398/_index.html +static inline double tdiff(fasttime_t start, fasttime_t end) { + static mach_timebase_info_data_t timebase; + int r = mach_timebase_info(&timebase); + assert(r == 0); + fasttime_t elapsed = end-start; + double ns = (double)elapsed * timebase.numer / timebase.denom; + return ns*1e-9; +} + +static inline unsigned int random_seed_from_clock(void) { + fasttime_t now = gettime(); + return (now & 0xFFFFFFFF) + (now>>32); +} + +#else // LINUX + +// We need _POSIX_C_SOURCE to pick up 'struct timespec' and clock_gettime. +// #define _POSIX_C_SOURCE 200809L + +#include + +typedef struct timespec fasttime_t; + +// Return the current time. +static inline fasttime_t gettime(void) { + struct timespec s; +#ifdef NDEBUG + clock_gettime(CLOCK_MONOTONIC, &s); +#else + int r = clock_gettime(CLOCK_MONOTONIC, &s); + assert(r == 0); +#endif + return s; +} + +// Return the time different between the start and the end, as a float +// in units of seconds. This function does not need to be fast. +static inline double tdiff(fasttime_t start, fasttime_t end) { + return end.tv_sec - start.tv_sec + 1e-9*(end.tv_nsec - start.tv_nsec); +} + +static inline unsigned int random_seed_from_clock(void) { + fasttime_t now = gettime(); + return now.tv_sec + now.tv_nsec; +} + +// Poison these symbols to help find portability problems. +int clock_gettime(clockid_t, struct timespec *) __attribute__((deprecated)); +time_t time(time_t *) __attribute__((deprecated)); + +#endif // LINUX + +#endif // INCLUDED_FASTTIME_DOT_H diff --git a/part2/main.c b/part2/main.c new file mode 100644 index 0000000..939b186 --- /dev/null +++ b/part2/main.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include "fasttime.h" +#include "test.h" + +void usage(const char *progname); +void initValue(float *values1, float *values2, double *value3, float *output, unsigned int N); + +extern void test1(float *a, float *b, float *c, int N); +extern void test2(float *__restrict a, float *__restrict b, float *__restrict c, int N); +extern double test3(double *__restrict a, int N) ; + +int main(int argc, char **argv) { + int N = 1024; + int whichTestToRun = 1; + + // parse commandline options + int opt; + static struct option long_options[] = { + {"size", 1, 0, 's'}, + {"test", 1, 0, 't'}, + {"help", 0, 0, '?'}, + {0 ,0, 0, 0} + }; + + while ((opt = getopt_long(argc, argv, "st:?", long_options, NULL)) != EOF) { + + switch (opt) { + case 's': + N = atoi(optarg); + if (N <= 0) { + printf("Error: Workload size is set to %d (<0).\n", N); + return -1; + } + break; + case 't': + whichTestToRun = atoi(optarg); + if (whichTestToRun <= 0 || whichTestToRun >= 4) { + printf("Error: test%d() is not available.\n", whichTestToRun); + return -1; + } + break; + case 'h': + default: + usage(argv[0]); + return 1; + } + } + +#define AVX_ALIGNMENT 256 + float *values1 = (float *)__builtin_alloca_with_align(N * sizeof(float), AVX_ALIGNMENT); + float *values2 = (float *)__builtin_alloca_with_align(N * sizeof(float), AVX_ALIGNMENT); + double *values3 = (double *)__builtin_alloca_with_align(N * sizeof(double), AVX_ALIGNMENT); + float *output = (float *)__builtin_alloca_with_align(N * sizeof(float), AVX_ALIGNMENT); +#undef AVX_ALIGNMENT + initValue(values1, values2, values3, output, N); + + printf("Running test%d()...\n", whichTestToRun); + fasttime_t time1 = gettime(); + switch (whichTestToRun) { + case 1: test1(values1, values2, output, N); break; + case 2: test2(values1, values2, output, N); break; + case 3: test3(values3, N); break; + } + fasttime_t time2 = gettime(); + + double elapsedf = tdiff(time1, time2); + printf("Elapsed execution time of the loop in test%d():\n", whichTestToRun); + printf("%lfsec (N: %d, I: %d)\n", elapsedf, N, I); + return 0; +} + +void usage(const char *progname) { + printf("Usage: %s [options]\n", progname); + printf("Program Options:\n"); + printf(" -s --size Use workload size N (Default = 1024)\n"); + printf(" -t --test Just run the testN function (Default = 1)\n"); + printf(" -h --help This message\n"); +} + +void initValue(float *values1, float *values2, double *values3, float *output, unsigned int N) { + for (unsigned int i=0; i a[j]) + c[j] = b[j]; + } + } +} diff --git a/part2/test2.c.patch b/part2/test2.c.patch new file mode 100644 index 0000000..67b5857 --- /dev/null +++ b/part2/test2.c.patch @@ -0,0 +1,13 @@ +--- test2.c ++++ test2.c +@@ -14,9 +14,8 @@ + for (int j = 0; j < N; j++) + { + /* max() */ +- c[j] = a[j]; +- if (b[j] > a[j]) +- c[j] = b[j]; ++ if (b[j] > a[j]) c[j] = b[j]; ++ else c[j] = a[j]; + } + } diff --git a/part2/test3.c b/part2/test3.c new file mode 100644 index 0000000..05a3468 --- /dev/null +++ b/part2/test3.c @@ -0,0 +1,14 @@ +#include "test.h" + +double test3(double *__restrict a, int N) { + __builtin_assume(N == 1024); + a = (double *)__builtin_assume_aligned(a, 16); + + double b = 0; + for (int i=0; i