异构环境下的线程迁移、负载均衡及多分区编译支持
在计算机系统中,异构环境下的线程迁移、负载均衡以及数据分区策略对于提升系统性能和资源利用率至关重要。下面我们将详细探讨这些方面的内容。
多线程与线程迁移
在专用的同构集群中,迭代科学代码的负载均衡通常并非必要。因为科学代码本身往往已经实现了平衡,而且专用集群不会引入不平衡因素。然而,资源竞争、处理器容量和类型的异构性这三个环境挑战,会导致不同节点的执行速率存在差异。由于大多数应用使用屏障同步来避免数据竞争,所以为了高效利用资源,应用程序或系统必须进行负载均衡。
运行中应用程序的重新配置通常在程序内部完成。例如,tsp应用使用集中式任务队列隐式地平衡负载。但大多数科学代码采用更静态的计算模型。我们通过线程迁移对应用程序进行透明的在线重新配置。线程迁移是一个明显的选择,因为线程对系统可见,并且通常每个线程都有静态分配的工作。移动线程也就意味着移动工作。所有应用程序都可以通过参数化在不同数量的处理器上运行。由于线程之间通过所有节点都可见的共享段进行共享,在四个处理器上每个运行8个线程与在32个处理器上每个运行1个线程(除了性能外)几乎没有区别。
我们的运行时策略如下:
1. 通过在线测量得出相对处理器容量。
2. 利用在线数据共享信息得出单个线程到节点的映射。
3. 执行一个单迁移阶段,对线程进行重新洗牌。
异构处理器
虽然利用异构环境有明显的潜在优势,但传统观点认为其劣势大于优势。处理分布式系统中异构性的方法有很多,从禁止(大多数情况)到使用类型安全的语言(如Java、Modula - 3或Emerald)实现,再到与语言无关的方法(要求用户在通信操作中明确标识所有类型,如MPI和PVM)。
异构处理器存在的问题及解决方案总结如下表:
| 问题 | 解决方案 |
| — | — |
| 不同的数据格式 | 每个页面和差异在传输时进行翻译 |
| 不同的数据精度 | 通知用户 |
| 不同的数据大小 | 填充分配并禁止某些类型的构造,如指向此类类型数组的指针 |
| 识别堆数据 | 为动态分配的共享数据添加额外参数,将共享变量分隔到类型化区域 |
| 识别栈数据 | 仅在已知位置迁移线程,明确识别栈数据 |
| 不同的页面大小 | 使用所有系统中的最大页面大小 |
| 不同的共享地址 | 禁止指针或要求所有系统使用已知地址 |
不同平台上不同类型标量的十六进制转储如下表所示:
| 数据类型 | Pentium II | SPARC | Alpha | Power - 2 |
| — | — | — | — | — |
| short | 0x2211 | 0x1122 | 0x2211 | 0x1122 |
| int | 0x44332211 | 0x11223344 | 0x44332211 | 0x11223344 |
| long | 0x44332211 | 0x11223344 | 0x4433221100000000 | 0x11223344 |
| float | 0x0050c347 | 0x47c35000 | 0x0050c347 | 0x47c35000 |
| double | 0x00000000d0126341 | 0x416312d0000000 | 0x00000000d0126341 | 0x416312d000000000 |
| char * | 0x5c960408 | 0x00020ac4 | 0x2000004001000000 | 0x20000640 |
从一种表示形式转换到另一种表示形式不仅需要交换字节,还需要调整数据类型大小和指针值。字节交换简单高效,但对于像C和C++这样的无类型语言,调整地址空间以适应大小依赖于位置的变量通常是不可能的。我们通过对应用程序代码的检查,发现了一些有用的特性。我们的程序只使用int、float、double和char *类型。int和float变量在所有平台上大小相同,可以通过反转每个四字数量的字节顺序在不同机器表示之间进行转换。double变量的处理类似,但在字节重新排序完成后需要交换两个字。我们禁止在共享内存中使用long和指针值,使得所有共享数据为int、float或double类型。
数据转换通过以下两步完成:
1. 如果系统处于异构模式(在启动时自动检测),所有传输到和从网络接收的数据的每个四字数量都转换为或从网络字节顺序格式转换。
2. 在复制数据时,必要时交换每个double值的两半。
我们的系统提供了一个接口,允许逐字地将类型信息传递给系统。为了提高效率,修改后的应用程序通过调用cvm_alloc_double()将所有double值与int和float值分隔到一个单独的段中。
异构线程迁移
CVM的异构线程迁移机制与同构机制基本相同,只是在处理栈时有所不同。栈除了在机器之间复制外,还必须进行转换以适应不同的数据类型和大小。栈通常还包含其他数据,如临时编译器数据和系统用于初始化状态的序言数据。
我们通过将迁移限制在每个应用程序的单个点(通常是科学应用程序主迭代循环的开始或结束)来处理指针。我们要求在启动时将所有栈数据(局部变量、参数)明确标识给系统。每个可能在迁移发生时在栈上有帧的函数必须通过调用cvm_stack_vars()预先标识其变量的偏移量。
在每个机器上,CVM在任何本地线程第一次到达迁移点时记录该序言代码的快照。当线程从另一个架构迁移到一个节点时,通过将快照中的序言与传入线程中由cvm_stack_vars()调用描述的类型化数据组合来创建一个新栈。这种技术不仅降低了复杂性,还显著减少了需要复制的字节数。
异构性的运行时影响包括三个模块的更改:所有消息发送和接收时与网络顺序的转换、包含double的页面发生页面错误时的字交换,以及略有不同的差异创建和应用代码。后一种差异可以忽略不计。由于全页错误仅在处理器首次访问页面时发生,这种开销也可以忽略不计。剩余的成本是与网络顺序的转换,该成本仅在基于Alpha和Pentium的机器上产生,对于adi应用,峰值为6.6%,其他应用的成本不超过2.5%,平均成本为2.1%。
多分区编译支持
在数据分区和计算分区策略方面,高性能Fortran(HPF)和OpenMP这两种标准的高级并行编程模型,为数据分区和计算分区分别提供了有限的选择。这些分区策略对于松散同步的计算可以带来良好的性能,但对于更紧密耦合的代码则存在问题。
一类重要的紧密耦合计算是使用线扫描来解决多维离散物理域每个维度上的一维递推问题。交替方向隐式(ADI)积分是求解偏微分方程的常用技术,NAS并行基准测试中的SP和BT使用ADI积分在三维中求解Navier - Stokes方程。对于这类计算,对任何空间维度应用标准块分区都会导致问题,因为沿分区维度的递推会部分序列化执行。
在新工作之前,Rice dHPF编译器使用粗粒度流水线策略来并行化线扫描计算。商业HPF编译器如PGI的pghpf缺乏对粗粒度流水线的支持,为了支持NAS基准测试,PGI重新编写了这些代码的变体,在方向扫描之间使用完全转置。但粗粒度流水线和转置都无法提供理想的可扩展性。
手工编写的消息传递版本的NAS SP和BT基准测试(版本NPB2.3b2)使用了一种复杂的数据分布,即“多分区”,它涉及数据块的偏斜循环分布。多分区为并行化线扫描计算提供了两个关键优势:
1. k维数据数组的多分区分布确保对于每个分区维度,每个处理器在分区定义的k - 1维平板中都拥有一个数据块。平板内的计算是完全并行的,因此使用这种分区可以有效地并行化线扫描计算。
2. 这种并行化只需要粗粒度通信,不像流水线那样。因此,多分区可以在大型系统上提供更好的可扩展性和加速比。
更一般地,许多数据和计算分区问题将一个域划分为每个处理器有一个或多个块的块,我们将这类技术统称为过度分区。文献中的例子包括用于循环和块循环分布的虚拟处理器方法、支持在异构系统上对数据并行程序进行动态和非均匀计算分区,以及支持管理非核心数组上的计算。
这篇文章描述了在Rice dHPF编译器中多分区的设计和实现,这被视为支持通用过度分区框架的第一步。我们描述了生成代码所需的几种编译器和运行时技术。
综上所述,在异构环境下,通过合理的线程迁移和负载均衡策略,以及高效的数据分区策略,可以显著提升系统性能和资源利用率。未来,我们可以进一步研究如何优化这些策略,以适应不断变化的计算机系统环境。
异构环境下的线程迁移、负载均衡及多分区编译支持(续)
多分区技术分析
多分区技术在解决复杂计算问题上展现出了独特的优势,下面我们进一步深入分析其原理和优势。
多分区的核心在于对数据进行合理的划分,使得每个处理器在不同维度的计算中都能充分发挥作用。以多维离散物理域的线扫描计算为例,传统的标准块分区会导致沿分区维度的递推部分序列化执行,影响计算效率。而多分区通过偏斜循环分布的数据块,让每个处理器在各个维度的平板中都有自己的数据块,从而实现平板内的完全并行计算。
为了更直观地理解多分区的优势,我们可以通过一个简单的流程图来展示其计算过程:
graph TD
A[开始] --> B[数据多分区]
B --> C[各处理器并行计算平板内数据]
C --> D{是否完成所有计算}
D -- 否 --> C
D -- 是 --> E[结束]
从这个流程图可以看出,多分区技术使得计算过程能够并行化,大大提高了计算效率。
多分区在具体应用中的表现
在NAS并行基准测试中的SP和BT应用中,多分区技术的优势得到了充分体现。我们可以通过对比不同分区策略下的执行时间和加速比来进一步说明。
| 分区策略 | 执行时间 | 加速比 |
|---|---|---|
| 标准块分区 | 较长 | 较低 |
| 粗粒度流水线 | 适中 | 一般 |
| 多分区 | 较短 | 较高 |
从表格中可以看出,多分区策略在执行时间和加速比方面都明显优于其他策略。这是因为多分区不仅实现了计算的并行化,还减少了通信开销,只需要粗粒度的通信。
多分区技术的实现要点
在Rice dHPF编译器中实现多分区技术,需要考虑多个方面的要点。
首先,编译器需要对代码进行深入分析,识别出适合多分区的计算部分。这涉及到对代码中数据依赖关系和计算逻辑的理解。例如,对于线扫描计算,编译器需要确定哪些维度适合进行多分区,以及如何划分数据块。
其次,运行时系统需要支持多分区的数据分布和通信。这包括在不同处理器之间高效地传输数据块,以及处理数据的同步和一致性问题。为了实现这一点,我们可以采用以下步骤:
1. 编译器在编译时生成多分区的数据分布信息,包括数据块的大小、位置和所属处理器等。
2. 运行时系统根据这些信息,将数据块分配到相应的处理器上。
3. 在计算过程中,运行时系统负责处理处理器之间的通信,确保数据的及时传输和同步。
未来展望
虽然多分区技术在提升系统性能和资源利用率方面取得了显著的成果,但仍然存在一些可以改进的地方。
未来的研究可以集中在以下几个方面:
1. 进一步优化多分区的算法,提高其在不同计算场景下的适应性。例如,针对不同规模和复杂度的计算问题,设计更合理的数据分区策略。
2. 探索多分区与其他并行计算技术的结合,如异构计算、分布式计算等。通过结合多种技术,可以充分发挥各自的优势,实现更高的性能提升。
3. 研究如何降低多分区技术的实现成本,包括编译器的复杂度和运行时的开销。这可以通过优化编译器算法和改进运行时系统来实现。
总之,多分区技术为解决复杂计算问题提供了一种有效的方法。通过不断的研究和改进,我们相信多分区技术将在未来的计算机系统中发挥更加重要的作用。同时,结合线程迁移和负载均衡策略,能够进一步提升异构环境下系统的整体性能和资源利用率。
超级会员免费看
1692

被折叠的 条评论
为什么被折叠?



