Add matrix multiplication

This commit is contained in:
2025-10-26 16:25:39 +04:00
parent b2cecf0ffc
commit 475cbe7d0b
7 changed files with 302 additions and 0 deletions

19
src/Makefile Normal file
View File

@@ -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

126
src/device.hpp Normal file
View File

@@ -0,0 +1,126 @@
#ifndef DEVICE_H
#define DEVICE_H
#include <CL/cl.h>
#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<char> 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<size_t>(M), static_cast<size_t>(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<float> &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

76
src/main.cpp Normal file
View File

@@ -0,0 +1,76 @@
#include <CL/cl.h>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#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<float> 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<float> 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<float> v = matrixCalculator.multiply(a, b, 2, 2, 3);
for (const auto &element : v) {
std::cout << element << " ";
}
return 0;
}

BIN
src/main.exe Normal file

Binary file not shown.

34
src/matrix.hpp Normal file
View File

@@ -0,0 +1,34 @@
#ifndef MATRIX_H
#define MATRIX_H
#include <iostream>
#include <stdexcept>
#include <vector>
#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

9
src/matrix_mult.cl Normal file
View File

@@ -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;
}

38
src/opencl.hpp Normal file
View File

@@ -0,0 +1,38 @@
#ifndef OPENCL_H
#define OPENCL_H
#include <CL/cl.h>
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<char>(file)),
std::istreambuf_iterator<char>());
}
};
#endif