Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
20
Help us understand the problem. What is going on with this article?
@y_miyoshi

Alea GPUで簡単C# GPUプログラミング

More than 3 years have passed since last update.

gloops Advent Calendar 2016】17日目の記事になります。

はじめに

最近、機械学習(ロジスティック回帰やニューラルネットワーク)の勉強をしているんですが、機械学習では仮説関数やコスト関数(目的関数/損失関数)の計算を並列に計算すると効率が良いです。
関数を行列計算で表せることも多いので、3Dグラフィック処理(行列処理/並列処理)が得意なGPUが大きな効果を発揮します。
そこで、gloopsにとって中心的な言語であるC#でGPUプログラミングをやってみたくなり調べたところ、Alea GPUというライブラリがあったので試してみました。

この記事の元はこれなので、英語ですが詳しく正確に知りたい方をそちらを参照しましょう。

Alea GPUとは

Alea GPUはGPUの並列処理能力を活用したプログラミングができ、シンプルかつ効率的なライブラリです。Alea GPUランタイムはGPU上での実行とすべてのメモリ管理を効率的に処理してくれます。

GPUでの並列処理

GPUを利用した並列処理を行うのは簡単で、Gpu.Default.Forとして中身に処理を書くだけ。

var n = ...
Gpu.Default.For(0, n, i => 
{
    ...
});

注意しないといけないのは、ループ本体の処理は並列処理されるため、変数はお互いに独立していないといけません。

メモリ管理

GPUは独自のオンボードメモリを持っています。このメモリはGPUとCPU間のデータ入出力に利用され、両方からアクセスして変更することができます。
CPUとGPUのメモリは物理的に区別されているので、通常はプログラムによってGPUとCPUのメモリにコピーしなければならないんですが、Alea GPUでは自動で管理することができます。

自動でGPUメモリ管理をするためには[GpuManaged]アトリビュートをつける必要があります。

ParallelForAutoMemMgtTest.cs

namespace Samples.CSharp
{
    class ParallelForAutoMemMgtTest
    {
        [Test]
        public static void Unmanaged()
        {
         .....
        }

        [GpuManaged, Test]
        public static void Managed()
        {
         .....
        }
    }
}

[GpuManaged]をつけたManaged処理と、つけないUnmanaged処理では効率が違うので性能に大きく差がでます。
1d0eccda-a493-1115-9661-cb50f2b85fc0.png

CUDAプログラミングモデル

Alea GPUではCUDAというNVIDIAが提供するGPU向けの開発環境を利用しているので、NVIDIA製のGPUでしか動作しません。

スレッド、ブロック、グリッド、カーネル

スレッドはプログラム実行の最小単位です。CPUでは1CPU(スレッド)につき1スレッドですが、GPUでは1つのコアにいくつものスレッドを実行して並列性を高めます。

スレッドをグループ化したものをブロックといいます。

さらにブロックをグループ化したものをグリッドといいます。
d98b2809-4844-5bd9-02a1-8f59fd62be2b.png

CUDAプログラミングモデルを利用してGPU上で並列変換を実装します。
以下の図は、スレッドブロックのグリッドが配列の要素をどのように処理するかの概略図です。
d3b7d19d-3888-be2d-7839-b59d9785dc06.png

実際のカーネルはstaticメソッドとして記述します。以下の例では2つの要素を足すだけの処理です。
stride変数で1度にまとめて処理する数を設定して繰り返していきます。

Kernel.cs
private static void Kernel(int[] result, int[] arg1, int[] arg2)
{
    var start = blockIdx.x*blockDim.x + threadIdx.x;
    var stride = gridDim.x*blockDim.x;
    for (var i = start; i < result.Length; i += stride)
    {
        result[i] = arg1[i] + arg2[i];
    }
}

ジェネリクスやデリゲート、ラムダ式

ジェネリクスでカーネル関数を定義したり、ラムダ式でカーネルを記述することもできます。

private static double op(double v1, double v2)
{
 ...
}

Gpu.Default.For(0, result.Length, i => result[i] = op(arg1[i], arg2[i]););

////
gpu.Launch(TransformGeneric.Kernel, lp, result, arg1, arg2, (x, y) => x + y);

テストコード

テスト用のコードとして行列計算プログラムを実行しました1
https://gist.github.com/ymiyoshi/6aa7af62d07051016db70792c4ee7fcc
テストでは (180 x 60000)の行列A と(60000 x 270)の行列Bを計算しています。
以下の3パターン実装しています。

A. シングルスレッドでシーケンシャルに処理
B. マルチスレッドでパラレルに処理
C. GPUでパラレルに処理

並列処理の差が出やすいように60000を並列処理しています。

GPU並列計算.cs
            Gpu.Default.For(0, matARows, i =>
            {
                for (int j = 0; j < matBCols; j++)
                {
                    double temp = 0;
                    for (int k = 0; k < matACols; k++)
                    {
                        temp += matA[i, k] * matB[k, j];
                    }
                    result[i, j] = temp;
                }
            }); // Parallel.For
Executing sequential loop...
Sequential loop time in milliseconds: 20858

Executing parallel loop...
Parallel loop time in milliseconds: 5675

Executing GPU parallel loop...
Gpu Parallel loop time in milliseconds: 1642
Press any key to exit.

image (1).png

実行環境
CPU: Corei5-6500
GPU: GTX 1050

※行列のパターンによってはAのシーケンシャルが一番速かったり、Bのマルチスレッドが速かったりします。むしろGPUで性能が出るのは特殊な計算環境です。グラフィックボードへメモリを転送するオーバーヘッド以上にGPUでの並列処理による時間短縮が勝らないと効果は見られません。

デバッグ

GPUカーネル関数のデバッグはVisual Studioのデバッガではデバッグできません。
NVIDIA Nsight Visual Studio Editionを利用することでデバッグできます。
CUDA WarpWatchウィンドウを利用し、Name行に変数名(temp/i/j)を入れることで値を見ることができます。
2e37d2d6-82c2-9189-c3a2-f90304db91e1.png

通常のデバッガに比べると大変使いづらいですが、並列処理の値がみれるだけでも良しとします。

今回はここまで

Alea GPUの紹介は以上にします。まだまだ触り始めたばかりなので使いこなすには程遠いですが、敷居が高そうなGPUプログラミングをAlea GPUを利用することで簡単に始められることは分かったんじゃないかと思います。

明日の【gloops Advent Calendar 2016】は@ys_yamadaさんです!ワクワク!

参照URL

Alea GPU C# Programing Overview
第6回 「CUDAプログラミングモデル①」
Extreme Optimization

20
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
20
Help us understand the problem. What is going on with this article?