为什么50个线程比4个快?

diz*_*l3d 23 c++ cpu multithreading intel

DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
    volatile auto x = 1;
    for (auto i = 0; i < 800000000 / MAX_THREADS; ++i) {
        x += i / 3;
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

此函数在MAX_THREADS线程中运行.
我已经使用Concand Visualizer 和Intel运行了Intel Core 2 Duo,Windows 7,MS Visual Studio 2012上的测试.(4个线程)在7.1秒内完成,但是(50个线程)在5.8秒内完成,同时有更多的上下文切换. 我在英特尔酷睿i5,Mac OS 10.7.5上运行了相同的测试,并得到了相同的结果.MAX_THREADS=4MAX_THREADS=50
test1test2test1test2

pad*_*ddy 45

我决定在我的4核机器上自己进行基准测试.我直接将4个线程与50个线程进行比较,每个线程交错进行100次测试.我使用自己的数字,以便每个任务都有合理的执行时间.

结果就像你描述的那样.50线程版本略快.这是我的结果的方框图:

并行任务比较图

为什么?我认为这归结于线程调度.在所有线程完成工作之前,任务才完成,每个线程必须完成四分之一的工作.因为您的进程正在与系统上的其他进程共享,所以如果将任何单个线程切换到另一个进程,这将延迟整个任务.当我们等待最后一个线程完成时,所有其他内核都处于空闲状态.请注意,4线程测试的时间分布比50线程测试要宽得多,我们可能会预期.

当你使用50个线程时,每个线程都要做的事情少.因此,单个线程中的任何延迟都会对总时间产生不太显着的影响.当调度程序忙于将内核配置为大量短线程时,可以通过在另一个内核上提供这些线程的时间来补偿一个内核上的延迟.延迟对一个核心的总影响不是一个显示阻止.

因此,在这种情况下,额外的上下文切换似乎不是最大的因素.虽然增益很小,但考虑到处理比上下文切换更重要,看起来有点淹没线程调度程序.与所有内容一样,您必须为您的应用找到正确的平衡.


[编辑]出于好奇,我在一夜之间进行了测试,而我的电脑没有做太多其他事情.这次我每次测试使用200个样本.同样,测试是交错的,以减少任何本地化后台任务的影响.

这些结果的第一个图是低线程计数(最多为核心数的3倍).你可以看到一些线程数的选择是如何很差的......也就是说,任何不是核心数的倍数,尤其是奇数值.

附加测试图 - 低线程数

第二个图是更高的线程数(从核心数的3倍到60).

附加测试图 - 高线程数

在上面,随着线程数的增加,您可以看到明确的下降趋势.随着线程数的增加,您还可以看到结果的扩展范围缩小.

在这个测试中,有趣的是注意到4线程和50线程测试的性能大致相同,并且4核心测试中结果的扩展并不像我原来的测试那么宽.因为计算机没有做太多其他事情,所以可以将时间用于测试.将一个核心置于75%负载下时重复测试将是有趣的.

为了保持透视,请考虑以下事项:

缩放线程


[另一个编辑]在发布了我的最后一批结果之后,我发现混乱的盒子图显示了那些4的倍数的测试趋势,但数据有点难以看到.

我决定只用四的倍数进行测试,并且我认为我可能同时找到收益递减的点.所以我使用的线程数是2的幂,最高可达1024.我本来会更高,但是Windows大约有1400个线程.

我认为结果相当不错.如果您想知道小圆圈是什么,那么这些是中值.我选择它而不是之前使用的红线,因为它更清楚地显示了趋势.

取消线程计数的趋势

在这种特殊情况下,付费污垢似乎介于50到150个线程之间.在那之后,好处很快就会消失,我们正在进入过度线程管理和上下文切换的领域.

任务越长或越短,结果可能会有很大差异.在这种情况下,这是一项涉及大量无意义算术的任务,在单个核心上计算大约需要18秒.

通过仅调整线程数,我能够将4线程版本的中位执行时间额外削减1.5%到2%.