PyOpenCLによるGPGPU入門
    Tokyo.SciPy#4 編
         @_likr
お前、誰よ

尾上 洋介(@_likr)

関西大学大学院 総合情報学研究科M2

 ナップザック問題とかやってる

PythonとかOCamlが好き
数理最適化とPython
CPLEX
   Python用ライブラリの提供

Gurobi
   インタラクティブシェルベースのUI

Pyomo
   モデリング用ライブラリ
発表概要
OpenCL / GPGPUの概要を解説

numpy.ndarrayライクのインタフェースで
細部を意識することなくGPGPUを利用

対応PyOpenCLのバージョンは
現行最新の2011.2と次期リリースの2012.1
GPGPUの基礎
GPGPUとは

GPGPU : General Purpose Computing on GPU
  GPUによる汎目的計算

スパコンなどでも利用

今後モバイルにも普及が進む(?)
GPU処理の特徴

数百∼数千のコア

高速なメモリアクセス

高い費用対効果

演算性能向上率が高い
OpenCLとは
ヘテロジニアス並列環境のためのフレームワーク

マルチコアCPU、GPU、DSP、FPGA

    Host            Device
    CPU            Processor
            Bus
   Memory          Memory
OpenCLを使う利点
各社のGPUが対応

 NVIDIA、AMD、Intel(Ivy Bridge)

CPUでも並列計算可能

非プラットフォーム依存

ピュアなC/C++
PyOpenCL

Andreas Klöckner 氏が開発したPythonから
OpenCL APIにアクセスするためのライブラリ

PyCUDAと同様のインタフェース
PyOpenCLを使う利点
ソースコードが簡潔

コンパイル不要

カーネルコードは通常のOpenCLと共通

Pythonの各種ライブラリを利用可能

GPGPU概念の習得、アプリケーション開発に最適
PyOpenCLの利用
インストール 1
OpenCLのインストール

 NVIDIA OpenCL
 AMD OpenCL
 Intel OpenCL
 Apple OpenCL
インストール 2
pip install numpy
pip install mako
pip install pyopencl
OpenCLのインストール先が標準以外の場合、
OpenGL連携を有効にするなどの場合は
~/.aksetup-defaults.py を作成
http://d.hatena.ne.jp/likr/20120604#1338786247
ContextとCommandQueue
create_some_context()              1   #!/usr/bin/env python
                                   2   # -*- coding: utf-8 -*-
実行時にデバイスと                          3
                                   4   import pyopencl as cl
プラットフォームを                          5   import numpy
                                   6
選択                                 7   # Contextの作成
                                   8 ctx = cl.create_some_context()
CommandQueue                       9
                                  10 # CommandQueueの作成

  host - device間の制御               11 queue = cl.CommandQueue(ctx)


        [onoue@localhost test]$ python sample.py
        Choose device(s):
        [0] <pyopencl.Device 'Tesla C2050' on 'NVIDIA CUDA' at 0xfd4000>
        [1] <pyopencl.Device 'GeForce GT 240' on 'NVIDIA CUDA' at 0xc85df0>
        Choice, comma-separated [0]:0
Arrayオブジェクト
device上のメモリ空間に確保される

numpy.ndarrayを経由してやり取り
  # host側メモリ
  host_a = numpy.random.rand(100)
  host_b = numpy.random.rand(100)

  # ndarrayからArrayオブジェクトを作成
  dev_a = clarray.to_device(queue, host_a)

  # hostからdeviceへデータ転送
  dev_a.set(host_b)

  # deviceからホストへデータ転送
  host_c = dev_a.get()
Arrayを直接生成する
        host - device間のデータ転送はコストがかかる

        簡単なデータ生成はdeviceで行うのが効率的

a   =   clarray.empty(queue, 100, numpy.float32)
b   =   clarray.zeros(queue, 100, numpy.float32)
c   =   clarray.arange(queue, 0, 1000, 10, dtype=numpy.float32)
d   =   clrandom.rand(queue, 100, numpy.float32)
同じ型のArrayを作る

size, shape, dtypeが同じArrayを作る

      e = clarray.empty_like(a)
      f = clarray.zeros_like(a)
fill

emptyで作成したArrayの初期化などに利用

      a.fill(2.0)
      clrandom.fill_rand(e)
演算子のサポート

二項演算はArray同士、    a+b     -a

Arrayとスカラー値で可能   a-b   abs(a)

** は浮動小数点数のみ     a*b   a ** b

