优化CUDA内存管理:基于NVIDIA Jetson的内存池封装

概述

本文将介绍如何设计和实现一个高效的CUDA内存池,以解决在多线程环境中频繁执行cudaMallocHost和cudaFreeHost操作导致的性能问题。
在使用NVIDIA Jetson进行多线程编程时,经常会遇到CUDA内存申请失败的情况。这主要是由于频繁执行cudaMallocHost和cudaFreeHost操作导致的内存碎片化和性能下降。特别是在项目中,我们的业务逻辑要求经常申请和释放固定大小的CUDA内存块,这使得优化内存管理尤为重要。

解决方案

为了优化CUDA内存管理,我们设计了一个内存池,通过unordered_map来管理不同大小的CUDA内存块链表。该内存池不仅支持自动扩容和自动缩容功能,还能有效地管理和分配CUDA内存,避免了频繁申请和释放带来的性能损失。

设计实现

内存池类设计
#pragma once
#include "cuda_runtime_api.h"
#include <iostream>
#include <list>
#include <stdint.h>
#include <mutex>
#include <unordered_map>

#define DEAULT_ELEMENT_SIZE 4 // 默认创建一次元素个数
#define MAX_ELEMENT_SIZE 32   // free时保留最多的元素个数

class CudaMemoryPool
{
public:
    // 获取内存池单例
    static CudaMemoryPool &instance()
    {
        static CudaMemoryPool ins;
        return ins;
    }

    // 分配节点内存,不存在则创建
    bool alloc(uint32_t elementSize, uint8_t *&element)
    {
        std::unique_lock<std::mutex> locker(m_mtx);
        if (m_elementListMap.count(elementSize) == 0)
        {
            std::list<uint8_t *> elementList;
            uint32_t ret = create(elementList, elementSize, DEAULT_ELEMENT_SIZE);

            while (ret == 0)
            {
                std::cerr << "element create " << ret << " success, elementSize: " << elementSize << std::endl;
                ret = create(elementList, elementSize, DEAULT_ELEMENT_SIZE);
            }

            m_elementListMap.insert({elementSize, elementList});
        }

        auto &elementList = m_elementListMap[elementSize];
        if (elementList.empty()) // 如果链表为空,重新创建
        {
            uint32_t ret = create(elementList, elementSize, DEAULT_ELEMENT_SIZE);
            if (ret == 0) // 全部创建失败
            {
                std::cerr << "create  " << elementSize << " elements all failed!" << std::endl;
                return false;
        }
        element = elementList.front();
        elementList.pop_front();
        if (element == 0)
        {
            std::cerr << "pop element is null!" << std::endl;
            return false;
        }
        return true;
    }

    // 回收内存,链表中元素个数超过一定数量时释放内存
    void free(uint32_t elementSize, uint8_t *element)
    {
        if (element == 0)
            return;

        std::unique_lock<std::mutex> locker(m_mtx);
        if (m_elementListMap.count(elementSize) > 0)
        {
            auto &elementList = m_elementListMap[elementSize];
            if (elementList.size() > MAX_ELEMENT_SIZE) // 超过一定数量时释放
                cudaFreeHost(element);
            else
                elementList.push_back(element);
        }
        else // 如果找不到直接释放
        {
            cudaFreeHost(element);
        }
    }

    // 释放内存池中所有内存
    void destroy()
    {
        std::unique_lock<std::mutex> locker(m_mtx);
        for (auto &it : m_elementListMap)
        {
            auto &elementList = it.second;
            for (uint8_t *element : elementList)
                cudaFreeHost(element);
        }
    }

    // 禁止复制和赋值操作
    CudaMemoryPool() = delete;
    ~CudaMemoryPool()
    {
        destroy();
    }

    CudaMemoryPool(const CudaMemoryPool &other) = delete;
    CudaMemoryPool &operator=(CudaMemoryPool &other) = delete;

private:
    // 创建指定数量的内存块
    uint32_t create(std::list<uint8_t *> &elementList, uint32_t elementSize, uint32_t elementCount)
    {
        uint32_t successCount = 0; // 成功创建的内存节点个数
        for (size_t i = 0; i < elementCount; i++)
        {
            uint8_t *element = 0;
            if (cudaMallocHost((void **)&element, elementSize) == cudaSuccess)
            {
                if (element != 0)
                {
                    elementList.push_back(element);
                    successCount++;
                }
            }
        }
        return successCount;
    }

private:
    // 哈希表,维护不同大小内存块链表
    std::unordered_map<uint32_t, std::list<uint8_t *>> m_elementListMap;
    std::mutex m_mtx; // 用于多线程场景的互斥锁
};

主要接口
// 分配节点内存
bool alloc(uint32_t elementSize, uint8_t *&element);

// 回收内存,超过一定数量时释放内存
void free(uint32_t elementSize, uint8_t *element);

// 释放内存池中的所有内存
void destroy();

使用示例

#include <functional>
#include <thread>
#include <iostream>
#include "CudaMemoryPool.h"

static uint32_t elementSize = 1080 * 1920 * 3 / 2 * sizeof(uint8_t);

// 测试函数
void test_mem(int n)
{
    std::cout << "Thread: " << n << std::endl;
    auto &pool = CudaMemoryPool::instance();
    for (int i = 0; i < 100; i++)
    {
        uint8_t *element;
        bool b = pool.alloc(elementSize, element);
        if (!b)
            printf("Allocation failed! \n");
        printf("Element address: %p\n", element);
        
        // 延迟一段时间再释放内存
        std::this_thread::sleep_for(std::chrono::milliseconds(i * n));
        pool.free(elementSize, element);
    }
}

int main()
{
    // 启动多个线程进行内存测试
    std::vector<std::thread> threads;
    for (size_t i = 0; i < 10; i++)
    {
        threads.emplace_back(test_mem, i);
    }

    // 等待所有线程结束
    for (auto &thread : threads)
    {
        thread.join();
    }

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值