Co-Array Fortran 看上去很美 ?

intel_iclifort 2011-05-09 03:40:15
Co-Array Fortran 看上去很美 ?



Co-Array Fortran(下面简称 CAF,中文暂称为”Fortran的集合数组阵列扩展“),最初是作为 Fortran 95 一个小小的扩展,由 Numrich 和 Reid 发表于 1998年 ACM Fortran Forum 17卷第2期的 “Co-Array Fortran for Parallel Programming” 文章中。

直到 2005 年的5月,国际标准组织 Fortran 委员会决定将 CAF 包含在下一版本的 Fortran 语言规范草案中 (也就是今天我们常说的 Fortran 2008).

为什么今天要讨论和学习 CAF 呢?

因为它提供了一种非常简洁、直观的标注方法,来处理那些通常必须要通过消息传递(message-passing)模型来处理的数据分解问题;同时它本身使用的是自然的,类似于 Fortran 的语法。 这种语法独立于硬件架构,可以在分布式内存系统,共享内存系统和集群系统等中得到使用。

有了 CAF 扩展之后,传统的 Fortran 语言就变成了一个稳定、高效的并行编程语言。熟悉 Fortran 语言的编程者只需要学习和掌握几条新的语法规则,而这些新规则解决了任何并行编程模型中都必须克服的几个基本问题:任务分发,和数据分发。

先来看看任务分发。一个 CAF 的程序被复制为固定数量的若干份,每一个复制拥有自己独立的数据对象。程序的每一个内存复本被称作一个“镜像” (image). 每一个镜像都是独立地异步执行,因此各个镜像执行的路径也是不相同的。程序员可以通过若干种方法决定镜像的实际执行路径,比如说借助每一个镜像唯一的“索引值"(index), 使用通常的Fortran控制语句;或者通过显式的同步。 而在同步之间的代码段,编译器可以自由地使用各种常用的优化技巧,就好像只有一个镜像存在。

