Chapel中begin语句的任务数量规范及相关限制问询(对比cobegin等)
Chapel中begin与cobegin的任务创建规则及限制疑问
问题描述
阅读Chapel的任务并行和Begin相关文档后,我有以下疑问:
begin语句/块的任务数量规范是什么?已知cobegin会为每个内部语句创建一个任务,想知道cobegin与begin之间是否存在类似forall和coforall的对应关系——比如forall的任务数由环境变量控制,coforall则由迭代器数量决定。begin与cobegin在任务创建时是否有类似forall范式的限制?比如begin会创建多少任务,是否和forall有相同要求(比如循环必须可顺序执行、线程间不能互访修改)?- 我做了简单测试,把代码中第二个测试的
cobegin换成begin后无法正常运行。文档说begin执行后主线程会立即继续,但这让我怀疑begin是否也有类似forall的限制。
测试代码
use Time; config const dur : real(64) = 3.0; config const run : bool = false; proc sayHello() { writeln("Hello there!"); } proc sayGoodbye() { writeln("Goodbye!"); } proc main() { // 阻塞式任务并行段 // 除了函数或块内部的逻辑顺序外,不保证处理顺序 // 这里的输出可能会交错,比如C部分的输出 cobegin { // A sayHello(); // B sayGoodbye(); // C { writeln("I'm a block"); writeln("^ and this message will come below"); } } writeln("\nI wait until everything in the above block is done."); if !run then exit(0); var a : atomic uint(64) = 0; var b : atomic bool = false; begin cobegin { // 创建计时器并读取原子整数a的值 { var it : uint(64) = 0; var t : stopwatch; t.start(); while (t.elapsed() < dur) { if (it % 1000000 == 0) { writeln("The value I read is ", a.read()); } it += 1; } t.stop(); b.write(true); writeln("\nThe amount of time passed: ", t.elapsed()); writeln("And the final value I read is ", a.read()); } // 持续递增原子整数的任务 { while(!b.read()) { a.write(a.read()+1); } } } writeln("Out here!"); var t2 : stopwatch; t2.start(); // 为每个索引创建一个任务 // 10万个任务会很慢 coforall i in 1..#100000 { if (i % 5000 == 0) { writeln(i); } } writeln("Time elapsed outside: ", t2.elapsed()); t2.stop(); }
解答
1. begin与cobegin的任务数量差异
cobegin:会为其代码块内的每一条顶级语句创建一个独立任务,主线程会阻塞,直到所有cobegin内的任务全部完成。比如你代码中第一个cobegin块里的3条顶级语句(sayHello()、sayGoodbye()、嵌套块)会各自对应一个任务。begin:只会创建一个任务来执行其代码块内的所有内容,主线程不会阻塞,会立即继续执行后续代码。这和cobegin的“多任务+阻塞等待”是核心区别,和forall/coforall的对应关系并不完全一致:coforall为迭代的每个元素创建任务,主线程阻塞等待所有任务完成;forall将迭代任务分配给当前可用的并行资源(由CHPL_RT_NUM_THREADS等环境变量控制),主线程同样阻塞等待;- 而
begin仅创建单个后台任务,主线程不等待,和前两者的“批量任务+阻塞”逻辑不同。
2. 任务创建的限制对比
cobegin:内部的各个任务并行执行,因此需要注意数据竞争——如果多个任务访问共享非原子变量,必须手动同步(比如用原子类型、锁),这和coforall的要求类似。但cobegin不要求内部语句可顺序执行,它的设计目的就是并行执行多条独立语句。begin:仅创建单个任务,不存在多任务并行执行的情况(除非begin块内部嵌套了cobegin/coforall等并行结构),因此它本身没有forall那样的“可顺序执行”限制。begin的核心是将一段代码放到后台执行,主线程继续推进,只要这段代码本身是合法的Chapel代码即可。
3. 测试中cobegin换begin后异常的原因
你提到的测试中,将内部的cobegin换成begin后,代码变成begin { ... 两个块 ... },此时begin只会创建一个任务来执行内部的两个块——这两个块会顺序执行,而非并行:
- 第一个块先运行,直到计时器结束并设置
b=true; - 之后才会执行第二个块的循环,但此时
b已经为true,循环直接退出,导致第二个块根本没机会递增a,程序行为和原逻辑完全不符,看起来像是“无法正常运行”。
而原来的cobegin是让两个块并行执行,一个计时读取a,一个持续递增a,这才是符合预期的并行行为。
内容的提问来源于stack exchange,提问作者Ethan Scheelk
相关产品推荐
相关产品推荐

