计算机研究与发展
JOURNAL OF COMPUTER RESEARCH
AND DEVELOPMENT
1999年 第36卷 第12期 Vol.36 No.12 1999



并行重构程序中过程调用的优化技术
冯晓兵　　　张兆庆
摘　要　过程间分析技术是提高编译器目标码效率的重要技术.在分布式存储的并行系统中，数据分布的好坏对应用程序的性能有重要影响.但是仅做过程内的数据对齐和分布推导难以获得良好的全局数据分布模型，而且会使过程调用的开销难以控制，因而如何处理过程调用，以及数据分布信息在过程调用点的收集与传播对生成高效率的目标码是极其重要的.文中应用过程调用实例化技术，减小过程调用的开销，并能很好地满足数据局部性要求.
关键词　并行编译器，数据局部性，实例化，数据分布模型
中图法分类号　TP311
A METHOD FOR PROCEDURE CALL OPTIMIZING
FENG Xiao-Bing and ZHANG Zhao-Qing
(Center of High Performance Computing, Institute of Computing Technology, 
Chinese Academy of Sciences, Beijing 100080)
Abstract　Global data distribution analysis is a key technology for parallel compilers. Compiler should let computing and data used by these computing locate at the same processor, otherwise, the performance of applications will be very poor. Interprocedure analysis is necessary for global data distribution analysis. When mapping functions of a formal array and actual array associated with this formal array are not compatible, the cost of this procedure callsite will be very high. Two issues must be solved: ① how to process procedure calls; ② how to collect array distribution information and transfer the useful information among procedures. In this paper, a method for solving these two issues is introduced. All callsites of a procedure are classified into patterns according to their calling contexts, with each pattern calling a special body of this procedure. Information used to classify the callsites can be applied for pattern body's parallel optimizing, too. This is called instantiation. This method can be used to decrease the cost of procedure call and to obtain a global optimizing data distribution model.
Key words　parallel compiler, data locality, instantiation, data distribution model
1　引　　言
　　并行处理系统的广泛应用促进了并行编译器的研究，如何提高并行编译器的性能是我们面临的重要问题.特别是对于具有分布式并行处理系统，应用程序的数据空间在分布式存储中的映射形式严重地影响着程序的执行效率.如何确定应用程序的全局数据分布与计算分割模型，以取得更好的数据局部性，这是提高应用程序性能的一个关键因素［1～3］.
　　全局优化的数据分布与计算分割模型的推导不能回避过程调用的处理.过程是一个相对封闭的程序体，过程在被调用时获得的形式参数或全局量的信息将约束着过程的动态行为，而过程对参数或全局量的操作以及返回值也反过来影响着调用者的行为.由于调用者对于被调用过程内部的信息是模糊的，编译器不得不在程序分析、优化时做保守估计，从而使得很多的优化、并行化工作无法完成，严重地影响了并行优化效果.特别是对于分布式系统上的并行程序，如果不做过程间优化措施，可能使得过程调用的开销大大提高.
　　例1.过程调用可能导致大量数据通信
　　　program sample　　　　　　　　　　　subroutine f(c)
　　　　real a(100,100),b(100,100)　　　　　real c(100,100)
　　　　　…　　　　　　　　　　　　　　　　　integer I,J
　　　s1: f(a)　　　　　　　　　　　　　　　do I = 1,100
　　　　　…　　　　　　　　　　　　　　　　　do J = 1,100
　　　s2: f(b)　　　　　　　　　　　　　　　　　c(I,J) = I×3+J
　　　　　…　　　　　　　　　　　　　　　　　enddo
　　　　　end　　　　　　　　　　　　　　　　return
　　　　　　　　　　　　　　　　　　　　　　end
　　在上例的FORTRAN程序中，过程f中的两层循环都是可以并行的，f在主程序中被调用了两次，f在调用点s1获得的实在参数是数组a，在调用点s2获得的实在参数是数组b.如果经过并行编译器的分析得出数组a的第一维被分布了，而数组b得第二维被分布了，此时如何确定过程f中形式参数数组c的数据分布模式呢？如果对过程f不做特殊处理，由于f的两次调用时实参数组的数据分布模式是矛盾的，对f的两次调用必然导致数据重映射（remap）或大量的数据通信，而这些操作可能会大大加大过程调用的开销.对于如何处理程序中的过程调用，一般有3种方法［1，4，5］：
　　(1)过程嵌入（inline）的方法：该方法是在过程被调用处直接嵌入过程体的一个副本，在进行程序分析、优化工作时可以全面地获得调用者和被调用者的信息.但这种方法会导致程序体的过度膨胀；而且系统不能在任何上下文的环境中都将一个调用点所调用的过程嵌入进来；最后，采用过程嵌入的方法有可能对数据依赖测试等环节是无效的.
　　(2)过程间的数据流分析技术：在分析一个过程时，其入口的信息是指所有调用点信息的综合，每个调用者所获得的被调用过程的信息将是一致的.为了程序分析的正确性必然会在某些环节采用保守估计，影响程序分析、优化的效果.
　　(3)过程繁衍：根据过程调用的上下文对同一过程的不同调用点进行分类，每个类对应了过程的一个实例.源程序中的一个过程可能裂变成为几个副本，但这种裂变是有选择的.
　　在本文中，我们将介绍一个过程间分析、优化的算法框架，以完成全局数据分布和计算分割模式的推导，以降低过程调用开销，提高并行程序的性能.核心技术是根据调用点的上下文信息对过程调用进行实例化.我们的工作背景是一个Fortran语言并行编译器，但其基本原理可以应用于C语言并行编译器的开发.
2　数据分布映射的变换
　　数据分布模式在过程间的收集、传播、比较，这是本文算法的重要组成部分，因为判断一个过程的两个被调用点是否可以共用同一个过程实体，依据是这个过程所涉及的参数数组或全局数组等的数据分布模式是否相同.另外，由于某些数组的数据分布信息对于全局优化的数据分布和计算分割模式的推导有重要的指导意义，需要在尽可能广的范围内进行扩散，所以在过程调用处可能将调用者的数据分布信息传递给被调用者，也可能将被调用者的数据分布信息反馈给调用者.
2.1　几个概念
　　首先，我们说明一个概念：形参数组的分布模式.在Fortran标准［6］中规定形式参数数组并不占据实际的内存空间，所有对哑数组的操作都是操作的与之结合的实参数组.因而在我们提及“形参数组的分布模式”时，并不意味着形参数组被分布了，而是指在形实结合后，与形参数组结合的实参数组在以形参数组的形(shape)出现时，其所对应的分布模式.
　　定义1.分布模式一致.在形参数组与实数组包含相等的数组元素个数的前提下，在过程的两个不同调用点与同一个形参数组C的相结合的实数组A和B，分别具有数据分布模式DA和DB.数组A在与形参数组C做形实结合时，将其分布模式DA传播给C，形式为DC1.数组B在与形参数组C做形实结合时，将其分布模式DB传播给C，形式为DC2.如果DC1和DC2相同，则称在这两个调用点处，形参数组C的分布模式是一致的.对于全局数组以及Fortran语言中的公共量做同样的约定.
　　定义2.调用点一致.对于一个过程F的两个调用点F1，F2，如果：
　　①对于被分布的形参数组，其“形”是相同的；
　　②各个相应的形参数组的分布模式是一致的；
　　③若这个过程中出现了公共量，则各个相应的公共数组的分布模式是一致的；
　　④若这个过程所引用了全局数组，则各个相应的全局数组在两个调用点处是一致的；
　　⑤若这个过程调用了其它的过程G，F1调用G的实体G1，F2调用G的实体G2，G1和G2是一致的；
　　则称调用点F1，F2是一致的.
　　此时，这两个调用点调用这个过程的同一个实体.否则，这两个调用点应归属于不同的类，调用不同的实体.
2.2　对公共量分布的一些限制及其处理
　　在Fortran语言中，COMMON语句和EQUIVALENCE语句指定了不同的数据对象的对齐关系，指明了存储结合［6］的关系.
　　例2.存储结合的例子
　　　SUBROUTINE f1　　　　　　　　　　　　　　　　SUBROUTINE f2
　　　COMMON/com1/a(100),b(20,20),c(50),d(150)　　COMMON /com1/x(100),y(400),z(200)
　　　　　REAL e(100)　　　　　　　　　　　　　　　　　　…
　　　　　EQUIVALENC (a(51), e(1))　　　　　　　　END
　　　　　…
　　　END
　　在例2中，数组a，b，e，x，y共享同一存储序列，因而f1中的a，b，c这3个数组分布模式的推导要统一考虑，不能分开.同样由于数组c，d，z也共享同一存储序列，因而对数组c，d的分布模式也要统一考虑.否则，可能使得数组z在同一时刻对应了两种数据分布模式.借鉴HPF［7］的经验，对公共、等价变量的数据分布模式的推导做一些限制.
　　定义3.集合变量组是一组变量的收集，这些变量各自的存储序列是一个唯一存储序列的一部分.集合变量组的大小是指这个集合变量组包含的所有存储单元的个数.
　　定义4.如果在一个集合变量组中，有一个成员的存储序列恰好可以覆盖这个集合变量组所包含的所有的存储序列，则称这个成员为这个集合变量组的集合覆盖子.
　　在例2中，数组a，b，e，x，y构成了一个集合变量组，其大小为500个实数，这5个数组都不是这个集合变量组的集合覆盖子；数组c，d，z构成了一个集合变量组，其大小为200个实数，其中数组z是这个集合变量组的集合覆盖子.一个公共块包含了一系列的成分(component)，每个成分或者是一个集合变量组，或者是一个不属于任何集合变量组的变量.在例2中，公共块com1包含了两个成分，均为集合变量组.
　　在处理集合变量组的成员时，把每一个集合变量组当作是一个模板数组，这个模板数组显然是这个集合变量组的集合覆盖子.将程序中出现的集合变量组的成员变量按集合覆盖子及偏移量做换名处理，这样就可以保证在同一时刻，一个集合变量组只对应于一种分布模式.
2.3　分布模式变换的方法
　　我们约定一个数组在其生存期内只对应一个数据分布模式.由于Fortran语言中的公用数组在各个过程中的形可能不一致，因而需要做变换.由于相结合的形参数组和实参数组都有可能从自己所属的作用域中获得数据分布模式，需要对相结合的形、实参数组的数据分布模式进行比较或变换.
　　分布模式变换的基本原则是要满足串行程序语义所决定的存储结合顺序.即如果形参数组的一个元素与实参数组一个元素在非分布状态下存在结合关系，则当两个数组都分布了，这两个元素仍应满足结合关系.　　
　　对于对应的形参数组和实参数组，其中一个数组已知数据分布模式，不失一般性，可以假设实参数组的数据分布模式已知，需要导出形参数组的分布模式.首先，确定实参数组分布的数据块的大小，即实参数组被分布后，被分布到同一个处理节点上的连续的数据块的大小；其次，根据分布的数据块的大小确定形参数组的哪一维要被分布，即寻找形参数组的某一维，假定是第j维，它满足第j维的跨距（stride）小于或等于被分布的数据块的大小，但第j+1维的跨距不小于被分布的数据块的大小；然后，根据该维元素长度和被分布的数据块的大小，确定该维的分布模式.如果不能精确地确定分布模式的变换，则相关过程的调用点作为一种特殊实体，在调用点的前后加入动态调整参数数组分布模式的机制.
　　采用如下的方法比较两个数组的数据分布模式是否一致：数组A具有分布模式DA，数组B具有分布模式DB，可以假定认为A和B是对应的形参数组和实参数组，其中A的分布模式（DA）已知，根据前面的方法求得B的分布模式DB′，如果DB和DB′相同，则认为A和B的数据分布模式是一致的.
3　优化过程调用的算法
　　在上节介绍的数据分布映射的变换基础上，我们构造以下优化过程调用的算法：
　　　①初始化：展开调用图，一个静态调用点对应调用图中的一个节点.每个节点标记为未被分析，并依据其静态估计的计算量、调用次数等，确定其权值.对每个过程，初始化一个pattern的集合，每个过程对应了一个集合.在本阶段，每个集合均为空.
　　　②自顶向下扫描调用图中的每个节点{//基本信息的收集和传播
　　　　根据上节的介绍自顶向下传播已知的数据分布信息；
将过程中的公用量根据2.2节的介绍进行分类标记；
记录：(a)与数组形相关的形参标量；(b)形参数组的分布形态；(c)全局数组（公用数组）的分布形态.这3种信息是对调用点进行分类的重要依据.将这个调用点加入相应的pattern集合中，收集到的信息标记在这个节点上.
　　　}
　　　③根据每个节点权值，从大到小，依次选取各个节点进行分析{
　　　　根据这个节点已被标记的信息，查询这个过程已被分析的调用点；
　　　　如果有一个调用点p的信息与这个节点一致（参见定义2），且那个节点已被标记为“已分析”，则将这个节点标记为“复用”，且指明复用节点p.
　　　　否则{
　　　　　根据已收集到的信息，对过程进行过程内分析优化［4］.标记这个调用点为“已分析”.收集在分析过程中新获得的信息，沿调用路径向上、下传播新收集的信息{
　　　　　　如果这个调用点的前驱或后继节点尚未被分析，则将新的信息加入到这个节点的信息集合中；
　　　　　　如果已到根节点或前驱节点的标记是“已分析”或“复用”，则终止向上传播；向下传播的终止条件是已到叶节点或后记节点已被处理过；
　　　　　　如果向上传播不是终止于根节点或向下传播不是终止于叶节点，而是终止于某一节点q，{
　　　　　　　　如果q的标记是“复用”，则将这个调用点标记为“已分析”，复制其原复用节点的过程内分析信息；
　　　　　　　　如果q的标记是“已分析”，且有其它的调用点复用这个调用点，将该过程的过程内信息复制，选择一个复用q的节点，将其标记为“已分析”，将过程内的信息挂在那个节点上，并令所有复用节点q的调用点复用那个节点.
　　　　}
　　　}
　　}
　}
　　　④以逆拓扑序依次处理每个过程的pattern集合{
　　　　对于每个pattern集合π，利用小节2中介绍的思想对其元素作划分，相互一致的调用点被分在同一个子集中.每个子集是过程的一个实例，子集中的调用点调用这个实例.
　　　}
　　　⑤在代码生成阶段，根据上述生成的子集将过程实例化，并对相关的调用点做换名处理.
　　调用图中的每个节点主要有两组数据项，一组是记录过程内的数据分布信息，另一组记录与过程调用点分类相关的数据信息.前一组的数据项并不是每个节点都填充的，只有标记为“已分析”的节点才会填充这组数据项，以减少存储开销.
　　我们暂不考虑“数据再分布”的问题，因此一个数组如果被分布了，则该数组只具有一个数据分布映射函数.在算法的第②、③两步中，数据分布模式的传播主要是指参数数组的数据分布模式的传播.
　　　　Program f
　　　Dimension a(100,100),b(100,100)
C　distribution a(BLOCK,*), b(*,BLOCK)
　　… …
s1: call g(a,…)
　　… …
s2: call g(b,…)
　　… …
s3: call h(a,…)
　　… …
end

