我在一台有 4 个英特尔处理器和 8 个内核的机器上运行一些并行代码。我正在使用 TBB。假设一个给定的循环(我并行化)有 X 次迭代我应该如何选择我的粒度以确保负载平均分配?
2 回答
假设你有 N 个同样强大的 CPU。
如果没有循环携带依赖项(例如,后续迭代不使用迭代 i 中的任何内容),那么您可以简单地在 CPU 1 上运行循环迭代 0..X/N 和迭代 (X/N)+1..( 2*X/N) 在 CPU 2 等上,假设每次迭代花费的时间完全相同,或者至少平均时间没有太大变化。
如果存在循环携带的依赖关系,如果迭代 i 依赖于所有先前的迭代,您可能会遇到问题。如果它只依赖于前面的 k 次迭代,你可以让 CPU1 做迭代 0..X/N,而 CPU2 做迭代 X/Nk..(2*X/N),浪费一些工作,但让 CPU2 收集所有处理器所需的结果等。
如果迭代花费的时间变化很大,那么您最好设置一个包含迭代的工作列表,并让 CPU 在完成之前的迭代时从工作列表中获取迭代。这样,随着需求的出现,工作被分割。您必须确保每单位工作所花费的时间远远大于获得工作的努力,否则您将不会获得平行优势;一种方法是从工作列表中获取小范围的迭代,以使该范围内的总工作量大大超过调度开销。
使用 TBB,您不必为 parallel_for 选择粒度。在大多数情况下,默认情况下,TBB 会很好地对工作进行动态负载平衡。Ira Baxter 的回答正确地描述了您应该如何在线程池中划分工作;但是 TBB 已经有类似的机制可以为您执行此操作。
补充:在复杂的情况下,手动工作分区肯定会得到更好的结果。尽管在这种情况下可能需要使用 TBB 任务,因为 parallel_for 可能无法提供足够的控制;例如,通常不可能指定每个线程块的确切大小。