性能評価:値の検索

一次元配列から特定の値を探すプログラムを応用して、C++言語で記述したプログラムと、アセンブリ言語で記述した関数を利用したときの性能差を検証してみましょう。
arques.hatenablog.com

64ビット浮動小数点型

性能評価プログラム

C++で記述した関数と、アセンブリ言語で記述した関数を複数回呼び出します。それぞれに要した時間をclock関数で計測し、性能を評価します。common.hやアセンブリ言語で記述した関数は「値の検索」で示したものと同じです。以降に、呼び出し側のソースリストを示します。

#include "..\common.h"  // TEMPLATES

#define T long long
extern "C"
{
    int asrchsq(const T*, const T, const size_t);
}

// main
int main(void)
{
    const size_t ArrLen = 32768;
    static_assert(ArrLen % 16 == 0,
        "number of elements must be an integral multiple of 16.");
    //T a[ArrLen];
    T value = 80000;

    try
    {
        T cindex, aindex;
        clock_t start;

        T* a = (T*)_mm_malloc(sizeof(T) * ArrLen, 64);

        #define LOOPS   10000
        for (size_t j = 1; j < 10; j++)
        {
            init(a, ArrLen);
            start = clock();
            for (int i = 0; i < LOOPS;i++)
            {
                a[ArrLen - j] = value;
                cindex = cSrch(a, value, ArrLen);
            }
            print_elTime("cpp: ", start, clock());

            init(a, ArrLen);
            start = clock();
            for (int i = 0; i < LOOPS;i++)
            {
                a[ArrLen - j] = value;
                aindex = asrchsq(a, value, ArrLen);
            }
            print_elTime("asm: ", start, clock());

            string okng = cindex == aindex ? " OK!" : " NG!";
            cout << "cpp: " << cindex << ", asm: " << aindex << okng << endl;
        }
        _mm_free(a);
    }
    catch (char* str)
    {
        cerr << str << endl;
    }
    return 0;
}

これまでと同様に、時間の計測を行います。アセンブリ言語で開発した関数、C++言語で開発した関数の両方を10,000回呼び出し、それぞれに要した時間を表示します。上記プログラムへ /O2 オプションを指定してビルド&実行したときの性能を示します。プログラムを起動すると、それぞれに要した時間が表示されます。横軸は比較の種類、縦軸は処理に要した時間(ミリ秒)です。

アセンブリ言語で開発した関は、C++言語で開発した関数より約4.8倍高速です。理想的には8倍高速になることを期待しますが、オーバヘッドやメモリアクセスなどに時間を奪われているのでしょう。

符号なし64ビット整数型

符号なし64ビット整数型関数の性能を観察してみます。呼び出し側プログラムの変更は僅かですので、その部分だけ示します。

#include "..\common.h"  // TEMPLATES

#define T unsigned long long
extern "C"
{
    int asrchuq(const T*, const T, const size_t);
}

// main
int main(void)
{
            :
            start = clock();
            for (int i = 0; i < LOOPS;i++)
            {
                a[ArrLen - j] = value;
                aindex = asrchuq(a, value, ArrLen);
            }
            :

同様の方法で性能測定します。

アセンブリ言語で開発した関は、C++言語で開発した関数より約4.8倍高速です。予想通り符号付64ビット整数型の結果と同じです。理想的には8倍高速になることを期待しますが、オーバヘッドやメモリアクセスなどに時間を奪われたようです。

64ビット浮動小数点型

64ビット浮動小数点型関数の性能を観察してみます。呼び出し側プログラムの変更は僅かですので、その部分だけ示します。

#include "..\common.h"  // TEMPLATES

#define T double
extern "C"
{
    int asrchpd(const T*, const T, const size_t);
}

// main
int main(void)
{
            :
            start = clock();
            for (int i = 0; i < LOOPS;i++)
            {
                a[ArrLen - j] = value;
                aindex = asrchpd(a, value, ArrLen);
            }
            :

同様の方法で性能測定します。

アセンブリ言語で開発した関は、C++言語で開発した関数より約9.96倍高速です。理想的には8倍高速になることを期待しますが、それ以上の高速化を実現できています。

32ビット浮動小数点型

32ビット浮動小数点型関数の性能を観察してみます。呼び出し側プログラムの変更は僅かですので、その部分だけ示します。

#include "..\common.h"  // TEMPLATES

#define T float
extern "C"
{
    int asrchps(const T*, const T, const size_t);
}

// main
int main(void)
{
            :
        for (size_t j = 1; j < 18; j++)
            :
            start = clock();
            for (int i = 0; i < LOOPS;i++)
            {
                a[ArrLen - j] = value;
                aindex = asrchps(a, value, ArrLen);
            }
            :

同様の方法で性能測定します。

アセンブリ言語で開発した関は、C++言語で開発した関数より約19.43倍高速です。ループ回数が1/16になりますので、予想として16倍高速になることを期待しますが、それ以上の高速化を観察できます。