44

私はかなり長い間 Intel の SSE 組み込み関数を使用しており、パフォーマンスが向上しています。したがって、AVX 組み込み関数が私のプログラムをさらに高速化することを期待していました。残念ながら、これまではそうではありませんでした。おそらく私はばかげた間違いをしているので、誰かが私を助けてくれたらとてもありがたいです.

g++ 4.6.1 で Ubuntu 11.10 を使用しています。プログラムをコンパイルしました(以下を参照)

g++ simpleExample.cpp -O3 -march=native -o simpleExample

テスト システムには Intel i7-2600 CPU が搭載されています。

これが私の問題を例示するコードです。私のシステムでは、出力が得られます

98.715 ms, b[42] = 0.900038 // Naive
24.457 ms, b[42] = 0.900038 // SSE
24.646 ms, b[42] = 0.900038 // AVX

計算 sqrt(sqrt(sqrt(x))) は、メモリ帯域幅が実行速度を制限しないようにするためにのみ選択されていることに注意してください。これは単なる例です。

シンプルな例.cpp:

#include <immintrin.h>
#include <iostream>
#include <math.h> 
#include <sys/time.h>

using namespace std;

// -----------------------------------------------------------------------------
// This function returns the current time, expressed as seconds since the Epoch
// -----------------------------------------------------------------------------
double getCurrentTime(){
  struct timeval curr;
  struct timezone tz;
  gettimeofday(&curr, &tz);
  double tmp = static_cast<double>(curr.tv_sec) * static_cast<double>(1000000)
             + static_cast<double>(curr.tv_usec);
  return tmp*1e-6;
}

// -----------------------------------------------------------------------------
// Main routine
// -----------------------------------------------------------------------------
int main() {

  srand48(0);            // seed PRNG
  double e,s;            // timestamp variables
  float *a, *b;          // data pointers
  float *pA,*pB;         // work pointer
  __m128 rA,rB;          // variables for SSE
  __m256 rA_AVX, rB_AVX; // variables for AVX

  // define vector size 
  const int vector_size = 10000000;

  // allocate memory 
  a = (float*) _mm_malloc (vector_size*sizeof(float),32);
  b = (float*) _mm_malloc (vector_size*sizeof(float),32);

  // initialize vectors //
  for(int i=0;i<vector_size;i++) {
    a[i]=fabs(drand48());
    b[i]=0.0f;
  }

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Naive implementation
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  s = getCurrentTime();
  for (int i=0; i<vector_size; i++){
    b[i] = sqrtf(sqrtf(sqrtf(a[i])));
  }
  e = getCurrentTime();
  cout << (e-s)*1000 << " ms" << ", b[42] = " << b[42] << endl;

// -----------------------------------------------------------------------------
  for(int i=0;i<vector_size;i++) {
    b[i]=0.0f;
  }
// -----------------------------------------------------------------------------

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// SSE2 implementation
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  pA = a; pB = b;

  s = getCurrentTime();
  for (int i=0; i<vector_size; i+=4){
    rA   = _mm_load_ps(pA);
    rB   = _mm_sqrt_ps(_mm_sqrt_ps(_mm_sqrt_ps(rA)));
    _mm_store_ps(pB,rB);
    pA += 4;
    pB += 4;
  }
  e = getCurrentTime();
  cout << (e-s)*1000 << " ms" << ", b[42] = " << b[42] << endl;

// -----------------------------------------------------------------------------
  for(int i=0;i<vector_size;i++) {
    b[i]=0.0f;
  }
// -----------------------------------------------------------------------------

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// AVX implementation
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  pA = a; pB = b;

  s = getCurrentTime();
  for (int i=0; i<vector_size; i+=8){
    rA_AVX   = _mm256_load_ps(pA);
    rB_AVX   = _mm256_sqrt_ps(_mm256_sqrt_ps(_mm256_sqrt_ps(rA_AVX)));
    _mm256_store_ps(pB,rB_AVX);
    pA += 8;
    pB += 8;
  }
  e = getCurrentTime();
  cout << (e-s)*1000 << " ms" << ", b[42] = " << b[42] << endl;

  _mm_free(a);
  _mm_free(b);

  return 0;
}

どんな助けでも大歓迎です!

4

4 に答える 4

43

これは、Sandy Bridge プロセッサでVSQRTPS(AVX 命令) が (SSE 命令) のちょうど 2 倍のサイクルを必要とするためです。SQRTPSAgner Fog の最適化ガイド:命令表、88 ページを参照してください。

平方根や除算などの命令は、AVX の恩恵を受けません。一方、加算、乗算などは行います。

于 2012-01-19T11:09:57.557 に答える
10

平方根のパフォーマンスを向上させたい場合は、VSQRTPS の代わりに VRSQRTPS とニュートン ラフソン式を使用できます。

x0 = vrsqrtps(a)
x1 = 0.5 * x0 * (3 - (a * x0) * x0)

VRSQRTPS 自体は AVX の恩恵を受けませんが、他の計算では恩恵を受けます。

23 ビットの精度で十分な場合に使用します。

于 2012-01-19T15:19:30.970 に答える
7

完全を期すためだけに。除算や平方根などの演算のニュートン ラフソン (NR) 実装は、コード内のこれらの演算の数が限られている場合にのみ有益です。これは、これらの代替方法を使用すると、乗算ポートや加算ポートなどの他のポートにより多くの圧力が発生するためです。これが基本的に、x86 アーキテクチャが代替ソフトウェア ソリューション (NR など) ではなく、これらの操作を処理するための特別なハードウェア ユニットを備えている理由です。Intel 64 and IA-32 Architectures Optimization Reference Manual p.556から引用します。

「場合によっては、除算または平方根演算が、これらの演算のレイテンシの一部を隠す大規模なアルゴリズムの一部である場合、ニュートン ラフソンによる近似によって実行が遅くなる可能性があります。」

そのため、大規模なアルゴリズムで NR を使用する場合は注意してください。実際、私はこの時点で修士論文を書いていました。公開されたら、将来の参考のためにここにリンクを残しておきます。

また、特定の命令のスループットとレイテンシーについて常に疑問に思っている人は、IACAを参照してください。コードのコア内実行パフォーマンスを静的に分析するためにインテルが提供する非常に便利なツールです。

ここで編集されたのは、興味のある人のための論文 へのリンクです

于 2015-03-16T21:24:02.367 に答える
6

プロセッサ ハードウェアによっては、AVX 命令がハードウェアで SSE 命令としてエミュレートされる場合があります。プロセッサの正確な仕様を取得するには、プロセッサの部品番号を調べる必要がありますが、これはローエンドとハイエンドの Intel プロセッサの主な違いの 1 つであり、特殊化された実行ユニットとハードウェア エミュレーションの数です。

于 2012-01-19T10:53:41.690 に答える