2.4 ALIGN 指令 BACKWARD FORWARD


ALIGN用于指明某些数据对象的映射方式同某些其它的数据对象相同. 已对准数据对象之间的操作可能比未被对准的数据对象之间的操作效率更高( 因为两个对准的对象都试图映射到同一抽象处理器上). 所设计的ALIGN指令能够非常容易地为一个数组的所有元素立即指定显式映射. 在某些情况下, 通过仔细使用相匹配的 DISTRIBUTE指令进行对象的对准, 可以使ALIGN更一般化并通常更方便。

ALIGN指令只能出现在作用域单元的说明部分(specification-part), 并且仅能包含一个说明表达式(specification-expr)作为下标(subscript) 或将其包含在下标三元组(subscript-triplet)中.

ALIGN语法的形式化描述如下所示:

H213 align-directive     is  ALIGN alignee align-directive-stuff
H214 align-directive-stuff  is  (align-sourc-list)align-with-clause
H215 align-attribute-stuff  is  [(align-source-list)]align-with-clause
H216 alignee          is  object-name
H217 align-source        is  : 
                 or  *
                 or  align-dummy
H218 align-dummy        is  scalar-int-variable

约束: 被作为对准子(alignee)的对象名(object-name)必须是一个简单名字并且不能是一个子对象指示器或者一个部件名(component-name).

约束: 被作为对准子(alignee)的对象名( object- name)不能同时是一个分配子(distributee)

约束: 被作为对准子(alignee)的对象名(object-name)不能具有POINTER属性

约束: 如果对准子(alignee)是一个标量, 则不允许出现align-source-list(及其周围的括弧). 在此情况下,不允许指令的语句形式.

约束: 如果出现align-source-list, 则它的长度必须同对准子(alignee)的维数相等.

约束: align-dummy必须是一个已命名的变量.

约束: 任何一个对象不能既有INHERIT属性又有ALIGN属性.

对用户的建议: 上面的一些约束在所认可的扩展中被放宽了(见第7节): 派生类型部件的映射(放宽了约束1)和指针的映射(放宽了约束3)

注意: 下面是ALIGN指令的一种可能形式

 !HPF$ ALIGN align-attribute-stuff :: alignee-list

它作为一个合成指令(combined-directive)被语法规则H201所覆盖.

ALIGN指令的语句形式可以认为是碰巧只有一个对准子(alignee)的属性形式的简写:

 !HPF$ ALIGN alignee(align-source-list) WITH align-spec

等价于

 !HPF$ ALIGN (align-source-list) WITH align-spec ::alignee

如果忽略了属性形式中的align-source-list且alignee不是标量, 则假定align -source-list包含了一系列用括弧括着的":", 其中":"的数目与alignee 的维数相等. 类似地, 无论对于任何形式, 如果忽略了align-spec 中的align-subscript-list, 则假定其包含了一系列用括弧括着的":", 而且": " 的数目与对准目标 (align-target)的维数相等. 因此, 指令

 !HPF$ ALIGN WITH B ::A1, A2, A3

意味着

 !HPF$ ALIGN(:,:) WITH B(:,:) :: A1, A2, A3

它依次等价于下面的指令

 !HPF$ ALIGN A1(:,:) WITH B(:,:)
 !HPF$ ALIGN A2(:,:) WITH B(:,:)
 !HPF$ ALIGN A3(:,:) WITH B(:,:)

这是因为具有多于一个alignee的属性形式的指令等价于一系列同样的指令, 其中每一个指令对应于一个alignee; 所有alignee必须有相同的维数. 理解这一点后, 为简化描述, 在下面我们都将假定ALIGN只有一个alignee.

每一个align-source都对应于alignee的某一维, 且其值或者是":", 或者是"*", 或者是一个哑元变量:

.若值为":", 则该维上的位置分布在align-spec中相匹配的维上.

.若值为"*", 则该维是坍塌的: 即该维上的位置对于确定align-target上的对应位置没有影响. (用一个未在指令的的其它位置出现过的哑变量名代替"*" 会产生同样的效果 ; "*"的唯一作用是很方便地省去了发明一个变量名的麻烦, 并清晰地标明了同要作用的那一维之间没有依赖)

.一个哑变量的取值范围可认为是alignee该维上的所有有效索引值.

ALIGN的WITH子句具有下列语法:

H219 align-with-clause      is  WITH align-spec
H220 align-spec           is  align-target[(align-subscrit-list)]
                   or  * align-target[(align-subscript-list)]
H221 align-target          is  object-name
                   or  template-name
H222 align-subscript       is  int-expr
                   or  align-subscript-use
                   or  subscript-triplet
                   or  *
H223 align-subscript-use     is  [[int-level-two-expr]add- op] align-add-ope
                    or  align-subscript-use add-op int-add-operand
H224 align-add-operand       is  [int-add-operand *]align-primary
                    or  align-add-operand * int-mult-operand
H225 align-primary          is  align-dummy
                    or  (align-subscript-use)
H226 int-add-operand        is  add-operand
H227 int-mult-operand        is  mult-operand
H228 int-level-two-expr      is  level-2-expr