subroutine g(c,…)
　　dimension c(100,100)
　　… …
s4: do m=1,100
s5: do n=1,100
　　　　c(m,n)=…
　　　enddo
　　enddo
　　… …
end

subroutine h(d,…)
　　dimension d(100,100)
　　… …
s6: call g(d,…)
　　… …
end在步骤②结束后，每个过程的pattern集合收集了这个过程的所有静态调用点.
　　由于在数据和计算划分的推导中，对结果产生影响的是在分析开始时参数数组和全局数组（公用数组）等所具有的数据分布模式，只要这些信息确定了，对于同一过程体的数据和计算划分推导所得的结果也是确定的，因此只要判定一个未被分析的调用点已具有的数据分布信息与同一过程的某个已分析的调用点相同，就可以认为这个调用点可以复用已分析获得的信息.这种处理方法可以减少过程内分析次数，提高编译器分析的效率.所以在步骤③中，对一些节点标记为“复用”，而不再作进一步的分析.而且在文献［8］中的过程内算法中，一个数组只要出现在被并行分割的循环中，这个数组必然被分布了，因此在步骤③中对新获得的数据分布信息的传播，采用遇到已处理过的过程调用点就终止的方法.因为如果一个调用点已被处理过后，对应过程的某个数组仍未被分布，表明该数组在这个过程中不出现在任何被并行分割的循环中，由于其它调用点的分析以及数据分布模式的传播而导致这个数组可能被分布，这对提高目标程序的效率并没有意义，因此算法使得分布模式的传播在此终止，而在相应的调用点处加入通信，以维护数据分布的一致性.
　　在步骤④中，特别强调一点是：若一个过程p的两个调用点分别对应于不同的两个实例，则p的这两个调用点所在的两个调用者q1和q2也必是不同的过程，或是分别对应于同一过程的不同实例，除非p的这两个调用点处于同一过程体中.由定义2可以容易地得到这个结论.
4　应用举例
　　右例是一个Fortran程序，其对应的展开的程序略图如图1所示.

