15

検索中に見つけた紹介リンク:

  1. 6.59.14 ループ固有のプラグマ
  2. 2.100 プラグマ Loop_Optimize
  3. ループ回数について gcc にヒントを与える方法
  4. ループを具体的にアンロールするように gcc に指示する
  5. C++ でベクトル化を強制する方法

ご覧のとおり、ほとんどが C 用ですが、C++ でも動作する可能性があると思いました。これが私のコードです:

template<typename T>
//__attribute__((optimize("unroll-loops")))
//__attribute__ ((pure))
void foo(std::vector<T> &p1, size_t start,
            size_t end, const std::vector<T> &p2) {
  typename std::vector<T>::const_iterator it2 = p2.begin();
  //#pragma simd
  //#pragma omp parallel for
  //#pragma GCC ivdep Unroll Vector
  for (size_t i = start; i < end; ++i, ++it2) {
    p1[i] = p1[i] - *it2;
    p1[i] += 1;
  }
}

int main()
{
    size_t n;
    double x,y;
    n = 12800000;
    vector<double> v,u;
    for(size_t i=0; i<n; ++i) {
        x = i;
        y = i - 1;
        v.push_back(x);
        u.push_back(y);
    }
    using namespace std::chrono;

    high_resolution_clock::time_point t1 = high_resolution_clock::now();
    foo(v,0,n,u);
    high_resolution_clock::time_point t2 = high_resolution_clock::now();

    duration<double> time_span = duration_cast<duration<double>>(t2 - t1);

    std::cout << "It took me " << time_span.count() << " seconds.";
    std::cout << std::endl;
    return 0;
}

上記でコメントされているすべてのヒントを使用しましたが、サンプル出力が示すように、スピードアップは得られませんでした (最初の実行でコメントを外した場合#pragma GCC ivdep Unroll Vector:

samaras@samaras-A15:~/Downloads$ g++ test.cpp -O3 -std=c++0x -funroll-loops -ftree-vectorize -o test
samaras@samaras-A15:~/Downloads$ ./test
It took me 0.026575 seconds.
samaras@samaras-A15:~/Downloads$ g++ test.cpp -O3 -std=c++0x -o test
samaras@samaras-A15:~/Downloads$ ./test
It took me 0.0252697 seconds.

希望はありますか?それとも、最適化フラグO3だけでうまくいくのでしょうか? このコード (foo関数) を高速化するための提案は大歓迎です!

g++ の私のバージョン:

samaras@samaras-A15:~/Downloads$ g++ --version
g++ (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1

ループの本体がランダムであることに注意してください。私はそれを別の形で書き直すことに興味がありません。


編集

これ以上は仕方がないという回答もOK!

4

2 に答える 2

15

フラグは自動的O3にオンになり-ftree-vectorizeます。https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

-O3 は、-O2 で指定されたすべての最適化をオンにし、-finline-functions、-funswitch-loops、-fpredictive-commoning、-fgcse-after-reload、-ftree-loop-vectorize、-ftree-loop-distribute もオンにします-patterns、-ftree-slp-vectorize、-fvect-cost-model、-ftree-partial-pre、および -fipa-cp-clone オプション

したがって、どちらの場合も、コンパイラーはループのベクトル化を試みています。

g++ 4.8.2 を使用してコンパイルするには:

# In newer versions of GCC use -fopt-info-vec-missed instead of -ftree-vectorize
g++ test.cpp -O2 -std=c++0x -funroll-loops -ftree-vectorize -ftree-vectorizer-verbose=1 -o test

これを与える:

Analyzing loop at test.cpp:16                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                
Vectorizing loop at test.cpp:16                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                
test.cpp:16: note: create runtime check for data references *it2$_M_current_106 and *_39                                                                                                                                                                                    
test.cpp:16: note: created 1 versioning for alias checks.                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                
test.cpp:16: note: LOOP VECTORIZED.                                                                                                                                                                                                                                         
Analyzing loop at test_old.cpp:29                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                
test.cpp:22: note: vectorized 1 loops in function.                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                
test.cpp:18: note: Unroll loop 7 times                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                
test.cpp:16: note: Unroll loop 7 times                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                
test.cpp:28: note: Unroll loop 1 times  

-ftree-vectorizeフラグなしでコンパイル:

g++ test.cpp -O2 -std=c++0x -funroll-loops -ftree-vectorizer-verbose=1 -o test

これだけを返します:

test_old.cpp:16: note: Unroll loop 7 times

test_old.cpp:28: note: Unroll loop 1 times

行 16 はループ関数の開始であるため、コンパイラーは間違いなくベクトル化しています。アセンブラをチェックすると、これも確認されます。

現在使用しているラップトップで積極的なキャッシュが行われているようで、関数の実行にかかる時間を正確に測定することが非常に困難になっています。

ただし、他にもいくつかのことを試すことができます。

  • 修飾子を使用して__restrict__、配列間に重複がないことをコンパイラに伝えます。

  • 配列が整列されていることをコンパイラに伝えます__builtin_assume_aligned(移植性はありません)

結果のコードは次のとおりです(データ型ごとに異なる配置を使用する必要があるため、テンプレートを削除しました)

#include <iostream>
#include <chrono>
#include <vector>

void foo( double * __restrict__ p1,
          double * __restrict__ p2,
          size_t start,
          size_t end )
{
  double* pA1 = static_cast<double*>(__builtin_assume_aligned(p1, 16));
  double* pA2 = static_cast<double*>(__builtin_assume_aligned(p2, 16));

  for (size_t i = start; i < end; ++i)
  {
      pA1[i] = pA1[i] - pA2[i];
      pA1[i] += 1;
  }
}

int main()
{
    size_t n;
    double x, y;
    n = 12800000;
    std::vector<double> v,u;

    for(size_t i=0; i<n; ++i) {
        x = i;
        y = i - 1;
        v.push_back(x);
        u.push_back(y);
    }

    using namespace std::chrono;

    high_resolution_clock::time_point t1 = high_resolution_clock::now();
    foo(&v[0], &u[0], 0, n );
    high_resolution_clock::time_point t2 = high_resolution_clock::now();

    duration<double> time_span = duration_cast<duration<double>>(t2 - t1);

    std::cout << "It took me " << time_span.count() << " seconds.";
    std::cout << std::endl;

    return 0;
}

私が言ったように、私は一貫した時間測定を得るのに苦労したので、これがパフォーマンスの向上をもたらすかどうかを確認することはできません (または、低下することさえあります!)

于 2015-03-27T03:47:40.907 に答える
1

GCC には、SIMD 命令を使用する新しいプリミティブを作成するコンパイラの拡張機能があります。詳細はこちらをご覧ください。

ほとんどのコンパイラーは操作を自動ベクトル化すると言っていますが、これはコンパイラーのパターンマッチングに依存しますが、ご想像のとおり、これは非常にうまくいかない場合があります。

于 2015-03-30T12:39:52.230 に答える