首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >优化索引数组求和

优化索引数组求和
EN

Stack Overflow用户
提问于 2010-12-05 04:07:27
回答 6查看 1.3K关注 0票数 0

我有以下C++代码:

代码语言:javascript
复制
const int N = 1000000
int id[N]; //Value can range from 0 to 9
float value[N];

// load id and value from an external source... 

int size[10] = { 0 };
float sum[10] = { 0 };
for (int i = 0; i < N; ++i)
{
    ++size[id[i]];
    sum[id[i]] += value[i];
}

我应该如何优化循环?

我考虑使用SSE将每4个浮点数加到一个sum中,然后在N次迭代后,总和就是xmm寄存器中4个浮点数的和,但是当源像这样被索引并且需要写出10个不同的数组时,这是不起作用的。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2010-12-05 04:24:54

这种循环很难使用SIMD指令进行优化。不仅在大多数SIMD指令集中没有一种简单的方法来执行这种索引读取(“聚集”)或写入(“散布”),即使有,这个特定的循环仍然存在这样的问题,即在一个SIMD寄存器中可能有两个值映射到相同的id,例如当

代码语言:javascript
复制
id[0] == 0
id[1] == 1
id[2] == 2
id[3] == 0

在这种情况下,最明显的方法(这里是伪代码)

代码语言:javascript
复制
x = gather(size, id[i]);
y = gather(sum, id[i]);
x += 1; // componentwise
y += value[i];
scatter(x, size, id[i]);
scatter(y, sum, id[i]);

也不会起作用!

如果可能的情况非常少(例如,假设sumsize各只有3个元素),你可以通过暴力比较来解决,但这并不是真正的可伸缩性。

在不使用SIMD的情况下,有一种方法可以更快地实现这一点,那就是使用展开来打破指令之间的依赖关系:

代码语言:javascript
复制
int size[10] = { 0 }, size2[10] = { 0 };
int sum[10] = { 0 }, sum2[10] = { 0 };
for (int i = 0; i < N/2; i++) {
  int id0 = id[i*2+0], id1 = id[i*2+1];
  ++size[id0];
  ++size2[id1];
  sum[id0] += value[i*2+0];
  sum2[id1] += value[i*2+1];
}

// if N was odd, process last element
if (N & 1) {
  ++size[id[N]];
  sum[id[N]] += value[N];
}

// add partial sums together
for (int i = 0; i < 10; i++) {
  size[i] += size2[i];
  sum[i] += sum2[i];
}

不过,这是否有帮助取决于目标CPU。

票数 2
EN

Stack Overflow用户

发布于 2010-12-05 04:22:51

那么,你在你的循环中调用了两次idi。您可以将其存储在一个变量中,如果您愿意,也可以存储在一个寄存器int中。

代码语言:javascript
复制
register int index;
for(int i = 0; i < N; ++i)
{
index = id[i];
++size[index];
sum[index] += value[i];
}

MSDN文档对寄存器作了如下说明:

关键字register指定变量将存储在机器寄存器中。特定于Microsoft

编译器不接受用户对寄存器变量的请求;相反,当全局寄存器分配优化(/Oe选项)打开时,编译器会自行选择寄存器。但是,将遵守与register关键字关联的所有其他语义。

票数 1
EN

Stack Overflow用户

发布于 2010-12-05 04:23:10

您可以做的事情是使用-S标志编译它(如果您不使用gcc,则使用等效标志),并使用-O-O2-O3标志比较各种汇编输出。优化循环的一种常见方法是进行某种程度的展开,例如(非常简单、幼稚的)示例:

代码语言:javascript
复制
int end = N/2;
int index = 0;
for (int i = 0; i < end; ++i)
{
    index = 2 * i;
    ++size[id[index]];
    sum[id[index]] += value[index];
    index++;
    ++size[id[index]];
    sum[id[index]] += value[index];
}

这将把cmp指令的数量减半。然而,任何不太好的优化编译器都能帮你做到这一点。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4355570

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档