2015-04-26 39 views
13

假设我必须编写一个C或C++计算密集型函数,它有两个数组作为输入,一个数组作为输出。如果计算使用2个输入数组的次数多于更新输出数组的次数,那么最终会出现输出数组很少被缓存的情况,因为它被驱逐以获取2个输入数组。是否有任何解决方法来“保留”缓存部分?

我想为输出数组保留一部分缓存,并强制以某种方式保证这些行在被提取后不会被驱逐,为了始终将部分结果写入缓存

Update1(output[]) // Output gets cached 
DoCompute1(input1[]); // Input 1 gets cached 
DoCompute2(input2[]); // Input 2 gets cached 
Update2(output[]); // Output is not in the cache anymore and has to get cached again 
... 

我知道有机制帮助驱逐:CLFLUSH,clevict,_mm_clevict等是否有相反的任何机制?

我想到的3个可能的解决方案:

  • 从时间使用_mm_prefetch时间到取数据回来,如果它已被驱逐。然而,这可能会产生不必要的流量,另外我需要非常小心地何时介绍它们;
  • 试图在较小的数据块上进行处理。然而,只有在问题允许的情况下,这才会起作用;
  • 禁用硬件预取程序,可以降低不需要的驱逐速率。

除此之外,有没有优雅的解决方案?

+2

当我处于类似的情况时,偶尔的预取指令已经产生了可测量的益处。诀窍是在*时聪明。 –

+0

它通常会这样做,特别是当这行被驱逐但是如果我对已经在缓存中的行进行预取时,它是否会更新缓存替换算法中的重用距离? – VAndrei

+1

你确定这将*实际*提高性能?我建议采取相反的方式:未缓存/非时间/写入组合商店。 – EOF

回答

0

我想更好地了解这个问题的:

如果这是真的,在“输出”阵列是严格的输出,你永远不会做这样的事情

output[i] = Foo(newVal, output[i]); 

然后,输出[]中的所有元素都是严格写入的。如果是这样,所有你需要'保留'是一个缓存线。这不正确吗?

在这种情况下,所有对“输出”的写入都会生成缓存填充,并可能与“输入”数组所需的缓存线相竞争。

难道你不想在缓存线的'输出'上限制可以消耗而不是保留一定数量的行。

+0

如果您有完全关联的缓存,但只有一个没有完全关联的缓存,则只需要一个缓存行。 – EOF

+0

问题是我需要使用output []来存储计算的中间结果。此外,为了写入输出[],CPU核心首先读取/提取缓存线。我只是想避免提取。 – VAndrei

+0

我认为避免在写入之前发生的读取所有权的唯一方法是使用非临时存储。 'movntdqa'等等。这不会给你想要的结果,因为写入将绕过缓存(只需使用存储缓冲区将多个16或32B写入组合到单个64B传输中)。由于你在写入临时结果后需要再次读取'output []',这是不可行的。 –

0

我看到两个选项,这可能会或可能不会取决于你的目标在CPU上运行,并在您精确的程序流程:

  1. 如果output只写和读不懂,你可以使用流式存储,即具有不读取提示的写入指令,所以它不会被提取到缓存中。

  2. 对于input,您可以使用非时间对齐(NTA)提示进行预取。我不知道这是如何实现的,但我确信在某些Intel CPU(例如Xeon Phi)上,每个硬件线程都使用特定的NTA数据缓存方式,即使用8路缓存每个螺纹1/8。

0

我想这个的解决方案是隐藏在内部,算法采用和L1缓存大小和缓存行大小。 尽管我不确定我们会看到多少性能改善。我们大概可以引入人工读取,它可以巧妙地避免编译器和执行,同时也不会伤害计算。单个人工读取应该填写缓存行,以满足一个页面的需要。因此,应修改算法以计算输出数组的块。类似于使用GPU完成的巨矩阵矩阵乘法。他们使用矩阵块进行计算和写入结果。

如前所述,写入输出数组应该发生在一个流中。

要人工读取带来,我们应该在正确的地方在编译时初始化输出数组,曾经在每个块,可能与0或1

1

英特尔CPU有一些所谓无驱逐模式(NEM),但我怀疑这是你需要的。

当您试图优化output []的第二次(不必要的)提取时,您是否考虑过使用SSE2/3/4寄存器来存储中间输出值,并在必要时更新它们,并仅将它们写回当所有与output []部分相关的更新都完成了? 我在计算FFT(快速傅立叶变换)时做了类似的事情,其中​​一部分输出在寄存器中,只有在知道它们不会再被访问时它们才移出(到内存)。在此之前,所有更新都发生在寄存器中。您需要引入内联汇编以有效使用SSE *寄存器。当然,这样的优化高度依赖于算法和数据放置的性质。

相关问题