gpt4 book ai didi

c - 说服编译器在循环外设置寄存器

转载 作者:太空宇宙 更新时间:2023-11-04 04:52:57 26 4
gpt4 key购买 nike

首先,我将在此之前声明我认为没有必要理解下面代码的功能来做出明智的尝试来解决我的问题。这主要是一个优化问题。代码是为了了解正在做什么。

我有以下稍微优化的卷积主循环(有效):

for(int i=0; i<length-kernel_length; i+=4){

acc = _mm_setzero_ps();

for(int k=0; k<KERNEL_LENGTH; k+=4){

int data_offset = i + k;

for (int l = 0; l < 4; l++){

data_block = _mm_load_ps(in_aligned[l] + data_offset);
prod = _mm_mul_ps(kernel_reverse[k+l], data_block);

acc = _mm_add_ps(acc, prod);
}
}
_mm_storeu_ps(out+i, acc);

}

KERNEL_LENGTH 为 4。in_aligned 是重复 4 次的输入数组(在其上执行卷积),每次重复都将一个样本从其他样本向左移动。这样每个样本都可以在 16 字节对齐的位置找到。kernel_reverse 是反向内核,每个样本重复 4 次以填充一个 4 vector ,声明和定义如下:

float kernel_block[4] __attribute__ ((aligned (16)));
__m128 kernel_reverse[KERNEL_LENGTH] __attribute__ ((aligned (16)));

// Repeat the kernel across the vector
for(int i=0; i<KERNEL_LENGTH; i++){
kernel_block[0] = kernel[kernel_length - i - 1];
kernel_block[1] = kernel[kernel_length - i - 1];
kernel_block[2] = kernel[kernel_length - i - 1];
kernel_block[3] = kernel[kernel_length - i - 1];

kernel_reverse[i] = _mm_load_ps(kernel_block);
}

代码也可以正确且快速地计算算法。

我用 gcc -std=c99 -Wall -O3 -msse3 -mtune=core2 编译代码

我的问题是:循环被编译成下面的机器代码。在这个循环中,每次加载内核都会花费不少指令。内核在循环的每次迭代中都不会改变,因此原则上可以保存在 SSE 寄存器中。据我了解,有足够的寄存器可以轻松存储内核(事实上,机器代码并没有暗示太多的寄存器压力)。

我如何说服编译器不要在每个循环中加载内核?

我期望编译器在内核长度设置为常量时自动执行此操作。

    testl   %edx, %edx
jle .L79
leaq (%rcx,%rcx,2), %rsi
movaps -144(%rbp), %xmm6
xorps %xmm2, %xmm2
leal -1(%rdx), %ecx
movaps -128(%rbp), %xmm5
xorl %eax, %eax
movaps -112(%rbp), %xmm4
leaq 0(%r13,%rsi,4), %rsi
shrl $2, %ecx
addq $1, %rcx
movaps -96(%rbp), %xmm3
salq $4, %rcx
.p2align 4,,10
.p2align 3
.L80:
movaps 0(%r13,%rax), %xmm0
movaps (%r14,%rax), %xmm1
mulps %xmm6, %xmm0
mulps %xmm5, %xmm1
addps %xmm2, %xmm0
addps %xmm1, %xmm0
movaps (%r9,%rax), %xmm1
mulps %xmm4, %xmm1
addps %xmm1, %xmm0
movaps (%rsi,%rax), %xmm1
mulps %xmm3, %xmm1
addps %xmm1, %xmm0
movups %xmm0, (%rbx,%rax)
addq $16, %rax
cmpq %rcx, %rax
jne .L80
.L79:

编辑:完整代码 list 如下:

#define KERNEL_LENGTH 4
int convolve_sse_in_aligned_fixed_kernel(float* in, float* out, int length,
float* kernel, int kernel_length)
{
float kernel_block[4] __attribute__ ((aligned (16)));
float in_aligned[4][length] __attribute__ ((aligned (16)));

__m128 kernel_reverse[KERNEL_LENGTH] __attribute__ ((aligned (16)));
__m128 data_block __attribute__ ((aligned (16)));

__m128 prod __attribute__ ((aligned (16)));
__m128 acc __attribute__ ((aligned (16)));

// Repeat the kernel across the vector
for(int i=0; i<KERNEL_LENGTH; i++){
int index = kernel_length - i - 1;
kernel_block[0] = kernel[index];
kernel_block[1] = kernel[index];
kernel_block[2] = kernel[index];
kernel_block[3] = kernel[index];

kernel_reverse[i] = _mm_load_ps(kernel_block);
}

/* Create a set of 4 aligned arrays
* Each array is offset by one sample from the one before
*/
for(int i=0; i<4; i++){
memcpy(in_aligned[i], (in+i), (length-i)*sizeof(float));
}

for(int i=0; i<length-kernel_length; i+=4){

acc = _mm_setzero_ps();

for(int k=0; k<KERNEL_LENGTH; k+=4){

int data_offset = i + k;

for (int l = 0; l < 4; l++){

data_block = _mm_load_ps(in_aligned[l] + data_offset);
prod = _mm_mul_ps(kernel_reverse[k+l], data_block);

acc = _mm_add_ps(acc, prod);
}
}
_mm_storeu_ps(out+i, acc);

}

// Need to do the last value as a special case
int i = length - kernel_length;
out[i] = 0.0;
for(int k=0; k<kernel_length; k++){
out[i] += in_aligned[0][i+k] * kernel[kernel_length - k - 1];
}

return 0;
}

最佳答案

答案是,它完全按照我的意愿行事。问题似乎是我不善于读取 objdump -d 的输出。按照@PascalCuoq 的建议修改问题以使用 gcc -S 的输出,循环明显更容易理解。

我留下了这个问题,因为有人可能会重视这一点! (实际上是代码)。

关于c - 说服编译器在循环外设置寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13176470/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com