mirror of
https://github.com/StepanovPlaton/NeuralNetwork.git
synced 2026-04-03 12:20:39 +04:00
Add matrix multiplication
This commit is contained in:
19
src/Makefile
Normal file
19
src/Makefile
Normal 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
126
src/device.hpp
Normal 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
76
src/main.cpp
Normal 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
BIN
src/main.exe
Normal file
Binary file not shown.
34
src/matrix.hpp
Normal file
34
src/matrix.hpp
Normal 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
9
src/matrix_mult.cl
Normal 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
38
src/opencl.hpp
Normal 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
|
||||
Reference in New Issue
Block a user