图1　展开的调用图
　　假设应用程序员指定主程序f中数组a的第一维被以BLOCK的方式分布，数组b的第二维被以BLOCK的方式分布.这个程序中对过程g的调用共有3处：s1，s2和s6，在调用图中分别以g1，g2，g3代表.进一步假设g中的循环s4和s5都是可并行循环.通过分析，在调用点s1处，g的形参数组c获得的数据分布模式是（BLOCK，*），在调用点s2处获得的数据分布模式是（*，BLOCK），在调用点s6处，与g的形参数组直接结合的是h的形参数组d，d的数据分布模式继承与主程序中的调用点s3处的数组a，可以计算出在s6处，形参数组c所获得的数据分布模式是（BLOCK，*），与s1处相同，因而调用点s1，s6可以归为一类（如果在这两个调用点，所有其它影响分类的信息是一致的），对应的g的实体中，循环s4将被分割；调用点s2是单独的一类，对应的g的实体中，循环s5将被分割.因此，在目标程序中过程g有两个过程实例，3个调用点根据上述的分析分别调用这两个实例，从而得到好的性能.而过程h被调用一次，因此只有一个过程实例.
5　结　　论
　　综上所述，本文所介绍的方法根据过程调用点的上下文的不同，将过程调用进行分类，不同的类调用该过程的不同实例，一方面由于不需运行时在调用点处处理数据映射变换的问题，从而可以显著减小过程调用的开销.在另一方面，由于每个过程实例都是针对一组确定的相容的上下文，因而对这个过程实例进行的过程内并行优化分析可以更精细，从而取得更高的并行执行效果.过程调用实例化的技术对于提高并行编译器的性能是有重要意义的.
本课题得到国家“八六三”高科技计划基金项目和国家高性能计算基金项目资助.
作者简介：冯晓兵，男，1969年11月生，博士，主要研究方向为编译技术.
　　　　　张兆庆，女，1938年11月生，研究员，博士生导师，主要研究方向为并行编译及工具
　　　　　环境.
作者单位：中国科学院计算技术研究所高性能中心　北京　100080
参考文献
　1　Chau-WenTseng. An optimizing fortran D compiler for MIMD distributed-memory machines［Ph D dissertation］, Rice University, Houston Texas, USA, 1992
　2　Lam M S. Locality optimization for parallel machines. In: Third Joint International Conf on Vector and Parallel Processing, 1994
　3　Ken Kennedy. Compiler technology for machine-independent parallel programming. International Journal of Parallel Programming, 1994，22(1)：79～98
　4　Cooper K D, Hall M W, Ken Kennedy. A methodology for procedure cloning. Computing Language，1993，19(2)：105～117 
　5　Cooper K D, Kennedy K, Torczon L. The impact of interprocedural analysis and optimization in the Rn programming environment. ACM Trans Programming Language Systems, 1986，8(4)： 491～523
　6　Fortran 工作组. 标准Fortran 90语言程序设计. 北京：学苑出版社，1994
　　(Fortran Group. Standard of Fortran 90(in Chinese). Beijing: Xueyuan Press, 1994)
　7　High Performance Fortran Forum, High Performance Fortran Language Specification, Version 1.0. Houston: Rice University, 1993
　8　林 进. 并行处理系统的循环分割和数据分布技术研究［博士论文］. 中国科学院计算技术研究所， 北京，1998
　　(Lin Jin，Research on loops and data distribution for SIMD system［Ph D dissertation］. Institute of Computing Technology, the Chinese Academy of Sciences, Beijing: 1998)
原稿收到日期：1999-02-10；修改稿收到日期：1999-05-22.
