Skip to main content
 首页 » 编程设计

c++11之控制台输出顺序减慢多线程程序

2025年05月04日164hnrainll

编译以下代码时

#include <iostream> 
#include <vector> 
#include <thread> 
#include <chrono> 
#include <mutex> 
 
std::mutex cout_mut; 
 
void task() 
{ 
    for(int i=0; i<10; i++) 
    { 
        double d=0.0; 
        for(size_t cnt=0; cnt<200000000; cnt++) d += 1.23456; 
 
        std::lock_guard<std::mutex> lg(cout_mut); 
        std::cout << d << "(Help)" << std::endl; 
        //        std::cout << "(Help)" << d << std::endl; 
    } 
} 
 
int main() 
{ 
    std::vector<std::thread> all_t(std::thread::hardware_concurrency()); 
 
    auto t_begin = std::chrono::high_resolution_clock::now(); 
 
    for(auto& t : all_t) t = std::thread{task}; 
    for(auto& t : all_t) t.join(); 
 
    auto t_end = std::chrono::high_resolution_clock::now(); 
 
    std::cout << "Took : " << (t_end - t_begin).count() << std::endl; 
} 

在 MinGW 4.8.1 下,在我的机器上执行大约需要 2.5 秒。这大约是单线程执行 task 函数所需的时间。

但是,当我取消注释中间的行并因此注释掉之前的行时(即,当我交换 d"(Help)" 写入 std::cout 的顺序时),整个过程现在需要 8-9 秒。

解释是什么?

我再次测试,发现我只有 MinGW-build x32-4.8.1-win32-dwarf-rev3 有问题,而 MinGW build x64-4.8.1-posix-seh-rev3 没有问题。我有一台 64 位机器。使用 64 位编译器,两个版本都需要三秒钟。但是,使用 32 位编译器时,问题仍然存在(并且不是由于发布/调试版本混淆)。

请您参考如下方法:

它与多线程无关。这是一个循环优化的问题。我重新排列了原始代码以获得一些简单的东西来演示这个问题:

#include <iostream> 
#include <chrono> 
#include <mutex> 
 
int main() 
{ 
    auto t_begin = std::chrono::high_resolution_clock::now(); 
    for(int i=0; i<2; i++) 
    { 
        double d=0.0; 
        for(int j=0; j<100000; j++) d += 1.23456; 
        std::mutex mutex; 
        std::lock_guard<std::mutex> lock(mutex); 
#ifdef SLOW 
        std::cout << 'a' << d << std::endl; 
#else 
        std::cout << d << 'a' << std::endl; 
#endif 
    } 
    auto t_end = std::chrono::high_resolution_clock::now(); 
    std::cout << "Took : " << (static_cast<double>((t_end - t_begin).count())/1000.0) << std::endl; 
} 

编译和执行时:
g++ -std=c++11 -DSLOW -o slow -O3 b.cpp -lpthread ; g++ -std=c++11 -o fast -O3 b.cpp -lpthread ; ./slow ; ./fast 

输出是:
a123456 
a123456 
Took : 931 
123456a 
123456a 
Took : 373 

大部分时间差异由为内循环生成的汇编代码解释:快速情况直接在 xmm0 中累积,而慢速情况累积到 xmm1 - 导致 2 个额外的 movsd 指令。

现在,当使用 '-ftree-loop-linear' 选项编译时:
g++ -std=c++11 -ftree-loop-linear -DSLOW -o slow -O3 b.cpp -lpthread ; g++ -std=c++11 -ftree-loop-linear -o fast -O3 b.cpp -lpthread ; ./slow ; ./fast 

输出变为:
a123456 
a123456 
Took : 340 
123456a 
123456a 
Took : 346