提问者:小点点

为什么使用全局变量会使多线程执行速度慢2倍,而在其他环境中会使多线程执行速度快2倍?


下面的代码取自一个用G++编译的示例。多线程比单线程快2倍。

我在Visual Studio2019中执行它,结果恰恰相反:单线程比多线程快2倍。

#include<thread>
#include<iostream>
#include<chrono>
using namespace std;

using ll = long long;
ll odd, even;
void par(const ll ini, const ll fim)
{
    for (auto i = ini; i <= fim; i++)
        if (!(i & 1))
            even += i;
        
}

void impar(const ll ini, const ll fim)
{
    for (auto i = ini; i <= fim; i++)
        if (i & 1)
            odd += i;
}
int main()
{
    const ll start = 0;
    const ll end = 190000000;

/* SINGLE THREADED */
    auto start_single = chrono::high_resolution_clock::now();
    par(start, end);
    impar(start, end);
    auto end_single = chrono::high_resolution_clock::now();
    auto single_duration = chrono::duration_cast<chrono::microseconds>(end_single - start_single).count();
    cout << "SINGLE THREADED\nEven sum: " << even << "\nOdd sum: " << odd << "\nTime: " << single_duration << "ms\n\n\n";
/* END OF SINGLE*/

/* MULTI THREADED */
    even = odd = 0;
    auto start_multi= chrono::high_resolution_clock::now();
    thread t(par, start, end);
    thread t2(impar, start, end);
    t.join();
    t2.join();
    auto end_multi = chrono::high_resolution_clock::now();
    auto multi_duration = chrono::duration_cast<chrono::microseconds>(end_multi - start_multi).count();
    cout << "MULTI THREADED\nEven sum: " << even << "\nOdd sum: " << odd << "\nTime: " << multi_duration << "ms\n";
/*END OF MULTI*/
    cout << "\n\nIs multi faster than single? => " << boolalpha << (multi_duration < single_duration) << '\n';
}

但是,如果我对我的函数做一个小的修改,如下所示:

void par(const ll ini, const ll fim)
{
    ll temp = 0;
    for (auto i = ini; i <= fim; i++)
        if (!(i & 1))
            temp += i;
    even = temp;
}

void impar(const ll ini, const ll fim)
{
    ll temp = 0;
    for (auto i = ini; i <= fim; i++)
        if (i & 1)
            odd += i;
    odd = temp;
}

多线程的性能更好。我想知道是什么导致了这种行为(在实现方面有哪些可能的差异解释了它)。

此外,我还使用www.onlinegdb.com上的gcc进行了编译,结果与我的计算机中的Visual Studio类似。


共1个答案

匿名用户

你是虚假分享的受害者。

oddeven紧挨着驻留,从两个线程访问它们会导致L3高速缓存行争用(也称为虚假共享)。

您可以通过将它们分散64个字节来修复它,以确保它们驻留在不同的缓存行中,例如,如下所示:

alignas(64) ll odd, even;

有了这个改变,我在使用2个线程时获得了很好的加速:

SINGLE THREADED
Even sum: 9025000095000000
Odd sum: 9025000000000000
Time: 825954ms


MULTI THREADED
Even sum: 9025000095000000
Odd sum: 9025000000000000
Time: 532420ms

至于G++的性能--它可能是在执行您手动为您做的优化。MSVC在优化全局变量时会更加小心。