Multithreading 如何在不同的处理器之间分配负载

Multithreading 如何在不同的处理器之间分配负载,multithreading,parallel-processing,intel,tbb,Multithreading,Parallel Processing,Intel,Tbb,我在一台有4个英特尔处理器,每个处理器上有8个内核的机器上运行一些并行代码。我使用的是TBB。假设一个给定的循环(我并行化)有X次迭代,我应该如何选择我的粒度以确保负载平均分配?假设您有N个同样强大的CPU 如果没有循环携带的依赖项(例如,迭代i中的任何内容都不会被后续迭代使用),那么您可以简单地在CPU 1上运行循环迭代0..X/N,在CPU 2上运行迭代(X/N)+1..(2*X/N),等等,假设每个迭代所花费的时间完全相同,或者至少平均时间不会有很大变化 如果有循环进行 依赖关系,如果迭代

我在一台有4个英特尔处理器,每个处理器上有8个内核的机器上运行一些并行代码。我使用的是TBB。假设一个给定的循环(我并行化)有X次迭代,我应该如何选择我的粒度以确保负载平均分配?

假设您有N个同样强大的CPU

如果没有循环携带的依赖项(例如,迭代i中的任何内容都不会被后续迭代使用),那么您可以简单地在CPU 1上运行循环迭代0..X/N,在CPU 2上运行迭代(X/N)+1..(2*X/N),等等,假设每个迭代所花费的时间完全相同,或者至少平均时间不会有很大变化

如果有循环进行 依赖关系,如果迭代i依赖于所有以前的迭代,您可能会遇到问题。如果它仅依赖于前k次迭代,则可以使用CPU1执行迭代0..X/N和CPU2执行迭代X/N-k..2*X/N,这会浪费一些工作,但允许CPU2收集所有处理器所需的结果,等等

如果迭代花费的时间变化很大,最好设置一个包含迭代的工作列表,
并让CPU在完成以前的迭代时从工作列表中获取迭代。这样,随着需求的出现,工作被划分。你必须确保每一个工作单元所花费的时间比完成工作所付出的努力要大得多,否则你将得不到同等的优势;一种方法是从工作列表中获取一个小范围的迭代,这样该范围内的总工作量就大大超过了调度开销。

使用TBB,您不必为并行任务选择粒度。在大多数情况下,默认情况下,TBB将动态地平衡工作。Ira Baxter的答案正确地描述了如何在线程池中划分工作;但是TBB已经有了类似的机制来为您实现这一点


补充:当然,在复杂的情况下,手动工作分区可能会得到更好的结果。虽然在这种情况下,可能需要使用TBB任务,因为并行_for可能无法提供足够的控制;例如,通常不可能指定每个线程块的确切大小。

迭代彼此完全独立。你为什么说X/2?我做X/4(因为有4个处理器?)但这似乎并没有给我想要的加速。还有,我怎样才能利用这4个处理器中每一个都有8个核的事实。@Manish:我误解了你的问题;我以为你只有2个CPU。如果你有N个CPU,你显然想把工作分成N个大小大致相等的部分。我其余的答案都是由把事物分成N等份所需要的细节驱动的;我修改了答案,用…/N代替了…/2tanks。内核也起作用了吗?我相信TBB会自动处理可用的内核,这是用户无法调整的参数?@Manish:我不知道TBB的具体情况,但大多数并行执行引擎为您提供了一些方法来指示您应该使用多少物理CPU。你必须检查TBB文件。如果您不知道要使用多少个内核,我会切换到单队列方案,无论您有多少个CPU都可以使用。那么您是说TBB库会自动识别我的4个处理器,每个处理器上有8个内核(在本例中)?我根据迭代次数动态更改grainsize,它的性能确实比没有任何grainsize的auto_partitioner要好。我不确定的是我是否获得了最大性能。是否有任何工具可以检查相同的性能?@Manish:你有理由认为有可能获得更好的性能吗?您是否做过性能或可伸缩性研究?至于性能分析工具,“英特尔(R)VTune放大器XE”可能是TBB应用程序的最佳工具,因为它了解某些TBB结构,可以以更有意义的方式显示信息。它的时间轴视图有利于负载平衡可视化。TBB如何知道循环携带的依赖关系?关于迭代的不均匀执行率?我相信它可能会处理无循环携带的依赖项,其本身大小大致相同,因为除以N非常简单。@Ira Baxter:我的回答是关于负载平衡。TBB不知道依赖关系,你是对的。对于不均衡的执行率,TBB通过工作窃取动态地平衡工作,因此能够应对不均衡。