任务类似一个函数,其参数可以是数组或标量。而每一个任务都是在存储结构树的特定一层中执行的。任务的操作对象是数组,而不是单个数组元素,也就是,每一个任务都是完成对参数数组中全体元素的操作。在此程序中,foo( )通过两次调用bar( )来实现对A、B数组的操作。任务在同一层次不同节点之间具有独立性。foo( )对数组A[N]、B[N]的操作都局限在这两个数组内部,不会超越。而在调用其它任务时,多任务之间的数据也不存在依赖关系。因为这种独立性的存在,不同节点能够并行执行任务,即任务是表示并行性的基本单位。同时,任务之间可以互相调用,任务调用的过程即是数据由存储树上的父节点传递到子节点的过程。而父子节点间的数据通信,就是通过调用任务(参数传递)来完成的。foo( )在调用bar( )的过程中,将数组划分成十个部分分别传递给两个子任务,从而实现了数据传递。子任务的调用过程既是数据划分的过程,也是将任务细化的过程。因此,任务也是表示程序局部性的基本构成。
任务可以分为两种,内部任务映射在存储树的内部节点上,其主要作用是对数据和任务进行划分,以表达程序的局部性;外部任务映射在存储树的叶子节点上,主要任务是完成计算操作。如图3所示,为将矩阵乘运算分别映射到两类节点的具体实现。
图3 将矩阵乘运算分别映射到两类节点的实现过程
程序中,内部节点的主要任务是完成任务划分及并行性的标注。在任务层层划分的过程中,采用三个可调变量:P、Q、R。这三个变量是根据系统的用户配置文件的系统参数设定的。内部节点通过递归调用的方式层层进行数据划分,直到遇到叶子节点才进行真正的计算操作。而对于叶子节点而言,根据不同的平台,可以有多个相应的实现,部署时在配置文件中选择相应的实现。在这个实现中,P、Q、R在配置文件中的设置可以使所操作的数据正好能放入二级缓存、一级缓存,使得程序的局部性能在对应平台里充分发挥。
在Sequoia中,由于程序员仅表达了程序的执行逻辑,并通过父子间数据拷贝来表现程序的局部性,编译器就能有充分的空间对程序进行优化。只要确保用户所写的语义没有改变,所有的变换都是合法的。
图4列举了一个编译器所作变换的实例。

图4 编译器所作变换的实例
在这个变换中,数组A拷贝回其父节点存为B,接着将B从父节点拷回,存为C。编译器通过依赖分析,发现中间拷贝B是可以删除的,于是消除了多余的拷贝操作。
应用Sequoia进行并行化的效果表明,Sequoia的编程模型不仅适用于分布式内存的体系结构,同样适用于共享内存的结构。它将体系结构的具体细节与算法的描述很好的划分,让程序员能更关注于局部性的表达,可以提供有效的并行化解决方案。
3 结语
多核技术的最大特征就是核的数目越来越多,核间越来越趋向多样化和异构化。平台的异构性、操作系统需要管理资源数量的增加以及所运行应用的多样性等都是未来多核系统所要面临的问题。以数据局部性描述为核心的并行编程语言Sequoia已经得到工业界的初步认可,笔者尝试将该方法应用于多核系统的并行化编程之中,实践证明其良好的并行化方法和策略具有提高效率、缩短周期的特点,因此,笔者也大胆预测Sequoia并行编程模型应该具有一定的应用前景,值得进一步的研究和探讨。