6

私は現在、パフォーマンスの改善が必要な Java アプリケーション用の C モジュールを開発しています (背景については、ネットワーク コーディング エンコーディングのパフォーマンスの改善を参照してください)。SSE-intrinsics を使用してコードを最適化しようとしましたが、Java バージョンよりもいくらか高速に実行されます (~20%)。ただし、まだ十分な速さではありません。

残念ながら、C コードの最適化に関する私の経験は多少限られています。したがって、現在の実装を改善する方法についていくつかのアイデアを得たいと思っています。

ホットスポットを構成する内側のループは次のようになります。

for (i = 0; i < numberOfGFVectorsInFragment; i++)   {

        // Load the 4 GF-elements from the message-fragment and add the log of the coefficeint to them.
        __m128i currentMessageFragmentVector = _mm_load_si128 (currentMessageFragmentPtr);
        __m128i currentEncodedResult = _mm_load_si128(encodedFragmentResultArray);

        __m128i logSumVector = _mm_add_epi32(coefficientLogValueVector, currentMessageFragmentVector);

        __m128i updatedResultVector = _mm_xor_si128(currentEncodedResult, valuesToXor);
        _mm_store_si128(encodedFragmentResultArray, updatedResultVector);

        encodedFragmentResultArray++;
        currentMessageFragmentPtr++;
    }
4

2 に答える 2

7

_mm_set_epi32アセンブリを見なくても、ボトルネックは4要素のギャザーメモリアクセスとパッキング操作にあることがすぐにわかります。内部的に_mm_set_epi32は、あなたの場合、おそらく一連のunpacklo/hi命令として実装されます。

このループの「作業」のほとんどは、これらの4つのメモリアクセスをパックすることによるものです。SSE4.1がない場合、これまでのところ、ループはベクトル化されていない方が高速である可能性がありますが、展開されている可能性があります。

SSE4.1を使用する場合は、これを試すことができます。それは速いかもしれませんが、そうではないかもしれません:

    int* logSumArray = (int*)(&logSumVector);

    __m128i valuesToXor = _mm_cvtsi32_si128(expTable[*(logSumArray++)]);
    valuesToXor = _mm_insert_epi32(valuesToXor, expTable[*(logSumArray++)], 1);
    valuesToXor = _mm_insert_epi32(valuesToXor, expTable[*(logSumArray++)], 2);
    valuesToXor = _mm_insert_epi32(valuesToXor, expTable[*(logSumArray++)], 3);

ループを少なくとも4回展開し、すべての命令をインターリーブして、このコードがうまく機能する可能性を与えることをお勧めします。

本当に必要なのは、IntelのAVX2収集/分散命令です。しかし、それは数年先のことです...

于 2011-10-17T14:28:53.607 に答える
1

http://web.eecs.utk.edu/~plank/plank/papers/CS-07-593/を試してみてください。名前に「地域」が含まれる関数は、おそらく高速です。特別な命令セットを使用していないようですが、他の方法で最適化されている可能性があります...

于 2011-10-17T18:40:41.530 に答える