Intrinsic

SIMD 命令などを Intrinsic で表記して高速な実行を実現する方法について記載します。

 

目次

1. 4バイト整数(int)の加算
2. ***
3. ***

 

1. 4バイト整数(int)の加算

[概要]
4バイト整数(int)の加算を、標準的な方法、SSE2、AVX2、の3つの方法で作成してみます。

[環境]

 

[方式1] 標準的な方法

 

[stdafx.h]

#pragma once

#include "targetver.h"

#include <iostream>
#include <stdio.h>
#include <tchar.h>

// tstring
typedef std::basic_string<TCHAR> tstring;

// tcout
#if defined(UNICODE) || defined(_UNICODE)
#define tcout std::wcout
#define tcin std::wcin
#else
#define tcout std::cout
#define tcin std::cin
#endif

[Intrinsic_add_01.cpp]

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
#include <string>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
    const size_t    n = 130000000;

    static INT32    a[n];
    static INT32    b[n];
    static INT32    c[n];
    DWORD    dwStartCount = GetTickCount();

    for (int i = 0; i < n; ++i) {
        a[i] = b[i] + c[i];
    }

    tcout << _T("(Execute time [ms]) ") << GetTickCount() - dwStartCount << _T("[ms]") << endl;

    /*========*/
    /* 後処理 */
    /*========*/
    {
        tstring str;

        tcout << _T("HIT [Enter] KEY !! ");
        getline(tcin, str);
    }

    return EXIT_SUCCESS;
}

 

[方式2] SSE2

[stdafx.h]

#pragma once

#include "targetver.h"

#include <iostream>
#include <stdio.h>
#include <tchar.h>

// tstring
typedef std::basic_string<TCHAR> tstring;

// tcout
#if defined(UNICODE) || defined(_UNICODE)
#define tcout std::wcout
#define tcin std::wcin
#else
#define tcout std::cout
#define tcin std::cin
#endif

[Intrinsic_add_02.cpp]

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
#include <string>

#include <emmintrin.h>      // for SSE2

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    const size_t    n = 130000000;

    __declspec(align(256)) static volatile INT32    a[n];
    __declspec(align(256)) static volatile INT32    b[n];
    __declspec(align(256)) static volatile INT32    c[n];

    // SIMD - _mm_add_epi32
    __m128i ma;
    __m128i mb;
    __m128i mc;

    DWORD    dwStartCount = GetTickCount();

    for (int i = 0; i < n; i+=4) {
        mb = _mm_load_si128((__m128i*)&b[i]);       // load - SSE2
        mc = _mm_load_si128((__m128i*)&c[i]);       // load - SSE2

        ma = _mm_add_epi32(mb, mc);                 // add - SSE2

        _mm_store_si128((__m128i*)a, ma);           // store - SSE2
    }

    cout << "(Execute time [ms]) " << GetTickCount() - dwStartCount << "[ms]" << endl;

    /*========*/
    /* 後処理 */
    /*========*/
    {
        string str;

        cout << "HIT [Enter] KEY !! ";
        getline(cin, str);
    }

    return EXIT_SUCCESS;
}

 

[方式3] AVX2

#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
#include <iostream>
#include <string>

#include <immintrin.h>      // for AVX2


using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
    const size_t    n = 130000000;

    __declspec(align(256)) static volatile INT32    a[n];
    __declspec(align(256)) static volatile INT32    b[n];
    __declspec(align(256)) static volatile INT32    c[n];

    // SIMD - _mm_add_epi32
    __m256i ma;
    __m256i mb;
    __m256i mc;

    DWORD    dwStartCount = GetTickCount();

    for (int i = 0; i < n; i+=8) {
        mb = _mm256_load_si256((__m256i*)&b[i]);    // load - AVX
        mc = _mm256_load_si256((__m256i*)&c[i]);    // load - AVX

        ma = _mm256_add_epi32(mb, mc);              // add - AVX2

        _mm256_store_si256((__m256i*)a, ma);        // store - AVX    }

    cout << "(Execute time [ms]) " << GetTickCount() - dwStartCount << "[ms]" << endl;

    /*========*/
    /* 後処理 */
    /*========*/
    {
        string str;

        cout << "HIT [Enter] KEY !! ";
        getline(cin, str);
    }

    return EXIT_SUCCESS;
}

 

上記の3つについて、私のPC環境での実行時間は以下のような結果となりました。
AVX2 の速度が SSE2 と変わらなかったことが不思議です。継続調査して AVX2 の性能を正しく出せるようにするノウハウを蓄積したいと思います。

方式 実行時間 [ms]
標準的な方法 約 1390ms
SSE2 約 890ms
AVX2 約 890ms

 

2. ***

[概要]

 

[環境]

 

[プログラムソース "***.cpp"]

		


サンプルプログラム

サンプルプログラム ダウンロード

 

参考


記載: 2017年09月30日 木下英俊:新規作成