如果循环的粒度(或循环执行的工作量)很小,则分布的效果可能并不明显。这是因为与循环工作量相比,并行循环启动的开销太大。在这种情况下,编译器使用循环合并将多个循环合并到单个并行循环中,从而增大循环的粒度。当具有相同行程计数的循环彼此相邻时,循环合并很方便且很安全。请看以下示例:
/* L1: short parallel loop */ for (i=0; i < 100; i++) { a[i] = a[i] + b[i]; /* S1 */ } /* L2: another short parallel loop */ for (i=0; i < 100; i++) { b[i] = a[i] * d[i]; /* S2 */ } |
这两个短并行循环彼此相邻,可以安全地合并,如下所示:
/* L3: a larger parallel loop */ for (i=0; i < 100; i++) { a[i] = a[i] + b[i]; /* S1 */ b[i] = a[i] * d[i]; /* S2 */ } |
新循环产生的开销是并行循环执行产生的开销的一半。循环合并在其他方面也很有帮助。例如,如果在两个循环中引用同一数据,则合并这两个循环可以改善引用环境。
然而,循环合并并非始终安全。如果循环合并创建之前并不存在的数据依赖性,则合并可能会导致错误执行。请看以下示例:
/* L1: short parallel loop */ for (i=0; i < 100; i++) { a[i] = a[i] + b[i]; /* S1 */ } /* L2: a short loop with data dependence */ for (i=0; i < 100; i++) { a[i+1] = a[i] * d[i]; /* S2 */ } |
如果合并3.7.2 循环合并中的循环,会产生语句 S2 至 S1 的数据依赖性。实际上,语句 S1 右边 a[i] 的值是在语句 S2 中计算的。如果不合并循环,此情况则不会发生。编译器执行安全性和有益性分析,以确定是否应执行循环合并。通常,编译器可以合并任意多个循环。以这种方式增大粒度有时可以大大改善循环,使其足从并行化中获益。