为什么多线程循环更快?

时间:2016-12-01 22:00:49

标签: c++ multithreading c++11 multiprocessing

我刚刚使用openMP阅读Naming params of nested routes并行处理。

我尝试了以下简单代码

#include <iostream>
#include <ctime>
#include <vector>

int main()
{
        // Create an object just to allow the following loops to do something
        std::vector<int> a;
        a.reserve(2000);

        // First single threaded loop
        std::clock_t begin;
        std::clock_t end;
        begin = std::clock();
        double elapsed_secs;

        for(int n=0; n<1000000000; ++n)
        {
                if (n%100000000 == 0) a.push_back(n);
        }

        end = std::clock();

        elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
        std::cout << "Time for single thread loop: " << elapsed_secs << std::endl;

        // Second multithreaded loop
        begin = std::clock();
        #pragma omp parallel for
        for(int n=0; n<1000000000; ++n)
        {
                if (n%100000000 == 0) a.push_back(n);
        }

        end = std::clock();
        elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
        std::cout << "Time for multi thread loop: " << elapsed_secs << std::endl;

        return 0;
}

已使用g++ -std=c++11 -o a a.cpp -fopenmp编译并输出

Time for single thread loop: 3.9438
Time for multi thread loop: 3.94977
  • 我是否误解了如何在C ++中进行并行化
  • 我是否误解了如何编译?
  • 代码是否并行化,但速度的提高无论出于何种原因都不明显?

请注意,我的计算机上有12个核心(当前没有正在运行的大进程)。

4 个答案:

答案 0 :(得分:1)

您没有使用std::clock测量实时时间,而是测量cpu时间。更好地使用std::chrono作为其他答案建议。

或者在不更改代码的情况下进行快速测试,请在shell中尝试:

date; time ./a; date

这是输出:

  

jue dic 1 23:12:57 CET 2016

     

单线程循环的时间:2.99741

     

多线程循环的时间:4.55788

     

真正的0m4.184s

     

用户0m7.556s

     

sys 0m0.000s

     

jue dic 1 23:13:01 CET 2016

时间与您的输出不同。实时它在我的电脑中大约是4s而不是7.5s,就像你的程序输出一样。

您应该阅读有关std::clock()的文档,特别是:

  

例如,如果CPU由其他进程共享,则std :: clock time   可能比挂钟慢。另一方面,如果是当前的   进程是多线程的,并且有多个执行核心   可用,std :: clock时间可能比挂钟更快。

答案 1 :(得分:1)

std::clock测量CPU时间,而不是墙上时间(至少是gcc实现,但我相信MSVC实现测量的是时间)。这是cppreference的摘录:

  

返回自与程序执行相关的实现定义时代开始以来进程使用的近似处理器时间。要将结果值转换为秒,请将其除以CLOCKS_PER_SEC

     

只有不同调用std :: clock返回的两个值之间的差异才有意义,因为std::clock时代的开头不一定与程序的开头一致。 std::clock时间可以比挂钟更快或更慢地进行,这取决于操作系统给予程序的执行资源。例如,如果CPU由其他进程共享,std::clock时间可能比挂钟慢。另一方面,如果当前进程是多线程的并且有多个执行核心可用,std::clock时间可能比挂钟更快。

您可以使用std::chrono设施来衡量待座时间:

auto Begin = std::chrono::high_resolution_clock::now();
// ...
auto End = std::chrono::high_resolution_clock::now();
std::cout << "Time for xxx: " << std::chrono::duration_cast<std::chrono::milliseconds>(End - Begin).count() << std::endl;

你会看到真正的加速。

作为旁注,我会说您的测试不是线程安全的,因为push_back需要修改矢量的end位置。

答案 2 :(得分:0)

我认为你第二次循环较慢的原因仅仅是线程所需的开销。你正在做的工作是非常小的,并且往往是快速的。 Push_back是常量,函数只是使用指向容器末尾的指针来添加新项,然后它更新指向'end'的指针。我想如果你把更复杂的代码放到循环中,你会开始看到差异。我还注意到你从来没有在循环之间清除'a',所以你要在'a'中添加额外的百万个项目,这可能会导致第二个循环(即使它没有线程化)运行得更慢。

我认为你没有误解如何线程,因为你只是忽略了线程所需的开销(包括创建,inti,上下文swice,ext)。

这个问题似乎相当普遍,但与此同时,每个问题的答案都可能非常不同,因为许多事情都会进入线程(https://unix.stackexchange.com/questions/80424/why-using-more-threads-makes-it-slower-than-using-less-threads)。在该链接中,答案更为广泛,指出线程速度非常依赖于系统性能,例如CPU资源,RAM资源和网络I / O资源。这个链接:Why is my multi-threading slower than my single threading?表明OP正在写入控制台,这就是问题所在(根据链接,控制台类处理线程同步,因此内置类中的代码正在创建单线程运行得更快。)

答案 3 :(得分:0)

还应该说,你在并行循环中所做的事情是非敏感的并且必然会导致运行时错误。有两个问题。

<强>第一

#pragma omp parallel for
for(int n=0; n<1000000000; ++n)
{   if(n%100000000 == 0) <some code>    }

只做10次( 10次)次。编译器可以优化循环变量n,并使用相当于

的代码
#pragma omp parallel for
for(int n=0; n<10; ++n)
{   <some code>   }

如果<some code>对计算要求很高,那么它只会受益于并行性。所以,你实际上没有测试任何东西

第二次,更严重的是,

a.push_back(n);

不是线程安全。也就是说,您不能(可能)从不同的线程同步调用它。每次调用std::vector::push_back()都会改变向量的状态,即其内部数据,从而导致竞争条件。

最后,我建议不要使用OpenMP 与C ++并行,因为它不支持/利用C ++语言功能(例如模板),甚至没有标准化最近的C ++标准。而是使用专为C ++设计的tbb之类的东西。