我正在多线程应用程序中编写C ++库,其中性能很重要,并且我不确定实现分配固定大小的模板化结构数组的目标的最佳方法。我想要一个连续的内存块用于缓存局部性,并填充缓存行以避免错误共享。
这是结构定义:
template<size_t buf_bytes, size_t cache_pad_bytes>
struct PaddedBuffer {
char buf[buf_bytes];
char cache_line_padding[cache_pad_bytes];
}
理想情况下,连续的内存块看起来像这样,并且可以作为数组访问:
[ {buf, pad}, {buf, pad}, ..., {buf, pad} ]
其中[]表示数组,{}表示结构。基本挑战是直到运行时才知道buf的大小。
这是我真正希望能够做到的,但鉴于buf_bytes不是常量表达式,C ++标准不允许使用模板化函数(当然)。
PaddedBuffer<buf_bytes, cache_pad_bytes> buffers[num_buffered_entries];
我可以想到几个选项,但不喜欢它们中的任何一个。还有更好的方法吗?
选项1:动态分配所有内容
更改PaddedBuffer定义以使缓冲区成为指针(char * buf)。然后,动态分配PaddedBuffer *数组,然后动态分配为num_buffered_entries
。这并不理想,因为缓冲区不会[必然]相邻。
选项2:动态分配一个大缓冲区
我基本上可以分配一大块大小为(num_buffered_entries * (buf_bytes + cache_pad_bytes)
的内存,然后定义我自己的operator []函数,并在此之后正常使用它。我觉得这很有效,但感觉有点笨拙。此外,我松开了命名的struct accessor功能,因此我无法在结构中添加字段foo
,只需编写object.foo
。我必须自己管理。在这种情况下,我只需要缓冲区(不是缓存行填充),所以它并不重要,但它确实感觉有点像我正在重新发明轮子。
(注意:我意识到我可以使用std::vector
并使用复制构造函数初始化它,但出于各种原因我不想要std :: vector。)
关于我正在解决的问题的更多背景知识:此数据结构主要用于无锁单生产者生产者 - 消费者队列,其中缓冲区在整个应用程序过程中重复使用数千或数百万次。我希望能够在程序开始时分配一次内存,然后有效地访问内存(并且没有错误共享)。我假设我可以通过将其分配为连续的块来实现更好的更高级缓存局部性;我假设我可以避免与缓存行填充的错误共享。 (我确实调查了使用相关的库(TBB,Boost,TBB),但它们很接近,但并没有完全映射到我给出的问题。)这是在一个大型库的内部循环中,因此希望它相当快。如果没有一个干净的解决方案,我可以选择其中一个选项(选项1将是最直接的),但如果有一个干净的解决方案,我想使用它。请注意,队列的大小应该是固定的,因此可以实现为带有适当检查边界的循环缓冲区。
是否有更清洁和推荐的方法?