接下来,我们必须要考虑数据的分发过程。 Fortran语言的Co-array扩展,允许程序员可以使用一个非常类似于通常 Fortran 数组表示的语法,来表明不同内存镜像之间的关系,从而实现数据的分发。 在如下的例子中,一个新的对象,coarray ("集合数组“), 被添加到原有的语言规范中:

REAL, DIMENSION(N)
  • :: X,Y
    X
  • (:) = Y(:)[Q]

    它声明了每个镜像都包含两个 real 类型的,大小为 N 的数组。 如果在每个镜像中的 Q 变量具有相同的值,那么赋值语句的效果就是,每个镜像都从镜像 Q 的数组 Y 复制数据,并且将它们复制到本地的数组 X 中。

    在圆括号 "()" 中的数组下标,在一个内存镜像的范围内,继续遵循通常的 Fortran 规范。 而在方括号 "[]" 中的数组下标, 则提供了一种非常便捷的标注方法,来表明访问不同镜像的对象。 一个指向co-array的不包含方括号的引用,会被指向在当前本地内存中正在运行的镜像中的对象,因此 co-arrary仅当需要时才会使用,大部分代码和原先的没有什么区别。

    如果和原有的 Fortran 90 数组的语法相结合,CAF 可以展现给我们一个非常强大的,却十分简洁的途径,来表达如何进行远程内存访问。下面是更多的一些例子:

    A = B[PE] ! 从 B[PE] 中取值
    B[PE] = A ! 复制给 B[PE]
    B[:] = A ! 广播 A 的值
    B[LIST] = A ! 广播 A 的值,只针对由LIST定义的所有镜像的一个子集
    C(:) = B[:] ! 收集所有镜像的 B
    S = MINVAL(Y[:]) ! 求所有镜像的变量 Y 的最小值 (reduce)
    B(1:M)[1:N] = S ! 标量 S , 按照数组形状 (1:M,1:N) 的方式赋值

    此外, CAF 还增加了若干 内部函数 (intrinsics), 比如:函数 NUM_IMAGES() 返回了当前程序镜像的总数量;而 函数 THIS_IMAGE() 返回了当前镜像的索引值( 显然应该在 1 和 NUM_IMAGES() 之间);而 子程序 SYNC_ALL() 则提供了一个全局的边界,要求在此之前的所有镜像中的所有操作必须全部结束后,任何镜像中后于此边界的语句才能继续执行。 当然通常情况下,只等待与之有关联的镜像会更加合适,执行速度也更快,SYNC_ALL(WAIT=LIST) 就提供了这种功能。 START_CRITICAL 和 END_CRITICAL 则提供了关键区的保护。

    镜像和集合数组的同步,是 CAF 程序中最最重要的部分。例如,在下面这个例子中,我们需要实现一个固定顺序的累积求和:

    REAL SUM

  • CALL SYNC_ALL
  • ( WAIT=1 )
    DO IMG= 2,NUM_IMAGES()
    IF (IMG==THIS_IMAGE()) THEN
    SUM = SUM + SUM[IMG-1]
    ENDIF
    CALL SYNC_ALL( WAIT=IMG )
    ENDDO

    虽然使用 SYNC_ALL 时仅仅等待某个正在运行中的镜像能够提高性能,但是我们仍然需要有 NUM_IMAGES() 次全局的同步。 可以考虑一个更好的方案,可以最小程度的减少同步

    REAL SUM

  • ME = THIS_IMAGE
  • ()
    IF (ME.GT.1) THEN
    SYNC IMAGES ( (/ME-1,ME/) )
    SUM = SUM + SUM[ME-1]
    ENDIF
    IF (ME.LT.NUM_IMAGES()) THEN
    SYNC IMAGES ( (/ME,ME+1/) )
    ENDIF

    现在每一个镜像最多只参与两次同步,并且仅仅发生在镜像顺序前后相邻的两个镜像之间。注意第一个 SYNC IMAGES 调用会和前一个镜像的第二个 SYNC IMAGES 调用匹配。这也充分展示了 CAF 同步机制的好处, 不仅可以提高数据并行算法的性能,还可以提供针对数据并行的隐式的程序流程控制。

    现在你应该相信,Co-Array Fortran 确实不错吧。

    后面我们会介绍如何结合 Intel Fortran Composer XE 2011 来学习和发挥 CAF 的巨大能量。


    顺便说一句, 如果希望得到“最终”版本的 Fortran 2008 标准的电子版本,当然是英文的,请到英特尔软件网络([url=http://software.intel.com/en-us/forums/]http://software.intel.com/en-us/forums/)相关的 Fortran 论坛中找吧。
...全文
937 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
intel_iclifort 2011-10-18
  • 打赏
  • 举报
回复
Intel MPI 库可以在如下的网站上,申请30天的试用许可文件

http://software.intel.com/en-us/articles/intel-software-evaluation-center/
intel_iclifort 2011-10-18
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 quaintchewster 的回复:]

运行时需要mpd
但是openmpi没有mpd
到底怎么搞,只能用intel mpi? 但是intel非商业版的编译器套件不含mpi
[/Quote]

首先,如果你使用的Intel编译器是非商业版的,那肯定是在Linux下了。

目前Intel编译器对CAF的实现还必须依赖于Intel MPI.

Installation of the compiler automatically installs the necessary Intel® MPI run-time libraries to run on shared memory.

The Intel® Cluster Studio (Cluster Toolkit) installs the necessary Intel® MPI Library run-time libraries to run on distributed memory.

Use of coarray applications with any other MPI implementation, or with OpenMP*, is not supported.


Examples on Linux* OS:

•-coarray=shared -coarray-num-images=8 runs a coarray program on shared memory using 8 images.

•-coarray=distributed -coarray-num-images=8 runs a coarray program on distributed memory across 8 images (the Intel® Cluster Toolkit license must be installed).

panjianlei 2011-10-14
  • 打赏
  • 举报
回复
怎么回事
quaintchewster 2011-08-05
  • 打赏
  • 举报
回复
运行时需要mpd
但是openmpi没有mpd
到底怎么搞,只能用intel mpi? 但是intel非商业版的编译器套件不含mpi
魏鵬 2011-05-13
  • 打赏
  • 举报
回复
同时谢谢你的耐心回答,发现帖子没法重新编辑,刚才忘记写了,呵呵
魏鵬 2011-05-13
  • 打赏
  • 举报
回复
得确是这样,fortran作为高性能计算领域大量应用的语言,人们选择他的原因正是其高效的计算能力,拥有简单却强大的并行方案对于其发展尤为关键。我一直觉的,面向对象可以没有,但并行一定要做,复杂的数据结构可以用混编用其他语言。
昨天在用的时候得确发现你里面的sync all、wait等语句与标准不一致。呵呵,最后放上一个我修改别人的计算圆周率的程序,里面正是用coarray实现的并行,里面肯定有不合理的地方,请大家拍砖。
PROGRAM COMPUTE_PI
IMPLICIT NONE

INTEGER ME,NIMG,I,N
REAL(SELECTED_REAL_KIND(P=33)) PI,PSUM,X,W
REAL(SELECTED_REAL_KIND(P=33)) MYPI

  • INTEGER
  • (SELECTED_INT_KIND(R=17)) ST,ET,RAT

    NIMG = NUM_IMAGES()
    ME = THIS_IMAGE()

    IF(ME==1) THEN
    WRITE(*,'("请输入迭代数 : ",\)')
    READ(*,*) N
    CALL SYSTEM_CLOCK(COUNT=ST,COUNT_RATE=RAT)
    END IF

    W = 1.D0/N
    PSUM = 0.D0
    DO I= ME,N,NIMG
    X = W * (I - 0.5D0)
    PSUM = PSUM + 4.D0/(1.D0+X*X)
    ENDDO
    MYPI = W * PSUM
    SYNC ALL

    IF (ME==1) THEN
    PI = MYPI
    DO I= 2,NIMG
    PI = PI + MYPI[I]
    ENDDO
    CALL SYSTEM_CLOCK(COUNT=ET)
    WRITE(*,*) 'COMPUTED TIME ',(ET-ST)/REAL(RAT,KIND(PI)),'SEC'
    WRITE(*,*) 'COMPUTED PI = ',PI
    WRITE(*,*) 'REAL PI = 3.1415926535897932384626433832795'
    PAUSE
    ENDIF

    END PROGRAM
intel_iclifort 2011-05-13
  • 打赏
  • 举报
回复
lh_wp, 谢谢你的回应。

一直以来,关于并行技术的更新,大多数都是基于 C/C++ 的。包括前几年以前的 TBB (Thread Building Block),现在很热的 Cilk Plus,(它们都是 Intel Parallel Studio 软件包中完全支持的多种并行开发模式)。 好像 Fortran 被大家遗忘了。

实际上,对高性能计算领域的来说,很多程序都是基于 Fortran 的。原先用 MPI 进行跨结点的分布式并行编程时,需要对算法进行彻底地改写,MPI 的一些使用方法也和 Fortran 有些冲突。

我觉得,就像当年的 OpenMP 一样,Intel 的编译器也是很早就在 Windows 和 Linux 两个平台上全面支持 OpenMP 2.0 的规范。后来,Microsoft,GCC都也陆续支持了OpenMP,它也继续发展,目前是 3.1 版本的规范,也成为了事实上的工业标准。对程序员来说,这是真正的好事情。

现在好了,Coarray 成为 Fortran 2008 规范的一部分,会推动更多的人来关注它。以前主要是 Cray,目前 Intel 在 12.0 版本的编译器都开始支持它了,同样是 Windows 和 Linux 版本,而且可以在单机的多核,或者多结点系统上运行(需要说明的是,目前的实现还只是基于Intel MPI library 的)。

关于你提到的问题,"有人认为编译时未考虑镜像机的硬件条件而倒是实际执行性能不能达到最佳等等(当然也可能是老的评论)", 其实不光是 coarray,MPI,甚至 OpenMP 都会有这样的问题。

一个基于高级编程语言的并行模式和开发机制,我个人觉得应该屏蔽掉不必要的硬件细节,或者将这些对于程序员是透明的。更主要的应该关注于,并行算法设计本身。 对于具体硬件的优化,完全可以在最后的阶段进行,实际上你用了coarray,那些向量化优化,循环优化还是照样可以同时使用的。 今天你用手写 SSE2 的汇编代码,可能会快了不少,可是将来的硬件环境变化了,指令增加了(SSE3, AVX...),寄存器变大了(128 bit -> 256 bit),还是要重写汇编的。

这些优化工作就让编译器去完成吧,我们需要做的就是把代码调整好,能够让编译器更加容易地“读懂”我们到底想要做什么,然后生成高效的可执行代码。


关于 coarry 的中文资料,确实非常少。我们正在努力翻译和整理,可能最近会有一系列的文章,希望能够对大家有些帮助。大家都是刚刚起步,如果有什么错误和意见,欢迎交流和指正。谢谢。


P.S.: 发现原始的 coarry 定义和 Fortran 2008 规范有些不一致。一旦发现,我们会在文字上改正,请注意。千万别说我们误人子弟啊。呵呵
魏鵬 2011-05-13
  • 打赏
  • 举报
回复
看了一下,得确是这么个问题,应该将N也定义为coarray的。
是用的ifort,不过是linux的,目前应该是cray几乎完全支持,不过不知道是不是标准写法;intel发展很快,而且是按标准发展的。
intel_iclifort 2011-05-13
  • 打赏
  • 举报
回复
关于第三楼的代码,似乎有一个问题没有考虑到,IMAGE 1 获得的 N,但是其它IMAGE中 N 的值呢 ?

另外,你目前用什么编译器编译 CAF程序呢? 我的经验是建议使用 Windows版的 ifort 版本 12.0 以上, 目前刚刚发布了 update 4.
intel_iclifort 2011-05-13
  • 打赏
  • 举报
回复
老外不是还用 “The Ideal HPC Programming Language”这样的标题来拿Fortran说事么

http://delivery.acm.org/10.1145/1830000/1820518/p30-loh.pdf?key1=1820518&key2=5164423031&coll=DL&dl=ACM&ip=134.134.139.72&CFID=18740764&CFTOKEN=89031664

“Almost immediately, we were struck by what we were seeing. Of course, the rewritten code was much more compact and readable than the original, but, surprisingly, the “ideal” programming language was basically Fortran.”
魏鵬 2011-05-12
  • 打赏
  • 举报
回复
不知道你是否为intel博客上署名Hao Jiang的那位大侠
为了为下一个程序做技术储备,最近查看了不少关于coarray的资料,但多为英文,导致理解不透;
针对这个新特性,批评的人与赞扬的人都很多,比如有人认为编译时未考虑镜像机的硬件条件而倒是实际执行性能不能达到最佳等等(当然也可能是老的评论),不知道你怎么看。

566

社区成员

发帖
与我相关
我的任务
社区描述
英特尔® 边缘计算,聚焦于边缘计算、AI、IoT等领域,为开发者提供丰富的开发资源、创新技术、解决方案与行业活动。
社区管理员
  • 英特尔技术社区
  • shere_lin
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