diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..c542ded --- /dev/null +++ b/src/Makefile @@ -0,0 +1,19 @@ +CXX = g++ +CXXFLAGS = -Wall -O2 -std=c++11 +LIBS = -lOpenCL +TARGET = main +SRC = main.cpp + +INCLUDES = -I"A:/Programs/OpenCL/include" +LIB_PATH = -L"A:/Programs/OpenCL/lib" + +$(TARGET): $(SRC) + $(CXX) $(CXXFLAGS) $(INCLUDES) $(LIB_PATH) -o $(TARGET) $(SRC) $(LIBS) + +clean: + rm -f $(TARGET) + +run: $(TARGET) + ./$(TARGET) + +.PHONY: clean run \ No newline at end of file diff --git a/src/device.hpp b/src/device.hpp new file mode 100644 index 0000000..cea2fe8 --- /dev/null +++ b/src/device.hpp @@ -0,0 +1,126 @@ +#ifndef DEVICE_H +#define DEVICE_H + +#include + +#include "opencl.hpp" + +class CalcEngine { +private: + cl_platform_id platform; + cl_device_id device; + cl_context context; + std::string device_name; + + void initializeOpenCL() { + OpenCL::checkError(clGetPlatformIDs(1, &platform, nullptr), + "clGetPlatformIDs"); + OpenCL::checkError( + clGetDeviceIDs(platform, CL_DEVICE_TYPE_DEFAULT, 1, &device, nullptr), + "clGetDeviceIDs"); + + char name[128]; + clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(name), name, nullptr); + device_name = name; + + context = clCreateContext(nullptr, 1, &device, nullptr, nullptr, nullptr); + if (!context) { + throw OpenCLException(-1, "clCreateContext"); + } + + std::cout << "OpenCL initialized successfully" << std::endl; + } + + void cleanup() { + if (context) + clReleaseContext(context); + } + +public: + CalcEngine() { initializeOpenCL(); } + + ~CalcEngine() { cleanup(); } + + const cl_platform_id getPlatform() const { return platform; }; + const cl_device_id getDevice() const { return device; }; + const cl_context getContext() const { return context; }; + const std::string getDeviceName() const { return device_name; }; + + void printDeviceInfo() const { + std::cout << "Using OpenCL device: " << device_name << std::endl; + } + + cl_mem createBuffer(cl_mem_flags flags, size_t size, void *host_ptr) { + cl_int ret; + cl_mem buffer = clCreateBuffer(context, flags, size, host_ptr, &ret); + OpenCL::checkError(ret, "clCreateBuffer"); + return buffer; + } + + cl_kernel loadKernel(const std::string &filename) { + std::string kernelSource = OpenCL::readFile(filename); + + const char *source_str = kernelSource.c_str(); + cl_program program = + clCreateProgramWithSource(context, 1, &source_str, nullptr, nullptr); + if (!program) { + throw OpenCLException(-1, "clCreateProgramWithSource"); + } + + cl_int ret = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr); + if (ret != CL_SUCCESS) { + size_t log_size; + clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, nullptr, + &log_size); + std::vector log(log_size); + clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, + log.data(), nullptr); + + std::cerr << "Build log:\n" << log.data() << std::endl; + throw OpenCLException(ret, "clBuildProgram"); + } + + cl_kernel kernel = clCreateKernel(program, "matrix_mult", nullptr); + if (!kernel) { + throw OpenCLException(-1, "clCreateKernel"); + } + + std::cout << "Kernel loaded and compiled successfully" << std::endl; + + return kernel; + } + + void runKernel(cl_command_queue queue, cl_kernel kernel, int M, int N) { + size_t globalSize[2] = {static_cast(M), static_cast(N)}; + OpenCL::checkError(clEnqueueNDRangeKernel(queue, kernel, 2, nullptr, + globalSize, nullptr, 0, nullptr, + nullptr), + "clEnqueueNDRangeKernel"); + } + + void readResult(cl_command_queue queue, cl_mem buf, + std::vector &result) { + OpenCL::checkError(clEnqueueReadBuffer(queue, buf, CL_TRUE, 0, + result.size() * sizeof(float), + result.data(), 0, nullptr, nullptr), + "clEnqueueReadBuffer"); + } + + void setKernelArgs(cl_kernel kernel, cl_mem bufA, cl_mem bufB, cl_mem bufC, + int M, int N, int K) { + OpenCL::checkError(clSetKernelArg(kernel, 0, sizeof(cl_mem), &bufA), + "clSetKernelArg for A"); + OpenCL::checkError(clSetKernelArg(kernel, 1, sizeof(cl_mem), &bufB), + "clSetKernelArg for B"); + OpenCL::checkError(clSetKernelArg(kernel, 2, sizeof(cl_mem), &bufC), + "clSetKernelArg for C"); + OpenCL::checkError(clSetKernelArg(kernel, 3, sizeof(int), &M), + "clSetKernelArg for M"); + OpenCL::checkError(clSetKernelArg(kernel, 4, sizeof(int), &N), + "clSetKernelArg for N"); + OpenCL::checkError(clSetKernelArg(kernel, 5, sizeof(int), &K), + "clSetKernelArg for K"); + } +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..fe21587 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device.hpp" +#include "matrix.hpp" + +class MatrixCalculator { +private: + CalcEngine *calcEngine; + cl_command_queue queue; + cl_kernel kernel; + +public: + MatrixCalculator(CalcEngine &calcEngine) { + this->calcEngine = &calcEngine; + kernel = calcEngine.loadKernel("matrix_mult.cl"); + queue = clCreateCommandQueue(calcEngine.getContext(), + calcEngine.getDevice(), 0, nullptr); + if (!queue) { + throw OpenCLException(-1, "clCreateCommandQueue"); + } + } + + ~MatrixCalculator() { + if (queue) + clReleaseCommandQueue(queue); + } + + std::vector multiply(Matrix &a, Matrix &b, int M, int N, int K) { + if (a.getRows() != M || a.getCols() != K || b.getRows() != K || + b.getCols() != N) { + throw std::invalid_argument("Invalid matrix dimensions"); + } + + cl_mem bufC = calcEngine->createBuffer(CL_MEM_WRITE_ONLY, + M * N * sizeof(float), nullptr); + + calcEngine->setKernelArgs(kernel, a.getBuf(), b.getBuf(), bufC, M, N, K); + + calcEngine->runKernel(queue, kernel, M, N); + + std::vector C(M * N); + calcEngine->readResult(queue, bufC, C); + + clReleaseMemObject(bufC); + + return C; + } +}; + +int main() { + CalcEngine calcEngine; + calcEngine.printDeviceInfo(); + + MatrixCalculator matrixCalculator(calcEngine); + + float matrixA[2 * 3] = {1, 2, 3, 4, 5, 6}; + Matrix a(calcEngine, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, 2, 3, matrixA); + + float matrixB[3 * 2] = {1, 2, 3, 4, 5, 6}; + Matrix b(calcEngine, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, 3, 2, matrixB); + + std::vector v = matrixCalculator.multiply(a, b, 2, 2, 3); + for (const auto &element : v) { + std::cout << element << " "; + } + + return 0; +} \ No newline at end of file diff --git a/src/main.exe b/src/main.exe new file mode 100644 index 0000000..39c99aa Binary files /dev/null and b/src/main.exe differ diff --git a/src/matrix.hpp b/src/matrix.hpp new file mode 100644 index 0000000..3e39e44 --- /dev/null +++ b/src/matrix.hpp @@ -0,0 +1,34 @@ +#ifndef MATRIX_H +#define MATRIX_H + +#include +#include +#include + +#include "opencl.hpp" + +class Matrix { +private: + cl_mem buf; + size_t rows; + size_t cols; + +public: + Matrix(CalcEngine &calcEngine, cl_mem_flags flags, size_t rows, size_t cols, + float *matrix) + : rows(rows), cols(cols) { + if (rows == 0 || cols == 0) { + throw std::invalid_argument("Размеры матрицы должны быть больше 0"); + } + buf = calcEngine.createBuffer(flags, rows * cols * sizeof(float), matrix); + } + + ~Matrix() { clReleaseMemObject(buf); } + + size_t getRows() const { return rows; } + size_t getCols() const { return cols; } + + const cl_mem getBuf() const { return buf; } +}; + +#endif \ No newline at end of file diff --git a/src/matrix_mult.cl b/src/matrix_mult.cl new file mode 100644 index 0000000..5cee616 --- /dev/null +++ b/src/matrix_mult.cl @@ -0,0 +1,9 @@ +__kernel void matrix_mult(__global float* A, __global float* B, __global float* C, int M, int N, int K) { + int i = get_global_id(0); + int j = get_global_id(1); + float sum = 0.0f; + for (int k = 0; k < K; k++) { + sum += A[i * K + k] * B[k * N + j]; + } + C[i * N + j] = sum; +} \ No newline at end of file diff --git a/src/opencl.hpp b/src/opencl.hpp new file mode 100644 index 0000000..78c9861 --- /dev/null +++ b/src/opencl.hpp @@ -0,0 +1,38 @@ +#ifndef OPENCL_H +#define OPENCL_H + +#include + +class OpenCLException : public std::runtime_error { +private: + cl_int error_code; + +public: + OpenCLException(cl_int error, const std::string &operation) + : std::runtime_error("Error during " + operation + ": " + + std::to_string(error)), + error_code(error) {} + + cl_int getErrorCode() const { return error_code; } +}; + +class OpenCL { +public: + static void checkError(cl_int error, const std::string &operation) { + if (error != CL_SUCCESS) { + throw OpenCLException(error, operation); + } + } + + static std::string readFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) { + throw std::runtime_error("Failed to open kernel file: " + filename); + } + + return std::string((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + } +}; + +#endif \ No newline at end of file