在Intel CPU

时间:2016-12-01 04:58:25

标签: c++ c performance memory cpu-cache

最近我遇到了英特尔TBB可扩展分配器的问题。基本使用模式如下,

  1. 分配了一些大小为N * sizeof(double)的向量
  2. 生成随机整数M,使M >= N / 2 && M <= N
  3. 访问每个向量的第一个M元素。
  4. 重复步骤2. 1000次。
  5. 我将M设置为随机,因为我不想对固定长度的性能进行基准测试。相反,我希望在一系列矢量长度上获得平均性能。

    对于N的不同值,程序性能差别很大。这种情况并不少见,因为我正在测试的功能是为大N的优先级性能而设计的。但是,当我尝试对性能与N之间的关系进行基准测试时,我发现在某个时刻,当N1016增加到{{}时,会有两倍的差异1}}。

    我的第一直觉是1017的性能退化与较小的矢量大小无关,而是有一些缓存。最有可能是虚假分享。品尝下的功能使用SIMD指令,但没有堆栈内存。它从第一个向量中读取一个32字节的元素,并在计算之后将32个字节写入第二个(和第三个)向量。如果发生错误共享,可能会丢失几十个周期,这正是我观察到的性能损失。一些分析证实了这一点。

    最初我将每个向量与32字节边界对齐,用于AVX指令。为了解决这个问题,我将向量与64字节边界对齐。但是,我仍然观察到相同的性能损失。对齐128个字节可以解决问题。

    我做了一些挖掘。英特尔TBB有N = 1016。在其源代码中,内存也以128个字节对齐。

    这是我不明白的。如果我没有弄错的话,现代x86 CPU的缓存行为64字节。 cache_aligned_allocator证实了这一点。以下是正在使用的CPU的基本缓存信息,从我用CPUID编写的小程序中提取来检查功能,

    CPUID

    此外,在英特尔TBB的源代码中,128字节对齐标记为注释,表示这是为了向后兼容。

    那么为什么64字节对齐在我的情况下还不够呢?

1 个答案:

答案 0 :(得分:2)

你被conflict misses击中。 从1016到1017时发生的原因是您开始使用关联列表中的最后一个缓存行。

你的缓存是8路32K,所以每套都是4K。您的64字节缓存行可以容纳8个双精度数。 但你的1017-1024矢量使用8K而不是4K ??? 1024 * sizeof(双),你使用N / 2-> N,所以你使用(除了恰好N / 2时)每个矢量两次相同的低地址位组合。

在您使用了所有的L1缓存之前,您不会遇到冲突命中问题,而您现在已经接近了。使用1个向量进行读取,使用2个向量进行写入,所有8K长,所以使用24K,如果在计算过程中使用8K +额外数据,则会增加驱逐所选数据的机会。

请注意,你只使用了向量的第一部分,但它们之间的冲突从未如此。

当您从1016到1017时,您将能够观察到L1缓存未命中的增加。当您超过1024倍时,性能损失应该会暂时消失,直到您达到L1缓存容量未命中为止。

&LT;在此处对图形进行成像,该图形显示当使用所有8组时的峰值&gt;

来自Ulrich Drepper的精彩文章:&#34; Memory part 5: What programmers can do&#34; enter image description here