特定数组大小的性能扭曲
Performance detoriation for certain array sizes
我对下面的代码有一个问题,我不明白问题在哪里。然而,问题只发生在V2英特尔处理器上,而不是V3。考虑下面的c++代码:
struct Tuple{
size_t _a;
size_t _b;
size_t _c;
size_t _d;
size_t _e;
size_t _f;
size_t _g;
size_t _h;
};
void
deref_A(Tuple& aTuple, const size_t& aIdx) {
aTuple._a = A[aIdx];
}
void
deref_AB(Tuple& aTuple, const size_t& aIdx) {
aTuple._a = A[aIdx];
aTuple._b = B[aIdx];
}
void
deref_ABC(Tuple& aTuple, const size_t& aIdx) {
aTuple._a = A[aIdx];
aTuple._b = B[aIdx];
aTuple._c = C[aIdx];
}
....
void
deref_ABCDEFG(Tuple& aTuple, const size_t& aIdx) {
aTuple._a = A[aIdx];
aTuple._b = B[aIdx];
aTuple._c = C[aIdx];
aTuple._d = D[aIdx];
aTuple._e = E[aIdx];
aTuple._f = F[aIdx];
aTuple._g = G[aIdx];
}
注意A, B, C,…, G是简单数组(全局声明)。数组用整数填充。
方法"deref_*",简单地从数组(通过index - aIdx访问)中分配一些值给给定的结构参数"aTuple"。我首先将给定结构体的单个字段作为参数赋值,然后继续对所有字段进行赋值。也就是说,每个方法比前一个多分配一个字段。方法"deref_*"被调用,索引(aIdx)从0开始,到数组的最大大小(顺便说一下,数组具有相同的大小)。索引用于访问数组元素,如代码所示——非常简单。
现在,考虑这个图(http://docdro.id/AUSil1f),它描述了数组大小从2000万个(size_t = 8字节)整数开始到24 m (x轴表示数组大小)的性能。
对于包含2100万个整数(size_t)的数组,如果方法涉及至少5个不同的数组(即deref_ACDE…G),则性能会下降,因此您将在图中看到峰值。对于包含22 m整数的数组,性能会再次提高。我想知道为什么这只发生在21米的数组大小?只有当我在CPU服务器上进行测试时才会发生这种情况:英特尔(R) Xeon(R) CPU E5-2690 v2 @ 3.00GHz,而不是Haswell,即v3。显然,这是英特尔已知的问题,已经解决了,但我不知道它是什么,以及如何改进v2的代码。
我将非常感谢您的任何提示。
我怀疑您可能看到缓存-银行冲突。Sandybridge/Ivybridge (Xeon Exxxx v1/v2)有它们,Haswell (v3)没有。
OP更新:DTLB未命中。缓存库冲突通常只在工作集适合缓存时才会成为问题。将每个时钟的读取限制为一个8B而不是两个,不应该阻止CPU跟上主内存,即使是单线程的。(8B * 3GHz = 24GB/s
,约等于主存顺序读带宽)
我认为有一个性能计数器,你可以用perf
或其他工具检查。
引用Agner Fog的微架构文档(章节9.13):
缓存库冲突
数据缓存中每连续128字节或两条缓存行为分为8组,每组16字节。做两个是不可能的如果两个内存地址有,则在相同的时钟周期内读取内存相同的银行号码,即,如果两个地址中的第4 - 6位是相同。
; Example 9.5. Sandy bridge cache
mov eax, [rsi] ; Use bank 0, assuming rsi is divisible by 40H
mov ebx, [rsi+100H] ; Use bank 0. Cache bank conflict
mov ecx, [rsi+110H] ; Use bank 1. No cache bank conflict
改变数组的总大小会改变具有相同索引的两个元素之间的距离,如果它们的布局或多或少是首尾相连的。
如果你让每个数组对齐到不同的16B偏移量(模128),这将有助于一些SnB/IvB。对每个数组中相同索引的访问将位于不同的缓存库中,因此可以并行进行。实现这一点可以像分配128b对齐的数组一样简单,在每个数组的开始处增加16*n个额外字节。(将最终释放的指针与解引用的指针分开跟踪会很麻烦。)
如果你写结果的元组与读的地址相同,取4096的模,你也会得到一个错误的依赖。(也就是说,从其中一个数组中读取数据可能需要等待元组的存储。)详见Agner Fog的文档。我没有引用这部分,因为我认为缓存-银行冲突是更可能的解释。Haswell仍然有错误依赖的问题,但是cache-bank冲突的问题已经完全解决了。
- 介于 [固定数组] 和 [带内存分配的指针] 之间的性能
- 数组中的不同浮点值会影响性能 10 倍 - 为什么?
- 一维数组映射方式的性能差异问题
- 将类成员数组组合到单个数组时性能下降
- 提高访问大型数组元素的性能
- 使用数组时的抽象与性能
- 为什么这个普通的数组实现比STD ::向量实现性能慢
- C 初始化列表std ::数组最佳性能
- 数组的正确值将提高性能
- std ::数组与数组性能
- 将特征矩阵转换为 c 数组以便我可以使用 gsl 的性能高效方法是什么
- C++中使用大型数组时的性能问题
- C++-静态数组的性能,在启动时具有可变大小
- 在性能方面,我应该更喜欢数组而不是矢量吗
- 数组的结构和结构数组 - 性能差异
- 为什么 std::vector 比本机数组的性能更高
- C++数组与矢量性能测试说明
- 性能问题C++ - 搜索数组
- 数组和过滤性能
- 在将 char 数组复制到字符串期间节省 CPU 周期(提高性能)