gpt4 book ai didi

c - 索引数组时循环变量类型会影响效率吗?

转载 作者:太空狗 更新时间:2023-10-29 15:07:12 24 4
gpt4 key购买 nike

我正在尝试将我的代码优化到最后一个可能的循环,并且想知道循环类型在用于数组索引时是否会影响性能?

我用下面的程序做了一些实验,它只用 0 填充数组:

int main(int argc, char **argv)
{
typedef int CounterType;
typedef int64_t CounterType;

CounterType N = atoi(argv[1]);
uint8_t volatile dummy[N + 16];
__m128i v = _mm_set1_epi8(0);
for (int j = 0; j < 1000000; ++j)
{
#pragma nounroll
for (CounterType i = 0; i <= N; i+= CounterType(16))
{
_mm_storeu_si128((__m128i *)&dummy[i], v);
}
}
return 0;
}

通过使用不同的循环计数器类型(CounterType)和不同的编译器,我已经使用硬件性能计数器(“perf stat a.out 32768”)记录了内循环的汇编代码和性能。我在 Xeon 5670 上运行。

GCC4.9,内部

.L3
movups %xmm0, (%rax)
addq $16, %rax
movl %eax, %edx
subl %esi, %edx
cmpl %ecx, %edx
jle .L3

4,127,525,521 cycles # 2.934 GHz
12,304,723,292 instructions # 2.98 insns per cycle

GCC4.9, int64

.L7
movups %xmm0, (%rcx,%rax)
addq $16, %rax
cmpq %rax, %rdx
jge .L7
4,123,315,191 cycles # 2.934 GHz
8,206,745,195 instructions # 1.99 insns per cycle

ICC11, int64

..B1.6:
movdqu %xmm0, (%rdx,%rdi)
addq $16, %rdx
incq %rcx
cmpq %rbx, %rcx
jb ..B1.6 # Prob 82% #24.5
2,069,719,166 cycles # 2.934 GHz
5,130,061,268 instructions

(因为微操作融合而更快?)

ICC11,整数

..B1.6:                         # Preds ..B1.4 ..B1.6
movdqu %xmm0, (%rdx,%rbx) #29.38
addq $16, %rdx #24.37
cmpq %rsi, %rdx #24.34
jle ..B1.6 # Prob 82% #24.34
4,136,109,529 cycles # 2.934 GHz
8,206,897,268 instructions

ICC13、整数和整数64

movdqu    %xmm0, (%rdi,%rax)                            #29.38
addq $16, %rdi #24.37
cmpq %rsi, %rdi #24.34
jle ..B1.7
4,123,963,321 cycles # 2.934 GHz
8,206,083,789 instructions # 1.99 insns per cycle

数据似乎表明 int64 更快。也许这是因为它匹配指针大小,因此避免了任何转换。但我不相信这个结论。另一种可能性可能是编译器在某些情况下决定在存储之前进行循环比较,以 1 条额外指令为代价实现更多并行性(由于 X86 2 操作数指令具有破坏性)。但这是偶然的,并不是由循环变量类型引起的。

有人可以解释这个谜团吗(最好了解编译器转换)?

CUDA C 最佳实践指南中也声称有符号循环计数器比无符号循环计数器更容易生成代码。但这在这里似乎并不相关,因为地址计算的内部循环中没有乘法,因为该表达式被转换为归纳变量。但显然在 CUDA 中,它更喜欢使用乘加法来计算地址,因为 MADD 是 1 条指令,就像加法一样,它可以将寄存器使用减少 1。

最佳答案

是的,循环变量类型会影响效率。

让我建议 an even better solution with GCC .

void distance(uint8_t* dummy, size_t n, const __m128 v0)
{
intptr_t i;
for(i = -n; i < 0; i += 4) {
_mm_store_ps(&((float*)dummy)[i+n], v0);
}
}

对于 GCC 4.9.2 和 GCC 5.3,这会产生这个主循环

.L5:
vmovaps %xmm0, (%rdi,%rax)
addq $16, %rax
js .L5

不过 Clang 3.6 仍然会生成 cmp

.LBB0_2:                                # =>This Inner Loop Header: 
vmovaps %xmm0, 8(%rdi,%rax)
addq $4, %rax
cmpq $-4, %rax
jl .LBB0_2

Clang 3.7 展开四次并使用 cmp

ICC 13 展开两次并使用 cmp 因此只有 GCC 能够在没有不必要的 cmp 指令的情况下做到这一点。

关于c - 索引数组时循环变量类型会影响效率吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23644897/

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