我有以下一段c代码,
double findIntraClustSimFullCoverage(cluster * pCluster)
{
double sum = 0;
register int i = 0, j = 0;
double perElemSimilarity = 0;
for (i = 0; i < 10000; i++)
{
perElemSimilarity = 0;
for (j = 0; j < 10000; j++)
{
perElemSimilarity += arr[i][j];
}
perElemSimilarity /= pCluster->size;
sum += perElemSimilarity;
}
return (sum / pCluster->size);
}
NOTE:
arr
是一个大小为10000 X 10000
这是GA代码的一部分,因此这个嵌套for循环运行多次。 这会影响代码的性能,即花费大量时间来提供结果。 我使用valgrind / kcachegrind分析了代码。 这表明70%的进程执行时间用于运行此嵌套for循环。 寄存器变量i和j似乎没有存储在寄存器值中(使用和不使用&#34进行分析;寄存器&#34;关键字表示这个)
我根本找不到一种方法来优化这个嵌套的for循环部分代码(因为它非常简单直接)。 请帮我优化这部分代码。
答案 0 :(得分:1)
我假设您经常更改arr矩阵,否则您只需计算一次总和(请参阅Lucian的答案)并记住它。
修改矩阵时可以使用类似的方法。在矩阵(可能)已经改变之后,不是完全重新计算总和,而是可以在某处存储“和”值,并且让更新矩阵的每一段代码都适当地更新存储的总和。例如,假设您从一个全零的数组开始:
double arr[10000][10000];
< initialize it to all zeros >
double sum = 0;
// you want set arr[27][53] to 82853
sum -= arr[27][53];
arr[27][53] = 82853;
sum += arr[27][53];
// you want set arr[27][53] to 473
sum -= arr[27][53];
arr[27][53] = 473;
sum += arr[27][53];
您可能希望不时地完全重新计算总和,以避免累积错误。
答案 1 :(得分:0)
我可能在这里错了,但不是以下等价物:
for (i = 0; i < 10000; i++)
{
for (j = 0; j < 10000; j++)
{
sum += arr[i][j];
}
}
return (sum / ( pCluster->size * pCluster->size ) );
答案 2 :(得分:0)
register
关键字是一个优化器提示,如果优化器不认为寄存器在那里花了很多,它就不会。arr
是否相当静态?这是否在同一个arr
上被多次调用?内循环的结果仅取决于j
遍历的行/列,因此懒惰地计算它并将其存储以供将来参考将产生很大的差异答案 3 :(得分:0)
如果您确定无法进行算法优化,则必须依靠非常低级别的优化来加速代码。这些是特定于平台/编译器的,因此您的里程可能会有所不同。
在某些时候,操作的瓶颈可能是从内存中提取arr
的值。因此,请确保您的数据以线性缓存友好的方式布局。也就是说&arr[i][j+1] - &arr[i][j] == sizeof(double)
。
如果您的编译器尚未执行此操作,您也可以尝试展开内部循环。你的代码:
for (j = 0; j < 10000; j++)
{
perElemSimilarity += arr[i][j];
}
例如会变成:
for (j = 0; j < 10000; j+=10)
{
perElemSimilarity += arr[i][j+0];
perElemSimilarity += arr[i][j+1];
perElemSimilarity += arr[i][j+2];
perElemSimilarity += arr[i][j+3];
perElemSimilarity += arr[i][j+4];
perElemSimilarity += arr[i][j+5];
perElemSimilarity += arr[i][j+6];
perElemSimilarity += arr[i][j+7];
perElemSimilarity += arr[i][j+8];
perElemSimilarity += arr[i][j+9];
}
这些是基本思想,如果不了解您的平台,编译器,查看生成的汇编代码,很难说更多。
您可能需要查看this presentation以获取有关优化机会的更完整示例。
如果您需要更高的性能,可以查看适用于您的平台的SIMD内在函数,尝试使用OpenMP,在多个线程上分发计算。
另一个步骤是尝试使用OpenMP,以下内容(未经测试):
#pragma omp parallel for private(perElemSimilarity) reduction(+:sum)
for (i = 0; i < 10000; i++)
{
perElemSimilarity = 0;
/* INSERT INNER LOOP HERE */
perElemSimilarity /= pCluster->size;
sum += perElemSimilarity;
}
但是请注意,即使您将这部分代码带到执行时间的0%(这是不可能的),您的GA算法仍然需要数小时才能运行。现在,您的性能瓶颈在于其他部分代码仅占用了22%的运行时间。
答案 4 :(得分:0)
说明这个问题的方法,你无能为力。您正在处理10,000 x 10,000双输入值,即800 MB。无论你做什么都受限于读取800 MB数据所需的时间。
另一方面,你每次调用它时还要写10,000 x 10,000个值吗?如果没有,您可以例如存储每行的总和,并且有一个布尔行,表示需要计算行总和,每次更改行元素时都会设置行总和。或者您甚至可以在每次更改数组元素时更新行的总和。