具体实施方式
在以下具体实施例中,对附图进行了参考,附图构成了实施例的一部分且在其中作为示例示出了可在其中实践本发明的各特定实施例。就此,诸如“顶部”、“底部”、“前方”、“后方”、“前导”、“尾部”等的方向性术语参考正在描述的附图的方向来使用。因为实施例的各组件可位于多个不同的方向,所以方向性术语出于说明的目的来使用而不是限制。可以理解,可以使用其它实施例并且可以做出结构上或逻辑上的改变而不背离本发明的范围。因此,以下详细描述并不旨在限制,并且本发明的范围由所附权利要求来限定。应该理解,此处描述的各示例性实施例的特征可相互组合,除非另外具体注明。
图1是示出具有灵活通信算子12的代码10的实施例的计算机代码图。当被编译并执行时,灵活通信算子12基于资源图来生成分段的计算空间以跨各计算节点(例如,在图4中示出并在下面更详细描述的计算节点121)来分布计算空间。灵活通信算子将计算空间(由图1的实施例中的输入可索引类型14表示的)分解成灵活可索引类型18(在图3B的示例中也示出了)的片段20,致使片段20被指派给计算节点,并允许用户集中管理并自动化在计算节点间的片段20的移动。可以使用这些片段的完整全局视图表示或局部全局视图表示来管理片段移动,如下面更详细描述的。
代码10包括来自高级通用或数据并行编程语言的指令序列,该指令序列可被编译成一个或多个可执行代码(例如,图4中所示的DP可执行代码138)以由一个或多个DP最优计算节点(例如,图4所示的DP最优计算节点121)执行。
在一个实施例中,代码10包括来自具有数据并行扩展的高级通用编程语言(之后称为GP语言)的指令序列,所述指令序列形成存储在一个或多个模块的集合中的程序。GP语言可允许程序被编写在不同部分(即模块)中,使得每个模块都可以被存储在可以由计算机系统访问的分开的文件或者位置中。GP语言提供用于对包括一个或多个通用处理器和一个或多个专用的DP最优计算节点的计算环境进行编程的单个语言。DP最优计算节点通常是通用处理器的图形处理单元(GPU)或SIMD单元,但也可包括通用处理器的标量或矢量执行单元、场可编程门阵列(FPGA)或一些计算环境中的其他合适设备。通过使用GP语言,程序员可将通用处理器和DP源代码两者包含在代码10中以便由通用处理器和DP计算节点分别执行,并协调通用处理器和DP源代码的执行。在该实施例中,代码10可以表示任何合适类型的代码,诸如应用、库函数或操作系统服务。
GP语言可以通过扩展诸如C或C++之类的广泛适用的高级且通用的编程语言以包括数据并行特征来形成。其中可出现DP特征的通用功能语言的其他示例包括:JavaTM,PHP,Visual Basic,Perl,PythonTM,C#,Ruby,Delphi,Fortran,VB,F#,OCaml,Haskell,Erlang,NESL,Chapel,以及JavaScriptTM。GP语言实现可包括允许程序的不同部分被包括在不同模块中的丰富链接能力。数据并行特征提供了利用DP最优计算节点的专用体系结构来允许数据并行操作比使用通用处理器(即,非DP最优计算节点)更快或更有效地执行的编程工具。GP语言也可以是允许程序员对通用处理器和DP最优计算节点两者均进行编程的另一合适的高级通用编程语言。
在另一个实施例中,代码10包括来自高级数据并行编程语言(此后称DP语言)的形成程序的指令序列。DP语言提供了用于对具有一个或多个DP最优计算节点的计算环境中的DP最优计算节点进行编程的专用语言。通过使用DP语言,程序员在代码10中生成旨在供在DP最优计算节点上执行的DP源代码。DP语言提供了利用DP最优计算节点的专用体系结构来允许数据并行操作比使用通用处理器更快或更有效地执行的编程工具。DP语言可以是现有的DP编程语言,诸如HLSL、GLSL、Cg、C、C++、NESL、Chapel、CUDA、OpenCL、Accelerator、Ct、PGI GPGPU Accelerator、CAPS GPGPU Accelerator、Brook+、CAL、APL、Fortran 90(以及更高版本)、Data Parallel C(数据并行C)、DAPPLE或APL。在该实施例中,代码10可以表示任何合适类型的DP源代码,诸如应用、库函数或操作系统服务。
代码10包括被指定为在DP最优计算节点上执行的代码部分。在使用GP语言来编写代码10的图1的实施例中,GP语言允许程序员在定义矢量函数时使用注释26(例如,__declspec(vector)...)来指定GP源代码。注释26与旨在供在DP最优计算节点上执行的矢量函数的函数名27(例如vector_func)相关联。代码10还可包括在调用点(诸如,forall,reduce,scan,或sort)处对矢量函数的一个或多个调用28(例如,forall...,vector_func,...)。对应于调用点的矢量函数被称为内核函数。内核函数可调用代码10中的其他矢量函数(即,其他DP源代码),并可被视为矢量函数调用图中的根。内核函数还可使用由代码10所定义的类型(例如,类或结构体)。这些类型可以被注释为DP源代码或可以不被注释成DP源代码。在其他实施例中,可使用其他合适的编程语言构造来将代码10的各部分指定为DP源代码和/或通用处理器代码。此外,在使用DP语言来编写代码10的实施例中,注释26可以被省略。
图2是示出将灵活通信算子12应用于输入可索引类型14以生成灵活可索引类型18的实施例的框图。如此处所使用的,可索引类型是实现带有为非负整数的秩以及被表示为element_type的类型的一个或多个下标算子(subscriptoperator)的任何数据类型。如果index<N>是表示整数的N元组的类型(即,任何类型的整数数据类型),那么index<N>的实例就是N个整数的集合{i0,i1,...,im},其中m等于N-1(即,N元组)。秩N的索引算子采用index<N>的N元组实例,并将该实例与被称为元素类型的类型的另一实例相关联,其中元素类型定义了可索引类型中的每个元素。在一个实施例中,可索引类型定义以下算子中的一个或多个:
其中index_declarator采用以下至少一种的形式:
const index<rank>&idx;
const index<rank>idx;
index<rank>&idx;
index<rank>idx.
在其他实施例中,算子可以是函数、函子或更通用的表示。可索引类型的形状是上述下标算子之一被定义的index<rank>的集合。可索引类型一般具有多面体的形状-即,可索引类型可被代数地表示为由坐标轴的线性函数所形成的有限数量个半空间的交点。
参考图1和2,在一个实施例中,代码10的高级语言提供了灵活通信算子12以在数据并行计算环境中的输入可索引类型14上使用。输入可索引类型14具有秩(例如,图1的实施例中的秩N)和元素类型(例如,图1的实施例中的元素类型T)并且定义了可由灵活通信算子12对其操作的计算空间。灵活通信算子12接收输入可索引类型14和资源图16(例如,图1的示例中的resource_map)。从输入可索引类型14和资源图16,灵活通信算子12生成带有由资源图16(也在图3B的示例中示出)指定的片段20(也被称为子网格)的灵活可索引类型18如代码10中所示,灵活通信算子12可用于将灵活可索引类型18传递到DP调用点(即,图1的示例中的forall)。通过这样做,灵活通信算子12致使被该调用点指定的矢量函数被在所有计算节点上复制(例如,图4中示出的计算节点121),其中每个计算节点接收被指派给该计算节点的片段20。
灵活通信算子12致使输入可索引类型14被分解为片段20并且将每个片段20指派给如由资源图16所指定的计算节点。资源图16提供对跨至少一个计算节点将存储器(即,输入可索引类型14)存储在何处的指定。资源图16指定各片段20以使得各片段20的集合覆盖灵活可索引类型18而不重叠。资源图16允许片段20被指定有相同或不同的块大小和/或正规或不正规的块组合。
图3A-3C是示出了生成并使用灵活可索引类型18(1)的示例的框图。在图3A-3C的示例中,灵活通信算子12将具有编号为0到35的元素的6x6矩阵(例如,输入可索引类型14(1))划分为图3B中示出的9个类型为灵活可索引类型18(1)的片段20,如由相应资源图16所指定的(在图2中示出)。在图3B中每个片段20由不同阴影表示。例如,第一片段20(1)包括元素0、1、6、和7,第二片段20(2)包括元素2、3、8和9,等等。灵活通信算子12还致使片段20(1)-20(9)被指派给一组一个或多个计算节点121(1)-121(Q),其中Q是大于或等于1的整数,如由资源图16中的协议所指定并由图3C的箭头30所指示的。
资源图18可以合并任何适当的指派协议,如块分解、环分解、块-块分解、或块-环分解。以下协议示例假定存在3个计算节点121(1)-121(3)(即Q=3)或4个计算节点121(1)-121(4)(即Q=4)并且假定片段20从第一行(即,顶行)开始从左到右在横跨的行中编号为20(1)-20(9)。
在行块分解且Q=3的情况下,输入可索引类型14(1)的36个元素被除以3以使得向每个计算节点121指派12个元素。因此,资源图18致使元素0到11(即,片段20(1)-20(3))被指派给计算节点121(1),元素12到23(即,片段20(4)-20(6))被指派给计算节点121(2),而元素24-35(即,片段20(7)-20(9))被指派给计算节点121(3)。
在行块分解且Q=4的情况下,输入可索引类型14(1)的36个元素被除以4以使得向每个计算节点121指派9个元素。相应地,资源图18致使元素0到8被指派给计算节点121(1),元素9到17被指派给计算节点121(2),元素18到26被指派给计算节点121(3),而元素27到36被指派给计算节点121(4)。
在列块分解且Q=3的情况下,资源图18致使第一和第二列片段20(即,片段20(1)、20(4)和20(7))被指派给计算节点121(1),第三和第四列片段20(即,片段20(2)、20(5)和20(8))被指派给计算节点121(2),第五和第六列片段20(即,片段20(3)、20(6)和20(9))被指派给计算节点121(3)。
在行环分解且Q=3的情况下,资源图18致使元素(3*k)(对于k=0到11)被指派给计算节点121(1),元素(3*k+1)被指派给计算节点121(2),元素(3*k+2)被指派给计算节点121(3)。
在行环分解且Q=4的情况下,资源图18致使元素(4*k)(对于k=0到8)被指派给计算节点121(1),元素(4*k+1)被指派给计算节点121(2),元素(4*k+2)被指派给计算节点121(3),且元素(4*k+3)被指派给计算节点121(4)。
在行块-环分解且Q=3的情况下,分解是对图3B中示出的片段20(1)-20(9)的环分解。相应地,资源图18致使片段20(1)、20(4)和20(7)被指派给计算节点121(1),片段20(2)、20(5)和20(8)被指派给计算节点121(2),并且片段20(3)、20(6)和20(9)被指派给计算节点121(3)。
在行块-环分解且Q=4的情况下,资源图18致使片段20(1)、20(5)和20(9)被指派给计算节点121(1),片段20(2)和20(6)被指派给计算节点121(2),片段20(3)和20(7)被指派给计算节点121(3),并且片段20(4)和20(8)被指派给计算节点121(4)。
在行块-块分解并且Q=3的情况下,资源图18致使片段20(1)-20(3)被指派给计算节点121(1),片段20(4)-20(6)被指派给计算节点121(2),片段20(7)-20(9)被指派给计算节点121(3)。
资源图16的行或列分解决策可以取决于存储器布局。例如,以列为主的存储器布局可能意味着使用适当的协议进行列分解。
在一个实施例中,资源图16包括资源片段的集合,其中每个资源片段将片段20与资源视图(即,计算节点的抽象)(未示出)相关联。例如,对于由如下语句所定义的可索引类型14:
grid<rank>parent_grid;
其中grid<rank>包含两个数据成员:
extent<rank>_M_extent;
index<rank>_M_offset;
例如,图3B中的第二片段20(2),该形状或网格具有_M_extent={2,2}和_M_offset={0,1},且第六片段20(6)具有_M_extent={2,2}和_M_offset={1,2}。相应地,可以使用如下语句来分解parent_grid:
grid<rank>algorithmic_blocks[M1];
grid<rank>memory_blocks[M2];
grid<rank>compute_nodes[M3];
其中M1,M2,M3>0且M1>=M2>=M3。通常,M3分割M2且M2分割M1。algorithmic_blocks(算法块)、memory_blocks(存储器_块)和compute_nodes(计算_节点)所有这三个覆盖parent_grid(父_网格)而没有重叠。algorithmic_blocks表示在所实现的算法中使用的分解。memory_blocks表示在必要时在各节点间移动的存储器的粒度。Compute_nodes表示被指派给计算节点以存储相应数据的粒度。
假定存在关联使得每个algorithmic_block或memory_block可查找它被存储在compute_node上的何处,并且使得每个algorithmic_block可查找它被存储在memory_block上的何处。可生成具有resource_segments(资源_片段)的被称为resource_map(资源_图)的类,所述resource_segments形成子网格和resource_view(资源_视图)之间的关联。
通过使用灵活通信算子12,可无缝地访问灵活可索引类型18的数据而无需用户知晓该数据当前所驻留的计算节点。对于示例可索引类型14,具有形状parent_grid的A,A的存储是由resource_segment的实例确定的。为了访问A在如下语句所述处的元素:
index<rank>_Index;
首先找到包含_Index的child-grid(子-网格),然后确定偏移(offset):
index<rank>_Offset;
以使得:
_Index=child-grid-offset+_Offset。
在resource_segment注释的情况下,该关系为:
_Index=_Resource_segment._M_child._M_offset+_Offset。
为了提高查找的速度,执行以下检查以确定_Index(当_Index改变时)是否仍属于_Resource_segment._M_child:
给定_Index所属的子网格或_Resource_segment的确定取决于分解模式。在最坏情况下,可以使用但是可以不避免每个维度中的二分搜索。然而,在所有小块具有相等范围的2048x2048小块分解(tile decomposition)的情况下,例如,寻找_M_child._M_offset等于下式的_Resource_segment:
index<2>_Tile(2048,2048);
(_Index+_Tile-1)/_Tile.
可使用_Resource_segment(即,当前的resource_segment),直到:
if(_Local_offset<_Local_bounds){...}
被违反,在这种情况下再次用_Tile分割新的_Index并重复。此机制对于具有只需要不频繁地寻找新包含的resource_segments的局部的算法是最优的。
在下面描述的局部全局视图表示中,用户索引算子可省略该是否检查(if-check)(在此处称为边界检查):
if(_Local_offset<_Local_bounds){...}
,因为用户被信任为在每次访问时处于边界内。如果给定resource_segment被耗尽且将使用另外一个,则该用户被信任以调用重新设定当前resource_segment的函数。在局部全局视图的最简单形式中,所有三个分解:
grid<rank>algorithmic_blocks[M1];
grid<rank>memory_blocks[M2];
grid<rank>compute_nodes[M3];
是正规的,具有相同大小的块或小块(tile)。将可索引类型分区成小块的小块通信算子可被应用于第一分解以产生:
algorithmic_tiles
个体小块是:
algorithmic_tiles(_tile_index)。
当拥有者复制在如下语句上启动时:
algorithmic_tiles(_tile_index)
确定所包含的memory_blocks[k1]和compute_nodes[k2]。接下来确定拥有者compute_nodes[k3],然后将memory_blocks[k1]从compute_nodes[k2]移到compute_nodes[k3]。
自动化存储器移动粒度通常比片段20的子网格分解在更细的粒度上。例如,假定图3A的矩阵表示一个6144x6144元素矩阵,即,每个被编号的算法块表示1024x1024数据元素。假定该6144x6144矩阵被分解成2048x2048compute_nodes块,诸如在图3B中。此外,假定Q=4且计算节点121(1)、121(2)、121(3)和121(4)被根据块-环分解指派给2048x2048块(即,片段20(1)-20(9))。然后片段20(1)、20(5)和20(9)被指派给计算节点121(1),片段20(2)和20(6)被指派给计算节点121(2),片段20(3)和20(7)被指派给计算节点121(3),并且片段20(4)和20(8)被指派给计算节点121(4)。在此示例中存储器可在1024x1024块中移动。相应地,如果计算寻求将单个数据元素从1024x1024块移动单个数据元素,则整个1024x1024块被移动。
灵活通信算子12允许用灵活可索引类型18的片段20的完整全局视图表示或局部全局视图表示来编码数据并行(DP)算法以管理片段20在各计算节点间的移动。
完整全局视图表示允许DP算法就像它们将要在单个计算节点上运行而自动化拥有者复制存储器移动在后台进行一样被编码。作为矩阵加法的一个示例,假定A、B和C每个是如图3A所示的6144x6144矩阵,其中每个所编号的块表示1024x1024数据元素。A和B携带有效数据,但是C被分配但不必然携带任何数据。进一步假定A、B和C每个被指派在计算节点121(1)-121(Q)上,其中在此情况下Q等于4且其中A、B和C中的每一个的片段20(1)-20(9)分别被存储在计算节点121(1)-121(Q)上。使用以下的计算:
C=A+B:其中C(i,j)=A(i,j)+B(i,j):0<=i,j<6
每个(i,j)表示1024x1024元素。
拥有者复制意味着如果必须的话则数据被移动到计算节点121,在计算节点121处计算答案,即C。在此示例中,A和B的块被移动到计算节点121,在计算节点121处C的相应块作为计算规定而被存储。然而,对于简单的矩阵加法,不需要任何移动,因为A和B的块和C的相应块被存储在相同计算节点121上。如下计算:
C(1,2)=A(1,2)+B(1,2)
为A、B和C中的每一个使用图3B中的块8。块8是对于A、B和C中的每一个存储在计算节点121(2)上的片段20(2)的一部分,所以不发生数据移动。类似地,对相应片段20和计算节点121进行如下计算:
在片段20(1)、计算节点121(1)上:C(0,0)=A(0,0)+B(0,0)
在片段20(1)、计算节点121(1)上:C(1,0)=A(1,0)+B(1,0)
在片段20(4)、计算节点121(4)上:C(2,0)=A(2,0)+B(2,0)
在片段20(4)、计算节点121(4)上:C(3,0)=A(3,0)+B(3,0)
在片段20(7)、计算节点121(3)上:C(4,0)=A(4,0)+B(4,0)
在片段20(7)、计算节点121(3)上:C(5,0)=A(5,0)+B(5,0)
这里片段是指分解的一个元素:
grid<2>compute_nodes[9]。
事实上:
grid<2>algorithmic_blocks[36];
grid<2>memory_blocks[18];
grid<2>compute_nodes[9];
其中algorithmic_blocks具有范围1024x1024,memory_blocks具有范围2048x1024,而compute_nodes具有范围2048x2048。因此,矩阵加法是非常典型的示例。
在使用上述假定的另一示例中,B的转置被加到A以产生C,如下:
C=A+BT:其中C(i,j)=A(i,j)+B(j,i)T:0<=i,j<6
其中每个(i,j)表示1024x1024元素而B(j,i)T是底层1024x1024块的转置。
在此情况下,B(j,i)被移动到计算节点121上,其中对于除片段20(1)、20(5)和20(9)中的那些块之外的所有块存储C(i,j)(和A(i,j))。例如,片段20(1)的块不需要被移动,因为C的片段20(1)的块的计算是:
C(0,0)=A(0,0)+B(0,0)T
C(0,1)=A(0,1)+B(1,0)T//B(1,0)T在B的片段20(1)中
C(1,0)=A(1,0)+B(0,1)T//B(0,1)T在B的片段20(1)中
C(1,1)=A(1,1)+B(1,1)T
然而,对于C的片段segment 20(4)的块:
C(2,0)=A(2,0)+B(0,2)T
C(2,1)=A(2,1)+B(1,2)T
C(3,0)=A(3,0)+B(0,3)T
C(3,1)=A(3,1)+B(1,3)T
B块来自存储在计算节点121(2)上的片段20(2)的块,而C块来自存储在计算节点121(4)上的片段20(4)的块。相应地,B的块2(即,B(0,2)T)的1024x1024元素被移动到计算节点121(4)、加到A(2,0)、并指派给C(2,0),B的块8(即,B(1,2)T)的1024x1024元素被移动到计算节点121(4)、加到A(2,1)、并指派给C(2,1),B的块3(即,B(0,3)T)的1024x1024元素被移动到计算节点121(4)、加到A(3,0)、并指派给C(3,0),B的块9(即,B(1,3)T)的1024x1024元素被移动到计算节点121(4)、加到A(3,1)、并指派给C(3,1)。
使用完整全局视图表示,存储器移动自动进行,因为每个块都携带哪个计算节点121存储该块的信息。这些计算可从计算节点121中的任何一个或一主机(如在图4中示出并在下面更详细地描述的主机101)指导。
在上面的示例的其他变型中,多个片段20可被指派给同一计算节点121,其中计算节点121的数量少于片段20的数量。此外,计算节点121的处理能力可被加权以使得可以相比较慢的计算节点121,向较快的计算节点121指派更多的片段20。所述指派可根据上述协议中的一个或多个来执行。
在上面的变型中还可实现使用工作窃取(work-stealing)的自动负载平衡。当计算节点121完成其计算时,计算节点121尝试窃取指派给其他节点121的计算。指导计算的计算节点121,或者有可能是主机,可存储工作项目(work-item)的工作窃取队列,其中这些队列包含表示拥有者矩阵(例如,C)上的存储器移动粒度(例如,1024x1024)的计算的任务。
在来自上面的矩阵的A、B和C加上B的转置的示例中,使用四个权重相等的计算节点121(1)-121(4)和块-环分解协议,下面的四个工作窃取队列可被存储如下。
因此在上述图像、且C=A+BT、且存储器移动粒度=1024x1024、且4个机器被相等加权(w0=w1=w2=w3=1)、且块-环分解的情况下:
由12个任务组成的queue0(队列0)——片段20(1)、20(5)和20(9)中的每一个的4个1024x1024块;
由8个任务组成的queue1(队列1)——片段20(2)和20(6)中的每一个的4个1024x1024块;
由8个任务组成的queue2(队列2)——片段20(3)和20(7)中的每一个的4个1024x1024块;
由8个任务组成的queue3(队列3)——片段20(4)和20(8)中的每一个的4个1024x1024块。
例如,queue2包括如下任务:
C(0,4)=A(0,4)+B(4,0)T
C(0,5)=A(0,5)+B(5,0)T
C(1,4)=A(1,4)+B(4,1)T
C(1,5)=A(1,5)+B(5,1)T
C(4,0)=A(4,0)+B(0,4)T
C(4,1)=A(4,1)+B(1,4)T
C(5,0)=A(5,0)+B(0,5)T
C(5,1)=A(5,1)+B(1,5)T
每个计算节点121从其相应工作窃取队列的顶部获取一个任务,直到完成来自该工作窃取队列的所有任务。当计算节点121的工作窃取队列为空时,计算节点121从与另一计算节点121相对应的工作窃取队列的底部窃取任务。通常在粒度级algorithmic_blocks通过该小块通信算子来启用局部全局视图。假定正规小块分解,该小块通信算子被应用于第一个以产生:
algorithmic_tiles
个体小块是:
algorithmic_tiles(_tile_index).
当拥有者复制在下式上启动时:
algorithmic_tiles(_tile_index)
确定所包含的memory_blocks[k1]和compute_nodes[k2]。接下来确定拥有者compute_nodes[k3],然后将memory_blocks[k1]从compute_nodes[k2]移到compute_nodes[k3]。这都是在algorithmic_tiles(_tile_index)访问级别上进行的。当实现该算法时,一元素(或,递归地,更细的块)被访问为:
algorithmic_tiles(tile_index)(local_index)
与完整全局视图表示不同,局部全局视图表示允许存储器移动由用户明确指定。在上述完整全局视图表示示例中,存储器移动粒度是1024x1024块,以使得如果计算节点121访问该块中的单个元素则整个1024x1024块被移动到该计算节点121。
在一些计算中,计算的粒度比存储器移动粒度更细,并且局部全局视图表示与用户明确指导每个存储器块要被移动到何处相比提供了优点。例如,假定在完整全局视图表示示例中存储器移动粒度是2048x1024,即,只要从两个块中的任何一个移动元素,则这两个块都被移动。所以对于C=A+BT,C的片段20(4)的块的计算是:
C(2,0)=A(2,0)+B(0,2)T
C(2,1)=A(2,1)+B(1,2)T
C(3,0)=A(3,0)+B(0,3)T
C(3,1)=A(3,1)+B(1,3)T
在每种情况下,B块被存储在计算节点121(2)上,而C块和A块(C是拥有者)被存储在计算节点121(4)上。因此,通过明确指导B的块2(即,B(0,2)T)的任何元素移动到计算节点121(4)来执行上述语句中的前两个。由于2048x1024存储器粒度,B的块2和块8(即,B(0,2)T和B(1,2)T)两者都被移动到计算节点121(4)以允许前两个语句的加法由计算节点121(4)执行。同样,通过明确指导B的块3(即,B(0,3)T)的任何元素移动到计算节点121(4)来执行上述语句中的后两个。由于2048x1024存储器粒度,B的块3和块9(即,B(0,3)T和B(1,3)T)两者都被移动到计算节点121(4)以允许后两个语句的加法由计算节点121(4)执行。
如这些示例所示,计算的粒度可以比存储器移动粒度(其可比计算节点粒度更细)更细,以使得可存在使用一种或多种算法在给定存储器移动块上执行的许多任务。移动块的元素的单个指示允许对该块进行操作的任务被更高效地执行。用户和实现两者都可省略为了查看存储器是否需要被移动而进行的检查,直到指导算法开始在另一存储器移动块上工作,并且,如上面所见,与algorithmic_blocks分解相对应的小块通信算子实际上在必要时指导存储器移动。如果1024x1024小块(例如,块3)要被移动,则包含的2048x1024存储器移动块(例如,存储器移动块3)被从包含的2048x2048计算节点块(例如,片段20(2))移动到拥有者复制确定的2048x2048块(例如,片段20(4))。如果现在块9被移动,则访问相应的小块将查找它的包含的存储器移动块并且确定它已被移动到片段20(4),所以不需要任何移动。上面阐述的边界检查可被省略,因为在小块中的实际数据元素访问之前已经在小块级别上进行了正确的存储器移动。也就是说:
algorithmic_tiles(_tile_index).
生成任何必需的存储器移动,则:
algorithmic_tiles(_tile_index)(_local_index)
可被访问而无需对每个local_index进行边界检查。例如,在上述计算中,每个存储器移动块有两个算法任务。
在实践中,对拥有者存储器上的给定存储器块(例如,C)执行的所有计算可被分组成一个大任务并且该任务中的第一个语句可以是单个存储器移动指示。在一个实施例中,该指示的形式可以是如下的对任务的标准C++注释:
[[move_memory(C,B)]]
void kernel(field<2,double>&C,const field<2,double>&A,const field<2,double>&B);
通过使用此注释,编译器可优化并交错存储器移动和计算。
以下代码提供了一个实施例中的灵活通信算子12的实现的概览。
图4是示出被配置成编译并执行包括灵活通信算子12的数据并行代码10的计算机系统100的实施例的框图。
计算机系统100包括主机101,主机101具有容纳在一个或多个处理器封装(未示出)中的一个或多个处理元件(PE)102、以及存储器系统104。计算机系统100还包括零个或更多个输入/输出设备106、零个或更多个显示设备108、零个或更多个外围设备110和零个或更多个网络设备112。计算机系统100还包括具有一个或多个DP最优计算节点121的计算引擎120,其中每一DP最优计算节点121包括一组一个或多个处理元件(PE)122和存储DP可执行代码138的存储器124。
主机101、输入/输出设备106、显示设备108、外围设备110、网络设备112和计算引擎120使用包括任何合适的类型、数量和配置的控制器、总线、接口、和/或其他有线或无线连接的一组互连114来进行通信。
计算机系统100表示出于通用或专用目的而配置的任何合适的处理设备。计算机系统100的示例包括服务器、个人计算机、膝上型计算机、图形输入板计算机、智能电话、个人数字助理(PDA)、移动电话、和音频/视频设备。计算机系统100的组件(即,主机101、输入/输出设备106、显示设备108、外围设备110、网络设备112、互连114和计算引擎120)可包含在公共外壳(未示出)中或任何合适数量的分立外壳(未示出)中。
处理元件102各自形成被配置成执行存储在存储器系统104中的指令(即软件)的执行硬件。每一处理器封装中的处理元件102可具有相同或不同的体系结构和/或指令集。例如,处理元件102可以包括顺序执行元件、超标量执行元件、以及数据并行执行元件(例如GPU执行元件)的任何组合。每一处理元件102被配置为访问并执行存储在存储器系统104中的指令。这些指令可包括基本输入输入系统(BIOS)或固件(未示出)、操作系统(OS)132、代码10、编译器134、GP可执行代码136以及DP可执行代码138。每一处理元件102可以结合或响应于从输入/输出设备106、显示设备108、外围设备110、网络设备112和/或计算引擎120接收到的信息来执行指令。
主机101引导并执行OS 132。OS 132包括可以由处理元件执行以管理计算机系统100的组件并提供允许程序访问并使用这些组件的一组功能的指令。在一实施例中,OS 132是Windows操作系统。在其他实施例中,OS 132是适于和计算机系统100一起使用的另一操作系统。
当计算机系统执行编译器134来编译代码10时,编译器134生成一个或多个可执行代码——例如,一个或多个GP可执行代码136和一个或多个DP可执行代码138。在其他实施例中,编译器134可生成一个或多个GP可执行代码136以使每个GP可执行代码136包括一个或多个DP可执行代码138,或可生成一个或多个DP可执行代码138而不生成任何GP可执行代码136。GP可执行代码136和/或DP可执行代码138是响应于为了编译代码10的全部或所选部分对具有数据并行扩展的编译器134的调用来生成的。该调用可以由例如程序员或计算机系统100的其他用户或者计算机系统100中的其他代码或另一个计算机系统(未示出)中的其他代码来生成。
GP可执行代码136表示旨在供在一个或多个通用处理元件102(例如中央处理单元(CPU))上执行的程序。GP可执行代码136包括来自一个或多个通用处理元件102的指令集的低级指令。
DP可执行代码138表示旨在并被优化成在一个或多个数据并行(DP)最优计算节点121上执行的数据并行程序或算法(例如着色器)。在一个实施例中,DP可执行代码138包括DP字节代码或者在DP最优计算节点121上被执行之前使用设备驱动程序(未示出)被转换成来自DP最优计算节点121的指令集的低级指令的某种其他中间表示(IL)。在其他实施例中,DP可执行代码138包括来自一个或多个DP最优计算节点121的指令集的低级指令,其中该低级指令是由编译器134插入的。因此,GP可执行代码136可以由一个或多个通用处理器(诸如,CPU)直接执行,并且DP可执行代码138或可以由一个或多个DP最优计算节点121直接执行或可以在被转换成DP最优计算节点121的低级指令以后由一个或多个DP最优计算节点121来执行。
计算机系统100可使用一个或多个处理元件102来执行GP可执行代码136,并且计算机系统100可使用下文中更详细描述的一个或多个PE 122来执行DP可执行代码138。
存储器系统104包括被配置成存储指令和数据的任何合适的类型、数量和配置的易失性或非易失性存储设备。存储器系统104的储存设备表示储存包括OS 132、代码10、编译器134、GP可执行代码136以及DP可执行代码138的计算机可执行指令(即软件)的计算机可读储存介质。指令可由计算机系统100执行以履行此处所述的OS 132、代码10、编译器134、GP可执行代码136以及DP可执行代码138的函数和方法。存储器系统104存储从处理元件102、输入/输出设备106、显示设备108、外围设备110、网络设备112和计算引擎120接收的指令和数据。存储器系统104将所存储的指令和数据提供给处理元件102、输入/输出设备106、显示设备108、外围设备110、网络设备112和计算引擎120。存储器系统104中的存储设备的示例包括硬盘驱动器、随机存取存储器(RAM)、只读存储器(ROM)、闪存驱动器和卡、以及诸如CD和DVD之类的磁盘和光盘。
输入/输出设备106包括被配置成将指令或数据从用户输入到计算机系统100并将指令或数据从计算机系统100输出到用户的任何合适的类型、数量和配置的输入/输出设备。输入/输出设备106的示例包括键盘、鼠标、触摸垫、触摸屏、按钮、拨盘、旋钮和开关。
显示设备108包括被配置成向计算机系统100的用户输出文本和/或图形信息的任何合适的类型、数量和配置的显示设备。显示设备108的示例包括监视器、显示屏和投影仪。
外围设备110包括被配置成用计算机系统100中的一个或多个其他组件来操作以执行通用或专用处理功能的任何合适的类型、数量和配置的外围设备。
网络设备112包括被配置成允许计算机系统100跨一个或多个网络(未示出)进行通信的任何合适的类型、数量和配置的网络设备。网络设备112可根据任何合适的网络协议和/或配置来操作以允许计算机系统100将信息发送给网络或者从网络接收信息。
计算引擎120被配置成执行DP可执行代码138。计算引擎120包括一个或多个计算节点121。每一计算节点121是共享一存储器分层结构的计算资源的集合。每一计算节点121包括一组一个或多个PE 122以及存储DP可执行代码138的存储器124。PE 122执行DP可执行代码138并将由DP可执行代码138生成的结果储存在存储器124中。具体而言,PE 122执行DP可执行代码138以将灵活通信算子12应用于输入可索引类型14以生成如图4所示且如上文中详细描述的输出可索引类型18。
具有一个或多个计算资源且该资源具有为数据并行计算(即,执行DP程序或算法)而优化的硬件体系结构的计算节点121被称为DP最优计算节点121。DP最优计算节点121的示例包括其中该组PE 122包括一个或多个GPU的节点121,以及其中该组PE 122包括通用处理器封装中的该组SIMD单元的节点121。不具有带有为数据并行计算而优化的硬件体系结构的任何计算资源的计算节点121(例如,仅仅具有通用处理元件102的处理器封装)被称为非DP最优计算节点121。在每一计算节点121中,存储器124可与存储器系统104(诸如,GPU所使用的GPU存储器)分开,或可以是存储器系统104的一部分(例如,通用处理器封装中的SIMD单元所使用的存储器)。
主机101形成被配置成将DP可执行代码138提供给计算节点121以供执行并且使用互连114接收由DP可执行代码138生成的结果的主机计算节点。主机计算节点包括共享一存储器分层结构(即,存储器系统104)的通用计算资源(即,通用处理元件102)的集合。主机计算节点可被配置成具有对称多处理体系结构(SMP),并且还可被配置为使用例如非均匀存储器存取(NUMA)体系结构来最大化存储器系统104的存储器局部性。
主机计算节点的OS 132被配置为执行DP调用点以使DP可执行代码138被DP最优计算节点或非DP最优计算节点121执行。在其中存储器124与存储器系统104分开的实施例中,主机计算节点使DP可执行代码138以及一个或多个可索引类型14从存储器系统104复制到存储器124。在其中存储器系统104包括存储器124的实施例中,主机计算节点可将存储器系统104中的DP可执行代码138和/或一个或多个可索引类型14的副本指定为存储器124,和/或将DP可执行代码138和/或一个或多个可索引类型14从存储器系统104的一部分复制到形成存储器124的存储器系统104的另一部分中。在计算节点121和主机计算节点之间的复制过程可以是同步点,除非它被指定为是异步的。
主机计算节点和每一计算节点121可并发地彼此独立地执行代码。主机计算节点和每一计算节点121可在同步点处进行交互以协调节点计算。
在一个实施例中,计算引擎120表示图形卡,其中一个或多个图形处理单元(GPU)包括PE 122以及与存储器系统104分开的存储器124。在该实施例中,图形卡的驱动程序(未示出)可以将DP可执行代码138的字节代码或某种其他中间表示(IL)转换成GPU的指令集以供GPU的PE 122执行。
在另一实施例中,从一个或多个GPU(即,PE 122)的组合中形成计算引擎120,该一个或多个GPU被包括在具有一个或多个通用处理元件102以及包括存储器124的存储器系统104的一部分的处理器封装中。在该实施例中,可以在计算机系统100上提供附加软件来将DP可执行代码138的字节代码或某种其他中间表示(IL)转换成处理器封装中的GPU的指令集。
在另一实施例中,从一个或多个处理器封装中的一个或多个SIMD单元的组合中形成计算引擎120,该一个或多个处理器封装包括处理元件102以及包括存储器124的存储器系统104的一部分。在该实施例中,可以在计算机系统100上提供附加软件来将DP可执行代码138的字节代码或某种其他中间表示(IL)转换成处理器封装中的SIMD单元的指令集。
在又一实施例中,从一个或多个处理器封装中的一个或多个标量或矢量处理流水线的组合中形成计算引擎120,该一个或多个处理器封装包括处理元件102以及包括存储器124的存储器系统104的一部分。在该实施例中,可以在计算机系统100上提供附加软件来将DP可执行代码138的字节代码或某种其他中间表示(IL)转换成处理器封装中的标量处理流水线的指令集。
尽管此处说明并描述了具体实施例,但本领域技术人员可以理解,可用各种替换和/或等价实现来替换所示出并描述的具体实施例而不背离本发明的范围。本申请旨在覆盖此处讨论的具体实施例的任何改编或变型。因此,本发明旨在仅由权利要求书及其等效方案来限制。