Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
c语言中数组循环的优化_C_Arrays_Optimization_Gcc - Fatal编程技术网

c语言中数组循环的优化

c语言中数组循环的优化,c,arrays,optimization,gcc,C,Arrays,Optimization,Gcc,我在网上和书里都看过,但我似乎没有找到这个。 我被要求优化程序的一小部分。特别是在不使用内置优化器的情况下,使用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] + arr

我在网上和书里都看过,但我似乎没有找到这个。 我被要求优化程序的一小部分。特别是在不使用内置优化器的情况下,使用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 length=数组大小;
int极限=长度-4;
对于(j=0;j

数组值为非常量
int
s,所有值都已初始化。

一种解决方案是始终保持一个和。当然,每次更改数组中的值时,您都必须更新它,但如果不这样做,那么这通常是值得的。

使用sse/mmx set:

__m128i sum;
for (j=0; j < limit; j+=4) {
    sum = _mm_add_epi32(sum, array+j);
}
\uuum128i总和;
对于(j=0;j
创建子和,然后将其相加为一个和

下面是它的基本外观

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;
(j=0;j{ sum1+=数组[j]; sum2+=数组[j+1]; sum3+=数组[j+2]; sum4+=数组[j+3]; } 总和=sum1+sum2+sum3+sum4; 这避免了一些先读后写的依赖关系-也就是说,每个循环迭代中sum2的计算不需要等待sum1的结果来执行,处理器可以同时调度循环中的两行。

我不确定为什么不能使用Optimizer,因为根据我的经验,它通常会产生比绝大多数“想要成为”的手动优化程序更快的代码:-)此外,您应该确保此代码实际上是一个问题区域-优化已经接近最大速度的代码没有意义,当其他地方的代码占20%时,您也不应该关心占时间0.01%的事情

优化应该有很强的针对性,否则就是白费力气

除了天真的“只需将数字相加”之外,任何解决方案都很可能需要在目标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];
}
正如我所说的,这可能不是一个选项,但是,如果您可以改变它,您将很难找到一种比提取单个数组索引更快的求和方法



而且,只要您实现了一个类来实现这一点,您就可以使用前两个元素来管理,一个用于当前和,一个用于最大索引,因此,您可以通过对照最大值检查
indx
来避免越界错误。

由于样本中每次要执行的加法数似乎是5,因此我也在这里执行此操作。通常你会按照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;

实际上,循环已经展开了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];
}
此类宏的一个示例是,如果数组大小为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)执行相同的操作。

您可以通过在滚动循环中预取数据来获得更高的性能。
我将以Drew的回答为基础:

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;
寄存器int value1、value2、value3、value4;
或(j=0;j
这里的想法是让处理器将连续数据加载到缓存中,然后对缓存的数据进行操作。为了使其有效,编译器不得优化预取;这可以通过将临时变量声明为
volatile
来实现。我不知道
volatile
是否可以与
寄存器组合使用


在web上搜索“数据驱动设计”。

您可以使用threads或
fork
?您如何衡量各种备选方案的性能?您是否调查过指针而不是数组下标是否能改善情况?你调查过这段代码是否真的是程序中的瓶颈吗?按5展开是不好的。试试4或8。用
-msse4
编译听起来像是重复的。但是找不到原始的…@newb@missingno这里有一个相关的问题:我假设,因为这是家庭作业,他应该只使用C,而不是查找疯狂的处理器指令。自动矢量化可以做到这一点,它将正确处理未对齐的数组。你不应该假设自动矢量化,我认为,+1:循环展开通常对现代CPU没有帮助,但在像这样的简单循环中打破依赖关系可能是有益的。在我看来,作业是为了演示优化器的功能,让学生来做。
sum += array[0];
sum += array[1];
...
sum += array[ARRAY_SIZE - 1];
#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);
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;