V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
0littleboy
V2EX  ›  C++

内存池项目求教?

  •  1
     
  •   0littleboy · 352 天前 · 2215 次点击
    这是一个创建于 352 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目链接: https://github.com/cs-moushuai/MemoryPool

    这是巨弱在简历上的一个项目,在面试的时候问到如何实现线程安全,回答是 C++11 新标准保证的

    然后面试官说这样直接锁住 instance 了,性能太低,有没有老哥了解的

    // Get the single memory pool instance
    template <typename T, size_t BlockSize>
    MemoryPool<T, BlockSize>&
    MemoryPool<T, BlockSize>::getInstance()
    {
        static MemoryPool instance;
        return instance;
    }
    

    下面是简历里的介绍

    C++ 实现高性能内存池

    C++, 数据结构, 测试 个人项目

    • 内容:设计了一个链表形式的区块链及空闲元素链以实现高性能的内存池,并实现相应的空间配 置器,内部使用单例模式获取内存池且实现线程安全,使用 assert 来除错
    • 测试:使用链表实现的栈来测试,通过频繁的 push 及 pop 操作,计算得到 STL 中 allocator 需要 58.65 s ,而内存池实现的 Allocator 只需要 12.09 s 即可
    第 1 条附言  ·  351 天前
    回复各位提到的一些问题
    @changnet
    @Yggdroot
    他的意思我觉得是,内存池需要对链表进行插入,删除操作,多个线程同时进行操作可能会引起竞争问题,所以就问我如何实现线程安全的,我说 static C++11 标准保证的,我当时的理解是 static 能够锁住这个变量,面试官听后可能也理解错了
    我现在自己回过头来看,发现 static 只能保证初始化的线程安全,似乎并不能保证 instance 内部插入删除操作安全

    我现在有几个思路:

    1. 要么不保证线程安全,一个线程用一个内存池,以空间换时间,反正也能解决 new ,delete 频繁申请释放的代价,但这种情况用不用单例模式似乎没有什么意义了
    2. 保证线程安全,直接对 instance 加互斥锁性能太低,需要对链表添加删除操作内部加锁,这样能实现空间的最优化
    3. 在 1 的空间换时间的思路上使用 thread_local ,保证线程安全,这个我不是很熟悉,是对 instance 添加关键词吗,那这还需要单例模式是吗

    大伙看看我理解对吗
    15 条回复    2023-04-12 14:15:35 +08:00
    jones2000
        1
    jones2000  
       352 天前
    给每个线程一个分配一个独立的线程池, 这个就不用锁了。
    0littleboy
        2
    0littleboy  
    OP
       352 天前
    @jones2000 意思是给每个线程一个分配一个独立的*内存池*吗,可这就达不到内存池的效果了,内存池就应该所有线程共享一个实例,这也是为什么使用的单例模式
    Yeen
        3
    Yeen  
       352 天前
    @0littleboy 我觉得你对内存池和共享内存是不是还有误解。内存池最初的初衷是解决频繁 new delete 的语言开销,以及从系统堆分配内存的系统 API 开销带来的性能损失,所以预先分配内存。然后在应用代码中自己管理。
    共享内存才是多线程 /进程公用的内存。
    这两个没有关联。
    duke807
        4
    duke807  
       352 天前
    你这一点性能提升看起来只是为了跑分,实际带来的风险远大于收益吧


    我很多年前写有一个 ipc 项目,使用 PI-futex 保证线程安全:

    https://github.com/dukelec/cdipc


    另外我常用的裸机代码,也是用链表管理内存:

    这个文件开头,把数组所占内存转成列表进行管理:
    https://github.com/dukelec/stepper_motor_controller/blob/master/mdrv_fw/usr/app_main.c

    这个文件,定义了多线程获取内存链表的函数:
    https://github.com/dukelec/cdnet/blob/master/dispatch/cdnet_dispatch.h

    这个文件,最终使用 local_irq_save 加锁:
    https://github.com/dukelec/cdnet/blob/master/utils/cd_list.h
    fournoas
        5
    fournoas  
       352 天前
    @0littleboy 谁跟你说内存池必须所有线程共享的,牺牲空间换取性能才是常规操作
    Nazz
        6
    Nazz  
       352 天前
    用 stack 性能更好, 线程安全加锁就是了
    artnowben
        7
    artnowben  
       351 天前
    尽量每个线程独立,另外可以参考一下 DPDK 的内存池。
    hankai17
        8
    hankai17  
       351 天前
    全局的 instance 必须加锁或者用原子操作
    内存池还得做 "local 化" 就是每个线程维护各自线程池
    内存池还得做 回收操作
    yingxiangyu
        9
    yingxiangyu  
       351 天前
    thread local , 基本降低并发竞争都离不开 thread local
    jones2000
        10
    jones2000  
       351 天前
    @0littleboy 共享就需要上锁, 一个牺牲内存提供性能, 一个是节省内存牺牲性能。 具体看项目需求了。
    Yggdroot
        11
    Yggdroot  
       351 天前
    面试官说的不对。
    XSDo
        12
    XSDo  
       351 天前
    又要共享又要不加锁 既要又要,想提升性能就减少竞争,比如只有一个池所有线程去争,改成每个线程自己用自己的池,内存不足,就一组线程用一个池这样也是减少竞争 提升性能。
    changnet
        13
    changnet  
       351 天前
    我都搞不清楚到底说的是哪个线程安全

    C++ 11 是有保证 local static 变量的初始化是安全的,因此 getInstance()这个函数是线程安全的

    但是整个库都没有用到锁,因此在操作内存的时候,这个库不是线程安全的。

    “面试官说这样直接锁住 instance ” 这里我没搞懂哪里会锁住 instance 。
    SenseHu
        14
    SenseHu  
       351 天前
    读一下 tcmalloc 或者 jemalloc 吧
    newmlp
        15
    newmlp  
       351 天前
    参考下 mimalloc 吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5417 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 08:43 · PVG 16:43 · LAX 01:43 · JFK 04:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.