【发布时间】:2016-02-17 10:41:52
【问题描述】:
让我们有一个只有一个数据成员的非常简单的 C++ 类:
class Container {
public:
std::vector<Element> elements;
Container(int elemCount);
};
现在创建 N 个线程来完成一个非常简单的任务:
- 创建具有一定向量大小的本地容器
- 遍历向量并简单地增加每个元素的值
- 重复第 2 步 10.000 次(以秒为单位而不是毫秒)
完整的代码清单可以在Pastebin找到
根据CoreInfo,我的 CPU(Intel Core i5 2400)有 4 个核心,每个核心都有自己的 L1/L2 缓存:
Logical to Physical Processor Map:
*--- Physical Processor 0
-*-- Physical Processor 1
--*- Physical Processor 2
Logical Processor to Cache Map:
*--- Data Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64
*--- Instruction Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64
*--- Unified Cache 0, Level 2, 256 KB, Assoc 8, LineSize 64
-*-- Data Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64
-*-- Instruction Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64
-*-- Unified Cache 1, Level 2, 256 KB, Assoc 8, LineSize 64
--*- Data Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64
--*- Instruction Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64
--*- Unified Cache 2, Level 2, 256 KB, Assoc 8, LineSize 64
---* Data Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64
---* Instruction Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64
---* Unified Cache 3, Level 2, 256 KB, Assoc 8, LineSize 64
**** Unified Cache 4, Level 3, 6 MB, Assoc 12, LineSize 64
---* Physical Processor 3
对于高达 100.000 个元素的向量大小,时间完全符合预期:
Elements count: 100.000
Threads: 1
loops: 10000 ms: 650
Threads: 4
loops: 2500 ms: 168
loops: 2500 ms: 169
loops: 2500 ms: 169
loops: 2500 ms: 171
但是,对于更大的向量大小,多核的性能是:
Elements count: 300.000
Threads: 1
loops: 10000 ms: 1968
Threads: 4
loops: 2500 ms: 3817
loops: 2500 ms: 3864
loops: 2500 ms: 3927
loops: 2500 ms: 4008
我的问题:
- 谁能给我解释一下这个原因?这是虚假分享吗?如果是这样,如果线程确实不共享任何数据并且所有内核都有自己的 L1/L2 缓存和缓存线,这怎么可能??
- 在多线程中处理独立数据时,是否可以实现(或接近)线性加速效率?
编辑:感谢所有答案,到目前为止。关于您的问题:
@user2079303:元素只包含一个双数据成员。大小(元素)= 8。完整源代码请见Pastebin。
@bku_drytt:resize() 是正确的。我的目的是在每个线程中创建一个包含 elemCount 元素的向量(不管它们的初始值)。
@Jorge González Lorenzo:您在共享 L3 缓存方面是绝对正确的。我进行了另一组测试,仅单线程:
Elements count: 50.000
Threads: 1
loops: 50000 ms: 1615
Elements count: 200.000 (4 times bigger)
Threads: 1
loops: 50000 ms: 1615 (slightly more than 4 time bigger)
Elements count: 800.000 (even 4 times bigger)
Threads: 1
loops: 50000 ms: 42181 (MUCH more than 4 time bigger)
【问题讨论】:
-
Element有多大?你还记得在启用优化的情况下进行编译吗? -
您是否故意在构造函数中调用
resize()而不是reserve()? -
我的猜测是您正在使用 4 个线程填充 L3 共享缓存(需要 x4 存储,因为每个线程有一个向量),因此导致许多缓存未命中,而在单线程执行中矢量适合它。 L1 和 L2 是每个内核,但 L3 不是。一个公平的比较是使用比 4 个线程执行更大的 x4 向量来运行单线程执行。
-
您的 CPU 有 6 MB 三级缓存。 100k * 8 字节 = 0.76MB。乘以 4 即 3 MB。三倍(300k 元素)是 9 MB,不适合 L3 缓存。当你的带宽受限时获得线性加速是不可能的,但如果你真的在做真正的工作,这看起来会有所不同。问题是你所有的“工作”很容易被一个缓存未命中所掩盖(你将比单线程版本多 10k * 4)
-
这真的取决于你的实际问题和访问模式。没有适用于每种访问模式的灵丹妙药(尽管您可能想查找缓存遗忘算法,如果它适合您的算法,那么这些算法非常酷)。而且,如果您真的只是带宽有限,那么巧妙地玩弄线程将永远不会改变这一事实。
标签: c++ multithreading performance