OpenMP 动态调度与引导调度

2024-05-03

我正在研究 OpenMP 的调度,特别是不同的类型。我了解每种类型的一般行为,但澄清一下何时进行选择会很有帮助dynamic and guided调度。

英特尔的文档 https://software.intel.com/en-us/articles/openmp-loop-scheduling描述dynamic日程安排:

使用内部工作队列给出块大小的循环块 每个线程的迭代。当一个线程完成时,它会检索 从工作队列顶部开始的下一个循环迭代块。经过 默认情况下,块大小为1。使用此调度时要小心 类型,因为涉及额外的开销。

它还描述了guided日程安排:

与动态调度类似,但块大小一开始就很大并且 减少以更好地处理迭代之间的负载不平衡。这 可选的块参数指定要使用的最小大小块。经过 默认块大小约为loop_count/number_of_threads。

Since guided调度在运行时动态减少块大小,为什么我会使用dynamic安排?

我研究过这个问题并且从达特茅斯找到这张桌子 https://www.dartmouth.edu/~rc/classes/intro_openmp/schedule_loops.html:

guided被列为具有high开销,同时dynamic具有中等开销。

这最初是有道理的,但经过进一步调查,我阅读英特尔文章 https://software.intel.com/en-us/articles/performance-obstacles-for-threading-how-do-they-affect-openmp-code关于这个话题。根据上表,我推测guided由于在运行时分析和调整块大小(即使使用正确),调度将花费更长的时间。然而,英特尔文章中指出:

引导式调度以小块大小为限制时效果最佳;这 提供最大的灵活性。目前尚不清楚为什么他们的表现会变得更糟 更大的块大小,但是当限制为大时,它们可能需要很长时间 块大小。

为什么块大小与guided花费的时间长于dynamic?由于缺乏“灵活性”,通过将块大小锁定得太高而导致性能损失是有道理的。然而,我不会将其描述为“开销”,并且锁定问题会质疑先前的理论。

最后,文章中指出:

动态时间表提供了最大的灵活性,但也占用了最大的空间 当计划错误时,性能会受到影响。

这是有道理的dynamic调度比更优化static,但为什么它比guided?我所质疑的只是开销吗?

This 有点相关的SO帖子 https://stackoverflow.com/questions/10850155/openmp-for-schedule解释与调度类型相关的 NUMA。这与这个问题无关,因为这些调度类型的“先到先服务”行为会丢失所需的组织。

dynamic调度可能是合并的,导致性能提高,但同样的假设应该适用于guided.

以下是英特尔文章中不同块大小的每种调度类型的时序,以供参考。它只是来自一个程序的记录,并且某些规则适用于每个程序和机器(尤其是调度),但它应该提供总体趋势。

EDIT(我的问题的核心):

  • 什么影响了运行时间guided安排?具体例子?为什么比慢dynamic在某些情况下?
  • 我什么时候会青睐guided over dynamic或相反亦然?
  • 解释完毕后,上述来源是否支持您的解释?它们完全矛盾吗?

什么影响引导调度的运行时间?

需要考虑三个影响:

1.负载均衡

动态/引导调度的重点是在并非每个循环迭代都包含相同工作量的情况下改善工作分配。从根本上来说:

  • schedule(dynamic, 1)提供最佳负载平衡
  • dynamic, k总会有相同或更好负载均衡比guided, k

该标准规定每个块的大小是成比例的未分配迭代的数量除以团队中的线程数量, 减少到k.

The GCC OpenMP 实施 https://github.com/gcc-mirror/gcc/blob/1cb6c2eb3b8361d850be8e8270c597270a1a7967/libgomp/iter.c从字面上理解,忽略成比例的。例如,对于 4 个线程,k=1,它将进行 32 次迭代8, 6, 5, 4, 3, 2, 1, 1, 1, 1。现在恕我直言,这真的很愚蠢:如果前 1/n 迭代包含超过 1/n 的工作,则会导致严重的负载不平衡。

具体例子?为什么在某些情况下它比动态慢?

好吧,让我们看一个简单的例子,其中内部工作随着循环迭代而减少:

#include <omp.h>

void work(long ww) {
    volatile long sum = 0;
    for (long w = 0; w < ww; w++) sum += w;
}

int main() {
    const long max = 32, factor = 10000000l;
    #pragma omp parallel for schedule(guided, 1)
    for (int i = 0; i < max; i++) {
         work((max - i) * factor);
    }
}

