跳到主要内容
研究论文
开放式访问

高性能COWS:不规则并行回路的成本意识工作偷窃

出版:2024年1月19日 出版历史
  • 获取引文提醒
  • 摘要

    OpenMP等并行库使用特定于程序的调度策略在线程之间分配并行循环的迭代。虽然现有的调度策略在平衡工作负载的环境中表现得相当好,但在涉及高度不平衡工作负荷的计算中,获得有效的工作分配是极其重要的(即使使用动态和引导等非状态调度方法)。本文提出了一种称为COst感知工作窃取(COWS)的方案,以有效地将工作密封的思想扩展到OpenMP。
    与传统的窃取工作的调度器相比,COWS考虑到(i)并非并行循环的所有迭代都可能花费相同的时间。(ii)识别合适的盗窃受害者对于负载平衡很重要,以及(iii)队列会导致传统工作盗窃中的大量开销,应该避免。我们提出了COWS的两种变体:WSRI(一种基于剩余迭代次数的简单工作密封方案)和WSRW(一种根据剩余工作量的工作密封方案。由于在图分析中发现的不规则循环中,不可能静态计算并行循环的迭代成本,因此我们使用编译时间+运行时组合方法,其中循环的剩余工作量在运行时通过使用编译时间组件生成的代码有效计算。我们对七个不同的基准程序进行了评估,使用五个不同的输入数据集,在两个不同的硬件上跨不同数量的线程;总共有275种配置。我们表明,在275种配置中,有225种配置与该配置的最佳OpenMP调度方案相比,我们的方法获得了明显的性能提升。

    1引言

    随着多核系统成为主流,高效的并行计算已成为一种必要而非选择。现代并行应用程序中常见的一种模式是循环级并行。几乎所有的现代任务并行语言,如OpenMP[27],西尔克[1],礼拜堂[6],香港[5],X10[7]和其他一些工具内置了对并行前循环的支持,程序员可以使用并行前循环来并行化应用程序的主要计算任务。因此,并行应用程序的性能通常取决于并行前循环的性能,而并行前循环又取决于循环的迭代在运行时线程之间的分布方式;这个问题通常被称为循环调度问题。循环调度的常见优化技术之一是确保线程之间的负载不平衡最小化。
    在常规循环中,每次迭代中完成的工作都是可比较的,并且大多使用常规数据访问模式,因此实现良好的负载平衡相对容易。然而,当使用不规则循环时,循环调度问题变得非常具有挑战性,如以下示例所示。
    图1。
    图1.在许多IMSuite内核中发现的一种典型模式。
    1显示了许多IMSuite中的典型模式(在C++OpenMP中)[13]内核和许多图形分析内核。在第行1,的omp公司 平行的,平行的pragma创建一组线程,以并发执行并行区域(行210). 在第行,的omp公司 对于pragma定义了一个工作共享结构(parallel-for-loop),该结构在线程组之间分配并行循环的迭代(使用用户选择的调度方法)。因此,在线4,并联环路有g->N要在线程组中分布的独立迭代数。尽管循环的每个迭代都执行相同的代码,但执行每个迭代的时间取决于Line处的串行循环7.由于cur->度取决于输入,并随以下值的不同而变化,很难确定此类并行环路的最佳调度,尤其是当顶点的区域变化很大。例如,对于YouTube数据集(在第6)顶点的程度在1到28800之间变化。这是所有幂律图的典型情况[37],一种非常重要的输入类型;幂律图的一些示例包括:X(以前称为推特)(和其他社交媒体)连接图、引用网络、web-graph等[23].
    循环调度是一个NP-hard问题,过去已经提出了多种方法[4,24,25]来解决这个问题。在过去十年左右的时间里,有许多使用不规则并行循环的性能关键型应用程序,这给循环调度带来了额外的挑战。在使用不规则并行循环的应用程序中获得性能的一些当前方法包括静态调度[27],循环切换[25],深层块[29]、动态和有指导的调度[27],工作密封[1]在静态调度、循环切换和深度切换中,循环的迭代在循环开始执行之前在线程之间进行分配。虽然这种方案的开销很低,但它的缺点是,一旦迭代被分配给任何线程,它们就不会被重新分配给另一个线程,即使后者已经完成了其分配的迭代列表的执行。这可能会导致相当大的负载不平衡。最近,Booth和Lane[]给出了一个OpenMP基于受害者随机选择的工作密封调度方案(具有动态块大小)的实例,并表明其性能与OpenMP的动态/引导方案相当。OpenMP的这些动态和引导调度方案以及流行的工作密封方案允许在运行时分发迭代,它们有多个缺点:(1)它们会产生相当大的开销来维护工作队列(例如,Nandivada等人[25]表明工作密封运行时的性能比简单的块封装代码差得多),(2)确定最佳块大小对于不规则循环来说非常重要,(3)一旦将一组迭代分配给线程,就无法根据实际工作负载重新分配它们。
    为了解决这些问题,在本文中,我们提出了一个方案,该方案将工作密封的思想有效地扩展到OpenMP,称为成本意识工作偷窃(COWS).COWS为我们提供了两全其美:静态调度的低开销和工作台的动态负载平衡。
    我们介绍了COWS的两种变体:基于剩余迭代的工作密封(WSRI)基于剩余工作量的工作密封(WSRW)对于WSRI,我们在一个有效的数据结构中为每个线程维护剩余的迭代。对于WSRW,我们使用混合编译时间和运行时方法:编译时间分析发出特定于应用程序的代码以有效估计工作负载(在运行时),剩余的工作负载在运行时通过执行此代码来计算。在WSRI和WSRW中,我们修改后的工作窃取调度器分别使用剩余的迭代和剩余的工作负载来选择合适的受害者。尽管我们在OpenMP环境中提出了COWS的概念,但它也可以用于支持并行循环的其他并行语言(如Cilk、X10、Chapel等)。
    我们的贡献:
    我们提出了一种称为COWS的新技术,用于不规则并行循环的负载平衡。COWS支持两种不同的方案,它们在选择受害者的方式上有所不同:基于剩余迭代的工作密封(WSRI)和基于剩余工作负载的工作密封。
    我们提出了一种无排队成本感知的工作窃取算法来选择工作密封的受害者。这是基于一个有效的方案来维护每个线程的剩余迭代。
    我们在IMOP编译器框架中实现了COWS的静态编译器组件(与WSRW相关),在GCC中的OpenMP共享库libgomp中实现了运行时组件(与WSRI和WSRW都相关)。我们研究了COWS在运行在两个不同硬件系统上的七个流行共享内存内核上的性能,并表明对于幂律图,COWS的性能明显优于循环、静态、动态、引导和WSR(带有随机受害者选择的工作密封)调度。我们还发现,在粗规则图和COWS的上下文中,相对于其他图形,COWS的性能有了合理的改进。
    论文的其余部分组织如下。我们简要介绍了第节中一些重要相关概念的背景2.在第节中,我们描述了我们提出的具有成本意识的工作窃取(COWS)方案。在节中4,我们讨论了四种新的优化,以提高所提COWS方案的性能。在节中5,我们讨论了我们提出的方案的一些显著特征。我们在第节中详细评估了我们的方案6我们在第节中讨论了相关工作7并在第节中总结8.

    2背景

    OpenMP中的调度方案。OpenMP支持四种调度策略(静态、动态、引导和运行时)来映射迭代并联回路的N个迭代)到其中一个T型线程。(i)静止的schedule:指定为的调度方法静态[,ch].可选参数中国可以由程序员传递以指定块大小和迭代被映射到线程(i/ch)%T。我们将静态调度的特殊情况称为中国=1,作为循环的,循环的分块。当选项中国未传递,则设置为默认值不适用; 我们用这个词静止的schedule表示此默认调度策略。(ii)动态schedule:调度方法指定为动态[,ch]。每次线程空闲时,最多分配一个线程中国(默认值=1)次迭代。(iii)引导明细表:明细表方法指定为导向[,ch]。每次线程空闲时,都会为其分配最大值(中国,remaining-interrations/number-of-idle-threads)迭代次数。(iv)运行时调度:在运行时通过读取环境变量推断调度策略(OMP_计划),则必须将其设置为上述三种策略之一。为了实现我们建议的工作密封方法,程序员必须将schedule子句声明为运行时并设置OMP_计划两个拟议方案之一:WSRW公司WSRI公司.
    图2。
    图2OpenMP并行前循环(a)和GCC(b)对其转换的草图。
    图3。
    图3.螺纹总体控制流程\(t_i),其当前迭代列表是\(语言t_j,x_i,y_i等级)预订规模为\(c).
    并联回路的平移。2显示了现有GCC编译器如何使用运行时调度转换并行前循环。翻译后的代码由每个线程执行。我们借助这个转换方案来调用我们提出的工作密封算法(在第节3.4.2).
    偷窃工作。在工作窃取调度中,每个线程最初都被赋予一组要执行的迭代。当一个线程自己的工作用完时,它会识别出一个牺牲品并从中窃取一些工作。通常,分配给每个线程(或被每个线程窃取的)的工作(例如,并行循环的迭代子集)存储在队列中。

    成本意识到工作偷窃

    传统的工作密封调度器尽管有其优点,但仍存在多个问题,尤其是在应用于并行循环时:(i)调度器不知道不同任务的工作量,并考虑到所有任务都是相等的,(ii)选择合适的被盗对象以改进负载平衡是一项挑战,(iii)维护队列的开销可能很高,并且(iv)这些调度程序的设计目的是高效地处理显式创建的单个任务列表(而不是像OpenMP并行循环那样的循环迭代)。在本节中,我们将介绍我们提出的解决这些问题的COst感知工作窃取(COWS)方案的详细信息。我们的调度方案以不规则任务并行程序中的并行操作为目标,其迭代分布在不同的运行时线程之间。
    COWS方案不维护任何全局队列。相反,for-loop的迭代最初分布在所有运行时线程之间(例如,总计\(T\)线程数)。为了提高效率,我们假设这个初始分布是使用循环分布(迭代\(i)分配给具有thread-id的线程\(i\%T\)). 为了便于解释,我们使用下标符号\(t_j)用螺纹id表示螺纹\(j)与典型的工作密封一样,一旦线程用完工作(分配给它的迭代次数),线程就会尝试从其他线程窃取工作。在这个过程中,我们的方案试图窃取剩余工作量最大的线程。我们现在在下面详细介绍我们的方案。

    3.1有效维持剩余工作量

    考虑到维护队列以存储分配给任何线程的任务的开销,我们使用了两个关键洞察力:(i)分配给任意线程的迭代的初始列表\(t_i)不需要存储,可以使用线程id随时推断\(i),线程总数\(T\),以及迭代总数\(否); 比如说\(N=10\),\(T=3\),然后是分配给的初始迭代列表\(t_0)是[0,3,6,9]。对于任何线程,我们将这个迭代的初始列表称为initial-iteration-list。(ii)在执行期间的任何时间点,线程可能正在从其initial-iteration列表执行迭代,或者从另一个线程的initial-iertation列表中偷取迭代。的当前迭代列表\(第i次)线\(t_i)可以表示为三元组\(语言t_j,x_i,y_i等级),其中\(t_j)代表\(j^{th}\)螺纹,\(x _ i \)指示的初始迭代列表中迭代的索引\(t_j)那个\(t_i)必须执行,并且\(y_i\)指示要执行的剩余迭代数\(t_i)从的首字母缩写列表中\(t_j)从开始\(x _ i \).

    3.2线程的控制流

    描述了任何线程的总体控制流的流程图。为了提高效率,我们允许一个参数\(c)(详见第节5)表示保留大小;每个线程保留\(c)每次从其current-iteration列表中迭代并执行它们,并保证没有线程可以从这些保留的迭代中窃取。一根线\(t_i),带有当前迭代列表\(语言t_j,x_i,y_i等级),首先检查剩余迭代数是否为非零(\(y_i\)).
    如果\(y_i\gt 0),线程首先最多原子保留\(c)当前迭代列表(框)中的迭代). 然后它执行这些\(c)迭代顺序。该方案确保了没有其他线程可以在\(t_i)正在执行这些迭代。在流程图中,带有粗体边界的灰色框表示关键部分。
    如果\(y_i\le 0\),\(t_i)会成为小偷并试图选择受害者线索\(_k\)。我们将在本节稍后讨论选择合适受害者的不同方案。如果\(t_i\)可以找到受害者\(_k\),然后\(t_i)将尝试从当前迭代列表中自动窃取剩余工作量的一半\(_k\)(盒子B类). 我们窃取了一半的迭代,因为根据剩余的迭代次数,它会导致最小的关键路径。这种偷窃包括(原子上)在受害者和小偷的当前重复列表中设置适当的值。注意,当小偷\(t_i)当选择一个牺牲品并试图设置其当前迭代列表时,牺牲品会继续执行其迭代,并且可能会继续完成更多的迭代\(t_i)在盒子里观察到F类因此,在继续执行被盗迭代之前,窃贼必须检查被盗迭代列表是否有非零迭代(\(y_i\gt 0)). 有关此改进的工作密封算法的更多详细信息,请参阅第节3.5。只要找到受害线程,每个线程都会继续此循环。
    例子。假设一个并行循环中有10次迭代。4显示了如果运行时有3个线程,不同的线程如何维护其迭代列表,以及\(c)= 1. initial-iteration-list与开头(第3行)中的current-iteration-list相同。由于每个线程都有一些迭代要执行,因此它们将保留\(c)当前迭代列表中的迭代。第4行显示了在第一次预订之后更新的当前迭代列表。假设\(t2)完成所有剩余的迭代,而其他线程仍在执行其第一次迭代;第5行显示了当前迭代列表。现在\(t2)成为一个小偷,并会选择一个受害者。假设它选择\(t_0)作为受害者并成功地从中窃取了工作(剩余迭代的一半=1次迭代)。第6行显示了窃取后的当前迭代列表。假设不再发生窃取,线程完成了指定的迭代,第7行显示了当前迭代列表的最终值。
    图4。
    图4。说明线程如何维护其迭代列表。
    在工作盗窃中,选择合适的受害者是获得高性能执行的重要考虑因素。另一个同样重要的考虑因素是选择受害者所需的时间,因为同样的管理费用可能会掩盖偷工作带来的收益。对于随机选择受害者的方案,由于窃贼可能要窃取迭代的大量次数,因此可能会产生大量开销,因此其效率会受到很大影响。我们称这种方案为WS-Random(简称WSR)。
    我们现在讨论两种选择受害者的策略,以使(i)我们最小化偷窃次数,(ii)受害者选择的开销最小。我们的两种策略都受到了一种简单直觉的启发,即小偷应该选择剩余工作量最大的受害者,这反过来可能会减少偷窃次数并提高整体性能。

    3.3剩余迭代作为剩余工作的估算

    由于我们只关注于在线程之间分配并行循环的迭代次数,因此对于测量任何线程剩余的工作量,最直观的估计之一是分配给该线程的剩余迭代次数。为了能够使用这个度量,小偷需要能够获得每个可能的受害者的剩余迭代次数。这很容易获得:窃贼可以迭代每个线程的当前迭代列表(通常线程数是一个较小的数字),以获得剩余迭代次数最大的受害者。我们将这种方案称为WS-Remaining迭代(简称WSRI)。

    3.4使用剩余工程作为成本估算

    当一组线程正在执行并行循环时,线程剩余迭代次数的度量可能无法准确估计当前分配给线程的剩余工作负载。在不规则任务并行程序的环境中尤其如此,在这种情况下,每次迭代的成本可能会有很大差异。为了应对这一挑战,我们提出了一种基于剩余工作量的工作密封方案(简称WSRW)。
    由于迭代代码可能依赖于输入,因此在编译期间静态估计迭代的成本可能是不可能的。例如,对于图中所示的代码片段1,每次迭代的邻域上进行并行迭代第个节点。因此,每次迭代的成本取决于第个节点,无法静态估计。为了解决这些问题,受之前认识到这种困难的工作的启发[29,33],我们建议使用静态+动态混合技术,其中我们首先将每次迭代的成本估计为一个在某个输入变量上参数化的函数。在前面的示例中,迭代的成本可以表示为elem_at(&g->顶点,i)->度我们采用这样的参数成本表达式,并使用它们来计算运行时的实际成本。5显示了整个流程的框图,包括编译时组件和运行时组件。
    图5。
    图5总体技术:灰色方框是我们技术的主要组成部分。
    图6。
    图6.运行时工作负载估计器代码,刚好在并行前循环之前发出,其for-loop格式为 对于(i=0;i<loopBound;i++){…} .

    3.4.1编译时组件。

    首先对输入的OpenMP C程序进行分析(插入指令并执行),以获得代码中每个与输入无关的部分的成本;我们使用了类似于施里瓦斯塔瓦和南迪瓦达的技术[33]. 编译的下一阶段(如图所示5(a) )有两个主要步骤:(i)成本表达式生成器和(ii)运行时工作初始值设定项发射器。
    成本表达式生成器。我们使用概要文件生成的成本为代码的依赖输入和独立部分生成成本表达式。这个成本表达生成器使用了Shrivastava和Nandivada的方案[33,第3.1节,图7]生成成本表达式(成本支出)对于每个并联回路的主体;我们在附录中对其进行了简要总结A类例如,在图中所示的代码中1,行56、和810与输入无关。在第行说出输入独立语句的成本\(i)由提供\(C_{i}\)类似地,对于for-loop来说\(C)_{{7}-1}\)给出初始化语句的成本,\(C)_{{7}-2}\)给出谓词的代价,并且\(C)_{{7}-3}\)给出了增量语句的成本。然后由获取WLShrivastava和Nandivada的功能[33]因为并行前循环的主体将是\(C_{{5}}\)+\(C_{{6}}\)+\(C)_{{7}-1}\)+(节点*)elem_at(&g->顶点,i)->度*(\(C)_{{7}-2}\)+\(C{8}}\)+\(C{9}}\)+\(C_{{10}}\)+\(C)_{{7}-3}\))。在这种情况下获取WL函数无法获得并行循环迭代的准确成本估算(例如,如果迭代包含while-lop),那么对于该并行循环,我们中止WSRW方案,并返回WSRI。
    运行时工作负载估计器发射器。正如我们之前所讨论的,我们的目标是发出一些小代码,以便在运行时估计每个迭代的剩余工作量。由于一个程序可能包含多个并行循环,并且它们在不同循环中迭代的工作量(简而言之,成本)可能不同,因此我们可能必须分别为每个并行循环发出工作量估算代码。
    对于每个并行循环,在生成成本表达式后,我们发出如图所示的代码6它估计该循环每次迭代的工作量。这里,我们使用T型表示运行时线程的数量(可以通过调用omp获取num线程功能)。对于每个线程,我们需要能够维护(并检索)分配给它的每个迭代的成本=第三天),我们维护(针对每个线程第三天)一个长整数数组,这样第个元素将保留第个分配给线程的迭代第三天这将需要一个额外的循环(在该数组上迭代)来计算每个线程的剩余工作负载。出于效率原因,我们转而存储\(\mbox{\tt TArr}\)[第三天],作为前缀和数组,从而更容易快速计算每个线程的剩余工作负载。线910显示维护前缀和的代码。例如,对于具有current-iteration-list的线程\(语言t、x、y),剩余工作量可以通过评估获得TArr公司[\(t\)][\(x+y \)]\(-\)TArr公司[\(t\)][\(x \)]。需要注意的一点是,我们使用系统调用重新分配在执行工作负载估计器代码之前分配内存(如果需要)。
    例子。对于图中所示的代码1,可以通过替换变量来获得发出的工作负载估计器代码loopBound(循环绑定)使用术语g->N在图中6.

    3.4.2运行时组件。

    5(b) 显示了我们建议的方案的运行时组件。在执行任何并行循环之前,每个线程计算在运行时分配给它的总工作负载(通过调用类似于图中所示的代码6).
    每个OpenMP并行前循环(由GCC)转换为三个部分(如图所示2):(i)指定初始迭代列表,(ii)执行指定的迭代,以及(iii)获得下一个要执行的迭代列表。最后两部分在循环中执行,直到有迭代要执行。与这三个部分相对应,运行时组件有三个子组件。虽然我们设计的前两个子组件与当前典型的OpenMP转换相匹配,但最后一个子组件有所不同,我们调用了成本敏感的工作密封算法。
    图7。
    图7.工作稳定算法。使用的缩写:
    CIL=当前迭代列表.
    图8。
    图8.WSRW受害者查找方案。\(T\)=线程数。使用的缩写:CIL=current-iteration-list。

    3.5具有成本意识的偷工算法

    我们现在讨论我们的工作密封算法(如图所示7),它实现了图中所示流程部分的工作密封部分我们的算法有四个有趣的特性:(i)算法可以同时被多个线程调用;(ii)当多个小偷试图确定谁将首先偷窃时,受害人没有被锁住;(iii)根据剩余工作量选择受害者;(iv)该算法避免了队列的使用,而是利用了当前迭代列表的有效结构。
    该算法使用两个锁,一个是所有线程共享的全局锁,另一个是每个受害者唯一的本地锁。窃贼在试图选择受害者之前,会持有全局锁,并确保每次只有一个窃贼选择受害者。这样可以确保两个小偷不应该同时选择同一个受害者。通过调用方法来选择受害者找到受害者.
    算法 找到受害者 .此算法的详细信息因工作密封方案的类型而异:例如,对于WSRI,它调用中描述的代码3.3对于WSRW,图8显示的代码找到受害者方法。该代码与WSRI的代码类似,不同之处在于,在WSRW中,我们选择剩余迭代次数最多的受害者\(t_i),我们使用先前填充的前缀和数组\(\mbox{\tt TArr}\)(行18).
    在图中7在找到受害者后,小偷设置了busyStatus标志并打开了全局锁。这种状态标志的设置可以确保在该标志设置为false之前,其他小偷无法选择该受害者。选择受害者后,我们获取该受害者线程的本地锁;每个线程都有一个本地锁,该锁是该线程在从自己的current-iteration列表中保留迭代之前获取的(图,框). 我们根据Work Divider函数分配迭代。请注意,在这种设计中,全局锁的使用时间很短,只是为了找到受害线程(而不是实际的窃取,这可能需要更长的时间)。然后,我们获取受害者线程资源上的单个锁,从而在很大程度上减少窃贼之间的争用。
    图9。
    图9.WSRW方案的工作划分器。
    工作划分器算法该算法的细节因工作密封策略而异。算法9给出了WSRW方案的WorkDivider函数的详细信息。理想情况下,我们的目标是窃取受害者线程剩余工作量的一半。但由于我们窃取了循环的迭代次数,我们最终可能窃取了剩余工作量的一半或不到一半。我们确定最早迭代的索引(steepPt),这样受害者的迭代,从\(x \)要窃取Pt,至少要有阈值(设置为剩余工作负载的一半)工作负载量。剩余迭代(从steepPt+1到\(年\))可以被偷;该算法返回steepPt。对于WSRI,WorkDivider只返回\(y-y/2\)(使用整数除数),如果受害者的当前迭代列表为\(\langle t_j,x,y\langle\).
    在图中7,如果我们发现steepPt(表示要偷的迭代列表的起始索引)与\(年\)(受害者的最后一次迭代),则表示没有要窃取的迭代。否则,在解锁之前,我们首先更新受害者的当前迭代列表\(本地版本\)然后,我们重置受害者的忙标志(使其可用于其他小偷的偷窃),并设置当前小偷的重复列表。

    4优化

    选项1:冗余初始化。第节中提出的WSRW方案发出代码以填充TArr公司数组在每个并行前循环。很多时候,相同的并行环路会连续运行多轮;例如,直到达到定点标准;例如,在page-rank中,执行一个并行操作,直到页面等级的差异在两个连续轮次之间没有“显著”变化。在这种情况下TArr公司一次又一次可能是多余的。我们通过在并行前发出额外的代码来解决这个问题,以检查之前是否立即执行了相同的并行前循环。如果是这样,我们就不填充TArr公司而是重用以前填充的值。只有当循环的迭代次数和循环迭代的成本表达式字符串中的变量在循环的两次连续调用之间不发生变化时,才能应用此优化。
    选项2:每次迭代具有相同成本的循环。我们提出的方案主要针对迭代成本不均匀的循环。因此,我们确定了循环的统一并行,其中所有迭代执行的工作量完全相同,然后使用默认的循环调度。只有当循环的迭代次数和循环迭代的成本表达式字符串中的变量在循环的两次连续调用之间不发生变化时,才能应用此优化。这种优化避免了WSRW不必要的初始化开销,因为我们知道对此类循环使用循环可以获得更好的性能。
    图10。
    图10。拓扑驱动算法的说明性伪代码。
    选项3:用谓词保护循环体。我们在一些内核中发现的一种模式是,在每次调用并行循环时,只有并行循环迭代的子集执行计算。例如,图10显示了显示类似行为的说明性伪代码。我们提出的成本估算方案(第节3.4)将if-else语句的成本计算为then-block和els-block的最大成本,因此可能导致不准确的工作估计(特别是当活动节点的数量可能是总节点数的一小部分时)。在这种情况下,由于WSRI的管理费用较低,我们再次依赖它。
    选项4:优化的WorkDivider。图中所示的WorkDivider函数7,遍历线程的所有剩余迭代,以确定变量steepPt的值。此算法效率低下,因为每个线程的迭代次数可能很大。我们利用TArr公司(前缀和数组)和一种类似于二进制搜索的策略,以设计一种更有效的方法来查找steepPt的值。而不是从开始顺序迭代\(x \)\(x+y \)(在图中9),我们使用中间点(\((2*x+y)/2\))将数组分成两半。每一半的工作量可以计算为\(O(1)\)时间:左右两半的功可以用表达式计算\({\t TArr}[(2*x+y)/2+1]-{\t TArr}[x]\)\({\tt TArr}[y]-{\tt TArr}[(2*x+y)/2]\)分别是。如果他们将工作负载精确地分为两半,那么我们将steepPt设置为中间点。否则,我们重复这个分而治之的策略来找到分割,这样受害者的迭代\(x \)要窃取Pt,至少要有阈值(设置为剩余工作负载的一半)工作负载量。此WorkDivider算法效率更高–复杂性为\(O(对数N)\)(与复杂性相比\(O(N)\)对于图中所示的代码9),其中\(否)是并行运算的迭代次数。

    5讨论

    重组数据vs.计划。在我们的研究中,我们发现不仅调度技术,而且输入图的数据布局都会影响并行循环的性能。这是因为数据布局可能会影响节点/边缘到线程的分布(从而影响负载平衡),以及每个线程的数据访问位置。我们生成了同一图的多个版本,其中每个版本都有不同的组织(例如,顶点按度排序,顶点按宽度优先搜索(BFS)图中的级别排序,等等)。我们针对同一输入图的不同版本运行不同的程序(每个程序都有固定的时间表)。虽然我们无法确定任何特定的重新排序方案是跨不同基准测试的最佳方案,但我们仍然认为,探索基于程序行为更改数据布局以改善负载不平衡和局部性的想法是一项有趣的未来工作。
    静态调度。静态调度是OpenMP提供的调度之一,在编译时将循环迭代分配给线程。在OpenMP提供的所有其他调度中,此方案的开销最小。虽然众所周知,当所有迭代都有统一的工作量时,静态调度工作得很好,但我们发现对于工作量不均匀的迭代,循环调度(参见第节2)与OpenMP提供的其他调度方案相比,在处理大型现实世界无标度图和大致规则图时,通常工作得更好。以前的工作也报告了循环分布(相对于静态计划)的这些优点[18,32]. 静态和循环调度工作得更好的原因是,OpenMP为这些方案提供了高度优化的实现,这些方案涉及的开销最小(不涉及锁或调用调度器等)。
    需要注意的一点是,当时间表设置为“runtime”时,在执行过程中,我们将环境变量OMP_NUM_THREADS设置为“static”(或static,1),然后我们发现,与引导/动态方案相比,静态方案(静态和循环)花费的时间更多/可比时间。但是,当我们将调度设置为“static”或“static,1”时,OpenMP使用一个高效的方案来实现这些调度策略,并且开销最小,这在许多基准测试中都提高了效率。因此,我们使用这些有效的基准OpenMP数进行比较。
    动态/指导调度。可以说,通过调整块大小可以提高动态和引导调度的性能。然而,调整每个应用程序的块大小是一项困难且耗时的任务。因此,我们在实验中坚持默认的块大小。
    我们发现,在引导/动态方案缺乏正确的块大小的情况下,对于静态/循环可能无法提供最佳负载平衡的不规则回路,我们提出的方案最为有效。
    COWS中使用的参数。COWS中使用了两个参数:(i)保留大小\(c)(见图)以及(ii)最小迭代限制,低于该限制,我们将不会从任何受害者那里窃取迭代。我们做了很多实验,并以此为基础\(c)=\(\sqrt[4]{\mbox{total-workload}}\)最小迭代极限=5。请注意,对该空间进行彻底搜索以研究不同公式的行为,并提出一个方案来确定最佳可能保留大小和最小迭代极限本身是一项具有挑战性的任务,但超出了本文的范围。我们把它作为一个有趣的未来工作。
    确定最佳调度策略。在节中6,我们表明,对于各种内核,与OpenMP的默认调度策略相比,我们提出的技术表现非常好,尤其是在不规则内核的情况下。然而,我们要注意的是,不同调度策略的比较效率不仅取决于单个调度策略,还取决于手头的特定内核和输入的确切组织。这两个因素都极大地影响了开销、负载平衡,甚至数据位置。因此,很难确定跨所有内核及其所有可能输入的最佳调度策略。我们将确定任何给定并行内核和输入的最佳调度策略留作将来的工作。此外,对于不同的内核和输入集,对不同的调度策略与最佳可能调度之间的差距进行极限研究将是一项有趣的未来工作。这将为未来的研究人员指明改进的最大范围。
    锁定的可能开销。在图中7在第一个关键部分,只有小偷才需要上锁(而不会挡住工人)。在第二个关键部分中,锁定仅在窃贼和工人之间进行(注意:锁的开销主要取决于争用线程的数量)。这些启发法确保了低管理费用,这在我们的经验评估中也得到了证实(第节6.3).

    6实施和评估

    如第节所述,我们提出的技术有两个组件:编译时组件和运行时组件。我们已经在IMOP中实现了我们建议的编译时组件[26]是一个用于OpenMP程序的开源编译器框架(源到源),可以方便地编写编译器传递。我们的实施跨越了\(1.3千\)行Java代码。我们已经在libgomp中实现了我们建议的运行时组件,这是GCC-11.2中OpenMP(版本:4.5)的共享库;此运行时组件跨越\(1.4k\)C代码行。运行时组件的实现方式是,现有GCC编译器生成的二进制代码将根据环境变量的值使用我们建议的COWS调度器(OMP_计划). 要使用所讨论的任何调度程序,程序必须设置日程安排子句中的parallel-for循环运行时并将此环境变量的值适当设置为WSRW公司,WSRI公司,或WSR(水下反应堆)为了进行比较评估,我们还使用了OpenMP提供的调度程序(静态、循环、动态或引导)。对于动态和引导,我们使用默认的块大小(=1)。我们选择了一个众所周知的固定块大小[]找到能够为所有基准测试提供最佳性能的正确块大小是一项相当艰巨的任务。此外,我们坚持OpenMP的默认chunk-size,以免在确定一个或一组固定的chunk-size时受到批评。对于这三个默认调度程序,我们设置日程安排子句,而不是使用“runtime”调度选项,因为后者的效率相对较低。所有输入代码都是用\(-\)GCC的O2水平优化。
    我们提出的技术在图中所示的七个流行的共享内存内核上进行了评估11:K委员会(KC)–创建委员会,使委员会中没有两名成员相距甚远\(\gt\)预定义的值\(K\); 通用图的领导者选举(LE)-在并行/分布式环境中查找领导者;宽度优先搜索–计算根图的直径;贝尔曼·福特(BF)–计算每个节点与根的距离;支配集–找到给定图的支配集;页面排名(PR)–迭代计算图中每个节点的秩;连接组件(CC)–计算图中连接组件的数量;前五个内核取自IMSuite[13](我们排除了其余实现随机算法的内核,因为它们的基本执行时间变化很大),剩下的两个内核是基于先前文献中使用的内核编写的[30]. 11还列出了这些内核的一些特性。这些包括代码行数、静态并行循环数、在上述五个输入的上下文中遇到的动态并行循环数,以及不同的优化(在第节中讨论4)这适用于基准测试。我们看到,在KC的情况下,输入端的动态并行环路数量是相同的。这是因为KC中并行环路的数量仅取决于\(K\)(社区成员之间的最大距离)–恒定值,与输入无关。
    为了生成与输入无关的语句的成本,分析是使用非常小的输入(随机生成的32个节点的图形)完成的。我们发现总编译时间开销可以忽略不计。
    所有这些内核都有一个共同的特征,即它们至少有一个不规则的循环。给定倾斜的输入(例如,幂律图),这些代码会导致工作负载高度不均匀,并行循环的迭代之间存在大量不平衡。由于我们提出的方案的主要目标是实现代码和输入的高性能,从而导致工作负载不均匀,因此我们将首先关注这些工作负载。为此,我们从SNAP中选择了三个输入图(即斯坦福大型网络数据集集合)[23]:YouTube、LiveJournal和Orkut图表。除了测试我们提议的系统在高度非均匀工作负载环境下的效率外,我们还针对输入测试了我们的代码,其中并行循环迭代之间产生的工作负载更加一致——这是我们最坏的情况,因为我们提议的方案的开销最为显著。为此,我们从SNAP中选取了两个大致规则的输入图:RoadNet-CA和RoadNet-PA图12显示了所有这些数据集的一些特征。
    图11。
    图11.用于评价的基准及其特点。缩写:LOC:代码行;SL:#static parallel-for-loops;UL:#统一并联环路;DA、DB、DC、DD和DE分别是YouTube、LiveJournal、Orkut、RoadNet-CA和RoadNet-PA的#动态并行环路;Opt1、Opt2、Opt3和Opt4是第节中讨论的四种优化4.
    图12。
    图12所用数据集的特征。
    我们对两个系统进行了评估:(i)Aqua:高端超级计算集群中的一个节点,包含一个Intel(R)Xeon(R)Gold 6248处理器,带有两个插槽,每个插槽有20个内核(总共40个内核),总内存192GB;(ii)Goodwill:一个独立服务器,其中包含一个Intel-Xeon,每个具有18个启用SMT的核(共36个核);导致最多72个硬件线程,总内存为125GB。
    对于每个基准内核,我们评估了七种不同的调度方案:i)动态,ii)静态,iii)循环,iv)引导,v) WSR、vi)WSRI和vii)WSRW。我们研究了这些调度方案在不同数量的硬件线程(2次方)下的性能。具体来说,我们特别关注循环调度,我们发现(在我们的实验中)在大多数情况下,它是OpenMP支持的默认调度中最好的。用于在具有\(T\)硬件线程数,我们设置软件线程数(OMP_NUM_螺纹)也适用于\(T\).
    为了理解我们提出的技术的影响,我们从五个方面进行了评估:(i)高度不规则图形环境下的性能提升,(ii)大致规则图形环境中的行为,(iii)受害者选择的开销,(iv)窃取操作次数的研究,以及(v)拟议优化的影响。

    6.1高度不规则图的性能增益

    在本节中,这些图显示了与OpenMP动态调度相比,在三个幂律图输入中,由于各种调度方案而获得的加速。在OpenMP的不同默认调度方案中,最佳调度方案取决于特定的基准、输入和运行时线程数。我们发现,在大多数情况下,循环调度在默认调度方案中表现最佳。因此,对于每个内核,我们额外强调了WSRI和WSRW相对于循环调度的性能(作为地理平均值)。
    图13。
    图13.Kernel=KC。动态调度方面的加速。连接这些点只是为了提高可见性。
    KC公司。13显示了KC内核获得的加速比。对于给定的配置(输入数据集、系统和硬件线程数),我们使用以下公式计算由于调度X导致的内核加速比:(使用dynamic-schedule执行内核所需的时间)/(使用调度X执行内核所用的时间)。
    我们的方案WSRW和WSRI明显优于所有其他方案。总的来说,我们看到Aqua系统的加速在2.47之间变化\(\次\)至10.741\(\次\)用于WSRW和2.479\(\次\)至10.66\(\次\)同样,在亲善系统上,加速比在1.538之间变化\(\次\)至8.899\(\次\)适用于WSRW和1.529\(\次\)至8.522\(\次\)用于WSRI。
    可以看出,WSRW的性能与WSRI相当。这是因为,在这里,作为工作负载主要来源的两个并行前循环发生了中断,因此我们WSRW退回到WSRI(请参阅第节3.4.1).
    与周期调度相比,对于不同数量的核心和所有三个输入,WSRW(和WSRI)为1.10\(\次\)和1.03\(\次\)商誉和Aqua系统分别更好。
    在Aqua系统上,当线程数量从16个增加到32个时,性能无法以相同(高)的速率扩展。这是因为它是一台双套接字机器,当线程数超过20个内核时(因为Aqua上的每个套接字都有20个内核),套接字之间的通信开销就会开始。Goodwill上也有类似的趋势,每个插座都有18个芯。其次,当我们将线程从32增加到64或72时,性能收益会有所下降。这是因为当我们将硬件线程的数量增加到36个以上时,我们开始在SMT线程上运行内核,而在SMT的线程上,性能不会像常规内核那样进行扩展。我们在其他四个内核上也观察到了类似的基于硬件的行为。
    图14。
    图14.Kernel=LE。动态调度方面的加速。连接这些点只是为了提高可见性。
    拉丁美洲。14显示了LE内核获得的加速比。总的来说,对于LE内核,WSRW明显优于所有其他方案。总的来说,我们看到Aqua系统的加速比在1.115之间变化\(\次\)至6.171\(\次\)用于WSRW和1.1\(\次\)至4.265\(\次\)同样,在亲善系统上,加速比在1.048之间变化\(\次\)至5.443\(\次\)用于WSRW和0.998\(\次\)至3.761\(\次\)用于WSRI。
    可以看出,WSRI和Cycle在Aqua上的性能是可比较的。可以看出,WSRI和cyclic在Aqua上的表现是可以比较的,但在Goodwill上WSRI表现更好。我们将此归因于Aqua上更快的内存子系统导致更少的开销。在LE上,我们可以观察到不同调度方案之间的近似顺序:WSRW、WSRI、循环、WSR、引导、静态、动态。唯一的例外是在Orkut输入中,我们看到静态和引导的性能比动态的差。我们在其他内核(PR、DS和CC)中也发现了类似的静态和引导退化。静态调度性能下降的原因是朴素调度策略导致的高负载不平衡。由于(i)无法在分配后重新分配,以及(ii)对锁定的激烈竞争,引导方案受到了影响。
    与周期计划相比,对于不同数量的核心和所有三个输入,WSRW为1.13\(\次\)和1.18\(\次\)分别在Aqua和Goodwill系统上表现更好。类似地,与周期计划相比,WSRI为1.01\(\次\)和1.05\(\次\)分别在Aqua和Goodwill系统上表现更好。
    图15。
    图15.Kernel=BFS。动态调度方面的加速。连接这些点只是为了提高可见性。
    BFS公司。15显示了BFS内核获得的加速比。总的来说,对于BFS内核,WSRW、WSRI和cyclic在大多数情况下都优于其他内核。此外,很难在这三者中找到BFS在这些投入上的明显赢家。总的来说,我们看到Aqua系统的加速比在1.17之间变化\(\次\)至9.98\(\次\)用于WSRW(和WSRI)。同样,在亲善系统上,加速比在1.233之间变化\(\次\)至17.352\(\次\)用于WSRW(和WSRI)。由于Opt3的应用,WSRW和WSRI的性能看起来相似(第节4)在主环路上。
    与循环调度相比,对于不同数量的内核和所有三个输入,WSRW(和WSRI)为0.95\(\次\)和1.09\(\次\)分别在Aqua和Goodwill系统上表现更好。WSRW和WSRI的性能与BFS环境中的循环性能相比略低的原因是,在这个内核中,尽管WSRI和WSRW支付了各自的开销,但工作量估计仍然不准确。因此,与循环调度(一种具有最小开销的高效调度方案;参见第节5). 然而,WSRW和WSRI的性能优于其余的调度方案。
    图16。
    图16.Kernel=BF.动态调度方面的速度。连接这些点只是为了提高可见性。
    高炉。16显示了BF内核获得的加速比。总的来说,我们看到,对于BF内核,WSRW优于所有其他方案,但Orkut和LiveJournal输入的Goodwill上的72个线程除外,其中引导方案略优于WSRW。我们认为,在72个线程上启用SMT所产生的一些低级问题将导致引导调度的性能相对提高。WSRI和Cycle在许多方面都表现出色。总的来说,我们看到Aqua系统的加速比在2.21之间变化\(\次\)至5.64\(\次\)用于WSRW和2.21\(\次\)至5.49\(\次\)同样,在亲善系统上,加速比在1.02之间变化\(\次\)至7.39\(\次\)对于WSRW和0.98\(\次\)至7.2\(\次\)用于WSRI。
    与周期计划相比,对于不同数量的核心和所有三个输入,WSRW为1.06\(\次\)和1.05\(\次\)在Aqua和Goodwill系统上分别表现更好。类似地,与循环时间表相比,WSRI为0.98\(\次\)和1.0\(\次\)分别在Aqua和Goodwill系统上表现更好。
    图17。
    图17.Kernel=DS.动态调度方面的速度。连接这些点只是为了提高可见性。
    DS。17显示了DS内核获得的加速比。Orkut(在两个系统上)的基本内核内存不足,而LiveJournal则需要数小时才能完成一次运行。因此,我们只对YouTube(在Aqua和Goodwill上)和LiveJournal(在Aqu上)进行了评估。总的来说,对于DS内核,WSRI在大多数情况下都优于WSRW,WSRW是第二好的调度。该内核中有12个并行for循环,因此TArr初始化的开销对WSRW的性能有轻微影响。总的来说,我们看到Aqua系统的加速比在1.05之间变化\(\次\)至1.28\(\次\)用于WSRW和1.02\(\次\)至1.31\(\次\)同样,在亲善系统上,加速比在1.11之间变化\(\次\)至1.64\(\次\)适用于WSRW和1.2\(\次\)至1.77\(\次\)用于WSRI。
    与周期计划相比,对于不同数量的核心和所有三个输入,WSRW为1.01\(\次\)和1.10\(\次\)分别在Aqua和Goodwill系统上表现更好。类似地,与周期计划相比,WSRI为1.04\(\次\)和1.20\(\次\)分别在Aqua和Goodwill系统上表现更好。
    图18。
    图18.Kernel=PR.动态调度方面的速度。连接这些点只是为了提高可见性。
    公共关系。18显示了PR内核获得的加速比。总的来说,对于PR内核,WSRW和(在很大程度上)WSRI明显优于所有其他方案。总的来说,我们看到Aqua系统的加速比在1.085之间变化\(\次\)至8.103\(\次\)用于WSRW和1.062\(\次\)至6.218\(\次\)同样,在亲善系统上,加速比在1.006之间变化\(\次\)至6.024\(\次\)对于WSRW和0.98\(\次\)至5.364\(\次\)用于WSRI。
    与周期计划相比,对于不同数量的核心和所有三个输入,WSRW为1.15\(\次\)和1.16\(\次\)分别在Aqua和Goodwill系统上表现更好。类似地,与周期计划相比,WSRI为1.03\(\次\)和1.10\(\次\)分别在Aqua和Goodwill系统上表现更好。
    图19。
    图19.Kernel=CC.动态调度方面的速度。连接这些点只是为了提高可见性。
    复写的副本。19显示了为CC内核获得的加速。总的来说,我们看到,对于CC内核,尽管WSRW的性能大多优于WSRI、WSR、循环、静态和引导,但WSRW、WSRI和循环在许多方面的性能都相当。总的来说,我们看到Aqua系统的加速比在0.711之间变化\(\次\)至2.952\(\次\)用于WSRW和0.718\(\次\)至2.87\(\次\)同样,在亲善系统上,加速比在0.703之间变化\(\次\)至3.279\(\次\)用于WSRW和0.671\(\次\)至2.867\(\次\)用于WSRI。
    Orkut输入数据集显示了一个有趣的点:动态调度执行其余部分。我们认为这种特殊行为是因为Orkut数据集中数据的组织方式。
    与周期计划相比,对于不同数量的核心和所有三个输入,WSRW为1.06\(\次\)和1.12\(\次\)分别在Aqua和Goodwill系统上表现更好。类似地,与周期计划相比,WSRI为1.03\(\次\)和1.08\(\次\)分别在Aqua和Goodwill系统上表现更好。
    总结。在五个不同的基准测试程序中,使用三个不同的幂律输入数据集,在两个不同的硬件上,在不同数量的线程上,我们显示WSRW和WSRI平均(geomean)性能提高了1.10\(\次\)和1.05\(\次\),分别(最多2个\(\次\)和1.57\(\次\)分别)在循环调度(OpenMP提供的默认调度中的总体最佳调度)上。此外,考虑到这些增益是在GCC-O2级优化代码之上获得的,我们认为这些增益是显著的。

    6.2粗略正则图的行为

    我们提出的调度方案的主要目标是导致并行循环迭代之间高度不平衡的图。为了理解我们提出的调度方案可能产生的负面影响,我们使用相同的核集合对其进行了评估(图11)在大致规则的图形数据集(RoadNet-CA和RoadNet-PA,如图所示12). 为了简单起见,我们只显示了各个硬件系统上可用内核的最大数量的性能结果;我们观察到,其余岩芯的数量相似。
    20显示了WSRW和WSRI的性能,如循环调度的加速比所示,这是默认OpenMP调度技术中的总体最佳技术;为了简单起见,我们跳过了与其他时间表的比较。总的来说,我们可以看到WSRW的性能略优于周期调度,而WSRI的性能稍差。请注意,尽管这些输入大致是规则的,但并行操作的迭代并没有完全相同的工作量,因此会导致不平衡(尽管与幂律图相比要小一些),COWS可以利用这种不平衡。WSRW显示了WSRW的开销由改进的负载平衡产生的收益补偿的收益。与WSRI相比,我们认为WSRW性能更好,因为它使用了分析信息,而WSRI没有。
    图20。
    图20WSRW和WSRI在大致规则图上的循环调度上的速度。线程数=最大内核数。
    在两个数据集和两个硬件中,我们发现WSRW的加速比为1.07\(\次\), 1.34\(\次\), 0.96\(\次\), 0.97\(\次\), 1.01\(\次\), 1.17\(\次\)和1\(\次\)分别用于KC、LE、BFS、BF、DS、PR和CC。类似地,在两个数据集和两个硬件中,我们发现WSRI的加速比为0.97\(\次\), 0.9\(\次\), 0.96\(\次\), 0.73\(\次\), 0.97\(\次\), 0.92\(\次\)和0.90\(\次\)分别用于KC、LE、BFS、BF、DS、PR和CC。我们看到,即使在大致规则图的上下文中,WSRW也表现得相当好,由于其开销没有太大影响。
    图21。
    图21.受害者选择的管理费用。

    6.3受害者选择的管理费用

    受害者选择的间接费用有两个主要组成部分:(i)TArr阵列的初始化成本,以及(ii)小偷寻找受害者所支付的成本(可能不属于关键路径的一部分)。我们发现总开销只占执行时间的一小部分。例如,图21显示了Aqua系统上不同基准的受害者选择开销。可以看出,平均开销非常低(0.47%)。这证明了我们提出的优化方案的有效性。商誉的结果同样低;为了简洁起见,省略了数字。
    图22:。
    图22与WSR相比,WSRI和WSRW方案的窃取操作数量减少了.%。

    6.4偷窃行动数量研究

    我们还比较了不同工作密封调度程序(WSR、WSRI和WSRW)执行的成功窃取操作的数量。22与WSR相比,WSRI和WSRW的窃取操作数量有所减少。虽然这种减少并不直接对应于性能的提高,但这种改进确实表明负载平衡可能会更好。此外,我们可以看到,与WSRI相比,WSRW的窃取操作数量要少得多。这是因为WSRW的窃取是基于剩余工作负载的数量,而不是基于剩余迭代次数;这反过来导致WSRW中更好的负载平衡,这反映在窃取次数更少。
    这一发现与人们的共识是一致的,即抢断次数过多或过少都不一定有益。例如,经典的调度方案不执行任何窃取操作,但性能仍然较差,因为它们完全失去了工作密封的好处。
    图23。
    图23各种优化对WSRW的影响。加速基准:没有优化的WSRW。

    6.5优化的影响

    研究拟议优化的影响(第节4)我们比较了WSRW的性能,包括有和没有提出的优化。23显示了由于我们提出的每个优化而获得的几何平均性能增益(在所有五个数据集中);内核是使用每个系统上可用硬件内核的最大数量来运行的。从图中回忆11并非所有优化都适用于所有基准,因此对于每个基准,图23仅显示适用于优化的条形图。在适用的情况下,Opt1和Opt3提供了最显著的性能提升。各种优化的影响范围为1.02\(\次\)至1.45\(\次\)关于Aqua和1.01\(\次\)至1.33\(\次\)亲善。
    总体评估总结。总的来说,我们发现与OpenMP支持的默认调度方案相比,我们提出的调度技术在并行循环及其迭代之间的高工作负载不平衡的情况下可以带来明显的收益,同时在并行循环环境中为我们提供了可接受的性能,在迭代之间或多或少平衡了工作负载。我们对七个不同的基准程序进行了评估,使用五个不同的输入数据集,在两个不同的硬件上跨不同数量的线程(导致总共275个配置),结果表明,在275种配置中的225种配置中,与该配置的最佳OpenMP调度方案相比,WSRW实现了明显的性能提升(最多2个\(\次\)). 在剩下的50个配置中,除了一个配置的降级率为30%(图19(b) ,Orkut图,16个线程),最坏情况下降级不超过10%。此外,与OpenMP的默认方案不同,我们不需要在COWS中手动选择块大小,仍然可以获得高性能。我们还表明,所提出的优化是有效的。

    7相关工作

    循环调度。有许多工作超出了OpenMP支持的默认调度方案集(静态、动态、引导)[17,21,24,27]. 特别是考虑到调度不规则并行循环的挑战,在这个方向上已经有了许多有重点的尝试。LLVM中实现了许多工作共享调度策略。[19]结果表明,在所有基准测试中,LLVM的调度策略都没有显著优于其他策略。大多数情况下,他们的方案与动态/制导方案类似。相反,如第节所示6,我们提出的方案优于(最多4个\(\次\))这两个在所有七个基准内核中,用于所有输入,用于不同数量的线程(CC内核和Orkut输入除外)。BinLPT公司[28]是一个工作负载感知的循环调度程序,它从用户那里获取并行循环的工作负载估计值,以分配迭代。Thoman等人。[35]还提出了一种使用编译器和运行时相结合的方法来计算并行循环的“工作量估计函数”的方案。他们的技术的一个主要问题是,他们的方案主要适用于没有堆访问的仿射循环。帕布和南迪瓦达[29]将此思想扩展到处理任意循环。所有这些方案的一个主要问题是,它们在循环开始执行之前分配循环的迭代,并且没有根据实际执行动态重新分配迭代的规定。相比之下,我们的方案通过基于剩余工作量的估计执行工作密封来实现动态负载平衡。使用一些先前的循环调度技术来扩展我们的工作,以改进处理器关联性和缓存局部性,这将很有意思[9,10,15,16,34].
    档案引导优化。为了提高并行程序的性能,很长一段时间以来一直使用概要文件引导优化。例如,Chen等人。[8]使用剖析改进进程到不同处理器以及Shrivastava和Nandivada的映射[33]使用剖析来迁移线程并调整处理器的频率以节省能源。类似地,Kejariwal等人。[20]和公牛[4]使用剖析(历史)信息有效地分块未来的并行循环实例。在本文中,我们使用轮廓引导信息来计算并行循环的成本,这有助于有效地选择工作密封的受害者。这总体上提高了我们方案的性能。
    工作密封。另一种流行的平衡并行环路非均匀工作负载的方法是工作密封[1,2,12,14]. 之前的工作[31,36]已经表明,尽管Cilk支持任务并行的工作密封,但对于数据并行循环,OpenMP的性能仍显著优于Cilk。Durand等人。[11]证明偷走受害者一半的迭代次数是一个有效的策略。我们通过偷取迭代来即兴解决这个问题,从而将受害者的工作量减少一半。我们表明,我们提出的具有成本意识的工作窃取方案优于基线OpenMP。最近,Booth和Lane[]使用基于动态块大小的调度(受害者随机选择)提供工作密封,该调度使用循环执行的历史记录来标识每个线程的“保留大小”。他们只将其方案与动态/引导(而不是与循环(OpenMP中性能最好的方案之一)进行比较,并表明其性能与引导/动态相当。相比之下,我们提出了一种具有成本意识的工作窃取方案,该方案使用剩余的工作负载来识别受害者和预留大小。此外,我们的方案开销非常低,总体性能优于OpenMP支持的几乎所有调度(包括循环调度)。
    已有前期工作[11,22]执行NUMA感知的工作密封。我们相信,我们提出的技术可以用这些思想进行扩展,以在NUMA感知系统中实现改进的性能。

    8结论

    本文提出了一种方案,通过考虑每个线程的剩余工作量,将工作密封的思想有效地扩展到OpenMP;我们称我们的方案为COst感知工作窃取(简称COWS)。我们给出了COWS的两种变体:WSRI(基于剩余迭代次数)和WSRW(基于剩余工作量)。在内部,COWS使用分配工作的有效表示,并以最低的开销执行工作密封。我们评估了我们在七个不同的基准内核上的实现,使用五个不同的输入数据集,在两个不同的硬件上,针对不同数量的线程(导致总共275个配置),并显示在275个组态中的225个组态中,与该组态的最佳OpenMP调度方案相比,我们的方法实现了明显的性能提升。我们认为,COWS实现了两全其美:静态调度的低开销和工作窃取的动态负载平衡。

    A附录

    为了完整起见,我们重申了Shrivastava和Nandivada成本表达式生成器方案的重要方面[33,第3.1节,图7]生成成本表达式(成本支出)对于每个并联回路的主体;感兴趣的读者可以在他们的论文中看到完整的细节。
    图24。
    图24函数计算OpenMP并行前循环每次迭代的工作量。
    功能获取WL将并行for循环的成本表达式分为两部分:输入相关部分(在字符串变量dynamicC中)和输入依赖部分(在整数变量staticC中)。通过串联dynamicC和staticC得到表示并行前循环迭代成本的最终表达式;我们使用\(\垂直\)运算符连接两个字符串。该函数迭代parallel for循环的所有语句。如果语句是循环,获取WL生成一个字符串,表示一个表达式,该表达式将循环绑定乘以循环迭代的开销(包括循环前缀开销和循环体),并将该字符串附加到dynamicC。此乘法确保两次迭代在运行时使用不同的循环表达式值执行相同的语法代码片段,将具有不同的工作负载。功能获取循环绑定尝试识别(并以字符串形式返回)表示循环绑定的封闭形式表达式。如果无法计算循环表达式(例如,while-lop),则获取循环绑定保守地返回一个特殊常量CL。如果任何被调用的函数(获取循环绑定,或获取WL)返回CL,然后获取WL返回CL。此特殊的大小写在图中没有明确显示24,以便于解释。如果语句是条件语句,获取WL生成一个表达式来计算语句的保守最坏情况成本(包括评估谓词的成本)。\(最大值(Y1,Y2)\),扩展到\(``(^{\prime\prime}\Vert Y1\Vert``\ge^{\prime\prime}\Vert Y2\Vert``)^{\prime\prime}\Vert Y1\Vert``:^{\prime\prime}Y2\)。如果当前语句是简单语句(既不是循环也不是条件语句),则获取WL按当前语句的静态开销递增staticC。

    工具书类

    [1]
    Robert D.Blumofe、Christopher F.Joerg、Bradley C.Kuszmaul、Charles E.Leiserson、Keith H.Randall和Yuli Zhou。1995.Cilk:高效的多线程运行时系统。SIGPLAN不是。30,8(1995年8月),207216。
    [2]
    罗伯特·布鲁莫夫(Robert D.Blumofe)和迪奥尼西奥斯·帕帕佐普洛斯(Dionisios Papadopoulos)。1998Hood:多程序多处理器的用户级线程库技术报告。
    [3]
    Joshua Dennis Booth和Phillip Allen Lane。2022.自适应自调度循环调度器。中央对手方清算所34, 6 (2022).
    [4]
    J.马克·布尔。1998年。反馈引导的动态循环调度:算法和实验。欧洲标准普尔. 377–382.
    [5]
    Vincent Cavé、Jisheng Zhao、Jun Shirako和Vivek Sarkar。2011年,哈巴内罗-爪哇:旧X10的新冒险。美国医学会,51–61。内政部:
    [6]
    B.L.Chamberlain、D.Callahan和H.P.Zima。2007.并行编程和Chapel语言。IJHPCA公司21, 3 (2007), 291–312.内政部:
    [7]
    Philippe Charles、Christian Grothoff、Vijay Saraswat、Christopher Donawa、Allan Kielstra、Kemal Ebcioglu、Christoph von Praun和Vivek Sarkar。2005.X10:非均匀集群计算的面向对象方法。SIGPLAN不是。40,10(2005年10月),519-538。内政部:
    [8]
    胡晨、陈文光、黄健、鲍勃·罗伯特和H.库恩。2006年。MPIPP:一个用于SMP集群和多集群的自动配置文件引导并行进程放置工具集。内部控制系统.
    [9]
    Q.Chen、M.Guo和Z.Huang。2012.CATS:基于多套多核架构中在线评测的缓存感知任务密封。内部控制系统.
    [10]
    亚历杭德罗·杜兰(Alejandro Duran)、朱利塔·科尔巴兰(Julita Corbalan)和爱德华·艾瓜德(Eduard Ayguade)。2008年。任务并行性的自适应截止值。SC'08:2008 ACM/IEEE超级计算会议记录. 1–11.内政部:
    [11]
    Marie Durand、François Broquedis、Thierry Gautier和Bruno Raffin。2013年,针对大型NUMA机器上不规则应用程序的高效OpenMP循环调度程序,第8122卷。内政部:
    [12]
    马特奥·弗里戈(Matteo Frigo)、查尔斯·雷瑟森(Charles E.Leiserson)和基思·兰德尔(Keith H.Randall)。1998年。cilk-5多线程语言的实现。SIGPLAN不是。33、5(1998年5月)、212–223。内政部:
    [13]
    苏亚什·古普塔和V.Krishna Nandivada。2015.IMSuite:模拟分布式算法的基准套件。JPDC公司75, 0 (2015), 1–19.
    [14]
    罗伯特·霍尔斯特德(Robert H.Halstead)。MULTILISP:并发符号计算语言。托普拉斯1985年10月7日、4日,501–538。内政部:
    [15]
    B.Hamidzadeh、L.Y.Kit和D.J.Lilja。2000.使用在线优化的动态任务调度。IEEE传输。平行配送系统。11, 11 (2000), 1151–1163.内政部:
    [16]
    B.Hamidzadeh和D.J.Lilja。1994.自调整调度:用于本地管理和负载平衡的在线优化技术。ICPP公司. 39–46.
    [17]
    Susan Flynn Hummel、Edith Schonberg和Lawrence E.Flynn。1991.因子分解:调度并行循环的一种实用且稳健的方法。SC程序美国医学会,610–632。
    [19]
    弗兰齐斯卡·卡西尔克(Franziska Kasielke)、罗尼·切特(Ronny Tschüter)、克里斯蒂安·伊万斯基(Christian Iwainsky)、马库斯·维尔滕(Markus Velten)、弗洛里娜·乔尔巴(Florina M.Ciorba)和伊奥娜·巴尼塞。2019.探索OpenMP中的循环调度增强功能:LLVM案例研究。ISPDC公司. 131–138.
    [20]
    阿伦·凯加里瓦尔(Arun Kejariwal)、亚历山德鲁·尼科劳(Alexandru Nicolau)和君士坦丁·D·多时罗普洛斯(Constantine D.Polychronopulos)。2006.历史软件自我调度。2006年国际并行处理会议(ICPP’06). 185–192.内政部:
    [21]
    C.P.Kruskal和A.Weiss。1985.在并行处理器上分配独立的子任务。IEEE标准SE-11,10(1985),1001-1016。内政部:
    [22]
    维维克·库马尔。2020年。PufferFish:使用弹性任务的NUMA感知工作密封库。高性能个人计算机. 251–260.
    [23]
    Jure Leskovec和Andrej Krevl。2014.SNAP数据集:斯坦福大型网络数据集收集。http://snap.stanford.edu/data(2014年6月)。
    [24]
    史蒂文·卢科。1992年。不规则并行程序的动态调度方法。SIGPLAN不是。27,7(1992年7月),200-211。内政部:
    [25]
    V.Krishna Nandivada、Jun Shirako、Jisheng Zhao和Vivek Sarkar。2013年。优化任务并行程序的转换框架。托普拉斯35,1,第3条(2013年4月),48页。内政部:
    [26]
    A.Nougrahiya和V.Krishna Nandivada。2018.IIT Madras OpenMP(IMOP)框架。(2018)。
    [27]
    OpenMP体系结构审查委员会。2021.OpenMP应用程序接口5.2版。(2021年11月)。https://www.openmp.org/wp-content/uploads/openmp-API规范-5-2.pdf
    [28]
    P.Penna、A.Gomes、M.Castro、P.D.M.Plentz、H.Freitas、F.Broquedis和J.-F.Mehaut。2019.BinLPT工作负载感知循环调度程序的综合性能评估。中央对手方清算所31, 18 (2019), 1–22.
    [29]
    Indu K.Prabhu和V.Krishna Nandivada。2020年。非均匀工作负载的分块循环。内部控制系统ACM,第40条,12页。内政部:
    [30]
    Anchu Rajendran和V.Krishna Nandivada。2020年DisGCo:分布式图形分析编译器。墨西哥煎玉米卷第17、4条,第28条(2020年9月),26页。内政部:
    [31]
    Solmaz Salehian、Jiawen Liu和Yonghong Yan。2017.线程编程模型比较。2017 IEEE国际并行和分布式处理研讨会(IPDPSW). 766–774.
    [32]
    Jun Shirako、Jisheng M.Zhao、V.Krishna Nandivada和Vivek N.Sarkar。2009年。在同步的情况下阻塞并行环路(ICS’09)。计算机协会,181-192。内政部:
    [33]
    Rahul Shrivastava和V.Krishna Nandivada。2017年,不规则任务并行循环的节能编译。ACM塔科第14、4条,第35条(2017年11月),共29页。内政部:
    [34]
    S.Subramaniam和D.L.Eager。1994.不平衡工作负载的相似性调度。联合国安全理事会IEEE出版社,214–226。
    [35]
    彼得·托曼、赫伯特·乔丹、西蒙·佩莱格里尼和托马斯·法林格。2012.自动OpenMP循环调度:编译器和运行时相结合的方法。异构世界中的OpenMP. 88–101.
    [36]
    Ashkan Tousimojarad和Wim Vanderbauwhede。2014.Intel Xeon Phi上三种流行并行编程模型的比较。2014年欧洲汽车展:平行加工车间斯普林格国际出版公司。
    [37]
    谢聪(Cong Xie)、凌燕(Ling Yan)、李武军(Wu Jun Li)和张志华(Zhihua Zhang)。2014.分布式幂律图计算:理论和实证分析。神经信息处理系统研究进展,第27卷。Curran Associates公司。

    索引术语

    1. 高性能COWS:不规则并行回路的成本意识工作偷窃

        建议

        评论

        信息和贡献者

        问询处

        发布于

        封面图片ACM架构和代码优化汇刊
        ACM体系结构和代码优化汇刊 第21卷第1期
        2024年3月
        500页
        国际标准编号:1544-3566
        EISSN公司:1544-3973
        内政部:10.1145/3613496
        • 编辑:
        • 大卫·凯利
        期刊目录

        出版商

        计算机协会

        美国纽约州纽约市

        出版历史

        出版:2024年1月19日
        在线AM:2023年11月18日
        认可的:2023年11月12日
        修订过的:2023年10月24日
        收到:2023年7月3日
        在TACO中发布体积21,问题1

        权限

        请求对此文章的权限。

        检查更新

        作者标记

        1. 高性能
        2. 有成本意识的偷工减料

        限定符

        • 研究文章

        贡献者

        其他指标

        文献计量学和引文

        文献计量学

        文章指标

        • 0
          引文总数
        • 486
          总下载次数
        • 下载次数(过去12个月)486
        • 下载次数(最近6周)74

        其他指标

        引文

        视图选项

        视图选项

        PDF格式

        以PDF文件查看或下载。

        PDF格式

        电子阅读器

        使用联机查看电子阅读器.

        电子阅读器

        获取访问权限

        登录选项

        完全访问权限

        媒体

        数字

        其他

        桌子

        分享

        分享

        共享此出版物链接

        在社交媒体上分享