You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化GPU内存封装实现类RAII特性?解决GpuMatrix现存缺陷

改进GPU内存封装的RAII实现及核函数传参优化

原问题分析

原GpuMatrix类存在两个核心问题:

  • 缺乏RAII特性:GPU内存需手动调用MallocGpuMemory和DestroyGpuMemory,析构函数不会自动释放内存,极易引发内存泄漏。
  • 核函数传参冗余:CUDA设备端无法直接访问主机端类对象的非POD成员,只能通过值传递整个对象,设计不够简洁高效。

改进方案

1. 实现RAII自动内存管理

  • 将GPU内存分配逻辑移入构造函数,析构函数自动执行内存释放
  • 禁用默认拷贝构造与赋值运算符,避免浅拷贝导致的重复释放问题
  • 实现移动构造与移动赋值,支持对象内存所有权的高效转移

2. 分离主机端管理类与设备端视图

  • 定义轻量的GpuMatrixDeviceView结构体,仅保留核函数所需的设备端数据(指针、尺寸)
  • 主机端GpuMatrix类提供GetDeviceView()方法,生成设备视图供核函数调用

改进后的代码实现

主机端RAII封装类

#include <cassert>
#include <utility>

template <typename T, size_t N, size_t M>
class GpuMatrix {
public:
    using DataType = T;
    static constexpr size_t HeightDim = N;
    static constexpr size_t WidthDim = M;
    static constexpr size_t ByteSize = HeightDim * WidthDim * sizeof(DataType);

    // 默认构造:自动分配GPU内存
    GpuMatrix() : _data(nullptr) {
        CUDA_CHECK(cudaMalloc((void**)&_data, ByteSize));
    }

    // 从CPU数据初始化构造
    explicit GpuMatrix(const DataType* cpu_data) : GpuMatrix() {
        CopyFromCpuData(cpu_data);
    }

    // 禁用拷贝构造与赋值,避免浅拷贝风险
    GpuMatrix(const GpuMatrix&) = delete;
    GpuMatrix& operator=(const GpuMatrix&) = delete;

    // 移动构造:转移内存所有权
    GpuMatrix(GpuMatrix&& other) noexcept : _data(other._data) {
        other._data = nullptr;
    }

    // 移动赋值:转移内存所有权
    GpuMatrix& operator=(GpuMatrix&& other) noexcept {
        if (this != &other) {
            if (_data != nullptr) {
                CUDA_CHECK(cudaFree(_data));
            }
            _data = other._data;
            other._data = nullptr;
        }
        return *this;
    }

    // 析构函数:自动释放GPU内存
    ~GpuMatrix() {
        if (_data != nullptr) {
            cudaFree(_data);
            _data = nullptr;
        }
    }

    // 从CPU拷贝数据到GPU
    void CopyFromCpuData(const DataType* cpu_data) {
        assert(cpu_data != nullptr);
        CUDA_CHECK(cudaMemcpy(_data, cpu_data, ByteSize, cudaMemcpyHostToDevice));
    }

    // 从GPU拷贝数据到CPU
    void CopyToCpuData(DataType* cpu_data) const {
        assert(cpu_data != nullptr);
        CUDA_CHECK(cudaMemcpy(cpu_data, _data, ByteSize, cudaMemcpyDeviceToHost));
    }

    // 设备端视图结构体:仅包含核函数所需信息
    struct GpuMatrixDeviceView {
        DataType* data;
        size_t height;
        size_t width;
    };

    // 获取设备视图,供核函数调用
    GpuMatrixDeviceView GetDeviceView() const {
        return {_data, HeightDim, WidthDim};
    }

    // 设备端安全访问函数(增加边界检查)
    __device__ DataType& at(size_t y, size_t x) {
        assert(y < HeightDim && x < WidthDim);
        return _data[y * WidthDim + x];
    }

    __device__ const DataType& at(size_t y, size_t x) const {
        assert(y < HeightDim && x < WidthDim);
        return _data[y * WidthDim + x];
    }

private:
    DataType* _data;
};

核函数使用示例

template <typename T, size_t N, size_t M, size_t Z>
__global__ void _multiplication_matrix(
    const typename GpuMatrix<T, N, M>::GpuMatrixDeviceView x,
    const typename GpuMatrix<T, M, Z>::GpuMatrixDeviceView y,
    typename GpuMatrix<T, N, Z>::GpuMatrixDeviceView z) {
    
    int tid_y = blockIdx.y * blockDim.y + threadIdx.y;
    int tid_x = blockIdx.x * blockDim.x + threadIdx.x;

    if (tid_y < z.height && tid_x < z.width) {
        T sum = 0;
        for (size_t k = 0; k < x.width; ++k) {
            sum += x.data[tid_y * x.width + k] * y.data[k * y.width + tid_x];
        }
        z.data[tid_y * z.width + tid_x] = sum;
    }
}

// 主机端调用示例
int main() {
    using MatrixType = float;
    constexpr size_t N = 1024, M = 512, Z = 256;

    // 初始化CPU数据
    MatrixType* cpu_x = new MatrixType[N*M];
    MatrixType* cpu_y = new MatrixType[M*Z];
    // ... 填充数据 ...

    // 自动分配GPU内存并拷贝数据
    GpuMatrix<MatrixType, N, M> gpu_x(cpu_x);
    GpuMatrix<MatrixType, M, Z> gpu_y(cpu_y);
    GpuMatrix<MatrixType, N, Z> gpu_z;

    // 配置核函数网格与块尺寸
    dim3 block(32, 32);
    dim3 grid((Z + block.x - 1)/block.x, (N + block.y - 1)/block.y);

    // 传递设备视图到核函数
    _multiplication_matrix<MatrixType, N, M, Z><<<grid, block>>>(
        gpu_x.GetDeviceView(),
        gpu_y.GetDeviceView(),
        gpu_z.GetDeviceView()
    );
    CUDA_CHECK(cudaDeviceSynchronize());

    // 拷贝结果回CPU
    MatrixType* cpu_z = new MatrixType[N*Z];
    gpu_z.CopyToCpuData(cpu_z);

    // 自动释放GPU内存(析构函数处理)
    delete[] cpu_x;
    delete[] cpu_y;
    delete[] cpu_z;
    return 0;
}

关键改进说明

  • RAII自动管理:构造函数分配内存,析构函数自动释放,无需手动调用内存管理接口,彻底规避内存泄漏风险。
  • 移动语义支持:通过移动构造和赋值,实现对象内存所有权的高效转移,避免不必要的内存分配与拷贝。
  • 设备视图分离:核函数仅传递轻量视图结构体,既符合CUDA设备端访问规则,又减少参数传递开销。
  • 安全性提升:禁用拷贝操作避免重复释放,at函数增加边界检查解决原代码的不安全问题。

内容的提问来源于stack exchange,提问作者peng zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 19:15:57