【发布时间】:2015-04-26 13:07:48
【问题描述】:
前提
我想做一些涉及k 长数据向量(每个长度为n)的计算,我在主存中接收到这些数据,并将某种结果写回主存。为简单起见,还假设计算只是
for(i = 0; i < n; i++)
v_out[i] = foo(v_1[i],v_2[i], ... ,v_k[i])
或许
for(i = 0; i < n; i++)
v_out[i] = bar_k(...bar_2(bar_1(v_1[i]),v_2[i]), ... ),v_k[i])
(这不是代码,它是伪代码。)foo() 和 bar_i() 函数没有副作用。 k 是常量(在编译时已知),n 仅在此计算发生之前才知道(并且它相对较大 - 至少比整个 L2 缓存大小大几倍甚至更多)。
假设我在 x86_64 处理器(Intel,或 AMD,或你有什么;选择很重要,可能)的单个内核上的单个线程上。最后,假设 foo() (resp. bar_i()) 不是密集计算,即从内存中读取数据并将其写回的时间相对于 n (resp. kx@ 987654333@) 调用 foo() (resp. bar_i())。
问题
我该如何安排这个计算,以避免:
- 来自一个输入向量的数据正在清除另一个向量的缓存数据。
- 输入矢量数据清除缓存的输出矢量数据。
-
bar_j(...bar_1(v_1[i])...)的中间结果保留在寄存器或 L1 缓存中,如果有足够的容量将它们保存在那里,直到 v_{j+1}[i] ... v_k[i] 的数据到达并允许我们完成计算。 L2 也一样。 - 当我们打算继续处理该缓存行中的元素时,输出向量的 L1 缓存行被清除。 L2 也一样。
- 内存带宽利用率不足。
- 尽可能的核心空闲时间。
- v_out 上的写-读-写-读-写序列(如果这些写操作需要更新到主内存,这可能会非常昂贵;这里的动机是可能很容易只读取一个向量,更新输出并重复)。
注意事项:
- 输入数据的任何重新排列都计入总计算时间。向量不会在交替排列中重复使用,因此基本上是浪费时间。
- 如果它让您更容易假设对齐或缺乏对齐,那很好,直接说出来。
- 使用 bar_i 函数进行计算可以提高访问模式的灵活性,但会带来额外的挑战 w.r.t. v_out 值的缓存。
【问题讨论】:
标签: c++ c performance caching optimization