为保证完整性, 这里给出了完整的语法, 但是其中的某些形式仅在第四节讨论. 这些"过程间"形式是:

.规则H220的第二个选项(包含*形式)

约束: 作为对准目标(align-target)的对象名(object-name)必须是一个简单名字且不能是一个子对象指示器或一个部件名.

约束: 一个align-target不能有OPTIONAL属性

约束: 如果ALIGN指令中的对准说明(align-spec)以"*"开始, 则每个alignee必须是一个哑元

约束: 在对准指令中, 所有的整表达式(int-expr), 二级整表达式(int-level-two-expr),整数加操作数(int-add-operand)或整数乘操作数(int-mult-operand) 必须是一个说明表达式

约束: 作为对准指令中对准下标的下标三元组中的任何下标或步长必须是一个说明表达式.

约束: 每个对准哑元(align-dummy)在对准下标列表中(align-subscript-list)只能最多出现一次.

约束: 一个对准表达式使用(align-subscript-use)中至多只能包含一个对准哑元

约束: 一个对准哑元只能出现在上面给出的语法特性所显式允许出现的地方. 即,用户可以构造一个以对准哑元开始的对准下标使用(align-subscript-use),然后,用任何不包含对准哑元的整数表达式与其作加法和乘法.

约束: 对准下标(align-subscript)中的下标(subscript)不能包含任何哑元

约束: 整数加操作数, 整数乘操作数, 或二级整表达式必须是整数类型.

对用户的建议: 在所认可的扩展中, 上面的一些约束可以放宽: 派生类型部件的映射(放宽了约束1), 指针的映射(放宽了约束3)以及数据对象的重映射(放宽了约束4和5)

align-subscript-use的语法规则考虑了操作码优先级问题, 但基本的观点非常简单: 尽量将align-subscript-use变成一个只有一个对准哑元的线性函数.

例如, 下面的align-subscript-use表达式是有效的, 假定J,K和M是对准哑元但N不是对准哑元: 

 J  J+1 3-K 2*M   N*M 100-3*M
 -J +J -K+3 M+2**3 M+N -(4*7+IOR(6,9))*K-(13-5/3)
 M*2 N*(M-N) 2*(J+1) 5-K*3 10000-M*3 2*(3*(K-1)+13)-100

下面的表达式不是有效的align-subscript-use表达式:

 J+J J-J 3*K-2*K M*(N-M) 2*J-3*J+J 2*(3*(K-1)+13)-K
 J*J J+K 3/K 2**M M*K K-3*M
 K-J IOR(J,1) -K/3 M*(2+M) M*(M-N) 2**(2*J-3*J+J)

align-spec中的下标三元组(subscript-triplets)的数目必须与align-source-list中所出现的冒号(":")的数目相等。它们按从左到右的顺序进行匹配,正因为这个原因,所以任何一个不是冒号的align-source以及任何一个不是下标三元组的align-subscript都将被忽略。若alignee某一维上以冒号作为align-source,且数组的上下界是LA和UA。令相应的下标三元组是:LT:UT:ST或它的等价形式。则可用一个新的尚未使用过的哑元变量代替冒号,假设是J,则用表达式(J-LA)*ST+LT代替下标三元组不影响指令的语义。而且这些维必须一致,这意味着

  

必须为真。(这种处理完全类似于数组赋值)

为简化下面的讨论,我们假定align-source-list中的每一个冒号都已被一个新的哑元变量所代替,该变量完全符合刚才所描述的风格,且align-source-list中的每一个星号“*”同样已被一个未使用过的的哑元变量所代替。例如:

 !HPF$ ALIGN A(:,*,K,:,:,*) WITH B(31,:,:,K+3,20:100:3)

