为啥使用超过 2 个线程会消耗更多时间?

Posted

技术标签:

【中文标题】为啥使用超过 2 个线程会消耗更多时间?【英文标题】:why using more than 2 threads consume more time?为什么使用超过 2 个线程会消耗更多时间? 【发布时间】:2017-02-20 13:06:53 【问题描述】:

我想优化我的顺序代码以制作渐变。

主线程计算图像边界的梯度,其他线程分别计算图像块的梯度, 使用 2 个线程和主线程提供的结果比顺序代码更好,但使用超过 2 个线程,但它消耗 更多 时间并且看起来比顺序代码最差。

我试过这段代码来加速渐变过程:

 for (int n = 0; n<iter_outer; n++)
        
            int chunk = 1 + ((row - 1) / num_threads); //ceiling
            int start=0;
            int end=0;
            //Launch a group of threads
            for (int tid = 0; tid < num_threads; ++tid)
            
                start = tid * chunk;
                end = start + chunk;
                t[tid] = thread(gradient, tid, g, vx, vy, row, col, 1, start, end);

            
            //Launched from the main;
            gradient(1, g, vx, vy, row, col,0, start, end);
            //Join the threads with the main thread
            for (int i = 0; i < num_threads; ++i)
            
                t[i].join();

            

        

【问题讨论】:

你的机器有几个核心?可能是 2 个? 每次在线程之间切换时,都需要时间来设置新的上下文。 对于短时间运行的线程,有一点是创建线程(以及在上下文之间切换)的开销抵消了拥有多个线程的速度增益。我怀疑你的图片太小了。 想象一下有 1000 个线程 - 程序必须不断地切换上下文以使所有线程都能获得一些工作,这会增加总执行时间 为了避免误解我之前的评论:即使您要处理一个巨大的图像,添加线程时您仍然不会看到线性速度增加。这是由于上下文切换的开销增加(正如其他人所指出的那样)。对于给定硬件上的给定工作负载,您可以确定最佳线程数量(例如通过猜测和微调)。 【参考方案1】:

对于任何并行执行,您必须考虑Amdahl's law。它指出并行执行某些任务所需的时间与处理器的数量不成线性关系:

t = ( (1-p) + p/n ) * T

在哪里

T is the time needed for the task when it is done sequentially
p fraction of time that can be parallelized
n is the number of processors

请注意,我使用了稍微不同的公式,但声明是相同的:您获得的总加速受到1/(1-p) 的限制(例如,如果p=50% 您的并行版本将运行最大两倍的速度)。

除此之外,您还必须考虑在现实中添加更多并行性也会增加更多开销(用于同步、设置线程等),因此更现实的估计是:

t = ( (1-p) + p/n ) * T  + o*p
                           ^^ overhead

这个t 作为处理器数量的函数p 具有一定数量的处理器的最小值。为问题添加更多处理器不会导致加速,而是会减慢速度,因为您需要执行 p 部分的最短时间为零,但通过添加更多处理器而增加的开销会无限增加。

这并不能解释为什么在您的情况下没有得到加速,但一般来说,简单地在任务上添加更多处理器并不总是会导致加速,这并不奇怪。

【讨论】:

首先感谢您的回复。我尝试使用 cuda 和 gpu 加速此代码,结果是正确的并且加速非常好,并且使用了许多达到 256 的线程,所以我认为并行化很好。但是在 CPU 上使用超过 2 个线程(如 3 个或 4 个)的线程比顺序的更糟糕!所以我问,在gpu上并行化但在cpu的多线程上不好是正常的吗?为什么?如果你有例子那就太好了。 @islamgad 我对 cuda 没有任何经验。请注意,我的回答仅概述了非常基础的内容,细节可能会涉及更多。例如,当您在处理器之间进行通信时,开销可能会像p*(p-1) 一样糟糕。也可能是您的问题规模太小而无法有效分配。等待其他人的线程可能会严重破坏您的加速。但是,我知道了解正在发生的事情的唯一可靠方法是测量。 感谢您的支持。【参考方案2】:

并行执行对于易于拆分且线程不依赖于自身的任务来说是一个巨大的好处,但是创建线程确实是有代价的。让我们想象一台计算机除了运行您的程序之外什么都不做(没有操作系统,也没有其他进程)。处理器有 2 个内核,它们本身就是处理器,可以同时运行任何代码。在只有一个线程的情况下,第二个核心就坐在那里,完全不做任何事情,因此有加速的潜力。如果你生成第二个线程(并给它 50% 的任务)第二个核心现在也可以工作,理论上加速是 2(忽略顺序部分和实际方面)。现在,让我们制作 4 个线程。等等...我们有两个处理器和 4 个线程?是的,现在每个 CPU 做的事情不止一件事,在更改其工作的任务之前,CPU 必须切换上下文(更改寄存器的值以保存适当的变量值,转到不同的代码部分等等)这需要时间和如果您创建的线程太多,实际上将花费比完成工作更多的时间。这可能会对任何线程应用程序产生巨大影响,在决定运行多少线程之前应注意。

请注意,这篇文章是为了简化许多现代 CPU 可以高效地运行每个内核一个线程以上(即超线程)。

【讨论】:

超线程并不意味着现代 CPU 可以在每个核心上高效地运行多个线程;只是他们可以在每个核心上一次运行多个线程。超线程仅获得大约 15% 的性能左右 - 远低于新内核 - 但超过零。它也更复杂,因为许多现代多核 CPU 如果只运行一个内核,运行速度会更快 - 因为过热的风险更小。【参考方案3】:

您的 CPU 似乎是双核的。所以,实际上,只能并行完成 2 个任务

【讨论】:

完全虚假的猜测。除了双核 CPU 能够并行运行超过 2 个任务(例如超线程)之外,这里的真正成本很可能是线程创建。这应该是最好的评论。 我的机器是core i7

以上是关于为啥使用超过 2 个线程会消耗更多时间?的主要内容,如果未能解决你的问题,请参考以下文章

为啥python线程会消耗这么多内存?

为啥使用线程池而不使用new Thread(runnable).start();

线程池ThreadPoolExecutor

线程池梳理

为啥增加工人数量(超过核心数量)仍然会减少执行时间?

为啥 gtkmm 有时会自动创建第二个线程?