制限キーワードはGCC/G ++で大きな利点を提供しますか?
-
21-09-2019 - |
質問
C/C ++の使用かどうかについての数字/分析を見たことがありますか restrict
GCC/G ++のキーワードは、実際には(理論だけでなく)、実際には大きなパフォーマンス向上を提供しますか?
私はその使用を推奨 /軽parするさまざまな記事を読んでいますが、どちらの側面の議論を実際に示している実際の数にもわたしを逃していません。
編集
そんなこと知ってる restrict
公式にはC ++の一部ではありませんが、一部のコンパイラによってサポートされており、私は Christer Ericson その使用法を強くお勧めします。
解決
制限キーワードは違いをもたらします。
一部の状況では、要因2以上の改善が見られました(画像処理)。ほとんどの場合、違いはそれほど大きくありません。約10%。
違いを示す小さな例を以下に示します。テストとして、非常に基本的な4x4ベクトル *マトリックス変換を書きました。関数を強制的にインラインしないように強制する必要があることに注意してください。それ以外の場合、GCCは、ベンチマークコードにエイリアスポインターがないことを検出し、制限はインラインのために違いをもたらさないことを検出します。
変換関数も別のファイルに移動できたかもしれません。
#include <math.h>
#ifdef USE_RESTRICT
#else
#define __restrict
#endif
void transform (float * __restrict dest, float * __restrict src,
float * __restrict matrix, int n) __attribute__ ((noinline));
void transform (float * __restrict dest, float * __restrict src,
float * __restrict matrix, int n)
{
int i;
// simple transform loop.
// written with aliasing in mind. dest, src and matrix
// are potentially aliasing, so the compiler is forced to reload
// the values of matrix and src for each iteration.
for (i=0; i<n; i++)
{
dest[0] = src[0] * matrix[0] + src[1] * matrix[1] +
src[2] * matrix[2] + src[3] * matrix[3];
dest[1] = src[0] * matrix[4] + src[1] * matrix[5] +
src[2] * matrix[6] + src[3] * matrix[7];
dest[2] = src[0] * matrix[8] + src[1] * matrix[9] +
src[2] * matrix[10] + src[3] * matrix[11];
dest[3] = src[0] * matrix[12] + src[1] * matrix[13] +
src[2] * matrix[14] + src[3] * matrix[15];
src += 4;
dest += 4;
}
}
float srcdata[4*10000];
float dstdata[4*10000];
int main (int argc, char**args)
{
int i,j;
float matrix[16];
// init all source-data, so we don't get NANs
for (i=0; i<16; i++) matrix[i] = 1;
for (i=0; i<4*10000; i++) srcdata[i] = i;
// do a bunch of tests for benchmarking.
for (j=0; j<10000; j++)
transform (dstdata, srcdata, matrix, 10000);
}
結果:(私の2 GHzコアデュオ)
nils@doofnase:~$ gcc -O3 test.c
nils@doofnase:~$ time ./a.out
real 0m2.517s
user 0m2.516s
sys 0m0.004s
nils@doofnase:~$ gcc -O3 -DUSE_RESTRICT test.c
nils@doofnase:~$ time ./a.out
real 0m2.034s
user 0m2.028s
sys 0m0.000s
親指で20%速い実行、オン それ システム。
アーキテクチャにどれだけ依存するかを示すために、同じコードを皮質A8埋め込みCPUで実行させました(ループカウントを少し調整して、それほど長く待たないので、少し調整します):
root@beagleboard:~# gcc -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp test.c
root@beagleboard:~# time ./a.out
real 0m 7.64s
user 0m 7.62s
sys 0m 0.00s
root@beagleboard:~# gcc -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -DUSE_RESTRICT test.c
root@beagleboard:~# time ./a.out
real 0m 7.00s
user 0m 6.98s
sys 0m 0.00s
ここでの違いはわずか9%です(同じコンパイラbtw。)
他のヒント
制限キーワードはGCC / G ++で大きな利点を提供しますか?
これ できる 以下の例に示すように、指示の数を減らすので、可能な限り使用してください。
GCC 4.8 Linux X86-64 Exmample
入力:
void f(int *a, int *b, int *x) {
*a += *x;
*b += *x;
}
void fr(int *restrict a, int *restrict b, int *restrict x) {
*a += *x;
*b += *x;
}
コンパイルと逆コンパイル:
gcc -g -std=c99 -O0 -c main.c
objdump -S main.o
と -O0
, 、彼らは同じです。
と -O3
:
void f(int *a, int *b, int *x) {
*a += *x;
0: 8b 02 mov (%rdx),%eax
2: 01 07 add %eax,(%rdi)
*b += *x;
4: 8b 02 mov (%rdx),%eax
6: 01 06 add %eax,(%rsi)
void fr(int *restrict a, int *restrict b, int *restrict x) {
*a += *x;
10: 8b 02 mov (%rdx),%eax
12: 01 07 add %eax,(%rdi)
*b += *x;
14: 01 06 add %eax,(%rsi)
初心者の場合、 電話会議 は:
rdi
=最初のパラメーターrsi
= 2番目のパラメーターrdx
= 3番目のパラメーター
結論: 4の代わりに3つの指示.
もちろん、指示 異なるレイテンシを持つことができます, 、しかし、これは良い考えを与えます。
なぜGCCがそれを最適化できたのですか?
上記のコードはから取得されました ウィキペディアの例 それはそうです とても 照明。
擬似アセンブリ f
:
load R1 ← *x ; Load the value of x pointer
load R2 ← *a ; Load the value of a pointer
add R2 += R1 ; Perform Addition
set R2 → *a ; Update the value of a pointer
; Similarly for b, note that x is loaded twice,
; because a may be equal to x.
load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b
為に fr
:
load R1 ← *x
load R2 ← *a
add R2 += R1
set R2 → *a
; Note that x is not reloaded,
; because the compiler knows it is unchanged
; load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b
本当に速いですか?
えーと...この簡単なテストのためではありません:
.text
.global _start
_start:
mov $0x10000000, %rbx
mov $x, %rdx
mov $x, %rdi
mov $x, %rsi
loop:
# START of interesting block
mov (%rdx),%eax
add %eax,(%rdi)
mov (%rdx),%eax # Comment out this line.
add %eax,(%rsi)
# END ------------------------
dec %rbx
cmp $0, %rbx
jnz loop
mov $60, %rax
mov $0, %rdi
syscall
.data
x:
.int 0
その後:
as -o a.o a.S && ld a.o && time ./a.out
Ubuntu 14.04 AMD64 CPU Intel I5-3210M。
私はまだ現代のCPUを理解していないことを告白します。あなたの場合は教えてください:
- 私の方法に欠陥が見つかりました
- それがはるかに速くなるアセンブラーテストケースを見つけました
- 違いがなかった理由を理解してください
記事 制限キーワードを分類します 論文を指します プログラマー指定エイリアシングが悪い考えである理由 (PDF)それは一般的に役に立たず、これをバックアップするための測定を提供します。
許可するC ++コンパイラに注意してください restrict
キーワードはまだそれを無視するかもしれません。たとえば、そうです ここ.
テストしました これ Cプログラム。それなし restrict
完了するのに12.640秒かかりました restrict
12.516。そのように見える できる 保存 いくつか 時間。