C ++によるGPUプログラミング

Gpu Programming With C



このガイドでは、C ++を使用したGPUプログラミングの能力について説明します。開発者はC ++で驚異的なパフォーマンスを期待でき、低水準言語でGPUの驚異的なパワーにアクセスすると、現在利用可能な最速の計算のいくつかを生み出すことができます。

要件

Linuxの最新バージョンを実行できるマシンはすべてC ++コンパイラをサポートできますが、この演習を進めるにはNVIDIAベースのGPUが必要です。 GPUがない場合は、Amazon WebServicesまたは選択した別のクラウドプロバイダーでGPUを利用したインスタンスを起動できます。







物理マシンを選択する場合は、NVIDIA独自のドライバーがインストールされていることを確認してください。これについての説明はここにあります: https://linuxhint.com/install-nvidia-drivers-linux/



ドライバーに加えて、CUDAツールキットが必要です。この例では、Ubuntu 16.04 LTSを使用しますが、ほとんどの主要なディストリビューションで利用可能なダウンロードが次のURLにあります。 https://developer.nvidia.com/cuda-downloads



Ubuntuの場合、.debベースのダウンロードを選択します。ダウンロードしたファイルには、デフォルトで.deb拡張子が付いていないため、名前を変更して、末尾に.debを付けることをお勧めします。次に、次のコマンドでインストールできます。





sudo dpkg -私package-name.deb

GPGキーをインストールするように求められる可能性があります。インストールする場合は、提供されている指示に従ってください。

それが済んだら、リポジトリを更新します。



sudo apt-get update
sudo apt-get install奇跡-と

完了したら、再起動してすべてが正しく読み込まれるようにすることをお勧めします。

GPU開発の利点

CPUは、さまざまな入力と出力を処理し、さまざまなプログラムニーズに対応するだけでなく、さまざまなハードウェア構成を管理するためのさまざまな機能を備えています。また、メモリ、キャッシング、システムバス、セグメンテーション、およびIO機能も処理するため、あらゆる取引のジャックになります。

GPUはその逆であり、非常に単純な数学関数に焦点を合わせた多くの個別のプロセッサが含まれています。このため、CPUよりも何倍も高速にタスクを処理します。スカラー関数(1つ以上の入力を受け取るが、単一の出力のみを返す関数)に特化することにより、極端な特殊化を犠牲にして、極端なパフォーマンスを実現します。

サンプルコード

サンプルコードでは、ベクトルを一緒に追加します。速度を比較するために、CPUバージョンとGPUバージョンのコードを追加しました。
gpu-example.cpp 以下の内容:

#include'cuda_runtime.h '
#含む
#含む
#含む
#含む
#含む

typedef時間:::クロノ:::high_resolution_clock時計;

#define ITER 65535

//ベクトル追加関数のCPUバージョン
空所vector_add_cpu((int *に、int *NS、int *NS、intNS)。 {{
int;

//ベクトル要素aとbをベクトルcに追加します
にとって ((= 0;<NS; ++)。 {{
NS[] =[] +NS[];
}
}

//ベクトル追加関数のGPUバージョン
__グローバル__空所vector_add_gpu((int *gpu_a、int *gpu_b、int *gpu_c、intNS)。 {{
int=threadIdx。NS;
// CUDAランタイムのため、forループは必要ありません
//このITER回スレッドします
gpu_c[] =gpu_a[] +gpu_b[];
}

int主要(()。 {{

int *に、*NS、*NS;
int *gpu_a、*gpu_b、*gpu_c;

= ((int *)。malloc((ITER* のサイズ((int)。)。;
NS= ((int *)。malloc((ITER* のサイズ((int)。)。;
NS= ((int *)。malloc((ITER* のサイズ((int)。)。;

// GPUにアクセスできる変数が必要です。
//したがって、cudaMallocManagedはこれらを提供します
cudaMallocManaged((gpu_a、ITER* のサイズ((int)。)。;
cudaMallocManaged((gpu_b、ITER* のサイズ((int)。)。;
cudaMallocManaged((gpu_c、ITER* のサイズ((int)。)。;

にとって ((int= 0;<ITER; ++)。 {{
[] =;
NS[] =;
NS[] =;
}

// CPU関数を呼び出して、時間を計ります
自動cpu_start=時計:::(()。;
vector_add_cpu((a、b、c、ITER)。;
自動cpu_end=時計:::(()。;
時間:::費用 << 'vector_add_cpu:'
<<時間:::クロノ:::duration_cast<時間:::クロノ:::ナノ秒>>((cpu_end-cpu_start)。カウント(()。
<< 'ナノ秒。NS';

// GPU関数を呼び出して、時間を計ります
//トリプルアングルブレーキは、CUDAランタイム拡張機能です。
//渡されるCUDAカーネル呼び出しのパラメーター。
//この例では、ITERスレッドで1つのスレッドブロックを渡します。
自動gpu_start=時計:::(()。;
vector_add_gpu<<<1、ITER>>> ((gpu_a、gpu_b、gpu_c、ITER)。;
cudaDeviceSynchronize(()。;
自動gpu_end=時計:::(()。;
時間:::費用 << 'vector_add_gpu:'
<<時間:::クロノ:::duration_cast<時間:::クロノ:::ナノ秒>>((gpu_end-gpu_start)。カウント(()。
<< 'ナノ秒。NS';

// GPU機能ベースのメモリ割り当てを解放します
cudaFree(()。;
cudaFree((NS)。;
cudaFree((NS)。;

// CPU機能ベースのメモリ割り当てを解放します
自由(()。;
自由((NS)。;
自由((NS)。;

戻る 0;
}

Makefile 以下の内容:

INC=-私/usr/ローカル/奇跡/含む
NVCC=/usr/ローカル/奇跡/午前/nvcc
NVCC_OPT= -std = c ++十一

全て:
$((NVCC)。$((NVCC_OPT)。gpu-example.cpp-またgpu-example

掃除:
-rm -NSgpu-example

例を実行するには、次のようにコンパイルします。

作る

次に、プログラムを実行します。

/gpu-example

ご覧のとおり、CPUバージョン(vector_add_cpu)は、GPUバージョン(vector_add_gpu)よりも実行速度がかなり遅くなっています。

そうでない場合は、gpu-example.cuのITER定義をより大きな数値に調整する必要があるかもしれません。これは、GPUのセットアップ時間がCPUを集中的に使用する小さなループよりも長いためです。 65535が私のマシンでうまく機能することがわかりましたが、マイレージは異なる場合があります。ただし、このしきい値をクリアすると、GPUはCPUよりも劇的に高速になります。

結論

C ++を使用したGPUプログラミングの紹介から多くのことを学んだことを願っています。上記の例では大きな成果は得られませんが、示されている概念は、GPUのパワーを解き放つためのアイデアを組み込むために使用できるフレームワークを提供します。