我已经在网上看了我的书,但我似乎无法得到这个。 我被要求优化程序的一小部分。特别是在不使用内置优化器的情况下,使用vi和gcc在一小段时间内获取数组并添加其内容。 我尝试过循环展开和其他一些针对产品的优化。你能帮忙吗?
int length = ARRAY_SIZE;
int limit = length-4;
for (j=0; j < limit; j+=5) {
sum += array[j] + array[j+1] + array[j+2] + array[j+3] + array[j+4];
}
for(; j < length; j++){
sum += array[j];
}
数组值是非常量int
s,并且所有值都已初始化。
答案 0 :(得分:9)
创建子总和,然后加总计。
这是它可能的基本版本
for (j=0; j < limit; j+=4) {
sum1 += array[j];
sum2 += array[j+1];
sum3 += array[j+2];
sum4 += array[j+3];
}
sum = sum1 + sum2 + sum3 + sum4;
这避免了一些写后读依赖 - 也就是说,每次循环迭代中sum2的计算不需要等待sum1的结果执行,并且处理器可以同时在循环中调度两行。
答案 1 :(得分:3)
使用sse / mmx set:
__m128i sum;
for (j=0; j < limit; j+=4) {
sum = _mm_add_epi32(sum, array+j);
}
答案 2 :(得分:1)
实际上,循环已经由5展开。
由于您正在禁用优化程序,因此所有索引都将花费您的成本。
第一个循环可以替换为:
int* p = array;
for (j = 0; j < ARRAY_SIZE - 4; j += 5, p += 5){
sum += p[0] + p[1] + p[2] + p[3] + p[4];
}
因此它没有进行任何索引(将j
乘以sizeof(int)
并将其添加到地址中。
补充:当然,由于ARRAY_SIZE可能是已知的常量,这可能是最快的代码,但您可能需要编写代码生成器(或聪明的宏)来实现它:
sum += array[0];
sum += array[1];
...
sum += array[ARRAY_SIZE - 1];
这样一个宏的一个例子是,如果ARRAY_SIZE是2的幂,如64,你可以:
#define FOO64(i) FOO32(i); FOO32((i)+32)
#define FOO32(i) FOO16(i); FOO16((i)+16)
#define FOO16(i) FOO8(i); FOO8((i)+8)
#define FOO8(i) FOO4(i); FOO4((i)+4)
#define FOO4(i) FOO2(i); FOO2((i)+2)
#define FOO2(i) FOO1(i); FOO1((i)+1)
#define FOO1(i) sum += array[i]
FOO64(0);
你可以为其他权力做同样的想法,比如10。
答案 3 :(得分:0)
一种解决方案是始终保持一笔金额。当然,每次更改数组中的值时,您都必须更新它,但如果不发生这种情况,通常可能值得这么做。
答案 4 :(得分:0)
我不确定为什么你不能使用优化器,因为根据我的经验,它通常会产生比绝大多数“想成为”手动优化器更快的代码:-)此外,您应该确保此代码 实际上是一个问题区域 - 优化已经接近最大速度的代码是没有意义的,您也不应该关注自己占据0.01%的时间的事情当其他地方可能有代码负责20%时。
优化应该是严重的目标,否则会浪费精力。
除了天真的“只是将数字加在一起”之外的任何解决方案都很可能必须在目标CPU中使用特殊功能。
如果您愿意在每个更新上轻轻敲击阵列(这可能不是一个选项,因为您的“所有值已初始化”评论),您可以获得总结在非常快的时间。使用“类”来并排维护数组和总和。伪代码如:
def initArray (sz):
allocate data as sz+1 integers
foreach i 0 thru sz:
set data[i] to 0
def killArray(data):
free data
def getArray (data,indx):
return data[indx+1]
def setArray (data,indx,val):
data[0] = data[0] - data[indx] + val
data[indx+1] = val
def sumArray(data):
return data[0]
应该这样做。
以下完整的C程序显示了一个非常粗略的第一次切割,您可以将其作为更强大的解决方案的基础:
#include <stdio.h>
#include <stdlib.h>
static int *initArray (int sz) {
int i;
int *ret = malloc (sizeof (int) * (sz + 1));
for (i = 0; i <= sz; i++)
ret[i] = 0;
return ret;
}
static void killArray(int *data) {
free (data);
}
static int getArray (int *data, int indx) {
return data[indx+1];
}
static void setArray (int *data, int indx, int val) {
data[0] = data[0] - data[indx] + val;
data[indx+1] = val;
}
static int sumArray (int *data) {
return data[0];
}
int main (void) {
int i;
int *mydata = initArray (10);
if (mydata != NULL) {
setArray (mydata, 5, 27);
setArray (mydata, 9, -7);
setArray (mydata, 7, 42);
for (i = 0; i < 10; i++)
printf ("Element %d is %3d\n", i, getArray (mydata, i));
printf ("Sum is %3d\n", sumArray (mydata));
}
killArray (mydata);
return 0;
}
这个输出是:
Element 0 is 0
Element 1 is 0
Element 2 is 0
Element 3 is 0
Element 4 is 0
Element 5 is 27
Element 6 is 0
Element 7 is 42
Element 8 is 0
Element 9 is -7
Sum is 62
正如我所说,这可能不是一个选择,但是,如果你可以摆动它,你会很难找到一个比单个数组索引提取更快的方法来获得总和。
而且,只要您实现一个类来执行此操作,您也可以使用第一个两个元素进行内务处理,一个用于当前总和,另一个用于最大索引,因此通过检查indx
与最大值的对比,可以避免越界错误。
答案 5 :(得分:0)
由于五个似乎是样本中一次要添加的数量,我也在这里做。正如Drew Hoskins建议的那样,你通常用2的幂来做。 通过在开始时使模数正确并向另一个方向步进,可能需要更少的值。 以不同的顺序进行计算通常在科学计算中是有利可图的,而不仅仅是用于索引。 要了解优化是否以及有多好,测试至关重要。
int sum1, sum2, sum3, sum4;
for(j = ARRAY_SIZE; j%5; j--){
sum += array[j];
}
sum1 = sum2 = sum3 = sum4 = 0;
for (; j; j-=5) {
sum += array[j-1];
sum1 += array[j-2];
sum2 += array[j-3];
sum3 += array[j-4];
sum4 += array[j-5];
}
sum += sum1+sum2+sum3+sum4;
答案 6 :(得分:0)
通过在滚动循环内预取数据,您可以获得更高的性能 我将以德鲁的答案为基础:
register int value1, value2, value3, value4;
or (j=0; j < limit; j+=4)
{
// Prefetch the data
value1 = array[j];
value2 = array[j + 1];
value3 = array[j + 2];
value4 = array[j + 4];
// Use the prefetched data
sum1 += value1;
sum2 += value2;
sum3 += value3;
sum4 += value4;
}
sum = sum1 + sum2 + sum3 + sum4;
这里的想法是让处理器将连续的数据加载到其缓存中,然后对缓存的数据进行操作。为了使其有效,编译器不得优化 - 取消预取;这可以通过将临时变量声明为volatile
来执行。我不知道volatile
是否可以与register
结合使用。
在网上搜索&#34;数据驱动设计&#34;。