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

请教大家一个架构的问题

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

    假设有 3 个模块(可能更多),为了提高效率,每个模块一个线程,并绑定到特定 CPU 核心。

    1. 每个模块处理特定的数据,数据长度是已知的
    2. 前一个模块的输出,是后一个模块的输出。比如模块 1 输出,是模块 2 输入;模块 2 输出,是模块 3 输入

    当初想法是,模块 1 、模块 2 、模块 3 ,分别在不同线程,则通过也给消息中间件如 zeromq 通信,数据也完全通过消息中间件传输。

    现在有一个想法,在模块 1 、模块 2 、模块 3 中,线程启动前,分配好内存空间,消息中间件给各模块传输一个内存指针和数据长度。

    1. 模块 1 中先建立 10 个内存块,处理完毕后,存入第一块内存,然后通知模块 2 ,模块 1 继续处理数据
    2. 模块 2 中也先建立 10 内存块,收到通知后,读取模块 1 的第一块内存地址和数据长度,处理完毕后,存入模块 2 的第一块内存,通知模块 3 ,模块 2 继续处理数据
    3. 模块 3 中也建立 10 块内存块,收到通知后,读取模块 2 的第一块内存地址和数据长度,处理完毕后,存入模块 3 的第一块内存,模块 3 继续处理数据
    4. 依次类推

    其中由于模块 1 、模块 2 、模块 3 ,读写并不是同一块内存,所以也不需要加锁等。因为各模块的性能要求很高,在线程中在堆上分配内存都被认为是耗时操作

    不知道这种传递内存地址的方式,是不是合适的?有没有更加高效的架构设计呢?

    22 条回复    2024-04-03 19:36:55 +08:00
    securityCoding
        1
    securityCoding  
       267 天前 via Android   ❤️ 1
    不了解你的业务场景,通用后台业务来说这不就是 Kafka 三个 comsumer 么
    Inn0Vat10n
        2
    Inn0Vat10n  
       267 天前   ❤️ 1
    数据源是啥样的呢,如果数据源本身是可拆的,输入数据拆成 n 份,分发到 k 个线程里,每个线程内部模块 1,2,3 串行在同一片内存上操作应该效率会更高
    wisefree
        3
    wisefree  
    OP
       267 天前 via Android
    @securityCoding 嗯,流程上可以,只是为了追求极致的效率,所以在想通过指针操作
    wisefree
        4
    wisefree  
    OP
       267 天前 via Android
    @Inn0Vat10n 数据源不能拆分,它是按时间先后外部传输过来的,模块一先接收
    Zhuzhuchenyan
        5
    Zhuzhuchenyan  
       267 天前   ❤️ 1
    这个思路整体没有问题,给我的感觉就是一个环形的 buffer 设计,不过我想到一个场景。

    假设模块 1 已经完成了 10 份工作并通知模块 2 ,此时不知为何模块 2 还在处理第 1 份工作,此时模块 1 需要一种方式知道第 1 份内存还在被使用从而避免造成多线程竞争问题。

    如果有一个高性能的跨线程内存池的话,可以简化这个问题,前提是可以容忍内存池带来的开销。

    模块 1 借了以后,处理完把内存的所有权给模块 2 ,模块 2 读了以后把所有权还给内存池,以此类推。
    cI137
        6
    cI137  
       267 天前 via iPhone   ❤️ 1
    actor 模型可以吗,云风的 skynet 框架,或者 go 的管道
    liberize
        7
    liberize  
       267 天前 via Android   ❤️ 1
    这不就是环形缓冲区(ring buffer)吗,有无锁版本,实际也相当于一个容量固定的队列。
    leonshaw
        8
    leonshaw  
       267 天前 via Android   ❤️ 1
    Ring buffer 基本上就是最高效的了,有不少库。自己实现也不难,主要注意内存屏障的使用和队满/空时的处理。
    codegenerator
        9
    codegenerator  
       267 天前   ❤️ 2
    看着非常糟糕
    1. 为什么不同模块不同 cpu ?你是数据处理,数据尽量放在一个核心上明显性能高很多
    2. 多线程数据同步有很多方法,zeromq 是来搞笑的吗?
    3. 模块 1 不知道模块 2 什么时候处理完,有可能把没处理完的数据覆盖了
    4. 每个模块处理速度不确定,你很难假定每个模块速度

    本人资深架构师,可以付费提供优质服务
    liberize
        10
    liberize  
       267 天前 via Android   ❤️ 1
    楼上说的问题很好解决,一般环形缓冲区队首和队尾之间会留一个空位,这样空和满是可以区分的。
    youngxhui838
        11
    youngxhui838  
       267 天前 via Android   ❤️ 1
    可以看看 go pipeline 这种设计,用管道进行通信。https://go.dev/blog/pipelines
    wisefree
        12
    wisefree  
    OP
       267 天前 via Android
    @Zhuzhuchenyan 是这样的,如果模块 1 的处理速度很快,模块 2 比较慢,形成了一个速度差的话,模块 1 迟早会把内存块用完,之前的设计就有漏洞。
    wisefree
        13
    wisefree  
    OP
       267 天前 via Android
    @cI137 没试过 skynet 和 go ,目前在用 c++
    exch4nge
        14
    exch4nge  
       267 天前 via iPhone   ❤️ 1
    绑核了就 spin 查消息吧,用什么 zeromq ,io 唤醒得有多慢。
    单个模块一个线程/核心?这么设计会更快么?不会有某个模块处理慢么,不加点线程/核心?
    切换线程可能把 cache 都丢了,有些情况下还不如一个线程跑完整个处理
    exch4nge
        15
    exch4nge  
       267 天前 via iPhone   ❤️ 1
    建议先用一般思路实现一版本,然后再做这种设计,比对性能。性能这东西基本都得靠实践。
    一般思路:找个线程池实现,如 folly 的 cpu thread pool ,内存分配用 jemalloc 之类的
    wisefree
        16
    wisefree  
    OP
       267 天前 via Android
    @liberize 其实我之前也搜了下环形缓冲,好像和提前分配内存,这个差不不大,但是环形缓冲确实更加方便
    wisefree
        17
    wisefree  
    OP
       267 天前 via Android
    @youngxhui838 好的,我去看看
    wisefree
        18
    wisefree  
    OP
       267 天前 via Android
    @liberize 是的,多谢啦,留空这个建议很有用
    wisefree
        19
    wisefree  
    OP
       267 天前 via Android
    @exch4nge 好的,多谢建议哈
    cnbatch
        20
    cnbatch  
       267 天前   ❤️ 1
    “在线程中在堆上分配内存都被认为是耗时操作”

    这个要视乎 libc 或 libc++而定,有些 libc 会把用过、free 掉的内存为该程序暂留着,不会那么快交还给 OS 。后续该程序再申请堆内存时就从暂留资源直接分配,耗时不会很高。
    cnbatch
        21
    cnbatch  
       267 天前   ❤️ 1
    忘了一个,如果 OP 的编译器支持 C++17 ,可以考虑下 std::pmr
    wisefree
        22
    wisefree  
    OP
       267 天前
    @cnbatch 多谢,我去学习这个功能
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3198 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 12:08 · PVG 20:08 · LAX 04:08 · JFK 07:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.