2017-10-30 8 views
1

おはよう短い配列よりも絶対に速い、 L1キャッシュサイズの配列を追加する。大きな配列が

は、私は2つのアレイを追加するには、次のプログラムを書いた:私はそれをコンパイル

#include<iostream> 

#define line 32 
inline void add(float a[], float b[]){ 
    for (int i=0; i<line; i++){a[i]+=b[i];} 
} 

int main(){ 
float a[line]; for (int i=0; i<line; i++){a[i]=0.;} 
float b[line]; for (int i=0; i<line; i++){b[i]=0.;} 

for (int i=0; i<1024*1024*512; i++){add(a,b);} //Add arrays several times 
for (int i=0; i<line; i++){std::cout << a[i] << std::endl;} //Print arrays, else -05 optimize it away. 
} 

(G ++バージョンを4.8.4 /私のハードウェアが古いです)

g++ add.c++ -O5 -o Test 

とし、それを実行

time ./Test 

それは私が理解し 1.3秒

それは2.3秒

私はそれを試してみましたいくつかの回を必要とし、実行時には常に同じである16行は、IF =

(そう、それは安定したのですが。)必要と32行は、IF = (ベクトルプロセッサなど)は比較的高速になる可能性がありますが、なぜそれが絶対より速いのか理解できません。私はこのプログラムを書いて、ピーク・パフォーマンスを達成する方法を見つけました。私の質問:CPUには何が起こっているのですか?どのように改善することができますか?

+0

これは配列です。 C++ベクタ型は 'std :: vector'です。 – Neo

答えて

3

あなたのコンパイラは16/32/64のようなハードコードされた制限を持つループを非常に簡単に展開できます。あなたの「32回」ループを展開し、AVXを使用すると正確に4つのAVXが追加される可能性があります。これは、2つのAVX追加をもたらす "16回"ループより速くなります。なぜなら、ブランチが各 "ライン"で動作したときにパイプラインの停止が発生するからです(ただし、xeonいくつかの経路を投機的に実行することができる)。

マイクロベンチマークは、慎重に行わなければ誤解を招く可能性があるため、生成されたアセンブリを常に確認する必要があります。あなたのベンチマークが同じメモリを何度も繰り返し打っているだけなので、これが実際に何が生産で起こるかを代表するかどうかを考えなければなりません。

1

コンパイルステートメントから、O5 optimizationを使用してコードをコンパイルすることは明らかです。

O2 optimizationを除いて、他のすべての変異体は安定でないことが知られている。私はあなたのテスト目的のためにそれらを使用しないことをお勧めします、そして、最も重要なのは、よく定義されていません。

また、ここには多くの条件がある可能性があります。 O5 optimizationのように、プログラムが大量のメモリを消費するか、または内部最適化アルゴリズムが計算に重い圧力を検出するまで、実際には動作しない可能性があります。私が言うようにO5 optimizationはよく定義されておらず、安定していません。 私はこのタイプの問題に直面してコーディングしていましたが、何もしないでくださいO.x optimization

私はこれが役に立ちます。

+0

あなたが正しいです、私が-O2から-O3に行くならば、奇妙な動作が起こります。 line = 32は突然7倍速くなり、line = 16は1.4倍になります。 – Markus