Up

C++ AMP (Accelerated Massive Parallelism)の試用

 

Visual C++ において、GPU(グラフィックスデバイス)などによる並列計算を行うC++ AMPが利用できるようになったので使ってみた。使用PCはPanasonicLet's note CF-SX2である。

C++ AMPVisual C++によるプログラミングの中で使え、画像処理のみならずデータ分析などのプログラミングにおいて処理の並列化を行うときの機能として有望である。データ分析プログラミングにおける並列化の効果を見るために、次の行列計算を行ってみた。

nvar個の変数のncase人のデータを表す型の行列に対して積を求める。この計算をAMPを用いる方法を2通りと、AMPを用いない方法1つを以下のように作成した。

 

#include <iostream>

#include <iomanip>

#include <amp.h>

#include <stdlib.h>

 

using namespace std;

using namespace concurrency;

 

LARGE_INTEGER L_I;

double f;

const double c = pow(2.0, 32.0);

 

double MyGetTime(){

        QueryPerformanceCounter(&L_I);

        double v = L_I.HighPart * c + L_I.LowPart;

        return 1000000.0 * v / f;

}

 

int main(){

        if (!QueryPerformanceFrequency(&L_I)){

                cout << "QueryPerformanceFrequency failed." << endl;

                return 9;

        }

        f = L_I.LowPart + L_I.HighPart * c;

 

        std::vector<accelerator> accs = accelerator::get_all();

        if (accs.size() > 0){

                wcout << accs[0].description << endl;

                wcout << accs[0].dedicated_memory << "(memory)" << endl;

                wcout << "Display..." << (accs[0].has_display ? "connected." : "not connected.") << endl;

                accelerator::set_default(accs[0].device_path);

        }

 

        srand(1);

 

        const int ncase = 1000;

        const int nvar = 3;

        cout << endl << "nvar = " << nvar << "   ncase = " << ncase << endl;

 

//#define my_real double

#define my_real float

 

        my_real ** X = new my_real*[nvar];

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

                X[i] = new my_real[ncase];

                for (int j = 0; j < ncase; j++)

                        X[i][j] = (rand() *  2.0 / 32767.0) - 1.0;

        }

 

        my_real ** XPX = new my_real*[nvar];

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

                XPX[i] = new my_real[nvar];

                XPX[i][nvar - 1] = 0.0;

        }

 

        my_real * Prod = new my_real[ncase];

 

        my_real t0 = MyGetTime();

        for (int i = 0; i < nvar; i++)

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

                        {

                                array_view<const my_real, 1> a(ncase, X[i]);

                                array_view<const my_real, 1> b(ncase, X[j]);

                                array_view<my_real, 1> c(ncase, Prod);

                                c.discard_data();

                                parallel_for_each(c.extent, [=](index<1> k) restrict(amp) {

                                        c[k] = a[k] * b[k];

                                });

                        }

                        my_real sum = 0.0;

                        for (int k = 0; k < ncase; k++)

                                sum += Prod[k];

                        XPX[i][j] = XPX[j][i] = sum;

                }

        my_real t1 = MyGetTime();

        cout << endl << "Elapsed time = " << (t1 - t0) << "micro sec" << endl;

 

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

                for (int j = 0; j < nvar; j++)

                        cout << setw(15) << XPX[i][j];

                cout << endl;

        }

 

        my_real * XL = new my_real[nvar * ncase];

        for (int i = 0; i < nvar; i++)

                for (int j = 0; j < ncase; j++)

                        XL[j + i * ncase] = X[i][j];

        my_real * ProdL = new my_real[nvar * nvar];

 

        t0 = MyGetTime();

        {

                array_view<const my_real, 2> aXL(nvar, ncase, XL);

                array_view<my_real, 2> aProdL(nvar, nvar, ProdL);

                aProdL.discard_data();

                parallel_for_each(aProdL.extent, [=](index<2> idx) restrict(amp)

                {

                        int h = idx[0];

                        int k = idx[1];

                        double sum = 0.0;

                        for (int s = 0; s < ncase; s++)

                                sum += aXL(h, s) * aXL(k, s);

                        aProdL[idx] = sum;

                });

        }

        for (int i = 0; i < nvar; i++)

                for (int j = 0; j < nvar; j++)

                        XPX[i][j] = XPX[j][i] = ProdL[j + i * nvar];

 

        t1 = MyGetTime();

        cout << endl << "Elapsed time(1) = " << (t1 - t0) << "micro sec" << endl;

 

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

                for (int j = 0; j < nvar; j++)

                        cout << setw(15) << XPX[i][j];

                cout << endl;

        }

 

        double tt0 = MyGetTime();

        for (int i = 0; i < nvar; i++)

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

                        my_real tv = 0.0;

                        for (int k = 0; k < ncase; k++)

                                tv += X[i][k] * X[j][k];

                        XPX[i][j] = XPX[j][i] = tv;

                }

        double tt1 = MyGetTime();

        cout << endl << "Elapsed time = " << (tt1 - tt0) << "micro sec" << endl;

 

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

                for (int j = 0; j < nvar; j++)

                        cout << setw(15) << XPX[i][j];

                cout << endl;

        }

 

        char ck_c;

        cin >> ck_c;

 

        return 0;

}

 

上のプログラムを実行したときの画面を図1に示す。ただし、AMPによってGPUが用いられるようにソリューション構成を「Release」に設定してビルドしている。実行は「デバッグなしで開始(Ctrl + F5)」で行っている。

図1

 

AMPを用いた場合の計算時間は、いずれも0 microsecondであるが、AMPを用いない場合は16.7 microsecondsとなっている。これらの値は、そのときのPCの状態によって変わり得るが、図1の結果からAMPによる処理の並列化の効果が確認できる。

 

Up