【问题标题】:Is there any tricks for fast-memory access?快速内存访问有什么技巧吗?
【发布时间】:2012-10-15 21:54:04
【问题描述】:

我对 C++ 编程世界很陌生,很抱歉我的业余问题:

我在主内存(一维数组)中存储了一大块数据,我需要经常访问那里的一些数据,我这样做的方法是:

float *x=new float[20];//array to store x;
int *indlistforx=new int[20];//array to store the index of x;
float *databank=new float[100000000];//a huge array to store data

/... fill data to databank.../


for (int i=0;i<N;i++)//where N is a very large number;
 {
  /... write index to indlistforx.../
  getdatafromdatabank(x, indlistforx, databank);
  //Based on the index provided by indlistforx, read data from databank then pass them to x

  /...do something with x.../
  };

有没有什么高效/快速的方式来访问这些数据(x 的索引没有对齐,不可能对齐)?

提前非常感谢!

【问题讨论】:

  • new float[100000000];?愿你生活在 70 年代...
  • 我不太明白你的问题。您的代码的某些部分是否运行得太慢?
  • 我不明白这个问题。 x 是什么?
  • @H2CO3:我处理的数据非常庞大,双格式占用大约 20+ GB 内存,这就是我使用 f32 而不是双格式的原因...
  • RAII 通常在正确性和简单性方面具有优势,但在性能方面不应该有任何劣势

标签: c++ performance memory pointers


【解决方案1】:

你还没有真正展示你是如何访问你的数据库的,所以这都是推测性的:

  • indlistforx 是数据库中 20 个索引的列表,所以您正在进行 20 次随机访问?

    • 这些索引的步幅是多少:它们是连续的、靠近的还是随机的?
    • 如果它们是连续的或靠近在一起的,对它们进行排序可能会有所帮助(因此您按升序读取以改进预取,并将来自同一缓存行的读取分组在一起)
  • 不同组的 20 个指数跳跃多少?它们可以重叠吗?

    • 如果它们不能重叠,因此您的数据库被有效地划分为一些块大小,那么在不同的处理器上处理每个分区可能会增加您可以使用的有效缓存数量(如果您有多个处理器)
    • 如果请求可以重叠,如果数据库是只读的,则同时运行它们仍然可以工作。如果有任何内容写入数据库,这将成为缓存抖动的秘诀
  • 您能否在更高级别对访问重新排序以获得更好的缓存行为:更顺序、更好的参考空间或时间局部性?

    • 这与我的第一个建议基本相同,但高于单个 indlistforx 请求的级别
    • 类似地,考虑重新排序它们以有效地划分数据库并尝试多处理器的想法

如果没有看到所有代码(或有代表性的示例,我知道这可能太大了),很难深入了解更多细节。

但是,有一种通用技术可能会奏效......它也可能非常繁重,以至于实施成本超过了节省。

  • 让您的 getfromdatabank 返回一个 future/promise/whatever,而不是同步完成(或 20 个 future 的向量,如果这不是太细粒度的话)
  • 尝试并行分派大量这些异步请求,或者在单独的线程中(访问期货将是一个阻塞操作),或者使用事件循环来处理具有显式协同例程之类的完成
  • 有一个专用线程聚合来自多个请求的所有数据库访问,并对它们重新排序以获得更好的缓存性能

只有在额外的同步开销主要由提高的读取性能支配并且您可以有效地并行运行许多查询时,这才有效。

【讨论】:

  • 谢谢,就我而言,这是随机访问数据,但没有重叠。
  • 非常感谢您的建议,是的,我不能发布代码的原因是因为我广泛使用simd内在函数和CUDA代码,所以代码太长而且大部分与随机无关内存访问。
【解决方案2】:

既然需要初始化一个浮点数,你真的应该使用一个std::vector,它并不慢,像这样构造和填充:

std::vector< float > databank( 100000000, 0.0f );

有几个加速选项:

1) 如果有相当大的键(索引)重用,那么您可以使用某种缓存或记忆策略。 示例见http://www.boost.org/doc/libs/1_51_0/libs/flyweight/doc/index.html

2) 您可以使用 std::async() 将处理拆分为多个线程。

3) 确保您的编译器已打开并正在使用 simd 指令(x86 上的 sse)。如果不通过使用编译器内在函数强制使用 simd。这将使性能提高近 4 倍。

【讨论】:

  • 非常感谢,我会尝试您的建议,至于 SIMD,是的,我的程序是使用 AVX 选项编译的。
  • 我不会投反对票,但这个答案本质上是不正确的。切换到向量不会有帮助。 std::vector&lt;float&gt;float* 之间的唯一区别是安全性。速度?不,向量只是原始指针周围的 RAII 安全包装器。罪魁祸首是随机访问;切换到向量不会改变这一点。将改变的是摆脱这种随机访问。
【解决方案3】:

问题不在于您如何代表您的databank。问题是你如何使用它。在短时间内随机访问您的databank 中广泛分离的部分会破坏您的表现。您的getdatafromdatabank(x, indlistforx, databank)indlistforx 几乎可以保证性能不佳。 indlistforx 启用的随机访问会带来显着的性能损失。如果这种随机访问是绝对必要的,因为使用您的databank 的算法是如何工作的,那只是您必须付出的代价。

如果您可以修改您的算法,以便它们访问您的databank 中的连续内存块,您将获得更好的性能。更改 getdatafromdatabank,以便您仅指定第一个索引(您要加载到 x[0] 中的元素的索引),而不是 20 个索引的数组。

x 的大小为 20 是否有原因?如果您勉强将输出x 数组和databank 的相关块保留在一级缓存中,您将获得最佳性能。如果x 的大小超过此最佳大小,性能将开始下降,并且可能会显着下降。

【讨论】:

  • 我必须使用随机访问,我可以重新安排数据库以允许基于块的访问,但这会降低其他部分代码的性能,并且权衡并不漂亮(实际上,我必须让它随机访问数据库的确切原因是让我的代码的另一部分“大块”地访问数据),这就是为什么我必须坚持随机访问大量数据,我只是想知道有没有什么方法可以提高随机访问的性能,不知道多线程会有多大帮助?
猜你喜欢
  • 1970-01-01
  • 2022-12-06
  • 1970-01-01
  • 1970-01-01
  • 2012-02-13
  • 1970-01-01
  • 2010-09-26
  • 2012-12-01
  • 2017-05-19
相关资源
最近更新 更多