可被转换为下面的等价形式:

 !HPF$ ALIGN A(I,J,K,L,M,N) WITH B(I-LBOUND(A,1)+31,   &
 !HFP$   L-LBOUND(A,4)+LBOUND(B,2),K+3,(M-LBOUND(A,5)*3+20)

同时要求:

   SIZE(A,1) .EQ. UBOUND(B,1)-30
   SIZE(A,4) .EQ. SIZE(B,2)
   SIZE(A,5) .EQ. (100-20+3)/3

这样我们仅须考虑align-source是哑变量且没有一个align-subscript是下标三元组的情形。

我们认为每个哑变量的取值范围是alignee相应维上所有有效索引值。索引变量每个可能值的组合对应于alignee的一个元素。align-spec指明了alignee的元素所应对准的align-target中的相应元素(或区域);这个指示可以是索引值的一个函数,但为了限制实现的复杂性,此函数在语法上被约束成线性函数。在align-spec中每个对准哑变量至多只出现一次且仅能出现在某种严格规定的上下文中。结论是每个对准下标表达式可以至多包含一个对准哑变量且将表达式约束为该变量的线性函数(因此扭曲对准是不可能的)

将星号作为对准下标指示了一个重复描述。alignee的每一个元素同align-target那一维上的每一个位置都对准。

基本原理:使用“*”既意味着坍塌又意味着重复这一点似乎很奇怪;原则上,“*”总是在概念上代表一个从未在语句的其它地方出现过的哑变量而且其取值范围是指定维上的一组索引的集合。例如:  

 !HPF$ ALIGN A(:) WITH D(:,*)

意味着A的一个拷贝同D的每一列都对准,因为它在概念上等价于

    对于每一个合法的索引值j, align A(:) with D(:,j)

正如

 !HPF$ ALIGN A(:,*) WITH D(:)

在概念上等价于

    对于每一个合法的索引值j, align A(:,j) with D(:)

注意,HPF语法允许

 !HPF$ ALIGN A(:,*) WITH D(:)

被写成下面的替代形式

 !HPF$ ALIGN A(:,J) WITH D(:)

但它不允许

 !HPF$ ALIGN A(:) WITH D(:,*)

被写成下面的形式

 !HPF$ ALIGN A(:) WITH D(:,J) 

这是因为它具有另一个意义(仅当一个变量出现在alignee后面的align-source-list时,它才被认为是一个哑变量,因此变量J的当前值被使用了,这样A只同D的某一列对准。)重复允许一个优化编译器安排读最近的拷贝(当然,当写一个重复数据对象时,必须更新所有的拷贝,而不是仅针对一个拷贝。重复描述对于用做一个小浏览表是非常有用的,每个物理处理器上拥有一个拷贝将导致更快的速度,而且无须增加一个在逻辑上对于算法没有必要的额外的维)(基本原理结束)

通过应用上面的转换,对准下标(align-subscript)可在概念上或被缩减成一个整表达式(不包含对准哑元)或缩减成一个对准下标使用(align-subscript-use),同时对准源列表(align-source-list)可以被缩减成一系列不带“*”或":"的索引变量。接着就可进行align-subscript-list的估算,对于任意指定的对准哑元变量值的组合,可以简单地通过将每个对准下标估算成一个表达式来实现它的估算。结果下标值对于对准目标来说必须是一个合法的下标。(这意味着不允许对准子(alignee)“卷回”或“超出对准目标(align-target)的边界”)。我们认为对准子选定的元素同对准目标指定的元素对准;更详细地讲,对准子选定元素所根本对准的对象同对准目标指定元素根本对准的对象(可能是其自身)是同一个。

ALIGN指令进一步举例:

     INTEGER D1(N)
     LOGICAL D2(N,N)
     REAL, DIMENSION(N,N) :: X,A,B,C,AR1,AR2A,P,Q,R,S
 !HPF$ ALIGN X(:,*) WITH D1(:)
 !HPF$ ALIGN (:,*) WITH D1 :: A,B,C,AR1,AR2A
 !HPF$ ALIGN WITH D2 :: P,Q,R,S

注意,在一个对准子列表中(alignee-list),所有的对准子必须具有相同的维数,但无须都具有相同的形状;其范围仅需与align-source-list中对应于冒号的那些维相匹配即可。这会带来非常重要的方便;当前实际中一个最一般的情形是对准数组在被分配(并行)的维中是匹配的,但在坍塌(on-processor)的维中可能是不同的:

     REAL A(3,N), B(4,N), C(43,N), Q(N)
 !HPF$ DISTRIBUTE Q(BLOCK)
 !HPF$ ALIGN(*,:) WITH Q :: A,B,C

在这里涉及到一些处理器(可能是它们中的N个)以及每个处理器里所需要的不同大小(3,4,43)的数组。正如HPF所关心的,3,4,43这三个数可以是不同的,因为那些维将要坍塌。这样,那些仅在那一维上索引值不同的数组元素将被对准到Q的同一元素上(同时将被指定在同一处理器上)。

在下面的例子中,每组中的指令意味着同一件事,假定相应维的上下界是匹配的:

  !X的第二维是坍塌的
  !HPF$ ALIGN X(:,*) WITH D1(:)
  !HPF$ ALIGN X(J,*) WITH D1(J)
  !HPF$ ALIGN X(J,K) WITH D1(J)

  !沿D3的第二维重复描述
  !HPF$ ALIGN X(:,:) WITH D3(:,*,:)
  !HPF$ ALIGN X(J,K) WITH D3(J,*,K)
  

  !将两个维转置
  !HPF$ ALIGN X(J,K) WITH D2(K,J)
  !HPF$ ALIGN X(J,:) WITH D2(:,J)
  !HPF$ ALIGN X(:,K) WITH D2(K,:)
  !没有任何一种方法可以将两个索引变量都去掉
  !单独的下标三元组语法不能解释转置

  !将两维都颠倒
  !HPF$ ALIGN X(J,K) WITH D2(M-J+1, N-K+1)
  !HPF$ ALIGN X(:,:) WITH D2(M:1:-1, N:1,-1)

  !简单情形

  !HPF$ ALIGN X(J,K) WITH D2(J,K)
  !HPF$ ALIGN X(:,:) WITH D2(:,:)
  !HPF$ ALIGN (J,K) WITH D2(J,K) :: X
  !HPF$ ALIGN (:,:) WITH D2(:,:) :: X
  !HPF$ ALIGN WITH D2 :: X


Copyright: NPACT BACKWARD FORWARD