我正在Chapel 中完成一种基于分布式存储桶排序的“Terasort Lite”程序,并且我注意到访问块分布式数组时似乎存在重大性能瓶颈。粗略的基准测试显示,Chapel 在 5 个语言环境下完成整个 3.5MB 的处理大约需要 7 秒,而原始的 MPI+C 程序在 5 个进程下大约需要 8.2 毫秒。我的本地计算机有 16 个核心,因此我不需要超额订阅 MPI 即可让 5 个进程正常工作。
要排序的数据被加载到每个区域设置的块分布式数组中,以便每个区域设置均匀(且连续)共享未排序的记录。在 MPI+C 桶排序中,每个进程都会将其记录存储在内存中并对这些本地记录进行排序。为此,我编写了一个支持区域设置的 qsort 实现(基于 C stdlib 实现),这就是我看到极端性能瓶颈的地方。整个桶排序过程引用块分布式数组,并qsort
使用本地子域调用:qsort(records[records.localSubdomain()])
从coforall
块和on loc do
子句内。
我的主要问题是 Chapel 如何保持分布式数组的一致性,以及跨区域的任何类型的一致性操作是否会影响我的性能。我已经检查过,每个语言环境的qsort
调用仅访问其本地子域内的数组索引;我希望这意味着不需要通信,因为每个区域设置仅访问其拥有的域的部分。这是一个合理的期望,还是同时访问分布式数组的私有部分会导致通信开销?
对于上下文,我使用UDP GASNET 通信底层在一台物理机上本地运行,文档指出这预计不会提供良好的性能。尽管如此,我确实计划将代码移动到具有 InfiniBand 的集群,因此我仍然想知道是否应该以不同的方式解决该问题以获得更好的性能。如果有任何其他信息可以帮助回答这个问题,请告诉我。谢谢你!
谢谢你的提问。我可以在这里回答一些问题。
首先,我想指出 Chapel 中的一些其他分布式排序实现:
一般来说,我希望基数排序对于局部问题的性能优于快速排序,除非它们非常小。
现在回答你的问题:
我的主要问题是 Chapel 如何保持分布式数组的一致性,以及跨区域的任何类型的一致性操作是否会影响我的性能。
默认情况下,远程数据的缓存处于打开状态。编译时可以禁用它--no-cache-remote
,但我怀疑这不是这里的问题。特别是,它主要在某种内存栅栏上执行任何一致性活动(包括 on 语句、任务结束、同步/原子变量的使用)。但是,您可以将其关闭,看看是否会改变情况。
分布式阵列和域当前使用急切的私有化策略。这意味着一旦创建它们,数据结构的某些元素就会在所有语言环境中复制。由于这涉及所有区域设置,因此在运行多区域设置时可能会导致性能问题。
您可以使用CommDiagnostics 模块或块local
检查内核内的通信情况。CommDiagnostics 模块将允许您计数或跟踪通信事件,而local
如果在其中尝试通信,该块将停止您的程序。
另一种可能性是编译器没有生成通信,但运行速度较慢,因为当数据可能是远程时,它无法进行优化。表明存在此问题的指标是,编译时获得的性能CHPL_COMM=none
明显快于使用 Gasnet 和 UDP 的 1 个语言环境运行时获得的性能。(您也可以使用--local
和--no-local
标志进行比较)。一些可能有帮助的方法:
records[records.localSubdomain()]
您可以尝试而records.localSlice(records.localSubdomain())
不是使用未记录的功能。但我不知道为什么它没有记录。local
块也应该可以解决这个问题,但请注意,我们通常会尝试以其他方式解决问题,因为块local
是一把大锤子。切片的开销超出了我们的预期。请参阅https://github.com/chapel-lang/chapel/issues/13317。正如我所说,可能还有私有化成本(我不记得目前切片和私有化的情况是什么,一时兴起)。根据我的经验,对于本地排序代码,最好将开始和结束参数作为整数或范围传递;但在分布式设置中,切片以获取数组的本地部分肯定更重要。
最后,您提到您正在尝试分析超额订阅运行时的性能。如果您还没有看过它,请查看有关超额订阅的文档,其中推荐了一个设置。