The execution looks like this1:

如你看到的,guided这里确实很糟糕。guided对于不同类型的工作分配会做得更好。也可以以不同的方式实施引导。 clang 中的实现(IIRC 源自 Intel)是更加复杂 https://github.com/llvm-mirror/openmp/blob/9c1e6c9bc10be652b92c40129c43c027aadc57b6/runtime/src/kmp_dispatch.cpp。我真的不明白 GCC 幼稚的实现背后的想法。在我看来,如果您给出,它实际上就违背了动态负载平衡的目的1/n工作到第一个线程。

2. 开销

现在,由于访问共享状态,每个动态块都会产生一些性能影响。开销为guided每块将略高于dynamic,因为还有更多的计算要做。然而,guided, k动态块总数将少于dynamic, k.

开销还取决于实现,例如它是否使用原子或锁来保护共享状态。

3. 缓存和 NUMA 效果

假设在循环迭代中写入整数向量。如果每隔一个迭代由不同的线程执行,则向量的每个第二个元素都将由不同的内核写入。这真的很糟糕,因为这样做会竞争包含相邻元素的缓存行(错误共享)。如果您的块大小较小和/或块大小与缓存不能很好地对齐,则块的“边缘”性能会很差。这就是为什么你通常更喜欢大的好(2^n) 块大小。guided平均可以给你更大的块大小,但不是2^n (or k*m).

这个答案 https://stackoverflow.com/a/10852852/620382(您已经引用过),详细讨论了 NUMA 方面的动态/引导调度的缺点,但这也适用于局部性/缓存。

不要猜测,要测量

考虑到各种因素和预测细节的难度,我只能建议使用特定的编译器在特定的系统上、特定的配置中测量特定的应用程序。不幸的是没有完美的性能可移植性。我个人认为,这对于guided.

我什么时候会喜欢引导而不是动态,反之亦然?

如果您对每次迭代的开销/工作量有具体的了解,我会说dynamic, k选择好的产品会给您最稳定的结果k。特别是,您不太依赖于实现的巧妙程度。

另一方面,guided可能是一个很好的初步猜测,具有合理的开销/负载平衡比,至少对于巧妙的实现而言。要特别小心guided如果您知道以后的迭代时间会更短。

请记住,还有schedule(auto),它可以完全控制编译器/运行时,并且schedule(runtime),它允许您在运行时选择调度策略。

解释完毕后,上述来源是否支持您的解释?它们完全矛盾吗?

对来源(包括这只雁)持保留态度。您发布的图表和我的时间线图片都不是科学上准确的数字。结果存在巨大差异,并且没有误差线,它们可能会因为这些很少的数据点而到处都是。该图表还结合了我提到的多种效果,但没有透露Work code.

[来自英特尔文档]

默认情况下,块大小约为loop_count/number_of_threads。

这与我的观察相矛盾,即 icc 可以更好地处理我的小例子。

