【问题标题】:efficiency in multithreading多线程效率
【发布时间】:2011-05-24 21:29:05
【问题描述】:

假设我有这样的代码

for(i = 0; i < i_max; i++)
  for(j = 0; j < j_max; j++)
     // do something

我想通过使用不同的线程来做到这一点(假设 //do something 任务彼此独立,例如考虑蒙特卡罗模拟)。我的问题是:为 i 的每个值创建一个线程是否一定比为 j 的每个值创建一个线程更好?像这样的

for(i = 0; i < i_max; i++)
  create_thread(j_max);

另外:合适的线程数是多少?我应该只创建 i_max 线程,或者使用一个信号量,在任何给定时间同时运行 k

谢谢,

【问题讨论】:

  • 这听起来像是你想使用线程池,而不是增加大量线程。
  • @Anon:线程池只会缓解从根本上仍然存在的问题。但当然,这可能确实有帮助。

标签: c++ multithreading performance simulation


【解决方案1】:

分配工作负载的最佳方式是依赖于工作负载。

广泛 - 对于可并行化的工作负载,使用 OpenMP;对于异构工作负载,请使用线程池。如果可以,请避免管理自己的线程。

Monte Carlo 模拟应该是真正并行代码而不是线程池的良好候选者。

顺便说一下 - 如果您使用 Visual C++,Visual C++ v10 中有一个有趣的新 Concurrency Runtime 正是针对此类问题的。这有点类似于添加到 .Net Framework 4 以简化多核/多 CPU 代码的实现的任务并行库。

【讨论】:

  • 如果考虑MSVC自带的conclib,也可以考虑Intel的TBB。 threadingbuildingblocks.org
  • @John - 是的,这绝对是一个经过更好验证的替代方案。那是多平台吗?
  • 我不能肯定,但我有 35% 的把握答案是“是”
  • 做到 100%。来自 TBB 的常见问题解答:“该项目致力于支持所有编译器、所有操作系统和所有处理器,这是该项目的基石目标。网站上提供了有关状态的最新信息。”
  • TBB 的“Paralel For”也是我首先想到的。我也会为 TBB 提供 +1。
【解决方案2】:

避免创建线程,除非您可以让它们保持忙碌!

如果您的场景是计算密集型的,那么您应该将生成的线程数最小化为您希望代码运行的内核数。如果创建的线程数多于内核数,那么操作系统必须浪费时间和资源来调度线程以在可用内核上执行。

如果您的方案是 IO 密集型的,那么您应该考虑使用排队的异步 IO 操作,并在返回异步结果后检查响应代码。同样,在这种情况下,为每个 IO 操作生成一个线程是非常浪费的,因为您将导致操作系统不得不浪费时间来调度停止的线程。

【讨论】:

  • +1 用于提及工作线程和核心之间的关系。理想情况下,您甚至可以使用基于实际内核数的线程数,而不是预期的。
【解决方案3】:

这里的每个人基本上都是对的,但这是一种快速而简单的方法来拆分工作并让所有处理器保持忙碌。当 1) 创建线程的成本高于迭代中完成的工作 2) 大多数迭代需要大约相同的时间来完成时,这种方法效果最好

首先,为每个处理器/内核创建 1 个线程。这些是您的工作线程。他们一直闲着,直到他们被告知要做某事。

现在,拆分您的工作,使同时需要的数据的工作紧密结合在一起。我的意思是,如果你在两处理器机器上处理一个十元素数组,你会把它拆分,这样一组是元素 1,2,3,4,5,另一组是 6,7 ,8,9,10。您可能很想将其拆分为 1、3、5、7、9 和 2、4、6、8、10,但随后您将导致更多错误共享(http://en.wikipedia.org/ wiki/False_sharing) 在您的缓存中。

现在每个处理器都有一个线程,每个线程都有一组数据,您只需告诉每个线程处理一组独立的数据。

所以在你的情况下,我会做这样的事情。

for (int t=0;t<n_processors;++t)
{
  thread[t]=create_thread();
  datamin[t]=t*(i_max/n_processors);
  datamax[t]=(t+1)*(i_max/n_processors);
}

for (int t=0;t<n_processors;++t)
  do_work(thread[t], datamin[t], datamax[t], j_max)

//wait for all threads to be done

//continue with rest of the program.

当然,我忽略了诸如处理您的数据不是处理器数量的整数倍之类的事情,但这些很容易解决。

此外,如果您不反对第 3 方库,英特尔的 TBB(线程构建块)可以很好地从您那里抽象出来,让您可以开始真正想做的工作。

【讨论】:

  • 快速思考;不能保证所有核心都是平等的,两个核心可能使用相同的资源(例如超线程)。您需要实现某种“工作窃取”,以便早期完成的线程开始在较慢的线程上工作。学习是一件有趣的事情,但是真正写一个线程池是一个大项目。 ;)
  • 是的,整个窃取工作都是英特尔的 TBB 所做的,它比必须自己重新实现要好。如果您只是尝试对某些工作进行多线程处理,那么您只需付出很少的努力即可获得 80% 的性能。
【解决方案4】:

围绕创建和调用线程的一切都相对昂贵,因此您希望尽可能少地这样做。

如果你并行化你的内循环而不是外循环,那么对于外循环的每次迭代都会创建 j_max 个线程。 i_max 的顺序比您并行化外部循环的顺序要多。

也就是说,最好的并行化取决于您的实际问题。取决于此,并行化内部循环实际上是有意义的。

【讨论】:

  • +1 - 不希望线程管理的成本超过使用并行处理的好处。
【解决方案5】:

取决于任务以及您要在哪个平台上进行模拟。例如,在 CUDA 的架构上,您可以将任务拆分,以便每个 i,j,1 单独完成。

您仍有时间将数据加载到卡上以供考虑。

使用 for 循环和诸如 OpenMP/MPI/您自己的线程机制之类的东西,您基本上可以选择。在一种情况下,并行线程被中断,并且 j 在每个线程上按顺序循环。反之,依次处理一个循环,每次并行化出一个循环。

并行化(中断线程)成本很高。请记住,您需要设置 n 个线程,然后同步 n 个线程。这代表了超出例程运行时间的成本 c,其本身可以使并行处理的总时间大于单线程模式下的总时间。这取决于所讨论的问题;通常,存在一个临界尺寸,超过该尺寸后并行速度会更快。

我建议在第一个 for 循环中进入并行区域会更快。如果在内部循环中这样做,则每次循环运行时都必须 fork/join,从而为代码的速度增加了很大的开销。理想情况下,您希望只创建一次线程。

【讨论】:

    猜你喜欢
    • 2015-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-20
    • 2018-09-02
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    相关资源
    最近更新 更多