美文网首页
线程通讯单读单写消息队列内存高性能分配策略

线程通讯单读单写消息队列内存高性能分配策略

作者: 谭英智 | 来源:发表于2023-09-24 22:32 被阅读0次

背景

在程序运行的过程中,经常会遇到使用std::queue的情况,例如线程之间的交互

而std::queue内的元素,一般会使用std::unique_ptr来维护内存的生命周期

通常在不优化内存的情况下,std::queue内的指针元素会发生很严重的碎片化

导致在入队列和出队列访问元素内存时发生非常多的CPU Cache不命中,使得程序性能下降

需要解决的问题

解决std::queue元素内存碎片化而导致的性能问题

解决思路

  • 申请相邻元素的地址越邻近越好

  • 释放的内存可以被循环再用

  • 生产者和消费者线程在申请内存和释放内存时尽量高效,避免同步和互斥

设计

申请内存

不需要任何锁和原子同步

  1. 如果可用buf不足分配的size,则向系统申请4k连续内存

  2. 把4k buf连续分配下去,只到不足分配size,则再次向系统申请4k内存

释放内存

不需要任何锁和原子同步

  1. 记录释放内存的起始地址

  2. 如果起始地址记录的内存满4k,则释放内存,并重新记录起始地址

内存复用

使用tcmalloc库

性能

对比直接使用new来申请内存的方法

提升了3.5x的性能


allocate.GIF

实现

#include <memory>
#include <queue>
#include <list>
#include <memory_resource>

struct FifoMemoryAlloc
{
    ~FifoMemoryAlloc() {
        if(available_memory != nullptr) {
            free(available_memory);
        }
    }
    void *allocate(size_t num_to_allocate)
    {
        if(num_to_allocate>to_allocate) {
            return malloc(num_to_allocate);
        }
        if(available_memory == nullptr) {
            available_memory = (uint8_t*)malloc(to_allocate);
            cur_available_idx = 0;
        }
        if(cur_available_idx + num_to_allocate > to_allocate) {
            available_memory = (uint8_t*)malloc(to_allocate);
            cur_available_idx = 0;
        }

        auto ret = &available_memory[cur_available_idx];
        cur_available_idx += num_to_allocate;
        return ret;
    }
    void deallocate(void *ptr, size_t num_to_free)
    {
        if(num_to_free>to_allocate) {
            free(ptr);
            return;
        }
        if(start_release_memory == nullptr) {
            start_release_memory = ptr;
        }

        if(cur_release_idx + num_to_free <= to_allocate) {
            cur_release_idx += num_to_free;
            if(cur_release_idx == to_allocate) {
                if(available_memory == start_release_memory) {
                    available_memory = nullptr;
                }
                free(start_release_memory);
                start_release_memory = nullptr;
                cur_release_idx = 0;
            }
        } else {
            free(start_release_memory);
            start_release_memory = ptr;
            cur_release_idx = num_to_free;
        }
        
    }
private:
    uint8_t* available_memory = nullptr;
    int cur_available_idx = 0;
    size_t to_allocate = 4096;
    void* start_release_memory = nullptr;
    int cur_release_idx = 0;
};
int loop_size = 1000;
int num_size = 100;

static void FifoMemoryAllocBenchMark(benchmark::State &state)
{
    FifoMemoryAlloc alloc;
    std::queue<int *> q;
    for (auto _ : state)
    {
        for (int m = 0; m < loop_size; ++m)
        {
            for (int i = 0; i < num_size; ++i)
            {
                int* t = (int*)alloc.allocate(sizeof(int));
                *t = i;
                q.push(t);
            }
            while (!q.empty())
            {
                auto t = q.front();
                q.pop();
                benchmark::DoNotOptimize(*t = 1);
                alloc.deallocate(t, sizeof(int));
            }
        }
    }
}
// Register the function as a benchmark
BENCHMARK(FifoMemoryAllocBenchMark);


static void SystemMemoryBenchMark(benchmark::State &state)
{
    std::queue<int *> q;
    for (auto _ : state)
    {
        for (int m = 0; m < loop_size; ++m)
        {
            for (int i = 0; i < num_size; ++i)
            {
                int* t = (int*)malloc(sizeof(int));
                *t = i;
                q.push(t);
            }
            while (!q.empty())
            {
                auto t = q.front();
                q.pop();
                benchmark::DoNotOptimize(*t = 1);
                free(t);
            }
        }
    }
}
BENCHMARK(SystemMemoryBenchMark);

相关文章

  • 算法实战3 - 高性能队列的实现思路

    本章关键词 高性能队列、并发、线程安全 Disruptor 是一个高性能的内存消息队列,用于不同线程之间的通信。为...

  • redis整理

    redis 使用单进程单线程,减少内存拷贝,不存在上下文切换,提高性能。 直接内存操作。 过期策略redis也是惰...

  • Android开发(6) - 初遇Handler

    概述 Handler是线程通讯工具类。用于传递消息。它有两个队列:1.消息队列2.线程队列 消息队列使用sendM...

  • 2018-04-02

    python高级 多线程通讯 队列 Queue---来完成多进程间的数据传递 管道 Pipe---方式单...

  • 330,GCD栅栏函数dispatch_barrier使用注意(

    1、多线程操作同一数据进行 多读单写 线程安全控制;2、多线程执行不同任务的前后时序控制; 思考一个问题,串行队列...

  • GCD 相关用法

    1.线程间通讯 2.队列组 3.dispatch_apply 4.delay 5.单例 6.栅栏函数

  • DPDK-suricata

    一 线程 单网卡单队列,worker模式下的线程情况: 二 线程堆栈 2.1 2号线程中断处理线程 中断线程执行主...

  • 并发相关

    并发相关 JAVA高性能内存队列-disruptor JAVA内置队列 高性能内存队列-disruptor dis...

  • iOS 中线程安全且高吞吐量的模型

    线程安全且高吞吐量的模型,意思就是多读单写的的意思,来自《高性能iOS应用开发》一书中4.5.4节,swift实现...

  • 2017.09.13

    腾讯电话面试: 进程通讯有哪些方式,各有哪些优缺点? 管道:套接字:共享内存:信号量:消息队列: 多线程编程需要注...

网友评论

      本文标题:线程通讯单读单写消息队列内存高性能分配策略

      本文链接:https://www.haomeiwen.com/subject/aqnfbdtx.html