1: Using GCC 6.3.1, Score-P / Vampir for visualization, two alternating work functions for coloring.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenMP 动态调度与引导调度 的相关文章

  • 计算 XML 中特定 XML 节点的数量

    请参阅此 XML
  • 为什么pow函数比简单运算慢?

    从我的一个朋友那里 我听说 pow 函数比简单地将底数乘以它的指数的等价函数要慢 例如 据他介绍 include
  • 如何在C(Linux)中的while循环中准确地睡眠?

    在 C 代码 Linux 操作系统 中 我需要在 while 循环内准确地休眠 比如说 10000 微秒 1000 次 我尝试过usleep nanosleep select pselect和其他一些方法 但没有成功 一旦大约 50 次 它
  • 查找进程的完整路径

    我已经编写了 C 控制台应用程序 当我启动应用程序时 不使用cmd 我可以看到它列在任务管理器的进程列表中 现在我需要编写另一个应用程序 在其中我需要查找以前的应用程序是否正在运行 我知道应用程序名称和路径 所以我已将管理对象搜索器查询写入
  • 函数参数的默认参数是否被视为该参数的初始值设定项?

    假设我有这样的函数声明 static const int R 0 static const int I 0 void f const int r R void g int i I 根据 dcl fct default 1 如果在参数声明中指
  • 使用可变参数包类型扩展的 C++ 函数调用者包装器

    我绑定了一些 API 并且绑定了一些函数签名 如下所示 static bool WrapperFunction JSContext cx unsigned argc JS Value vp 我尝试将对象和函数包装在 SpiderMonkey
  • unordered_map 中字符串的 C++ 哈希函数

    看起来 C 标准库中没有字符串的哈希函数 这是真的 在任何 c 编译器上使用字符串作为 unordered map 中的键的工作示例是什么 C STL提供模板专业化 http en cppreference com w cpp string
  • File.AppendText 尝试写入错误的位置

    我有一个 C 控制台应用程序 它作为 Windows 任务计划程序中的计划任务运行 此控制台应用程序写入日志文件 该日志文件在调试模式下运行时会创建并写入应用程序文件夹本身内的文件 但是 当它在任务计划程序中运行时 它会抛出一个错误 指出访
  • 告诉 Nancy 将枚举序列化为字符串

    Nancy 默认情况下在生成 JSON 响应时将枚举序列化为整数 我需要将枚举序列化为字符串 有一种方法可以通过创建来自定义 Nancy 的 JSON 序列化JavaScript 原始转换器 https github com NancyFx
  • 将 Long 转换为 DateTime 从 C# 日期到 Java 日期

    我一直尝试用Java读取二进制文件 而二进制文件是用C 编写的 其中一些数据包含日期时间数据 当 DateTime 数据写入文件 以二进制形式 时 它使用DateTime ToBinary on C 为了读取 DateTime 数据 它将首
  • 在 NaN 情况下 to_string() 可以返回什么

    我使用 VS 2012 遇到了非常令人恼火的行为 有时我的浮点数是 NaN auto dbgHelp std to string myFloat dbgHelp最终包含5008角色 你不能发明这个东西 其中大部分为0 最终结果是 0 INF
  • 如何在 C 中安全地声明 16 位字符串文字?

    我知道已经有一个标准方法 前缀为L wchar t test literal L Test 问题是wchar t不保证是16位 但是对于我的项目 我需要16位wchar t 我还想避免通过的要求 fshort wchar 那么 C 不是 C
  • 在屏幕上获取字符

    我浏览了 NCurses 函数列表 似乎找不到返回已打印在屏幕上的字符的函数 每个字符单元格中存储的字符是否有可访问的值 如果没有的话Windows终端有类似的功能吗 我想用它来替换屏幕上某个值的所有字符 例如 所有a s 具有不同的特征
  • Unity:通过拦截将两个接口注册为一个单例

    我有一个实现两个接口的类 我想对该类的方法应用拦截 我正在遵循中的建议Unity 将两个接口注册为一个单例 https stackoverflow com questions 1394650 unity register two inter
  • C++ new * char 不为空

    我有一个问题 我在 ASIO 中开发服务器 数据包采用尖头字符 当我创建新字符时 例如char buffer new char 128 我必须手动将其清理为空 By for int i 0 i lt 128 i buffer i 0x00
  • 实体框架中的“it”是什么

    如果以前有人问过这个问题 请原谅我 但我的任何搜索中都没有出现 它 我有两个数据库表 Person 和 Employee 对每个类型的表进行建模 例如 Employee is a Person 在我的 edmx 设计器中 我定义了一个实体
  • 堆栈是向上增长还是向下增长?

    我在 C 中有这段代码 int q 10 int s 5 int a 3 printf Address of a d n int a printf Address of a 1 d n int a 1 printf Address of a
  • 使用 MPI 的 Allreduce 对 Python 对象求和

    我正在使用使用 Python 中的字典和计数器构建的稀疏张量数组操作 我想让并行使用这个数组操作成为可能 最重要的是 我最终在每个节点上都有计数器 我想使用 MPI Allreduce 或另一个不错的解决方案 将其添加在一起 例如 使用计数
  • 是否可以在不连接数据库的情况下检索 MetadataWorkspace?

    我正在编写一个需要遍历实体框架的测试库MetadataWorkspace对于给定的DbContext类型 但是 由于这是一个测试库 我宁愿不连接到数据库 它引入了测试环境中可能无法使用的依赖项 当我尝试获取参考时MetadataWorksp
  • 如何将十六进制字符串转换为无符号长整型?

    我有以下十六进制值 CString str str T FFF000 如何将其转换为unsigned long 您可以使用strtol作用于常规 C 字符串的函数 它使用指定的基数将字符串转换为 long long l strtol str

随机推荐