bit演算は未対応        a/b   len(a)
リダクション

                  sum
GPU上で効率よく計算するには
テクニックが必要な処理が      min

関数として提供
                  max
性能比較
n = 100000
          numpy    Core i7 920   GTX 465
 a+b      0.0001     0.0004      0.0002
  a+s     0.0001     0.0004      0.0002
 a*b      0.0001     0.0004      0.0002
  a*s     0.0001     0.0002      0.0002
 sin(a)   0.0012     0.0004      0.0002
 sum(a)   0.0001     0.0027      0.0005
 dot(a)   0.0001     0.0027      0.0005
  rand    0.0016     0.0001      0.0008
n = 1000000
          numpy    Core i7 920   GTX 465
 a+b      0.0013     0.0004      0.0004
  a+s     0.0011     0.0004      0.0003
 a*b      0.0013     0.0004      0.0003
  a*s     0.0011     0.0002      0.0003
 sin(a)   0.0124     0.0004      0.0003
 sum(a)   0.0011     0.0028      0.0005
 dot(a)   0.0012     0.0026      0.0005
  rand    0.0158     0.0001      0.0041
傾向

データサイズが十分に大きければOpenCLが
速い、n < 1000000ぐらいではnumpyが速い

OpenCLではまだスケールしそう

数学関数、乱数生成はGPUが高速
チューニングtips
複合代入演算
a = a + bはa += bと書くとメモリが節約される

__idiv__は未実装なので、
スカラー値による除算はa *= 1./sなどで対応
  a = clarray.zeros(queue, 1000000, numpy.float32)
  start = time()
  for _ in range(10000):
      a += 2.0
  stop = time()
  print stop - start # 0.942986965179

  a = clarray.zeros(queue, 1000000, numpy.float32)
  start = time()
  for _ in range(10000):
      a = a + 2.0
  stop = time()
  print stop - start # 3.3877761364
数学関数の場合
clmathの関数は新しいメモリを確保する

インプレースに演算するにはElementwiseKernel
 from pyopencl.elementwise import ElementwiseKernel

 k = ElementwiseKernel(ctx, 'float* a', 'a[i] = sin(a[i]);')
 a = clarray.zeros(queue, 1000000, numpy.float32)
 start = time()
 for _ in range(10000):
     k(a)
 stop = time()
 print stop - start # 0.979743003845

 a = clarray.zeros(queue, 1000000, numpy.float32)
 start = time()
 for _ in range(10000):
     a = clmath.sin(a)
 stop = time()
 print stop - start # 3.34474182129
カーネル関数の適用
dataメソッドでBufferオブジェクトを取得
   import pyopencl as cl
   from pyopencl import clrandom
   import numpy

   ctx = cl.create_some_context(False)
   queue = cl.CommandQueue(ctx)

   prg = cl.Program(ctx, '''//CL//
   __kernel void add2(__global float* a)
   {
       const int i = get_global_id(0);
       a[i] += 2;
   }
   ''').build()

   a = clrandom.rand(queue, 1000, numpy.float32)

   prg.add2(queue, a.shape, None, a.data)
参考情報
kyoto.py
Pythonの勉強会

京都を中心とした関西圏で活動

活動予定

   8月4日 OSC Kyoto

   8月末頃 勉強会(予定)

https://groups.google.com/forum/#!forum/kyotopy
GPGPU勉強会
GPGPU関連技術、事例に関する
情報共有を目的

次回開催予定

 関東 夏頃

 関西 秋頃
参考資料 1
CUDA プログラミング入門(白山工業 森野編)
http://www.youtube.com/user/NVIDIAJapan

はじめてのCUDAプログラミング
ー脅威の開発環境[GPU+CUDA]を使いこなす!
http://www.amazon.co.jp/dp/4777514773

PyCUDAの紹介 - PythonとAWSですぐ始めるGPUコンピューティング
http://www.slideshare.net/likr/pycuda
参考資料 2
改訂新版 OpenCL入門 1.2対応
マルチコアCPU・GPUのための並列プログラミング
http://www.amazon.co.jp/dp/4844331728

The OpenCL Specification Version 1.2
http://www.khronos.org/registry/cl/specs/opencl-1.2.pdf

PyOpenCL
http://mathema.tician.de/software/pyopencl

PyOpenCLハンズオン in kyoto.py 資料
http://pykyoto201109-pyopencl.s3-website-ap-northeast-1.amazonaws.com/pyopencl.html
ご清聴ありがとございました

PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編