我正在研究解决红/蓝计算的程序;程序是用C语言编写的。
问题描述如下:http://www.cs.utah.edu/~mhall/cs4961f10/CS4961-L9.pdf
tl; dr你有一个颜色网格(红色/蓝色/白色),第一个红色单元格按照一定的规则向右移动,然后蓝色单元格按照其他规则向下移动。
我的程序正在运行并提供正确的输出,我现在正试图看看我是否无法加速它。
使用英特尔的VTune放大器(这是一个并行编程课程,我们正在使用并行工作室集成的visual studio中进行pthread),我发现我的代码中最大的热点是移动蓝色单元格时。
实现细节:网格存储为动态分配的int **,以这种方式设置
globalBoard = malloc(sizeof(int *) * size);
for (i = 0; i < size; i++)
{
globalBoard[i] = malloc(sizeof(int) * size);
for (j = 0; j < size; j++)
globalBoard[i][j] = rand() % 3;
}
经过一番研究,我认为热点的原因(几乎是移动红细胞的4倍CPU时间)是逐列遍历时的缓存未命中。
据我所知,在引擎盖下,这个网格将被存储为1d数组,因此当我将红色单元格向右移动并逐行时,我通常会检查连续的值,因此CPU不会需要经常将新值加载到缓存中,而逐列导致跳过数组的数量只会随着板的大小而增加。
所有这一切,我希望这个特定的部分更快。这是现在的代码:
void blueStep(int col)
{
int i;
int local[size];
for (i = 0; i < size; local[i] = globalBoard[i++][col]);
for (i = 0; i < size; i++)
{
if (i < size - 1)
{
if (globalBoard[i][col] == 2 && globalBoard[i + 1][col] == 0)
{
local[i++] = 0;
local[i] = 2;
}
}
else
{
if (globalBoard[i][col] == 2 && globalBoard[0][col] == 0)
{
local[i++] = 0;
local[0] = 2;
}
}
}
for (i = 0; i < size; i++)
globalBoard[i][col] = local[i];
}
在这里,col是要处理的列,大小是网格的大小(它总是正方形)。
我在想我可能会做一些花哨的指针算术来加快速度,并且正在读这个:http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html。
考虑到这一点,我觉得我可能需要更改声明网格的方式以利用2d数组指针算法,但我仍然不确定如何使用该方法遍历列。< / p>
欢迎任何有关这方面的帮助,或任何其他快速通过专栏的建议。
更新:经过一番研究和讨论后,似乎我的假设是不正确的。事实证明,由于错误共享,将结果写回全局数组的时间几乎是循环列的两倍。也就是说,我仍然有点好奇,看看是否有更好的方法进行列遍历。
答案 0 :(得分:0)
我认为答案是处理瓷砖中的网格。您可以在16x16或32x32磁贴中向下或向右快速移动磁贴。它们的两个动作将实际相同,并以相同的速度运行:将所有值读入XMM寄存器,进行处理,写入。您可能需要在此处调查MASKMOVDQU指令。如果我理解问题的性质,您可以将磁贴重叠一行/列,如果按照通常(扫描)顺序处理它们,这将正常工作。如果没有,您必须单独处理拼接瓷砖。
在C代码中没有真正的快速方法。但是,您可以尝试(1)将您的电路板类型更改为unit8_t,(2)用算术替换所有if ...语句,如下所示:value =(mask&amp; value)| (^ mask&amp; newvalue),以及(3)在编译器选项中启用最大循环展开和自动矢量化。这将给你一个很好的加速 - 特别是避免条件。
编辑除了可以放入寄存器的图块之外,您还可以执行第二级图块,以适合您的缓存。我认为这种组合将大致以你的内存带宽运行。
EDIT 或者,使您的电路板类型为两位:将四个单元格打包到一个字节。与带有算术思想的替换if语句完美匹配:)