64bit化Visual Studio 2010 ExpressでCUDA 7.5を使う

# はじめに

64bit化したVisual Studio 2010 ExpressとCUDA7.5でGPGPUプログラムをコンパイルする方法をまとめた。  

OS: Windows7SP1 64bit
VS: Visualt C++ 2010 Express + WindowsSDK7.1
CUDA: CUDA7.5
GPU: GeForce GT520

# 64bit化 Visual Studio 2010 Express のインストール

1. ### ISOイメージの入手
以前はダウンロードセンターから入手できたが、今は残念ながらリンクが切れている。今回は保存しておいたISOイメージを使用した。
※ 無料で登録できる[Visual Studio Dev Essentials](https://www.visualstudio.com/ja/vs/older-downloads/)にも2010はさすがに古いようで置いてなかった。

1. ### Visual C++ 2010 Expressのインストール
ISOイメージVS2010ExpressJPN.isoを[Virtual CloneDrive](http://www.elby.ch/)などでマウントしてインストーラSteup.htaを実行
ついでにVC#, VBもインストール

1. ### 阻害要因をアンインストール
WindowsSDK7.1のインストール時にエラーが発生するので、次の2つのライブラリをアンインストール
    - Microsoft Visual C++ 2010 x86 Redistributable
    - Microsoft Visual C++ 2010 x64 Redistributable  

1. ### .NET Frameworkのバージョンダウン
WindowsSDK7.1のインストーラが.NET Framework 4.6.1を認識してくれないので、4.6.1をアンインストールして4.5.2をインストール
    - Microsoft .NET Framework 4.6.1　→アンインストール
    - [Microsoft .NET Framework 4.5.2](https://www.microsoft.com/ja-JP/download/details.aspx?id=42641)　→インストール

1. ### Windows SDK for Windows7.1のインストール
ISOイメージ[GRMSDKX_EN_DVD.iso](https://www.microsoft.com/en-us/download/details.aspx?id=8442)をマウントしてインストーラsetup.exeを実行
Help Library ManagerはVisual C++など好きなものを入れておく

1. ### Visual Studio 2010 SP1のインストール
ISOイメージVS2010SP1dvd1.isoをマウントしてインストーラSetup.exeを実行

1. ### WindowsSDK7.1の更新プログラムを適用
[WindowsSDK7.1用Visual C++ 2010 SP1更新プログラム](https://www.microsoft.com/ja-jp/download/details.aspx?id=4422)をダウンロードして適用

1. ### .NET Framework 4.6の入れ直し
オフラインインストーラ[NDP462-KB3151800-x86-x64-AllOS-ENU.exe](https://www.microsoft.com/ja-jp/download/details.aspx?id=53344)をダウンロードして実行

1. ### 64bitコンパイルテスト
・Microsoft Visual C++ 2010 Expressを起動
・新しいプロジェクト→Win32コンソールアプリケーション
・ビルド→構成マネージャー→アクティブソリューションプラットフォーム→新規作成→x64
・プロジェクト→プロパティ→構成プロパティ→全般→プロットフォームツールセット→Windows7.1SDK
・サンプルコード

```cpp:sample_64bit.cpp
#include "stdafx.h"
#include <iostream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int  a = 0;
	cout << "address size = " << sizeof(&a) << " bytes" << endl;
	return 0;
}
```

・サンプルコードの実行

```shell:コマンドプロンプト
C:\...\x64\Debug> sample_64bit.exe
address size = 8 bytes
```

# CUDA7.5のインストール


1. ### NVIDIAドライバのインストール
[タウンロードページ](http://www.nvidia.co.jp/Download/index.aspx?lang=jp)からGPUボードに合った最新のドライバ(今回は381.65-desktop-win8-win7-64bit-international-whql.exe)をダウンロードして実行

2. ### CUDAのインストール
[ダウンロードページ](https://developer.nvidia.com/cuda-downloads)のLegacy CUDA ToolkitsからCUDA Toolkit 7.5をダウンロードして実行
・カスタム
・次の3つのみインストール(他は最新のドライバでインストール済み)
```
    CUDA Samples
    CUDA Toolkit
    CUDA Visual Studio Integration
```
・インストール場所はエクスプローラで事前に作成しておく
```
    CUDA Toolkit: C:\USR\CUDA\v7.5
    CUDA Samples: C:\USR\CUDA\v7.5\SDK
```

3. ### CUDA SDKのコンパイル
例としてdeviceQueryをコンパイル＆実行
・次のソリューションファイルをVisual C++ 2010 Expressで起動
```C:\USR\CUDA\v7.5\SDK\1_Utilities\deviceQuery\deviceQuery_vs2010.sln```
・プロジェクト→プロパティ→構成プロパティ→全般→プロットフォームツールセット→Windows7.1SDK
・ビルドしてコマンドプロンプトから実行

```shell:コマンドプロンプト
C:\Users\myname> cd C:\USR\CUDA\v7.5\SDK\bin\win64\Debug
C:\...\Debug> deviceQuery.exe
deviceQuery.exe Starting...

 CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 1 CUDA Capable device(s)

Device 0: "GeForce GT 520"
  CUDA Driver Version / Runtime Version          8.0 / 7.5
  CUDA Capability Major/Minor version number:    2.1
(途中省略)
Result = PASS
```

# 新規プロジェクトでCUDAを使う

1. ### 新規プロジェクト
・Microsoft Visual C++ 2010 Expressを起動
・新しいプロジェクト→Win32コンソールアプリケーション
・ビルド→構成マネージャー→アクティブソリューションプラットフォーム→新規作成→x64
・プロジェクト→プロパティ→構成プロパティ→全般→プロットフォームツールセット→Windows7.1SDK
・プロジェクト→ビルドのカスタマイズ→CUDA7.5をチェック

1. ### 構成プロパティ
・C/C++→追加のインクルードディレクトリ
`./;$(CudaToolkitIncludeDir);$(CudaToolkitDir)/common/inc`
・リンカー→追加のライブラリディレクトリ
`$(CudaToolkitLibDir)`
・リンカー→入力→追加の依存ファイル
`cudart_static.lib`

1. ### サンプルコード
・サンプルコード

```cpp:sample_main.cpp
#include "stdafx.h"
#include <cuda_runtime.h>
#include <stdio.h>
#include <vector>
using namespace std;

void saxpyz(float a, vector<float>& x, vector<float>& y, vector<float>& z);

int main(int argc, char* argv[]) {
    printf("%s starting...\n", argv[0]);
    const int n = 10000;
    vector<float> x(n, 0.0f);
    vector<float> y(n, 0.0f);
    vector<float> z(n, 0.0f);
    for(int i = 0; i < n; ++i) {
        x[i] = 0.2f;
        y[i] = 0.4f;
        z[i] = 0.0f;
    }
    const float a = 3.0f;

    saxpyz(a, x, y, z);

    float sum = 0.0f;
    for(int i = 0; i < n; ++i) {
        sum += z[i];
    }
    bool success = (sum - n)*(sum - n) < 0.1;
    printf("n=%d, sum=%.0f, result=%s\n", n, sum, (success ? "OK" : "NG"));
    return  success ? 0 : 1;
}
```
```cpp:sample_cuda.cu
#include <cuda.h>
#include <vector>
using namespace std;

__global__ void saxpyzKernel(int n, float a, float* x, float* y, float* z) {
  int i = blockIdx.x*blockDim.x + threadIdx.x;
  if(i < n) z[i] = a*x[i] + y[i];
}

void saxpyz(float a, vector<float>& x, vector<float>& y, vector<float>& z) {
    float* x_d;
    float* y_d;
    float* z_d;
    const int n = (int)z.size();
    cudaMalloc((void**)&x_d, n*sizeof(float));
    cudaMalloc((void**)&y_d, n*sizeof(float));
    cudaMalloc((void**)&z_d, n*sizeof(float));
    cudaMemcpy(x_d, &x[0], n*sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(y_d, &y[0], n*sizeof(float), cudaMemcpyHostToDevice);

    const int blockSize = 256;
    const int blockNum  = (n + blockSize - 1)/blockSize;
    saxpyzKernel<<<blockNum, blockSize>>>(n, a, x_d, y_d, z_d);
    cudaDeviceSynchronize();

    cudaMemcpy(&z[0], z_d, n*sizeof(float), cudaMemcpyDeviceToHost);
    cudaFree(z_d);
    cudaFree(y_d);
    cudaFree(x_d);
}
```

・サンプルコードの実行

```shell:コマンドプロンプト
C:\...\x64\Release> sample_cuda.exe
sample_cuda.exe starting...
n=10000, sum=10000, result=OK
```

# 参考URL
- [Visual C++ 2010 Expressで64bitコンパイル](http://d.hatena.ne.jp/torutk/20100927/p1)
- [Visual Studio Expressで64bit版DLLを作成してみる](http://nahitafu.cocolog-nifty.com/nahitafu/2014/04/visual-studio-e)
- [Visual Studio 2010(Windows 7 64bit + CUDA 4.0)の環境設定](http://cudasample.net/ins/vs201064.html)