Ruby で GPU計算できる Cumo/NArray を使ってみた

RubyでGPUを使って行列計算ができるCumo/NArrayを使ってみたので紹介します。

# Cumo/NArray について

Cumo/NArray は、Numo/NArray と同じ書き方でGPUで行列計算ができるライブラリです。
（Numo/NArray はRubyで行列計算を提供するnumpyのようなライブラリです）
https://github.com/sonots/cumo

# インストール
公式の通りにインストールします。
`gem install cumo`　でもインストールできなくはないのでしょうが、cuda に関連するトラブルが発生するときの備えてマニュアルのインストールがよさそうです。

リポジトリのダウンロード

```
git clone https://github.com/sonots/cumo
```

CUDAのインストール状況にあわせて環境変数を設定します。

```
export CUDA_PATH="/usr/local/cuda"
export CPATH="$CUDA_PATH/include:$CPATH"
export LD_LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LD_LIBRARY_PATH"
export PATH="$CUDA_PATH/bin:$PATH"
export LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LIBRARY_PATH"
```


最初から `rake install` としてもよいのですが、一度 `rake compile` を実行するとエラーメッセージの状況を確認しやすくなります。

```
bundle install
bundle exec rake compile
# マルチコア
# bundle exec env MAKEFLAG=-j8 rake compile
bundle exec rake build
bundle exec rake install
```

正しくインストールできているか確認するためにテストを実行します。

```
bundle exec rake test
```
GPUの使用率が上昇しているか `watch -n 1 nvidia-smi` 等で確認してみてください。

# リファレンスなど
- Yard
 - [cumo](https://www.rubydoc.info/gems/cumo/)
 - [numo-narray](https://www.rubydoc.info/gems/numo-narray/)
- NumpyとNumo/Narrayの対応表
 - [Numo vs numpy](https://github.com/ruby-numo/numo-narray/wiki/Numo-vs-numpy)
 - [Numo vs ndarray](https://github.com/ruby-numo/numo-narray/wiki/Numo-vs-ndarray)
- 公式 RubyKaigi 2018スライド
 - [Fast Numerical Computing and Deep Learning in Ruby with Cumo ](https://speakerdeck.com/sonots/fast-numerical-computing-and-deep-learning-in-ruby-with-cumo)

# Numo/NArray への対応状況
Numo/NArray の機能にどこまで対応しているかはGithubのissueで公開されています。

- [Progress of CUDA kernelize (File Point of View)](https://github.com/sonots/cumo/issues/36)
- [Progress of CUDA kernelize (Feature point of View)](https://github.com/sonots/cumo/issues/37)

# 簡単な使い方

### 配列を生成する

```ruby

require 'cumo'

a = Cumo::SFloat.new(3,3).seq
b = Cumo::SFloat.new(3,3).seq + 1

p a
p b
```
実行結果

```
Cumo::SFloat#shape=[3,3]
[[0, 1, 2], 
 [3, 4, 5], 
 [6, 7, 8]]

Cumo::SFloat#shape=[3,3]
[[1, 2, 3], 
 [4, 5, 6], 
 [7, 8, 9]]
```

### 行列の和・積

```ruby
p a + b
p a * b
```
実行結果

```
Cumo::SFloat#shape=[3,3]
[[1, 3, 5], 
 [7, 9, 11], 
 [13, 15, 17]]

Cumo::SFloat#shape=[3,3]
[[0, 2, 6], 
 [12, 20, 30], 
 [42, 56, 72]]
```
ほぼNumo::NArrayと同様の使用感です。

# 簡単なベンチマーク
四則演算について簡単なベンチマークをしてみました。
なお、Benchmarkのちゃんとした取り方はわかっておらず、色々おかしいかもしれないので、参考程度に思ってください。[^1]
https://gist.github.com/kojix2/9d22fecf8c7c5f304c2311f005b7ecc1

1. Cumo/NArray
2. Numo/NArray
3. Ruby標準のMatrixライブラリ

について(1000,1000)の行列の四則演算を比較します。環境は以下のとおりです。

- OS Ubuntu 18.10
- CPU Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz
- GPU GeForce GTX 1070

結果です。とにかくRubyのMatrixが遅いのがわかります。
右端 RubyのMatrix の掛け算と割り算の結果がないのは、速いからではなく、時間がかかりすぎるため途中で実行をキャンセルしたからです。最低でもグラフに表示されている和・差のさらに10倍以上の時間がかかるようです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/144608/d0b17d55-cd24-3340-2e40-794112c7b629.png)

Ruby Matrix を取り除いた結果はこんな感じです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/144608/4c801a01-098c-0714-2294-96e540053534.png)

とりあえず、UInt8の演算や、和算に関しては、Cumo/NArray は爆速なんだなあと思います。
Arrayのサイズを変えるとまた違う結果が出るかもしれません。

[^1]: Cumoの公式のベンチマークがある　https://github.com/sonots/cumo/blob/master/bench/cumo_bench.rb