C++ 融合三角形循环进行并行化,计算子索引
并行化中的一种常见技术是为这样的循环融合嵌套C++ 融合三角形循环进行并行化,计算子索引,c++,c,math,for-loop,C++,C,Math,For Loop,并行化中的一种常见技术是为这样的循环融合嵌套 for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { for(int i=0; i<n; i++) { for(int j=0; j<i+1; j++) { 与融合方形循环不同,这需要使用sqrt函数以及从int到float和从float到int的转换 我想知道是否有更简单或更有效的方法?例如,不需要sqrt函数或从int到float或float到int的转换的解
for(int i=0; i<n; i++) {
for(int j=0; j<n; j++) {
for(int i=0; i<n; i++) {
for(int j=0; j<i+1; j++) {
与融合方形循环不同,这需要使用sqrt
函数以及从int到float和从float到int的转换
我想知道是否有更简单或更有效的方法?例如,不需要sqrt
函数或从int到float或float到int的转换的解决方案。
编辑:我不想要依赖于上一次或下一次迭代的解决方案。我只想要像intI=funci(x)和int j=funcj(x,I)这样的解决方案。
下面是一些代码,说明了这一点:
#include <stdio.h>
#include <math.h>
int main() {
int n = 5;
int cnt = 0;
for(int i=0; i<n; i++) {
for(int j=0; j<i+1; j++) {
printf("%d: %d %d\n", cnt++, i,j);
}
} printf("\n");
int nmax = n*(n+1)/2;
for(int x=0; x<nmax; x++) {
int i = (-1 + sqrt(1.0+8.0*x))/2;
int j = x - i*(i+1)/2;
printf("%d: %d %d\n", x,i,j);
}
}
#包括
#包括
int main(){
int n=5;
int-cnt=0;
对于(inti=0;i来说,最理智的形式当然是第一种形式
也就是说,融合形式最好使用条件句:
int i = 0; int j = 0;
for(int x=0; x<(n*(n+1)/2); x++) {
// ...
++j;
if (j>i)
{
j = 0;
++i;
}
}
inti=0;intj=0;
对于(int x=0;xi)
{
j=0;
++一,;
}
}
我想知道是否有一个更简单或更有效的方法来做到这一点
是的,您必须以代码开头。请记住以下几点:
- 不存在浮点运算比普通整数更快的情况
- 然而,在很多情况下,浮点运算的速度要比普通整数慢得多
- 在大多数系统中,浮点变量通常比普通整数大,因此,仅出于这个原因,浮点变量的速度就较慢
- 代码的第一个版本可能对缓存最友好。至于任何手动优化的情况,这完全取决于您使用的CPU
- 在大多数系统中,除法通常很慢,无论是对纯整数还是浮点进行除法
- 任何形式的复杂算术都会比简单的计数慢
因此,对于世界上任何给定的CPU,您的第二个示例几乎肯定比第一个示例慢得多。此外,它也完全不可读。考虑到您试图融合三角形以实现并行化,不明显的解决方案是选择一个非平凡的x到(i,j)的映射:
毕竟,您没有按任何特殊顺序处理它们,因此精确映射是一个不重要的问题
因此,计算x->i,j
,就像计算矩形一样,但是如果i>j
那么{i=N-i,j=N-j}
(镜像Y轴,然后镜像x轴)
为什么?如果是为了性能,在最内部的循环中调用sqrt()
似乎是一个非常消极的权衡。@unwind,它可以用于融合并行for循环。无论如何,融合方形循环需要一个除法(i=x/n,j=x%n)这并不比现代CPU上的sqrt指令慢多少。但这就是问题的重点。我能在没有sqrt
函数的情况下做到这一点吗?sqrt
并不是唯一昂贵的函数,它涉及到双精度的转换。请注意,使用很重要;前两个答案有一个顺序的upi和j的日期不并行。@mAlters,是的,你是正确的,存在到和从双转换。我没有想到这一点。但我想我可以通过将x设为双转换来消除到双转换,然后我只需要从双转换为int。这不容易(容易)实现并行for循环。我不想要依赖于上一次或下一次迭代的解决方案。它们必须是独立的。我很抱歉在我的问题中没有说明这一点。我更新了我的问题。你是否认为融合方形循环在并行化中通常没有帮助?@Zboson我认为复杂代码永远不会比sim更快ple代码。在您对程序进行基准测试并发现瓶颈之前,尝试手动优化是毫无意义的,您有一个特定的系统和CPU,并且您对这个系统/CPU有非常深入的了解。尽管事实证明,您编写的程序在我听过的任何CPU上都会非常慢,从70年代的古代8位MCU到现代64位怪物。我理解你的所有观点。我可能不应该使用“更高效”这个词。通常在循环中计算的计算/加载比计算迭代器所需的时间要长得多,所以融合的效率不是很重要。但是融合g对负载分配很有用。我最感兴趣的是融合回路的解决方案,而不是我提出的,正如你所说的“完全不可读”的解决方案。如果解决方案更有效就更好了。我认为有一个输入错误:但是如果I>N/2
应该读I>j
,对吗?谢谢,这就是我一直在寻找的答案。这是一个聪明的解决方案。OpenMP有一种融合嵌套循环的方法。我不知道它是否能融合三角形循环,但我想知道它是如何融合的omeone会手动操作,因为我已经为方形循环做过多次,但从来没有为三角形循环做过。我最终解决了这个问题,并在一个实例中使用了您的解决方案(请参见答案末尾)。三角形循环与我的问题不完全相同。我的问题是左下角带对角线的三角形,而在另一个问题中,三角形是右上角,不包括对角线。映射与你说的不完全一样,但很接近。如果(j@Zboson:从三角形到矩形的几何变换是一种很好的证明三角形的方法。编程实际上是数学。+1用于说明。我最近为分段(更大)提出了一个类似的解决方案我自己的解决方案是使用一个迭代计数器,告诉每个线程向前跳过多少;
#include <stdio.h>
#include <math.h>
int main() {
int n = 5;
int cnt = 0;
for(int i=0; i<n; i++) {
for(int j=0; j<i+1; j++) {
printf("%d: %d %d\n", cnt++, i,j);
}
} printf("\n");
int nmax = n*(n+1)/2;
for(int x=0; x<nmax; x++) {
int i = (-1 + sqrt(1.0+8.0*x))/2;
int j = x - i*(i+1)/2;
printf("%d: %d %d\n", x,i,j);
}
}
int i = 0; int j = 0;
for(int x=0; x<(n*(n+1)/2); x++) {
// ...
++j;
if (j>i)
{
j = 0;
++i;
}
}
j |\ i ->
| \ ____
| | \ => |\\ |
V |___\ |_\\__|
____
|\\ | |\ |\
|_\\__| ==> |_\ __ => | \
/ | | \
/__| |___\