为什么线程会减慢程序的执行速度?

时间:2017-08-07 08:48:45

标签: c++ multithreading

我有一些代码可以利用并行性来提高效率。由于我的PC有双处理器,我尝试在两个线程上运行代码。所以我写了下面的代码(这是一个非常简化的版本):

Evaluator::evaluate(vector<inpType> input, bool parallel) {
    std::thread t0;
    if(parallel) {
        // Evaluate half of the input in a spawned thread
        t0 = std::thread(&Evaluator::evaluatePart, this, std::ref(input), 0, input.size()/2 + input.size()%2);
        // Evaluate the other half of the input in this thread
        evaluatePart(input, input.size()/2 + input.size()%2 - 1, input.size()/2);
    } else {
        // sequential evaluate all of the input
        evaluatePart(input, 0, input.size());
    }
    // some other code

    // after finishing everything join the thread
    if(parallel) t0.join();
}

Evaluator::evaluatePart(vector<inpType> &input, int start, int count) {
    for(int i=start; i<count; i++) {
        evaluateSingle(input[i]);
    }
}

Evaluator::evaluateSingle(inpType &input) {
    // do stuff with input
    // note I use a vector<int> belonging to Evaluator object in here, not sure it matters though
}

顺序运行大约需要3 ms,但并行运行大约需要6 ms。这是否意味着产生一个线程需要花费很多时间才能更有效地按顺序进行评估?或者我做错了什么?

请注意,我没有使用任何锁定机制,因为评估是相互独立的。每个evaluateSingle都从一个向量中读取,该向量是Evaluator对象的成员,但只改变给它的单个输入。因此,不需要任何锁定。

更新

我道歉,我没有说清楚。这更像是一个伪代码,用抽象的方式描述我的代码是什么样的。它不会工作或编译,但我的确不是问题。无论如何,我修复了此代码中的t0范围问题。

此外,输入大小约为38,000,我认为这足以充分利用并行性。

更新

我尝试将输入的大小增加到5,000,000,但这没有帮助。顺序仍然比多线程更快。

更新

我尝试增加运行的线程数,同时在它们之间均匀地分割矢量以进行评估,并得到了一些有趣的结果:

enter image description here

请注意,我有一个i7-7500U CPU可以并行运行4个线程。这让我有两个问题:

  1. 与2,3相比,为什么创建4个或更多线程开始看到性能提升。
  2. 为什么创建超过4个线程比效率更高 只有4个线程(CPU可以同时运行的最大值)。

1 个答案:

答案 0 :(得分:8)

“为什么会这样?”很容易回答。想象一下,你有一个可以让四个人并排放下的走廊。你想把所有的垃圾一端移到另一端。最有效率的人数是4。

如果你有1-3个人,那么你错过了使用一些走廊空间。如果你有5个或更多的人,那么这些人中至少有一个基本上一直在排队等候另一个人。添加越来越多的人只是堵塞了走廊,它并没有加速活动。

因此,您希望拥有尽可能多的人,而不会造成任何排队。为什么排队(或瓶颈)取决于以下几点。

在不了解线程性质的情况下,很难说。诊断系统性能时需要考虑的一些事项:

是流程/主题

  1. CPU绑定(需要大量CPU资源)
  2. 内存限制(需要大量RAM资源)
  3. I / O绑定(网络和/或硬盘资源)
  4. 所有这三种资源都是有限的,任何人都可以限制系统的性能。您需要查看您的特定情况正在消耗哪些(可能是2或3)。

    您可以使用ntopiostat以及vmstat来诊断正在发生的事情。

    编辑1:

      

    我尝试将输入的大小增加到5,000,000,但这没有帮助。顺序仍然比多线程更快。

    顺序版本更快,因为在您的示例中对每次迭代执行操作所花费的时间可能非常短,并且创建和管理多个线程涉及相当大的开销。

    并行编程只会在每次迭代在处理器时间方面足够昂贵时提高效率。

    要真正看到差异,请尝试增加此功能的工作。

    Evaluator::evaluateSingle(inpType &input) {
        // do stuff with input
        // note I use a vector<int> belonging to Evaluator object in here, not sure it matters though
    }
    

    编辑2:

      
        
    1. 与2,3相比,为什么创建4个或更多线程开始看到性能提升。
    2.   
    3. 为什么创建超过4个线程比仅4个线程(CPU可以运行的最大值)更有效   同时地)。
    4.   

    一个常见的建议是n + 1个线程,n是可用的CPU核心数。这样n个线程可以在1个线程正在等待磁盘I / O的情况下运行CPU。拥有更少的线程将无法充分利用CPU资源(在某些时候总会有I / O等待),拥有更多线程会导致线程争夺CPU资源。您还可以参考this answer进一步了解为什么更多线程是有益的。