一学期也没干成什么工作。每天拿半小时翻译一点东西,虽然也没什么用,养成一个习惯也不错。贴上原文链接http://charm.cs.illinois.edu/manuals/html/charm++/manual-1p.html。如有翻译或版权问题,欢迎留言。
charm++是一个基于C++的并行编程系统,它建立在可迁移对象的编程模型上,并且charm++被一个全新和强大的自适应运行时系统所支持。它支持常规或不常规的应用程序,也可用于在单个应用程序中特定的任务并行或数据并行。它可以通过独立的负载均衡套件自动的为任务并行或数据并行应用程序提高负载均衡。通过它的消息驱动的执行模型,charm++支持自动的时延容错、模块化和并行组合。charm++还支持自动检查点/重启,以及基于分布式检查点的容错。
charm++是一个具有生产质量的在全世界范围内的超级计算机或者更小的集群上的很多科学或工程应用程序所使用的并行编程系统。目前charm++支持一个BlueGene/L, BlueGene/P, BlueGene/Q, Cray XT, XE 以及 XK系列 (包括XK6和XE6),单一的工作站或网络工作站(包括x86(运行Linux,Windows,MacOS))等。charm++支持UDP,TCP,Myrinet,InfiniBand,MPI, uGNI以及PAMI等通信协议和底层设备。charm++程序可以在所有的这些平台上运行,而不需要改动平台的任何源码。如果基于MPI编译charm++,charm++程序也可以与纯MPI程序互操作(§25)。更多关于安装、编译和运行charm++程序的细节请的阅读cham++/Conversed的安装和使用手册。
可迁移对象编程模型的关键特征是过分解(over-decomposition):程序员的程序分解成大量的工作单元和数据单元,并指定单元的构造,这些单元之间的相互作用。不直接与任何的处理器相联系。这使运行时系统分配处理器给执行单元,并在运行时根据需要重新分配。charm++是这种编程模型的主要和早期的典范。AMPI是的charm++家族中同一模式的另一个例子。
在charm++程序中并行计算的基本单位是一个chare。一个chare类似于一个处理器,一个actor,一个ADA任务,在最基本的层面上,它是一个C++对象。charm++计算由大量分布在处理器上的chare组成,它们之间通过异步方法调用相互作用。异步调用远程对象的方法,也可以认为是发送一个“消息”给它。因此这些方法的调用,有时被称为消息。(另外,在实现上,方法调用都会被封装成消息)。chare可以动态创建。
从概念上讲,charm++系统维护了一个”工作池”,由新chare的种子,现存chare的消息组成。charm++运行时系统(Charm RTS)会从工作池非确定性的取出多个消息并执行它,其限制性条件是两个不同的方法,不能同时在同一个chare对象上执行(也就是说,在不同的处理器上执行)。虽然可以为charm++的这种执行方式定义一个合理的理论化的操作语义,但是一个有助于理解charm++的执行模型的更实际的描述是:对每PE(“PE”意思是'处理单元'。PE类似于处理器;精确的描述请看1.4部分),都有一个调度程序,它有自己私有的消息池。每个实例化的chare都驻留在一个PE上。每个PE的池包含现存chare对象的消息,构造新chare的种子,为了在PE实例化。调度程序取出一个消息,如果消息是种子(即u函数的调用)那么就创建一个新的chare,并调用消息指定的方法。当方法返回到调度程序的时候,它会重复循环。没有抢占式的调度。
当一个chare的方法执行时,它可能会产生其他chare的方法调用。charm运行时系统(RTS,在手册中有时被称为chare内核)定位目标chare驻留的PE,并发送这个调用到PE的调度程序。
一个chare的能被远程调用的方法称为入口(entry )方法。入口方法可能有可序列化的参数,或者指向消息对象的指针。由于chare可以在远程处理器被创建,显然chare的一些构造函数需要是入口方法。普通的入口方法1.1是完全非抢占式的,charm++不会中断正在执行的方法去启动任何其他工作,并且所有的调用都是异步的。
charm++提供了基于种子的负载平衡。从而位置(处理器号)在创建远程chare的时候不能是特定的。Charm RTS将把chare放到一个合适的处理器上。由此可以想象chare的构造可当做一个能在一些特定处理器上生根发芽的种子。
chares可分成集合。charm++支持chares集合的类型有:chare-arrays ,chare-groups以及chare-nodegroups,在手册中被简化称为arrays ,groups 以及nodegroups。一个Chare-Array由任意数量可迁移chare组成的集合,这些chare由一个索引类索引,并根据用户定义的映射组映射到处理器上。一个group(groupnode)是一些在同一个PE(“node”)上的chare的集合。
charm++不允许全局变量,除了只读变量(参见3.3)。一个chare通常只能直接访问自己的数据。然而,每一个chare可以通过一个全局有效名称被访问到。所以可以认为charm++支持全局对象的空间。
每charm++程序必须至少有一个mainchare。当程序开始时mainchare由系统在0处理器上创建。一个charm++程序的执行开始于charm内核构建所有指定mainchare。对于名为X的mainchare,从构造函数X()或X(CkArgMsg*)开始执行,这两个是等价的。通常地,mainchare构造函数通过构造chare-array,其他chares以及group开始计算。它也可以被用来初始化共享的只读对象。
charm++程序通过调用CkExit终止执行,就像exit系统调用一样,CkExit调用后不会返回。Charm RTS确保在调用CkExit之后再没有消息需要处理,没有入口方法需要调用。 CkExit不必在所有的处理器上都调用,在一个处理器上的计算结束时调用它就可以了。
如上所述,chare的执行是“反应的”:当一个方法被调用,chare就执行这段代码,如此反复。但很多时候,chare有特定的生命周期,他们执行入口方法的顺序可以用一个结构化的方式来指定,同时允许一些本地化的非确定性(例如两个方法能以任意顺序执行的,但是当他们都结束,继续执行预先确定的方法,也就是说执行第3个的入口方法)。为了简化这种控制结构的表达式,charm++提供了两种方法:结构化有向图标记(第5节),这是我们建议您使用的主要标记。另外,您也可以使用线程入口方法(threaded entry method),结合futures和sync方法(见12.1)。线程入口方法运行于轻量用户级线程,并可以多种方式阻塞等待数据。再次,只有特定的chare的特定线程被阻塞,而PE继续执行其他chare。
通常的入口方法,是异步的,不运行返回任何值,并用void类型声明。然而,同步方法是一个例外。他们必须从线程方法被调用,因此允许返回(某些类型的)值。
为了支持异步方法调用和全局对象空间,RTS需要能够序列化(“marshall”)参数,并能够为chare生成全局”名字“。为此,程序员必须声明chare类并且在一个特殊的``.ci''文件称为接口文件内声明入口方法。除了接口文件,一个charm++程序的其余部分只由普通的c++代码组成。系统基于在接口文件中的声明生成了几个类,其中包括每个chare类的”代理“类。那些熟悉分布式计算里各种组件模型(如CORBA)将把”代理“当做一个虚拟的,站在实体指的是一个实际的实体。对于每一个chare类型,都存在``代理''类。这个”代理“方法“类的方法对应于的实际类的远程方法,并作为”转发器“。也就是说,当调用一个远程对象的代理的方法,代理序列化参数放入一个消息中,并把关于目标chare的适当的信息放在消息的封装里,并将其转发给远程对象。独立的chare,chare-array,group,node-group,以及这些集合的元素都有一个这样的代理。手册中描述了多种获得代理的方法。charm++里每种类型的实体的代理所支持的功能有些差别,但基本的语法和语义保持不变 - 即通过调用代理上的方法来调用远程对象上方法。
以下各节提供了charm++编程系统的各种功能的详细信息。第一部分,”基本用法“,足以编写全面的应用程序。请注意,这部分只有最后两章涉及到的物理处理器(核心,节点,..)的概念,除简单的查询型工具之外(2.9节)。我们强烈建议所有的应用开发者,初学者,同样对于专家,尽量坚持最基本的语言然后再尽可能的扩展。高级部分的特征的使用仅当你确信他们是必不可少的。(他们在特定的情况下非常有用,但我们检查初学者所写的程序时发现一个常见的错误是程序包含了对于他们的目标没必要的复杂特性,因此慎用)。在手册第二部分的高级概念支持优化,方便的特性及更复杂的或深奥的功能。
在基本层面,charm++的机器模型非常的简单:把每一个chare自身当做一个独立的处理器。每一个chare的方法可以访问自己的实例变量(都是私有的,在这个层面上),并且任何全局变量都声明为只读变量。chare也可以访问所有其他chare的名字(全局对象空间),但是唯一能做的就是向其他chare发送异步远程方法调用。(当然,实例变量可以包括许多他拥有的其他常规的C++对象但不是chare对象。它只能拥有其他chare对象的引用)。
按照这一设想,该手册的第一部分(直至包括负载均衡的一章)几乎没有提及物理意义(内核,节点等)的实体。运行时系统负责密切保持临近物理位置上的通信对象,并通过利用物理上可用的共享内存优化同一节点或核心上的通信。程序员完全不必处理这些。在基础部分这个纯粹模型的唯一例外是那些用于找出对象在哪个“处理器“上运行,以及找出一共有多少处理器的函数。
然而,对于实现底层的库以及某些优化,程序员需要对处理器有意识。在任何情况下,了解charm++在底层如何实现都是有用的。因此,我们在这里描述了机器模型和一些相关的术语。
在物理资源方面,我们假设并行机由一个或多个节点组成,其中节点是高速缓存共享内存一致性可行的最大的单元(因此,节点可看做可以独立运行一个线程的核心的最大集合。一个节点可以包含一个或多个处理器芯片,它们之间共享或私有缓存。每个芯片可以包含多个核,每个核可以支持多个硬件线程(SMT为例)。
charm++识别两种逻辑实体:PE(处理单元)和节点。在charm++程序中,PE是映射和调度的单元:每个PE都有一个关联了一个消息池的调度程序。每个chare每次执行都驻留在一个PE上。根据运行时的命令行参数,一个PE可能与核心或硬件线程的子集相关联。一个或多个PE组成一个逻辑“节点”,可以划分到物理节点的单元。在实现中,一个独立的进程存在于每个逻辑节点中并且逻辑节点内的所有PE共享相同的内存地址空间。charm++运行时系统在一个逻辑节点内通过使用共享内存优化通信。
例如,一台机器有16个核,每个核都有2个硬件线程,可以用每个物理节点上一个或多个(逻辑)节点启动一个charm++程序。你可以选择32个PE每(逻辑)节点,每个物理节点一个逻辑节点。另外,你可以用每个逻辑节点12个PE,并且每个物理节点一个逻辑节点。你也可以选择分区物理节点,每个物理节点4个逻辑节点来启动程序(举个例子),并且每个节点4个PE。在charm++中每个节点上PE数量超过底层物理内核或硬件线程数量不是一个一般的做法。换句话说,一个charm++程序通常不使用比分配给它的物理核心或硬件线程多的PE。关于这些启动时选项的更多信息在附录C中。在用户程序中获取关于charm++逻辑机器实体的信息的工具函数可以参考2.9节。