2.3 DISTRIBUTE指令 |
DISTRIBUTE指令指定了在一个处理器排列中数据对象向抽象处理器的映射. 例如:
REAL SALAMI(10000)
!HPF$ DISTRIBUTE SALAMI(BLOCK)
指明数组SALAMI应被分配到一组抽象处理器上, 分配的方式是将其统一切成具有连续元素的块. 如果有50个处理器, 则指令暗示数组所划分成的组, 每组应有200个元素, 即SALAMI(1:200)映射到第一个处理器上, SALAMI(201:400)映射到第二个处理器上, 等等. 如果仅有一个处理器, 则整个数组作为单一的一个具有10000个元素的块被映射到该处理器上.
块的大小可以显式指定:
REAL WEISSWURST(10000)
!HPF$ DISTRIBUTE WEISSWURST(BLOCK(256))
它指明恰好具有256个元素的组应被映射到连续的抽象处理器上. (如果指令满足条件, 则必须有至少个抽象处理器. 第四十个处理器将包含一个仅有16个元素的部分块, 即WEISSWURST(9985:10000).)
HPF还提供了一种循环分配格式:
REAL DECK_OF_CARDS(52)
!HPF$ DISTRIBUTE DECK_OF_CARDS(CYCLIC)
如果有4个抽象处理器, 则第一个处理器将包含DECK_OF_CARDS(1:49:4), 第二个处理器将包含DECK_OF_CARDS(2:50:4), 第三个处理器将包含DECK_OF_CARDS(3:51:4), 同时第四个处理器将包含DECK_OF_CARDS(4:52:4). 连续的数组元素以循环往复的方式被分配到连续的抽象处理器中.
可对多维数组的每一维分别指定分配:
INTEGER CHESS_BOARD(8,8), GO_BOARD(19,19)
!HPF$ DISTRIBUTE CHESS_BOARD(BLOCK, BLOCK)
!HPF$ DISTRIBUTE GO_BOARD(CYCLIC, *)
CHESS_BOARD数组被划分为连续的矩形块, 这些块将被分配到一个二维的抽象处理器排列中. GO_BOARD数组将让它的各行在一个一维抽象处理器排列中循环分配. ("*"指明GO_BOARD沿它的第二个轴线不分配; 这样将把一个整行作为一个对象分配.有时把这种方式叫做"on-processor"分配)
DISTRIBUTE指令只能出现在作用域单元的说明部分并可以仅包含一个specification-expr作为BLOCK或CYCLIC选项的参数.
DISTRIBUTE指令的形式化语法是:
H205 distribute-directive is DISTRIBUTE distributee dist-directive-stuff H206 dist-directive-stuff is dist-format-clause[dist-onto-clause] H207 dist-attribute-stuff is dist-directive-stuff or dist-onto-clause H208 distributee is object-name or template-name H209 dist-format-clause is (dist-format-list) or *(dist-format-list) or * H210 dist-format is BLOCK[(int-expr)] or CYCLIC[(int-expr)] or * H211 dist-onto-clause is ONTO dist-target H212 dist-target is processor-name or * processor-name or *
为保证完整性, 这里给出了完整的语法. 但其中的一些形式将仅在第四节讨论. 这些"过程间"形式是:
约束: 被当作分配子(distributee)而提到的object-name必须是一个简单的名字, 不能是一个子对象指示器或一个部件名(component-name)
约束: 被当作分配子(distributee)而提到的object-name不能作为对准子(alignee)出现.
约束: 被当作分配子(distributee)而提到的object-name不能有POINTER属性.
约束: 如果指定了一个dist-format-list, 则它的长度必须与每个distributee的维数相等.
约束: 如果一个dist-format-list和一个processors-name同时出现, 则dist-format-list中那些不是"*"的元素个数必须与所命名的处理器排列的维数相等.
约束: 如果出现了processors-name但没出现dist-format-list, 则每个distributee的维数必须与所命名的处理器排列的维数相等.
约束: 如果一个DISTRIBUTE指令中无论dist-format-clause或dist-target都是以"*"开始的, 每个distributee必须是一个哑元
约束: 出现在DISTRIBUTE指令的dist-format中的任何int-expr都必须是一个说明表达式(specification-expr)
对用户的建议: 上面的一些约束在所认可的扩展中被放宽了(见第七节): 派生类型(derived type)部件的映射(放宽了约束1), 指针的映射(放宽了约束3和7)以及数据对象的重映射(放宽了约束8)
注意DISTRIBUTE指令的可能形式:
!HPF$ DISTRIBUTE dist-attribute-stuff : distributee-list
它作为一个合成指令(combined-directive)被语法H201所覆盖.
例子:
!HPF$ DISTRIBUTE D1(BLOCK)
!HPF$ DISTRIBUTE (BLOCK, *, BLOCK) ONTO SQUARE : : D2,D3,D4
下面给出了dist-format取舍的意义.
定义上限除函数CD(J,K)=(J+K-1)/K(使用Fortran向零舍位整数运算)
定义上限余数函数CR(J,K)=J-K*CD(J,K)
作为dist-target出现的处理器排列的各维按从左到右的顺序与distributee中dist-format不为"*"的那些维相对应. 在上面的例子中, 处理器排列SQUARE必须是二维的; 它的第一维对应于D2, D3和D4的第一维而它的第二维则对应于D2, D3和D4的第三维.
令d是distributee某一维的大小且p是处理器排列相应维的大小. 为简化起见, 假定所有维的下界都是1.则BLOCK(m)意味着若一个通信子那一维的索引值是j, 当将该位置映射到一个抽象处理器时, 处理器排列相应维的索引值是CD(j,m) (注意m*pd必须为真), 同时在所映射到的那个抽象处理器上的位置号是m+CR(j,m). 抽象处理器中该维上distributee的第一个位置号是1+m*(k-1).
块的大小m必须是一个正整数.
BLOCK的意义等同于BLOCK(CD(d,p)).
CYCLIC(m)意味着若一个通信子那一维的索引值是j, 当将该位置映射到一个抽象处理器时, 处理器排列相应维的索引值是1+MODULO(CD(j,m)-1,p). 抽象处理器中该轴上distributee的第一个位置号是1+m*(k-1).
块的大小m必须是一个正整数.
CYCLIC的意义等同于CYCLIC(1).
当m*pd时, CYCLIC(m)和BLOCK(m)表示同样的分配, 但是BLOCK(m)另外还声称该分配将不以循环(cyclic)的方式卷回(wrap around). 如果m不是常数, 则编译器就不能在编译时确定是否卷回. 注意: 除非pd, 否则CYCLIC和BLOCK(无参数表达式)不表示同样的分配. 另外, 我们还可看到一种退化的情形, 在该情形中, 块的大小为1, 且分配不卷回.
假定我们有16个抽象处理器, 且数组的长度为100:
!HPF$ PROCESSORS SEDECIM(16)
REAL CENTURY(100)
以BLOCK方式分配数组(在此情形下, BLOCK等价于BLOCK(7))
!HPF$ DISTRIBUTE CENTURY(BLOCK) ONTO SEDECIM
所导致的数组元素向抽象处理器的映射如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 | 8 | 15 | 22 | 29 | 36 | 43 | 50 | 57 | 64 | 71 | 78 | 85 | 92 | 99 | |
2 | 9 | 16 | 23 | 30 | 37 | 44 | 51 | 58 | 65 | 72 | 79 | 86 | 93 | 100 | |
3 | 10 | 17 | 24 | 31 | 38 | 45 | 52 | 59 | 66 | 73 | 80 | 87 | 94 | ||
4 | 11 | 18 | 25 | 32 | 39 | 46 | 53 | 60 | 67 | 74 | 81 | 88 | 95 | ||
5 | 12 | 19 | 26 | 33 | 40 | 47 | 54 | 61 | 68 | 75 | 82 | 89 | 96 | ||
6 | 13 | 20 | 27 | 34 | 41 | 48 | 55 | 62 | 69 | 76 | 83 | 90 | 97 | ||
7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 | 77 | 84 | 91 | 98 |
以BLOCK(8)方式分配数组
!HPF$ DISTRIBUTE CENTURY(BLOCK(8)) ONTO SEDECIM
所导致的数组元素向抽象处理器的映射如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 | 9 | 17 | 25 | 33 | 41 | 49 | 57 | 65 | 73 | 81 | 89 | 97 | |||
2 | 10 | 18 | 26 | 34 | 42 | 50 | 58 | 66 | 74 | 82 | 90 | 98 | |||
3 | 11 | 19 | 27 | 35 | 43 | 51 | 59 | 67 | 75 | 83 | 91 | 99 | |||
4 | 12 | 20 | 28 | 36 | 44 | 52 | 60 | 68 | 76 | 84 | 92 | 100 | |||
5 | 13 | 21 | 29 | 37 | 45 | 53 | 61 | 69 | 77 | 85 | 93 | ||||
6 | 14 | 22 | 30 | 38 | 46 | 54 | 62 | 70 | 78 | 86 | 94 | ||||
7 | 15 | 23 | 31 | 39 | 47 | 55 | 63 | 71 | 79 | 87 | 95 | ||||
8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 88 | 96 |
以BLOCK(6)分配数组不符合HPF规范,因为6*16〈100
以CYCLIC方式分配数组(等价于CYCLIC(1)):
!HPF$ DISTRIBUTE CENTURY(CYCLIC) ONTO SEDECIM
导致的数组元素向抽象处理器的映射如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
97 | 98 | 99 | 100 |
以CYCLIC(3)方式分配数组:
!HPF$ DISTRIBUTE CENTURY(CYCLIC(3)) ONTO SEDECIM
导致的数组元素向抽象处理器的映射如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 | 4 | 7 | 10 | 13 | 16 | 19 | 22 | 25 | 28 | 31 | 34 | 37 | 40 | 43 | 46 |
2 | 5 | 8 | 11 | 14 | 17 | 20 | 23 | 26 | 29 | 32 | 35 | 38 | 41 | 44 | 47 |
3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48 |
49 | 52 | 55 | 58 | 61 | 64 | 67 | 70 | 73 | 76 | 79 | 82 | 85 | 88 | 91 | 94 |
50 | 53 | 56 | 59 | 62 | 65 | 68 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 95 |
51 | 54 | 57 | 60 | 63 | 66 | 69 | 72 | 75 | 78 | 81 | 84 | 87 | 90 | 93 | 96 |
97 | 100 | ||||||||||||||
98 | |||||||||||||||
99 |
注意:允许对一个数组分配后,在某些处理器上没有元素。事实上,可将一个数组的所有元素分配到一个处理器上。例如:
!HPF$ DISTRIBUTE CENTURY(BLOCK(256)) ONTO SEDECIM
导致仅有一个非空块--仅在1号处理器上有100个元素,而从2号处理器一直到16号处理器没有任何数组元素。
如果某个数据对象通过存储相联(COMMON或EQUIVALENCE)同distributee建立关联, 则不允许使用DISTRIBUTE对其进行映射,例如不允许将一个标量数据对象的存储单元分割到超过一个的抽象处理器上。见2.8节关于存储相联的进一步讨论。
DISTRIBUTE指令的语句形式可认为是碰巧只有一个distributee的属性形式的缩写;例如:
!HPF$ DISTRIBUTE distributee(dist-format-list) ONTO dist-target
等价于
!HPF$ DISTRIBUTE (dist-format-list) ONTO dist-target :: distributee
注意:为防止语义含糊, dist-format子句必须在语句形式中出现,因此一般来说, 指令的语句形式不能用来指定标量的映射。
如果忽略了属性形式中的dist-format子句, 则语言处理器可以为每个模板或数组选择任意的分配格式。因此指令
!HPF$ DISTRIBUTE ONTO P ::D1,D2,D3
等价于
!HPF$ DISTRIBUTE ONTO P ::D1
!HPF$ DISTRIBUTE ONTO P ::D2
!HPF$ DISTRIBUTE ONTO P ::D3
对于它,编译器或许会考虑代码里D1,D2,D3的使用模式,可能会选择提供三种不同的分配,例如:
!HPF$ DISTRIBUTE D1(BLOCK,BLOCK) ONTO P
!HPF$ DISTRIBUTE D2(CYCLIC,BLOCK) ONTO P
!HPF$ DISTRIBUTE D3(BLOCK(43),CYCLIC) ONTO P
另一方面,编译器也可能为三个数组选择相同的分配。
无论是在语句形式还是在属性形式中,如果出现ONTO子句,则它指明了作为分配目标的处理器排列。如果忽略ONTO子句,则为每一个distributee任意选择一个依赖于实现的处理器排列。因此,例如:
REAL,DIMENSION(1000) :: ARTHUR,ARNOLD,LINUS,LUCY
!HPF$ PROCESSORS EXCALIBUR(32)
!HPF$ DISTRIBUTE (BLOCK) ONTO EXCALIBUR :: ARTHUR,ARNOLD
!HPF$ DISTRIBUTE (BLOCK) :: LINUS,LUCY
使得数组ARTHUR和ARNOLD具有相同的映射,因为它们大小相等且以同样的方式(BLOCK)被分配到同一处理器排列(EXCALIBUR)中,这样其相应的元素就放在了同一抽象处理器中。但是,LUCY和LINUS不一定具有相同的映射,这是因为依赖于不同的实现,它们很可能被分配到不同的处理器排列中;因此LUCY和LINUS的相应元素可能不放在同一抽象处理器中。(ALIGN指令提供了一种方式来保证两个数组具有同一映射,而无须指定显式处理器排列。)
在给定的环境中,一些分配可能没有适当的处理器排列。
Copyright: NPACT |