2021年9月13日月曜日

SIMDライブラリ、libsimdppを使う

 最近、カメラの動画で画像処理やディープラーニングなどの計算をやることが増えています。

その時に常に課題にあがるのが、処理速度の高速化。



ディープラーニングのネットワークってそもそも計算の量を減らしながら良い結果が得られるように設計されているし、ディープラーニングのフレームワークも相当高速化されているから、そう簡単にこれ以上早くならないっつーの。


最近はいろいろな機械学習フレームワークが、学習の計算はGPUでやって、その結果を利用するのはSIMD命令でやって高速化するようになっているようです。

SIMD命令ってコンパイラやCPUに依存するからあんまり使いたくないんだよねぇと思っていたら、SIMD命令を抽象化するライブラリを見つけました。

いろいろなプロジェクトがSIMD命令のところをどう書くのか、いろいろ試行錯誤しているようですが、なかなか読みやすくて、移植性があるように書くのが難しいようです。

いろいろ調べていたらついに便利なライブラリを見つけました。

https://qiita.com/engineer/items/f47a54bffe2691f9784a


なんだ、ちょー簡単にSIMD命令を抽象化できる、libsimdppライブラリがあるじゃないか!


https://github.com/p12tic/libsimdpp


これ本当によくできています。

SIMD命令を使わない設定やSIMD命令がないコンパイラのときはSIMD命令をエミュレーションしてくれるので、PCの高度なIDE環境で開発を行って、実際には組み込みで動かすというようなことも簡単にできます。

簡単にSIMDを使う場合と使わない場合を切り替えられるので、実行速度を比較しながらパフォーマンスチューニングを行うこともできます。


こんだけ便利なライブラリなのになぜか使い方のサンプルがないので、サンプルを作ってみました。


------------------------------------------------------

#define SIMDPP_ARCH_X86_SSE4_1 true

//#define SIMDPP_ARCH_POWER_ALTIVEC true

//#define SIMDPP_ARCH_NULL true


#include <iostream>

#include <chrono>

#include <simdpp/simd.h>

//#include "simdpp_simd.h"


//example where i got this from

//https://github.com/p12tic/libsimdpp/tree/2e5c0464a8069310d7eb3048e1afa0e96e08f344


// Initializes vector to store values

void init_vector(float* a, float* b, size_t size) {

    for (int i=0; i<size; i++) {

        a[i] = i * 1.0;

        b[i] = (size * 1.0) - i - 1;

    }

}




using namespace simdpp;

int main() {

    //1048576

    const unsigned long SIZE = 4 * 1500000;


    float* vec_a=new float[SIZE];

    float* vec_b= new float[SIZE];

    float* result= new float[SIZE];


///////////////////////////*/

//LibSIMDpp

    //*

    auto t1 = std::chrono::high_resolution_clock::now();


    init_vector(vec_a, vec_b, SIZE);

    for (int i=0; i<SIZE; i+=4) {

        float32<4> xmmA = load(vec_a + i);  //loads 4 floats into xmmA

        float32<4> xmmB = load(vec_b + i);  //loads 4 floats into xmmB

        float32<4> xmmC = add(xmmA, xmmB);  //Vector add of xmmA and xmmB

        //float32<4> xmmD = div(xmmA, xmmB);  //Vector add of xmmA and xmmB

        store(result + i, xmmC);            //Store result into the vector

    }


    auto t2 = std::chrono::high_resolution_clock::now();


    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count()

              << " milliseconds\n";

    //*/



///////////////////////////*/

//standard

    //*

    init_vector(vec_a, vec_b, SIZE);

    t1 = std::chrono::high_resolution_clock::now();


    for (auto i = 0; i < SIZE; i++) {

        result[i] = vec_a[i]  + vec_b[i];

    }


    t2 = std::chrono::high_resolution_clock::now();


    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count()

              << " milliseconds\n";

    //*/



    delete[] vec_a;

    delete[] vec_b;

    delete[] result;

    return 0;

}


------------------------------------------------------


0 件のコメント:

コメントを投稿