From 2dcd0ddfdc72e84e91b6a0686bc589ccfdded32e Mon Sep 17 00:00:00 2001 From: Blaise Tine Date: Thu, 21 Nov 2019 00:41:17 -0500 Subject: [PATCH] opencl sgemm benchmark --- benchmarks/opencl/hotspot/README | 0 benchmarks/opencl/kmeans/README | 0 benchmarks/opencl/sgemm/Makefile | 54 +++ benchmarks/opencl/sgemm/kernel.cl | 9 + benchmarks/opencl/sgemm/kernel.pocl | Bin 0 -> 33824 bytes benchmarks/opencl/sgemm/libsgemm.a | Bin 0 -> 4392 bytes benchmarks/opencl/sgemm/main.cc | 500 ++++++++++++++++++++++++++++ runtime/startup/vx_start.s | 8 +- simX/Makefile | 4 +- 9 files changed, 570 insertions(+), 5 deletions(-) create mode 100644 benchmarks/opencl/hotspot/README create mode 100644 benchmarks/opencl/kmeans/README create mode 100644 benchmarks/opencl/sgemm/Makefile create mode 100644 benchmarks/opencl/sgemm/kernel.cl create mode 100644 benchmarks/opencl/sgemm/kernel.pocl create mode 100644 benchmarks/opencl/sgemm/libsgemm.a create mode 100644 benchmarks/opencl/sgemm/main.cc diff --git a/benchmarks/opencl/hotspot/README b/benchmarks/opencl/hotspot/README new file mode 100644 index 00000000..e69de29b diff --git a/benchmarks/opencl/kmeans/README b/benchmarks/opencl/kmeans/README new file mode 100644 index 00000000..e69de29b diff --git a/benchmarks/opencl/sgemm/Makefile b/benchmarks/opencl/sgemm/Makefile new file mode 100644 index 00000000..7fa16540 --- /dev/null +++ b/benchmarks/opencl/sgemm/Makefile @@ -0,0 +1,54 @@ + +RISCV_TOOL_PATH=$(wildcard ~/dev/riscv-gnu-toolchain/drops) + +POCL_CC_PATH=$(wildcard ~/dev/pocl/drops_riscv_cc) +POCL_RT_PATH=$(wildcard ~/dev/pocl/drops_riscv_rt) + +VX_RT_PATH=$(wildcard ../../../runtime) +VX_SIMX_PATH=$(wildcard ../../../simX/obj_dir) + +CC=$(RISCV_TOOL_PATH)/bin/riscv32-unknown-elf-gcc +CXX=$(RISCV_TOOL_PATH)/bin/riscv32-unknown-elf-g++ +DMP=$(RISCV_TOOL_PATH)/bin/riscv32-unknown-elf-objdump +HEX=$(RISCV_TOOL_PATH)/bin/riscv32-unknown-elf-objcopy +NEWLIB_PATH=$(RISCV_TOOL_PATH)/riscv32-unknown-elf/lib + +VX_NEWLIB = $(VX_RT_PATH)/newlib/newlib.c +VX_STR = $(VX_RT_PATH)/startup/vx_start.s +VX_INT = $(VX_RT_PATH)/intrinsics/vx_intrinsics.s +VX_IO = $(VX_RT_PATH)/io/vx_io.s $(VX_RT_PATH)/io/vx_io.c +VX_FIO = $(VX_RT_PATH)/fileio/fileio.s +VX_API = $(VX_RT_PATH)/vx_api/vx_api.c + +VX_SRCS = $(VX_STR) $(VX_FIO) $(NEWLIB) $(VX_INT) $(VX_IO) $(VX_API) $(VX_TEST) + +CXXFLAGS = -g -O0 -nostartfiles -Wl,-Bstatic,-T,$(VX_RT_PATH)/mains/nativevecadd/linker.ld -march=rv32im -mabi=ilp32 +CXXFLAGS += -ffreestanding # program may not begin at main() +CXXFLAGS += -fno-rtti -fno-non-call-exceptions # disable RTTI and exceptions +CXXFLAGS += -Wl,--gc-sections # enable garbage collection of unused input sections + +LIBS = -lOpenCL + +#$(NEWLIB_PATH)/libc.a $(NEWLIB_PATH)/libstdc++.a -static-libgcc -lgcc + +PROJECT=sgemm + +all: $(PROJECT).dump $(PROJECT).hex + +libsgemm.a: kernel.cl + POCL_DEBUG=all POCL_DEBUG_LLVM_PASSES=1 LD_LIBRARY_PATH=$(RISCV_TOOL_PATH)/lib:$(POCL_CC_PATH)/lib $(POCL_CC_PATH)/bin/poclcc -o kernel.pocl kernel.cl + +$(PROJECT).elf: main.cc libsgemm.a + $(CXX) $(CXXFLAGS) -I$(POCL_RT_PATH)/include -L$(POCL_RT_PATH)/lib/static -L. $(VX_SRCS) main.cc -Wl,--whole-archive -lsgemm -Wl,--no-whole-archive $(LIBS) -o $(PROJECT).elf + +$(PROJECT).hex: $(PROJECT).elf + $(HEX) -O ihex $(PROJECT).elf $(PROJECT).hex + +$(PROJECT).dump: $(PROJECT).elf + $(DMP) -D $(PROJECT).elf > $(PROJECT).dump + +run: + $(VX_SIMX_PATH)/Vcache_simX -E -a rv32i --core $(PROJECT).hex -s -b 1> emulator.debug + +clean: + rm -rf *.elf *.dump *.hex *.a *.pocl diff --git a/benchmarks/opencl/sgemm/kernel.cl b/benchmarks/opencl/sgemm/kernel.cl new file mode 100644 index 00000000..17ece1d1 --- /dev/null +++ b/benchmarks/opencl/sgemm/kernel.cl @@ -0,0 +1,9 @@ +__kernel void sgemm(__global float *A, __global float *B, __global float *C, int ldc) +{ + long i = get_global_id(0); + long m = get_global_id(1); + long n = get_global_id(2); + float a = A[m+n*ldc]; + float b = B[m*ldc+i]; + C[i+n*ldc] = C[i+n*ldc] + a * b; +} diff --git a/benchmarks/opencl/sgemm/kernel.pocl b/benchmarks/opencl/sgemm/kernel.pocl new file mode 100644 index 0000000000000000000000000000000000000000..6a643faeced315489bc199fff8f7be88e637c81c GIT binary patch literal 33824 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * gcc -o cldemo -std=gnu99 -Wall -I/usr/include/nvidia-current cldemo.c -lOpenCL + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_DATA 64 + +#define CL_CHECK(_expr) \ + do { \ + cl_int _err = _expr; \ + if (_err == CL_SUCCESS) \ + break; \ + fprintf(stderr, "OpenCL Error: '%s' returned %d!\n", #_expr, (int)_err); \ + abort(); \ + } while (0) + +#define CL_CHECK_ERR(_expr) \ + ({ \ + cl_int _err = CL_INVALID_VALUE; \ + typeof(_expr) _ret = _expr; \ + if (_err != CL_SUCCESS) { \ + fprintf(stderr, "OpenCL Error: '%s' returned %d!\n", #_expr, (int)_err); \ + abort(); \ + } \ + _ret; \ + }) + +void pfn_notify(const char *errinfo, const void *private_info, size_t cb, void *user_data) +{ + fprintf(stderr, "OpenCL Error (via pfn_notify): %s\n", errinfo); +} + +/// +// Create an OpenCL program from the kernel source file +// +cl_program CreateProgram(cl_context context, cl_device_id device, const char* fileName) +{ + cl_int errNum; + cl_program program; + + std::ifstream kernelFile(fileName, std::ios::in); + if (!kernelFile.is_open()) + { + std::cerr << "Failed to open file for reading: " << fileName << std::endl; + return NULL; + } + + std::ostringstream oss; + oss << kernelFile.rdbuf(); + + std::string srcStdStr = oss.str(); + const char *srcStr = srcStdStr.c_str(); + program = clCreateProgramWithSource(context, 1, + (const char**)&srcStr, + NULL, NULL); + if (program == NULL) + { + std::cerr << "Failed to create CL program from source." << std::endl; + return NULL; + } + + errNum = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); + if (errNum != CL_SUCCESS) + { + // Determine the reason for the error + char buildLog[16384]; + clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, + sizeof(buildLog), buildLog, NULL); + + std::cerr << "Error in kernel: " << std::endl; + std::cerr << buildLog; + clReleaseProgram(program); + return NULL; + } + + return program; +} + +// +/// +// Retreive program binary for all of the devices attached to the +// program an and store the one for the device passed in +// +bool SaveProgramBinary(cl_program program, cl_device_id device, const char* fileName) +{ + //cl_uint numDevices = malloc(sizeof(cl_uint)); + //cl_uint* numDevices = malloc(sizeof(cl_uint)); + cl_int errNum; + + printf("try getting program info\n"); + // 1 - Query for number of devices attached to program + /*errNum = clGetProgramInfo(program, CL_PROGRAM_NUM_DEVICES, sizeof(cl_uint), + &numDevices, NULL); + printf("Got program_num_devices\n"); + if (errNum != CL_SUCCESS) + { + std::cerr << "Error querying for number of devices." << std::endl; + return false; + }*/ + + // 2 - Get all of the Device IDs + cl_device_id *devices = new cl_device_id[1]; + errNum = clGetProgramInfo(program, CL_PROGRAM_DEVICES, + sizeof(cl_device_id) * 1, + devices, NULL); + printf("Got program_devices\n"); + if (errNum != CL_SUCCESS) + { + std::cerr << "Error querying for devices." << std::endl; + delete [] devices; + return false; + } + + // 3 - Determine the size of each program binary + size_t *programBinarySizes = new size_t [1]; + errNum = clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, + sizeof(size_t) * 1, + programBinarySizes, NULL); + printf("Got program_binary_sizes\n"); + if (errNum != CL_SUCCESS) + { + std::cerr << "Error querying for program binary sizes." << std::endl; + delete [] devices; + delete [] programBinarySizes; + return false; + } + + unsigned char **programBinaries = new unsigned char*[1]; + for (cl_uint i = 0; i < 1; i++) + { + programBinaries[i] = new unsigned char[programBinarySizes[i]]; + } + + // 4 - Get all of the program binaries + errNum = clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(unsigned char*) * 1, + programBinaries, NULL); + printf("Got program_binarys\n"); + if (errNum != CL_SUCCESS) + { + std::cerr << "Error querying for program binaries" << std::endl; + + delete [] devices; + delete [] programBinarySizes; + for (cl_uint i = 0; i < 1; i++) + { + delete [] programBinaries[i]; + } + delete [] programBinaries; + return false; + } + + // 5 - Finally store the binaries for the device requested out to disk for future reading. + for (cl_uint i = 0; i < 1; i++) + { + // Store the binary just for the device requested. In a scenario where + // multiple devices were being used you would save all of the binaries out here. + if (devices[i] == device) + { + FILE *fp = fopen(fileName, "wb"); + if(fp ==NULL){ + delete [] devices; + delete [] programBinarySizes; + for (cl_uint i = 0; i < 1; i++) + { + delete [] programBinaries[i]; + } + delete [] programBinaries; + return false; + } + printf("Opened file\n"); + fwrite(programBinaries[i], 1, programBinarySizes[i], fp); + printf("wrote file\n"); + fclose(fp); + printf("close file\n"); + break; + } + } + + // Cleanup + delete [] devices; + delete [] programBinarySizes; + for (cl_uint i = 0; i < 1; i++) + { + delete [] programBinaries[i]; + } + delete [] programBinaries; + return true; +} + +/// +// Attempt to create the program object from a cached binary. Note that +// on first run this will fail because the binary has not yet been created. +// +cl_program CreateProgramFromBinary(cl_context context, cl_device_id device, const char* fileName) +{ + FILE *fp = fopen(fileName, "rb"); + if (fp == NULL) + { + return NULL; + } + + // Determine the size of the binary + size_t binarySize; + fseek(fp, 0, SEEK_END); + binarySize = ftell(fp); + rewind(fp); + + unsigned char *programBinary = new unsigned char[binarySize]; + fread(programBinary, 1, binarySize, fp); + fclose(fp); + + cl_int errNum = 0; + cl_program program; + cl_int binaryStatus; + + program = clCreateProgramWithBinary(context, + 1, + &device, + &binarySize, + (const unsigned char**)&programBinary, + &binaryStatus, + &errNum); + delete [] programBinary; + if (errNum != CL_SUCCESS) + { + std::cerr << "Error loading program binary." << std::endl; + return NULL; + } + + if (binaryStatus != CL_SUCCESS) + { + std::cerr << "Invalid binary for device" << std::endl; + return NULL; + } + + errNum = clBuildProgram(program, 1, &device, NULL, NULL, NULL); + if (errNum != CL_SUCCESS) + { + printf("build errNum:%d\n", errNum); + // Determine the reason for the error + char buildLog[16384]; + clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, + sizeof(buildLog), buildLog, NULL); + + std::cerr << "Error in program: " << std::endl; + std::cerr << buildLog << std::endl; + clReleaseProgram(program); + return NULL; + } + + return program; +} + +/// +// Cleanup any created OpenCL resources +// +void Cleanup(cl_context context, cl_command_queue commandQueue, + cl_program program, cl_kernel kernel, cl_mem memObjects[3]) +{ + for (int i = 0; i < 3; i++) + { + if (memObjects[i] != 0) + clReleaseMemObject(memObjects[i]); + } + if (commandQueue != 0) + clReleaseCommandQueue(commandQueue); + + if (kernel != 0) + clReleaseKernel(kernel); + + if (program != 0) + clReleaseProgram(program); + + if (context != 0) + clReleaseContext(context); + +} + +int main(int argc, char **argv) +{ + printf("enter demo main\n"); + fflush(stdout); + putenv("POCL_VERBOSE=1"); + putenv("POCL_DEVICES=basic"); + putenv("POCL_LEAVE_TEMP_DIRS=1"); + putenv("POCL_LEAVE_KERNEL_COMPILER_TEMP_FILES=1"); + putenv("POCL_TEMP_DIR=pocl"); + putenv("POCL_CACHE_DIR=pocl"); + putenv("POCL_WORK_GROUP_METHOD=spmd"); + if(argc >= 2){ + printf("argv[1]:%s:\n",argv[1]); + if(!strcmp(argv[1], "h")) + putenv("POCL_WORK_GROUP_METHOD=spmd"); + if(!strcmp(argv[1], "c")) + putenv("POCL_CROSS_COMPILE=1"); + } + if(argc >= 3){ + printf("argv[2]:%s:\n",argv[2]); + if(!strcmp(argv[2], "h")) + putenv("POCL_WORK_GROUP_METHOD=spmd"); + if(!strcmp(argv[2], "c")) + putenv("POCL_CROSS_COMPILE=1"); + } + + //putenv("LD_LIBRARY_PATH=/scratch/colins/build/linux/fs/lib"); + //putenv("LTDL_LIBRARY_PATH=/scratch/colins/build/linux/fs/lib"); + //lt_dlsetsearchpath("/scratch/colins/build/linux/fs/lib"); + //printf("SEARCH_PATH:%s\n",lt_dlgetsearchpath()); + cl_platform_id platforms[100]; + cl_uint platforms_n = 0; + CL_CHECK(clGetPlatformIDs(100, platforms, &platforms_n)); + + printf("=== %d OpenCL platform(s) found: ===\n", platforms_n); + for (int i=0; i