同じ計算を 2 つの異なる方法で実行する次の 2 つのプログラムを考えてみましょう。
// v1.c
#include <stdio.h>
#include <math.h>
int main(void) {
int i, j;
int nbr_values = 8192;
int n_iter = 100000;
float x;
for (j = 0; j < nbr_values; j++) {
x = 1;
for (i = 0; i < n_iter; i++)
x = sin(x);
}
printf("%f\n", x);
return 0;
}
と
// v2.c
#include <stdio.h>
#include <math.h>
int main(void) {
int i, j;
int nbr_values = 8192;
int n_iter = 100000;
float x[nbr_values];
for (i = 0; i < nbr_values; ++i) {
x[i] = 1;
}
for (i = 0; i < n_iter; i++) {
for (j = 0; j < nbr_values; ++j) {
x[j] = sin(x[j]);
}
}
printf("%f\n", x[0]);
return 0;
}
gcc 4.7.2 を使用してそれらをコンパイルし-O3 -ffast-math
、Sandy Bridge ボックスで実行すると、2 番目のプログラムは最初のプログラムの 2 倍の速度になります。
何故ですか?
疑わしい点の 1 つは、 のi
ループの連続する反復間のデータ依存性v1
です。ただし、完全な説明が何であるかはよくわかりません。
(なぜ私の python/numpy の例は純粋な C 実装よりも速いのか? に触発された質問)
編集:
の生成されたアセンブリは次のv1
とおりです。
movl $8192, %ebp
pushq %rbx
LCFI1:
subq $8, %rsp
LCFI2:
.align 4
L2:
movl $100000, %ebx
movss LC0(%rip), %xmm0
jmp L5
.align 4
L3:
call _sinf
L5:
subl $1, %ebx
jne L3
subl $1, %ebp
.p2align 4,,2
jne L2
およびv2
:
movl $100000, %r14d
.align 4
L8:
xorl %ebx, %ebx
.align 4
L9:
movss (%r12,%rbx), %xmm0
call _sinf
movss %xmm0, (%r12,%rbx)
addq $4, %rbx
cmpq $32768, %rbx
jne L9
subl $1, %r14d
jne L8