Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

GPGPU Seminar (PyCUDA)

5,569 views

Published on

PyCUDA - CUDA C以外の開発環境
長岡技術科学大学2015年度GPGPU講習会(2015年11月25日実施)

開発および講義には長岡技術科学大学のGPU搭載ラップトップPC(GROUSE2)を利用しています。

開発環境
Dell Precision M4600
CPU Intel Core i7 2.7GHz
メモリ 32GB
GPU NVIDIA Quadro 2000M
CUDA 6.5
Visual Studio Community 2013
Python 3.4

GPGPU講習会
・PyCUDA
http://www.slideshare.net/ssuserf87701/gpgpu-seminar-pycuda

・CUDA Fortranによる格子ボルツマン法の高速化
http://www.slideshare.net/ssuserf87701/gpgpu-seminar-accelerataion-of-lattice-boltzmann-method-using-cuda-fortran
・補足資料 GPGPUとCUDA Fortran
http://www.slideshare.net/ssuserf87701/gpgpu-seminar-gpgpu-and-cuda-fortran

・GPU最適化ライブラリの利用(その1,cuBLAS)
http://www.slideshare.net/ssuserf87701/gpgpu-seminar-gpu-accelerated-libraries-1-of-3-cublas
・GPU最適化ライブラリの利用(その2,cuSPARSE)
http://www.slideshare.net/ssuserf87701/gpgpu-seminar-gpu-accelerated-libraries-2-of-3-cusparse
・GPU最適化ライブラリの利用(その3,Thrust)
http://www.slideshare.net/ssuserf87701/gpgpu-seminar-gpu-accelerated-libraries-3-of-3-thrust

2015年度GPGPU実践基礎工学
・第1回 学際的分野における先端シミュレーション技術の歴史
http://www.slideshare.net/ssuserf87701/2015gpgpu1

2015年度GPGPU実践プログラミング
・第1回 GPGPUの歴史と応用例
http://www.slideshare.net/ssuserf87701/2015gpgpu1-59179080

2015年度先端GPGPUシミュレーション工学特論
・第1回 先端シミュレーションおよび産業界におけるGPUの役割
http://www.slideshare.net/ssuserf87701/2015gpgpu1-59180313

Published in: Engineering
  • Dating direct: ♥♥♥ http://bit.ly/369VOVb ♥♥♥
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Sex in your area is here: ♥♥♥ http://bit.ly/369VOVb ♥♥♥
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

GPGPU Seminar (PyCUDA)

  1. 1. 長岡技術科学大学電気電子情報工学専攻 出川智啓 GPGPU講習会 CUDA C以外の開発環境(PyCUDA)
  2. 2. 本講習会の目標  GPGPU先端シミュレーションシステムの使用方法の 習得  GPUの活用方法の修得  CUDAプログラミング技法の修得  並列計算手法の修得 2015/11/25GPGPU講習会2
  3. 3. 本日の内容 2015/11/25GPGPU講習会3  CUDA C以外のGPU開発環境  Python  Pythonについて  Pythonのライブラリ  numpy  移流方程式の計算  PyCUDAの使い方 既に使える人には必要ありません
  4. 4. お断り 2015/11/25GPGPU講習会4  デモは全てWindows, Python 3.4で行います  シェルの実行イメージはLinuxです  プロンプトは$です  grouseではPython 2.4が利用できます  Pythonはバージョン2と3があり,互換性がない処理もあ ります
  5. 5. Python
  6. 6. Python 2015/11/25GPGPU講習会6  インタプリタ言語  コンパイル不要  変数の型宣言不要  型は代入する右辺値から動的に決定  汎用プログラミング言語  Web開発や数値計算向けなどと用途を限定していない  海外では広く普及  GoogleやNASAでも利用  日本での知名度は上昇しているが今一歩
  7. 7. Python 2015/11/25GPGPU講習会7  利点  様々なライブラリが利用可能  読みやすいコードが書ける  メモリ管理が楽  フリー  欠点  コンパイルが不要な分,実行速度が遅い  MatlabやMathematicaと比べると開発環境が使いにくい  メーカーサポートは存在しない  専門的すぎる分野ではライブラリがない場合がある
  8. 8. PythonでHello World 2015/11/25GPGPU講習会8  中カッコが存在しない  インデントでスコープ(構造の深さ)を表現  海外では,「Pythonユーザは中カッコを嫌っている人もしくは 科学者である」とも言われている def main(): ˽˽˽˽print("hello world") if __name__ == "__main__": ˽˽˽˽main() #include<stdio.h> int main(void){ printf("hello world¥n"); return 0; } helloworld.py helloworld.c print("hello world") or
  9. 9. Pythonスクリプトの実行 2015/11/25GPGPU講習会9  .pyファイルの中身を解釈して実行  コンパイル不要 $ python helloworld.py⏎ hello world $ $ python⏎ >>> def main():⏎ ... ˽˽˽˽print("hello world")⏎ ... ⏎ >>> main()⏎ hello world >>> print("hello world")⏎ hello world >>> Ctrl + D (Ctrl+Dでpython shellを終了,WindowsはCtrl+Z)
  10. 10. Pythonスクリプトの実行 2015/11/25GPGPU講習会10  処理するプログラムを明記実行権限を付与すればファイ ル単体でスクリプトが実行可能 $ chmod u+x helloworld.py⏎ $ ./helloworld.py⏎ hello world $ #!/usr/bin/env python print("hello world") #!/usr/bin/env python def main(): ˽˽˽˽print("hello world") if __name__ == "__main__": ˽˽˽˽main()
  11. 11. コメント 2015/11/25GPGPU講習会11  1行コメント  #がコメント記号  #以降がコメントとして扱われる  複数行コメント  ダブルクオート3個を複数行コメントとして利用することもある def main(): ˽˽˽˽"""hello world ˽˽˽˽画面にhelloと表示""" ˽˽˽˽print("hello world") #この項目については後ほど説明 if __name__ == "__main__": ˽˽˽˽main() #main関数を呼出
  12. 12. コメント 2015/11/25GPGPU講習会12  複数行コメント(ドットストリング)の正しい使い方  関数の使い方をダブルクオート3個で囲んで記述 $ python⏎ >>> def main():⏎ ... ˽˽˽˽"""hello world⏎ ... ˽˽˽˽画面にhelloと表示"""⏎ ... ˽˽˽˽print("hello world")⏎ ... ⏎ >>> help(main)⏎ Help on function main in module __main__: main() hello world 画面にhelloと表示 (END) q (qキーでヘルプを終了) >>> 
  13. 13. 既存スクリプトの読込 2015/11/25GPGPU講習会13  import ファイル名(拡張子.pyは不要)  関数を呼び出す際は,ファイル名.関数名 $ python⏎ >>> import helloworld⏎ >>> helloworld.main()⏎ hello world >>> help(helloworld.main)⏎ Help on function main in module helloworld: main() hello world 画面にhelloと表示 (END) q >>> 
  14. 14. 変数 2015/11/25GPGPU講習会14  Pythonの変数  変数の型宣言が不要  名前を書いて数値を代入すれば自動で型を決定  異なる型を代入すると新たな型になる  Pythonの変数の型  int型(整数型)  float型(浮動小数点型)  complex型(複素数型)  文字列  論理型
  15. 15. int型 2015/11/25GPGPU講習会15  精度の制限がない  メモリのある限り大きな値を保持 $ python⏎ >>> a = 1⏎ >>> a⏎ 1 >>> a = ‐1⏎ >>> a⏎ ‐1 >>> ‐a⏎ 1 >>> import math⏎ >>> math.factorial(100)⏎ 9332621544394415268169923885626670049071596826438162146859296389 5217599993229915608941463976156518286253697920827223758251185210 916864000000000000000000000000
  16. 16. int型 2015/11/25GPGPU講習会16  2から36進数で記述可能  基本は10進数  0b, 0o, 0xをつけるとそれぞれ2, 8, 16進数表記 $ python⏎ >>> a = 0x11⏎ >>> a⏎ 17 >>> a = 0b01010⏎ >>> a⏎ 10 >>> bin(10)⏎ '0b01010' >>> hex(10)⏎ '0xa'
  17. 17. int型 2015/11/25GPGPU講習会17  2から36進数で記述可能  int('文字',基数)で10進数を生成 $ python⏎ >>> int('0')⏎ #省略すると10進数 0 >>> int('a')⏎ #10進数ではaは使えないのでエラー >>> int('a',16)⏎ #基数を指定 10 >>> int('z',36)⏎ #36進数は0~9,a~zを利用 35 >>> int('python2',36)⏎ 56524942334 >>> int('python3',36)⏎ 56524942335
  18. 18. float型 2015/11/25GPGPU講習会18  Cのdouble型と同じ  8バイト(64bit),数字の表現は53bit  有効桁数は10進数で約15桁  指数表記も可能  有効桁数や丸めの問題は回避できない $ python⏎ >>> a = 3.14⏎ >>> a⏎ 3.1400000000000001 >>> a = 1e‐2⏎ >>> a⏎ 0.01 >>> a = 0.1+0.1+0.1⏎ >>> a⏎ 0.30000000000000004
  19. 19. float型 2015/11/25GPGPU講習会19  int型同士の除算でfloat型が生成される場合  Python2系はint型を生成  小数点以下は切り捨て  Python3系はfloat型を生成 >>> 3/2⏎ 1.5 >>> 3//2⏎ 1 >>> 3/2⏎ 1
  20. 20. complex型 2015/11/25GPGPU講習会20  実数と虚数からなる  実数+虚数j として記述  変数名.realで実数を取り出す  変数名.imagで虚数を取り出す >>> a = 1+5j⏎ >>> a⏎ (1+5j) >>> a.real⏎ 1.0 >>> a.imag⏎ 5.0 >>>
  21. 21. complex型 2015/11/25GPGPU講習会21  四則演算も可能 >>> a=1+5j⏎ >>> a⏎ (1+5j) >>> b=2+6j⏎ >>> b⏎ (2+6j) >>> a+b⏎ (3+11j) >>> a‐b⏎ (‐1‐1j) >>> a*b⏎ (‐28+16j) >>> a/b⏎ (0.79999999999999993+0.099999999999999978j)
  22. 22. 数学関数 2015/11/25GPGPU講習会22  mathモジュール  int型とfloat型に対する演算(返値はfloat型)  cmathモジュール  complex型に対する演算(返値はcomplex型) >>> import math⏎ >>> math.sin(math.pi)⏎ 1.2246467991473532e‐16 >>> math.cos(math.pi)⏎ ‐1.0 >>> import cmath⏎ >>> a = cmath.sqrt(complex(1,1))⏎ >>> a⏎ (1.09868411346781+0.45508986056222733j) >>> a*a⏎ (1.0000000000000002+1j)
  23. 23. 文字列 2015/11/25GPGPU講習会23  シングルクオート''もしくはダブルクオート""で文字を囲 んで表現  Python2系ではASCII, Python3系ではUnicodeで保持 >>> a = 'string'⏎ >>> a⏎ 'string' >>> a*2⏎ 'stringstring' >>> b = 'other string'⏎ >>> a+b⏎ 'stringother string' >>> a = a+b⏎ >>> a⏎ 'stringother string' >>> len(a)⏎ 18
  24. 24. 文字列へのアクセス 2015/11/25GPGPU講習会24  インデックスを使ったアクセス  変数名[インデックス]  範囲は0~文字列の長さ‐1 (C言語と同じ)  正の整数は先頭からの位置  負の整数は終端からの位置 >>> a = 'string'⏎ #文字列の長さは6なので,インデックスの範囲は0~5 >>> a[0]⏎ 's' >>> a[1]⏎ 't' >>> a[6]⏎ #エラー >>> a[‐1]⏎ #実質の終端 'g' >>> a[‐4]⏎ 'r'
  25. 25. 文字列へのアクセス 2015/11/25GPGPU講習会25  スライスを使ったアクセス  変数名[開始位置:終了位置:ストライド]  : だけ書けば全範囲  C言語のfor文の処理に類似  for(i=開始位置;i<終了位置;i+=ストライド) >>> a = 'string'⏎ #文字列の長さは6なので,インデックスの範囲は0~5 >>> a[:]⏎ 'string' >>> a[5]⏎ 'g' >>> a[2:5]⏎ #5番目のインデックスを含むなら'g'まで出てくるはず 'rin' >>> a[2:]⏎ #終端まで表示したいときは終了インデックスを書かない 'ring'
  26. 26. 文字列の要素へのアクセス 2015/11/25GPGPU講習会26  スライスを使ったアクセス  変数名[開始位置:終了位置:ストライド]  : だけ書けば全範囲  C言語のfor文の処理に類似  for(i=開始位置;i<終了位置‐|負のインデックス|;i+=ストライド) >>> a[:‐1]⏎ #開始インデックスを書かなければ先頭から 'strin' >>> a[3:‐2]⏎ #for(i=3;i<6‐|‐2|;i+=1) 'i' >>> a[1:5:3]⏎ 'tn' >>> a[::2]⏎ #先頭から終端まで1文字飛ばしで表示 'srn'
  27. 27. 分岐 2015/11/25GPGPU講習会27  if  渡された値の真偽を評価し,実行する処理を切替 if 値1: ˽˽˽˽値1が真の時の処理 elif 値2: ˽˽˽˽値2が真の時の処理 elif 値3: ˽˽˽˽値3が真の時の処理 else: ˽˽˽˽上の全ての値が偽のときに実行する処理 if 値: #elifやelseは必須ではない ˽˽˽˽値が真の時の処理
  28. 28. 分岐 2015/11/25GPGPU講習会28  比較  a > b aがbより大きい  a >= b aがbより大きいか等しい  a < b aがbより小さい  a <= b aがbより小さいか等しい  a == b aとbが等しい  a != b aとbが等しくない  論理演算  条件1 and 条件2 条件1が真 かつ 条件2が真  条件1 or 条件2 条件1が真 もしくは 条件2が真  not 条件 条件が真でない
  29. 29. 分岐 2015/11/25GPGPU講習会29  比較結果はTrueかFalseで評価 >>> a=3⏎ >>> b=4⏎ >>> a>b⏎ False >>> a<=b⏎ True >>> a==b⏎ False >>> a!=b⏎ True >>> 0<=a<=b⏎ #比較はまとめて行うことが可能 True >>> flag = 0<=a<=b⏎ #flagは論理型変数(TrueかFalseを扱う) >>> flag⏎ True
  30. 30. 分岐 2015/11/25GPGPU講習会30  比較結果はTrueかFalseで評価 >>> a>=3 and b<=4⏎ True >>> a>3 or b<=4⏎ True >>> not a>3⏎ True >>> a = 'string'⏎ >>> b = 'string'⏎ >>> a==b⏎ #文字列同士の比較も可能 True >>> b = 'string2'⏎ >>> a==b⏎ False
  31. 31. 分岐 2015/11/25GPGPU講習会31  絶対値の計算  実行 def main(): a = 10 if a<0: a=‐a print(a) if __name__ == "__main__": main() abs.py $ python abs.py⏎
  32. 32. 分岐 2015/11/25GPGPU講習会32  if __name__ == "__main__":の意味  実行の仕方で__name__の値が変化  スクリプトを実行 __name__の値は"__main__"  shellからimport __name__の値はファイル名  スクリプトを実行したときはmain関数を実行  shellからimportされたときはmain関数を実行しない print("__name__ is"+__name__) $ python test.py⏎ __name__ is __main__ $ python⏎ >>> import test⏎ __name__ is test #importした時点で勝手に実行されている test.py
  33. 33. 繰り返し 2015/11/25GPGPU講習会33  for  処理をある一定回数繰り返す  C言語とは書き方が異なる  コンテナ  データの格納方法の一つ for ループ内変数 in コンテナ: ˽˽˽˽繰り返し実行する処理 else: ˽˽˽˽ループ終了後(forループを実行しなかった場合)に行う処理 >>> x = [0,1,2,3,4] >>> x[1] 1
  34. 34. 繰り返し 2015/11/25GPGPU講習会34 >>> x = [0,1,2,3,4]⏎ >>> for i in x:⏎ #xの最初から最後まで変化させながら処理を実行 ... ˽˽˽˽print(i)⏎ #print(x[i])と等価 ... ⏎ 0 1 2 3 4 >>> a = 'string'⏎ #文字列もコンテナの一種 >>> for i in a:⏎ #aの最初から最後まで変化させながら処理を実行 ... ˽˽˽˽print(i)⏎ #print(a[i])と等価 ... ⏎ s t r i n g
  35. 35. 繰り返し 2015/11/25GPGPU講習会35  決まった回数繰り返す  range()関数でコンテナを生成  range(終端) 0から終端‐1まで1ずつ変化するコンテナを生成  range(先頭,終端,ストライド)という使い方もできる(先頭から終端‐1ま でストライドずつ変化するコンテナを生成) >>> range(5)⏎ [0,1,2,3,4] >>> range(1,5)⏎ [1,2,3,4] >>> range(1,5,2)⏎ [1,3] >>> sum=0⏎ #1から10までの合計を計算 >>> for i in range(1,11):⏎ # ... ˽˽˽˽sum+=i⏎ # ... ⏎ >>> sum⏎ 55
  36. 36. 関数の定義と呼出 2015/11/25GPGPU講習会36  関数の定義  関数の名前  仮引数の名前  0個以上  返値(戻り値)  型の指定は不要  省略可能 def 関数名(仮引数,仮引数 ・・・(必要な数だけ書く)) ˽˽˽˽処理の内容 ˽˽˽˽return 戻り値
  37. 37. 絶対値を計算する関数の定義と呼出 2015/11/25GPGPU講習会37  定義位置はかなり柔軟 def abs(i): #C言語などを知っている人にとって標準的な位置 if i<0: return ‐i else: return i def main(): i = 10 absi = abs(i) print(absi) if __name__ == "__main__": main() abs.py
  38. 38. 絶対値を計算する関数の定義と呼出 2015/11/25GPGPU講習会38  定義位置はかなり柔軟 def main(): i = 10 absi = abs(i) print(absi) def abs(i): #関数を呼び出す位置より下で定義することも可能 if i<0: return ‐i else: return i if __name__ == "__main__": main() abs.py
  39. 39. 絶対値を計算する関数の定義と呼出 2015/11/25GPGPU講習会39  定義位置はかなり柔軟 def main(): i = 10 def abs(i): #関数の中で別の関数を定義する事も可能 if i<0: return ‐i else: return i absi = abs(i) print(absi) if __name__ == "__main__": main() abs.py
  40. 40. Pythonのライブラリ 2015/11/25GPGPU講習会40  数学関数のライブラリ(モジュール)  math, cmath  数値計算に有用なライブラリ(拡張モジュール)  別途インストールする必要がある  NumPy  多次元配列や行列の宣言と操作,演算  SciPy  線形代数演算,フーリエ変換,補間,画像処理など  SymPy  記号計算(数式の微積分,因数分解など)  matplotlib  グラフ描画
  41. 41. NumPy(Numerical Python) 2015/11/25GPGPU講習会41  NumPyを使う理由  多次元配列arrayが利用できる  C言語の配列に相当  配列の各要素の型は全て同じ  配列の形状が固定  配列の各要素が連続なメモリアドレスに配置  PyCUDAでもarrayを利用  CUDAのカーネルに配列を渡す場合  Pythonではarrayを利用して明示的に型を指定  NumPyの機能は全てSciPyで提供されている
  42. 42. NumPy 2015/11/25GPGPU講習会42  NumPyの利用方法  import numpy  NumPyの機能を利用するにはnumpy.を付ける必要がある  import numpy as np  numpyよりも記述量が少なく,他のモジュールと衝突することもない >>> import numpy >>> numpy.array([0,1,2,3]) array([0,1,2,3]) >>> import numpy as np >>> np.array([0,1,2,3]) array([0,1,2,3])
  43. 43. 1次元配列 2015/11/25GPGPU講習会43  定義とアクセス >>> import numpy as np⏎ >>> x = np.array([0,1,2,3])⏎ >>> print(x)⏎ [0 1 2 3] >>> x[3]⏎ 3 >>> for i in range(4):⏎ ... ˽˽˽˽print(x[i])⏎ ... ⏎ 0 1 2 3 >>>
  44. 44. 1次元配列 2015/11/25GPGPU講習会44  演算 >>> import numpy as np⏎ >>> x = np.array([0,1,2,3])⏎ >>> y = np.array([4,5,6,7])⏎ >>> print(x+y)⏎ [ 4 6 8 10] >>> print(x*y)⏎ [ 0 5 12 21] >>> print(x.dot(y))⏎ 38 >>> dot = 0⏎ >>> for i in range(4):⏎ ... ˽˽˽˽dot += x[i]*y[i]⏎ ... ⏎ >>> dot⏎ 38
  45. 45. 2次元配列 2015/11/25GPGPU講習会45  定義とアクセス >>> import numpy as np⏎ >>> A = np.array([[1,2,3],[4,5,6],[7,8,9]])⏎ >>> print(A)⏎ [[1 2 3] [4 5 6] [7 8 9]] >>> A[2][1]⏎ 8 >>> for i in range(3):⏎ ... ˽˽˽˽for j in range(3):⏎ ... ˽˽˽˽˽˽˽˽print(A[i][j])⏎ #格納順序はj方向が優先 ... ⏎ #行方向を固定して列方向に変化 1 2 : 9
  46. 46. 2次元配列 2015/11/25GPGPU講習会46  演算  2次元配列は行列ではない  行列として取り扱いたい場合はarrayではなくmatrixを利用 >>> import numpy as np⏎ >>> A = np.array([[1,2,3],[4,5,6],[7,8,9]])⏎ >>> B = np.array([[10,11,12],[13,14,15],[16,17,18]])⏎ >>> print(A+B)⏎ [[11 13 15] [17 19 21] [23 25 27]] >>> print(A*B)⏎ #行列‐行列積ではなく要素同士の積 [[ 10  22  36] [ 52  70  90] [112 136 162]] >>>
  47. 47. 配列情報の確認 2015/11/25GPGPU講習会47  変数名からarrayの情報を確認可能  ndim 次元  size 配列の要素数  shape 配列の形状(各次元の要素数)  dtype 配列要素の型 >>> import numpy as np⏎ >>> x = np.array([0,1,2,3])⏎ >>> x.ndim⏎ 1 >>> x.size⏎ 4 >>> x.shape⏎ (4,) >>> x.dtype⏎ dtype('int32')
  48. 48. ベクトル和の計算 2015/11/25GPGPU講習会48 import numpy as np def main(): N = 1024 a = np.ones(N) #N個の要素を持つ配列を確保し,1.0で初期化 b = np.empty_like(a) #aの形状と同じ配列を確保し,初期化はしない c = np.zeros_like(a) #aの形状と同じ配列を確保し,0.0で初期化 b[:]=2.  #b=2と書くと,配列bが破棄されてスカラ変数b(値は2)になる c=a+b #[:]を書いて配列の演算であることを示す方がよいと思う print(a) print(b) print(c) if __name__ == "__main__": main() vectoradd.py
  49. 49. 配列の作り方 2015/11/25GPGPU講習会49  形状を指定して一定値で初期化(あるいは未初期化)  引数で指定された形状の配列を生成  _likeを付けると,引数として既存のarrayをとる  型はfloat型(C言語のdouble型)  empty() 全要素を初期化しない  zeros() 全要素を0.0で初期化  ones() 全要素を1.0で初期化 >>> import numpy as np⏎ >>> a = np.empty(1024)⏎ >>> a⏎ array([  2.00229098e‐295,               nan,   1.12646967e‐321, ..., 1.00000000e+000,   1.00000000e+000,   1.00000000e+000]) >>> b = np.zeros_like(a)⏎ >>> b⏎ array([ 0.,  0.,  0., ...,  0.,  0.,  0.]) >>> b.dtype⏎ dtype('float64')
  50. 50. 配列の作り方 2015/11/25GPGPU講習会50  生成する数値の範囲と間隔を指定して生成  numpy.arange(始点,終点,間隔)  終点は含まれない  生成する数値の範囲と点数を指定して生成  numpy.linspace(始点,終点,点数)  終点を含む >>> import numpy as np⏎ >>> np.arange(0, 3, 0.5)⏎ array([ 0. ,  0.5,  1. ,  1.5,  2. ,  2.5]) >>> import numpy as np⏎ >>> np.linspace(0, 3, 7)⏎ array([ 0. ,  0.5,  1. ,  1.5,  2. ,  2.5,  3. ])
  51. 51. 差分法による1階微分の計算 2015/11/25GPGPU講習会51  計算機で微分を計算する方法の一つ  微分の定義  xの関数uについて,xだけ離れた2点間の傾きを計算し,2点の間隔を 無限小に近づけたときの極限  差分近似  関数をある間隔でサンプリング  その間隔xがuの変化に対して十分小さいと仮定 Δx xuΔxxu dx du )()(   Δx xuΔxxu dx du Δx )()( lim 0   
  52. 52. 差分法による1階微分の計算 2015/11/25GPGPU講習会52  複数の点の取り方が存在  中心差分を採用 u(x) x x=0 ・・・ x−x x x+x x Δx xuΔxxu )()(  Δx Δxxuxu )()(  Δx ΔxxuΔxxu 2 )()(  中心差分 前進差分 後退差分
  53. 53. 差分法による1階微分の計算 2015/11/25GPGPU講習会53  複数の点の取り方が存在  中心差分を採用 u(x) x i=0 ・・・ i−1 i i+1 x=0 ・・・ (i−1)x ix (i+1)x x サンプリングされた関数値 をarray uで保持
  54. 54. 差分法による1階微分の計算 2015/11/25GPGPU講習会54  複数の点の取り方が存在  中心差分を採用 u[i] i i=0 ・・・ i−1 i i+1 dx サンプリングされた関数値 をarray uで保持 u[i] u[i‐1] u[i+1] 中心差分 (u[i+1]‐u[i‐1])/(2*dx)
  55. 55. 差分法による1階微分の計算  計算領域内部  dudx[i]=(u[i+1]‐u[i‐1])/(2.*dx) dudx[i] u[i] + + + + 2015/11/25GPGPU講習会55 Δx2 1 ×−1
  56. 56. 差分法による1階微分の計算  境界条件(関数値が無いため処理を変更)  dudx[0] =(‐3*u[0] +4*u[1] ‐u[2] )/(2.*dx)  dudx[‐1]=( 3*u[‐1]‐4*u[‐2]+u[‐3])/(2.*dx)  2階微分値が一定と仮定して関数を補外した事に相当 dudx[i] u[i] + 2015/11/25GPGPU講習会56 Δx2 1 ×−3 ×4 ×−1
  57. 57. 差分法による1階微分の計算  境界条件(関数値が無いため処理を変更)  dudx[0] =(‐3*u[0] +4*u[1] ‐u[2] )/(2.*dx)  dudx[‐1]=( 3*u[‐1]‐4*u[‐2]+u[‐3])/(2.*dx)  2階微分値が一定と仮定して関数を補外した事に相当 dudx[i] u[i] + 2015/11/25GPGPU講習会57 Δx2 1 ×−4 ×3
  58. 58. 差分法による1階微分の計算 2015/11/25GPGPU講習会58 import numpy as np def main(): Nx = 5 #サンプリング点の数 Lx = 2.*np.pi #関数の範囲 dx = Lx/(Nx‐1) #サンプリング点の間隔 x = np.linspace(0, Lx, Nx) #x座標の値を持つarrayを作成 u = np.sin(x) #全てのxに対して関数値uを計算 dudx = diff(u,dx) #微分を計算 print(dudx) #結果を表示 print(np.cos(x)) #理論値を表示 differentiate.py
  59. 59. 差分法による1階微分の計算 2015/11/25GPGPU講習会59 def diff(u,dx): dudx = np.empty_like(u) dudx[ 0]   = (‐3.*u[ 0] + 4.*u[ 1] ‐ u[ 2] )/(2.*dx) dudx[1:‐1] = (u[2:]‐ u[:‐2])/(2.*dx) #スライスは終端を含まない dudx[‐1]   = ( 3.*u[‐1] ‐ 4.*u[‐2] + u[‐3] )/(2.*dx) return dudx if __name__ == "__main__": main() differentiate.py
  60. 60. NumPyの数学関数 2015/11/25GPGPU講習会60  sin,cosなどmathモジュールと同じ関数を提供  配列を引数に渡すと,全ての配列要素に対して関数を 適用し,結果を配列で返す >>> import numpy as np⏎ >>> import math⏎ >>> x = np.linspace(0,2*np.pi,5)⏎ >>> x⏎ array([ 0.        ,  1.57079633,  3.14159265,  4.71238898,   6.28318531]) >>> math.sin(x[0])⏎ 0.0 >>> math.sin(x)⏎ #エラー >>> np.sin(x)⏎ array([  0.00000000e+00,   1.00000000e+00,   1.22464680e‐16, ‐1.00000000e+00,  ‐2.44929360e‐16])
  61. 61. 実行結果 2015/11/25GPGPU講習会61  結果の確認  どの程度理論値と一致しているかを図で視覚的に確認  ファイル出力関数を書いて,gnuplotで・・・  グラフ描画ライブラリを利用 $ python differentiate.py⏎ [  1.27323954e+000   3.89817183e‐017  ‐6.36619772e‐001   1.38338381e‐321 1.27323954e+000] [  1.00000000e+00   6.12323400e‐17  ‐1.00000000e+00  ‐1.83697020e‐16 1.00000000e+00] $
  62. 62. matplotlib 2015/11/25GPGPU講習会62  Pythonから利用できるグラフ描画ライブラリ  2次元および3次元グラフの描画が可能  非常に多くの機能を備えており,大体のグラフが描ける  matplotlibのギャラリー  http://matplotlib.org/gallery.html  Python+NumPyと併用することで,計算しながら結果の 図示が可能  設定はプログラム中で命令を記述して決定  同じ言語で計算とグラフ描画を制御  C,Fortran+gnuplotよりも親和性が高い
  63. 63. matplotlib 2015/11/25GPGPU講習会63  matplotlibのインポート  from matplotlib import pyplot as pl  1次元データのプロット  プロットするデータやグラフの体裁の設定  pl.plot(x座標, 値, (ラベルやプロットの方法など))  pl.xlim(x座標の左端,右端)  pl.ylim(x座標の最小値,最大値)  pl.xlable(x座標の軸ラベル)  pl.ylable(y座標の軸ラベル)  グラフをプロット  pl.show()
  64. 64. 差分法による計算結果のプロット 2015/11/25GPGPU講習会64 import numpy as np from matplotlib import pyplot as pl def main(): :(これ以前は同じなので省略) pl.plot(x,dudx, label='Computational') X = np.linspace(0,Lx,101) pl.plot(X, np.cos(X), label = 'Analytical') pl.xlim(0,Lx) pl.ylim(‐1.3,1.3) pl.xlabel('$x$') pl.ylabel(r'$¥frac{d}{dx}¥sin x') pl.xticks([0,np.pi/2,np.pi,1.5*np.pi,2*np.pi], [r'$0$',r'$¥pi/2$',r'$¥pi$',r'$1.5¥pi$',r'$2¥pi$']) pl.legend(loc='best') pl.show() :(これ以後は同じなので省略) differentiate.py
  65. 65. 実行結果 2015/11/25GPGPU講習会65 $ python differentiate.py⏎ [  1.27323954e+000   3.89817183e‐017  ‐6.36619772e‐001   1.38338381e‐321 1.27323954e+000] [  1.00000000e+00   6.12323400e‐17  ‐1.00000000e+00  ‐1.83697020e‐16 1.00000000e+00] $
  66. 66. 移流方程式の計算 2015/11/25GPGPU講習会66  流体中の物質の移動を表す方程式  流れている水の中に落ちたインクの移動等  時刻t=0におけるuの分布(初期値)が既知  時間進行に伴い,uがどのように変化するかを計算  時間積分しながらuの分布を求める 0 ),(),(       x txu c t txu c : x方向速度
  67. 67. 移流方程式の計算 2015/11/25GPGPU講習会67  時間微分項の離散化  時間微分項を前進差分で離散化  右辺のt+tの項を移行 Δt txuΔttxu t u ),(),(     x u c t u      t u ΔttxuΔttxu    ),(),( 移流方程式を代入 x txu ΔtctxuΔttxu    ),( ),(),(
  68. 68. 移流方程式の計算 2015/11/25GPGPU講習会68  連続系  離散系  t秒後の値 0 ),(),(       x txu c t txu 0 2 11 1       Δx uu c Δt uu n i n i n i n i Δx uu Δtcuu n i n in i n i 2 111   
  69. 69. 計算手順 1. 計算条件の決定  計算領域の大きさLx,分割数(離散点の個数)Nx,離散点の間隔x  計算時間間隔t 2. 初期値の決定  uの初期分布の決定 3. 差分値の計算  uの分布からx方向の1階微分値を計算  境界条件に基づいて境界の値を決定  t秒後のuを計算 5. 3.にもどり,t秒後のuを基に1階微分の計算と積分を,所定 の時間まで繰り返す GPGPU講習会69 2015/11/25
  70. 70. 移流方程式の計算 2015/11/25GPGPU講習会70 import numpy as np import time def main(): Lx = 2. #計算領域の長さ Nx = 2**20 #離散点の数 dx = Lx/(Nx‐1) #離散点の間隔 C  = 1. #移流速度 dt = 0.01*dx/C #時間積分の間隔 Lt = 2. #計算終了時間 Nt = int(Lt/dt) #積分回数 t = 0. #初期値の設定 x = np.linspace(0, Lx, Nx).astype(np.float32) # uNew = ( 0.5*(1‐np.cos(2.*np.pi*(x‐C*t)/Lx)) )**3 # uOld = np.empty_like(uNew) X = np.linspace(0,Lx, (Nx‐1)*10+1) #解析解の設定 u_analytical = ( 0.5*(1‐np.cos(2.*np.pi*(X‐C*t)/Lx)) )**3 # start_s = time.time() for n in range(0,Nt): uOld = uNew.copy() uNew = uOld ‐ C*dt*diff(uOld,dx) #移流方程式を時間積分 end_s = time.time() print('processing time',(end_s‐start_s)*1e+3/Nt,'msec/step') t = (Nt+1)*dt u_analytical = ( 0.5*(1‐np.cos(2.*np.pi*(X‐C*t)/Lx)) )**3 3 2 cos1 2 1                          xL x u  3 exact )(2 cos1 2 1                           xL tCx u  convection.py
  71. 71. 移流方程式の計算 2015/11/25GPGPU講習会71 def diff(u, dx): Nx = u.size d_u_dx = np.zeros(Nx) d_u_dx[0]    = (u[1] ‐ u[‐2] )/(2.*dx) #周期境界条件を採用 d_u_dx[1:‐1] = (u[2:]‐ u[:‐2])/(2.*dx) d_u_dx[‐1]   = (u[1] ‐ u[‐2] )/(2.*dx) #周期境界条件を採用 return d_u_dx if __name__ == '__main__': main() convection.py
  72. 72. 実行結果 2015/11/25GPGPU講習会72 t=0 s t=1 s t=2 s
  73. 73. PyCUDA
  74. 74. PyCUDA 2015/11/25GPGPU講習会74  PythonからCUDAを利用するためのモジュール群  CUDAのPythonバインディングと表現される  現在の所,PyCUDAが最も利用しやすい  CUDA Cのカーネルの取込みが可能  CUDAのDriver APIを全て利用できる  自動でエラーチェックを行い,Pythonのエラーとして表示  C++で書かれており,高速に動作  cuBLASなどのライブラリを直接呼ぶ事は不可能  BLAS‐1については配列に対する処理として再実装されている
  75. 75. PyCUDAのデモ 2015/11/25GPGPU講習会75  PyCUDAのチュートリアルにあるdemo.py  4×4の配列に乱数を代入し,それをGPUで2倍して返却 import pycuda.gpuarray as gpuarray import pycuda.driver as cuda import pycuda.autoinit import numpy a_gpu = gpuarray.to_gpu(numpy.random.randn(4,4).astype(numpy.float32)) a_doubled = (2*a_gpu).get() print(a_doubled) print(a_gpu) [[ 0.51360393  1.40589952  2.25009012  3.02563429] [‐0.75841576 ‐1.18757617  2.72269917  3.12156057] [ 0.28826082 ‐2.92448163  1.21624792  2.86353827] [ 1.57651746  0.63500965  2.21570683 ‐0.44537592]] [[ 0.25680196  0.70294976  1.12504506  1.51281714] [‐0.37920788 ‐0.59378809  1.36134958  1.56078029] [ 0.14413041 ‐1.46224082  0.60812396  1.43176913] [ 0.78825873  0.31750482  1.10785341 ‐0.22268796]] PyCUDA/demo.py
  76. 76. CUDA Cで書くと 2015/11/25GPGPU講習会76 #include<stdio.h> #include<stdlib.h> #define nbytes (4*4*sizeof(float)) __global__ void doublify(float *a){ int i = blockIdx.x*blockDim.x + threadIdx.x; a[i] *= 2.0f; } int main(void){ float *a = (float *)malloc(nbytes); float *a_gpu; cudaMalloc((void **)&a_gpu , nbytes); for(int i=0;i<4*4;i++) a[i] = (float)rand()/RAND_MAX; cudaMemcpy(a_gpu, a, nbytes, cudaMemcpyHostToDevice); doublify<<<4,4>>>(a_gpu); float *a_doubled = (float *)malloc(nbytes); cudaMemcpy(a_doubled, a_gpu, nbytes, cudaMemcpyDeviceToHost); return 0; }
  77. 77. PyCUDAのデモ 2015/11/25GPGPU講習会77  これがPyCUDAですと言われても・・・  Pythonの知識との対応  CUDA Cの知識との対応  段階を踏んでPyCUDAの使い方を確認 1. PyCUDAからGPU情報を取得 2. mathモジュールやnumpyモジュールの関数をCUDAに置換 1. cumathモジュール 2. Elementise Operation 3. CUDA Cのkernelの取込 4. Pythonスクリプト内のパラメータをCUDA kernelに反映 5. 少し進んだ使い方 が不可欠
  78. 78. GPU情報の取得 2015/11/25GPGPU講習会78  処理系がGPUの情報にアクセスできているかの確認  pgaccelinfoやdeviceQueryに相当  複数GPUを利用する場合やGPUの世代に応じてパラメータを 変更する場合にも必要  PyCUDAのモジュールのimport  import pycuda.autoinit  初期化や解放を自動で行う場合に利用  特に理由がない限り利用した方がよい(Pythonらしさを維持)  import pycuda.driver as cuda  CUDAのAPIを利用
  79. 79. GPU情報の取得 2015/11/25GPGPU講習会79  実行結果 1 device(s) found. Deivce : Quadro 2000M Compute Capalibity : 2.1 Total Memory Size : 2048 MB import pycuda.autoinit import pycuda.driver as cuda print("%d device(s) found." % cuda.Device.count()) for id in range(cuda.Device.count()): dev = cuda.Device(id) print("Deivce : %s" % dev.name()) print("¥t Compute Capalibity : %d.%d" % dev.compute_capability()) print("¥t Total Memory Size : %s MB" % (dev.total_memory()//(2**20))) PyCUDA/device.py
  80. 80. GPU情報の取得 2015/11/25GPGPU講習会80  PyCUDAが取得できる全情報の表示  属性(attribute)を取得し,その名称と対応する値を表示  Pythonのfor文のよい練習 import pycuda.autoinit import pycuda.driver as cuda print("%d device(s) found." % cuda.Device.count()) for id in range(cuda.Device.count()): dev = cuda.Device(id) print("Deivce : %s" % dev.name()) print("¥t Compute Capalibity : %d.%d" % dev.compute_capability()) print("¥t Total Memory Size : %s MB" % (dev.total_memory()//(2**20))) attrs = dev.get_attributes() for key, value in attrs.items(): print("¥t %s : %s" % (str(key), str(value)) ) PyCUDA/devicequery.py
  81. 81. GPU情報の取得 2015/11/25GPGPU講習会81  実行結果 1 device(s) found. Deivce : Quadro 2000M Compute Capalibity : 2.1 Total Memory Size : 2048 MB MAX_THREADS_PER_BLOCK : 1024 MAX_BLOCK_DIM_X : 1024 MAX_BLOCK_DIM_Y : 1024 MAX_BLOCK_DIM_Z : 64 MAX_GRID_DIM_X : 65535 MAX_GRID_DIM_Y : 65535 MAX_GRID_DIM_Z : 65535 :(中略) STREAM_PRIORITIES_SUPPORTED : 0 GLOBAL_L1_CACHE_SUPPORTED : 1 LOCAL_L1_CACHE_SUPPORTED : 1 MAX_SHARED_MEMORY_PER_MULTIPROCESSOR : 49152 MAX_REGISTERS_PER_MULTIPROCESSOR : 32768 MANAGED_MEMORY : 0 MULTI_GPU_BOARD : 0 MULTI_GPU_BOARD_GROUP_ID : 0
  82. 82. NumPyの置換(cumathモジュール) 2015/11/25GPGPU講習会82  numpyの数学関数  sin,cosなどmathモジュールと同じ関数を提供  配列を引数に渡すと,全ての配列要素に対して関数を適用し, 結果を配列で返す  配列を宣言→全要素に値を設定→全要素に同じ処理を実行  GPU向きの処理 import numpy as np N  = 2**20 Lx = 2*np.pi x = np.linspace(0, Lx, N).astype(np.float32) #C言語のfloat型として宣言 y = np.sin(x)
  83. 83. NumPyの置換(cumathモジュール) 2015/11/25GPGPU講習会83  配列の型指定  numpyのarrayはC言語のdouble型  C言語のfloat型を利用する場合はastype(型)で型を指定  float型 np.float32  double型 np.float64  型は配列変数名.dtypeで確認 >>> import numpy as np >>> x = np.linspace(0, 1, 101) #何も指定しないとC言語のdouble型に相当 >>> x.dtype dtype('float64') >>> x=np.linspace(0,1,101).astype(np.float32) #C言語のfloat型を指定 >>> x.dtype dtype('float32') >>> x = x.astype(np.float32) >>> x.dtype dtype('float32')
  84. 84. GPUで処理を実行する流れ 2015/11/25GPGPU講習会84  GPUのメモリ上に配列を確保  CPUのデータをGPUへ転送  GPU上で関数を呼び出して処理を実行  GPUから結果を読み出す
  85. 85. cumathモジュールを利用したy=sin(x) 2015/11/25GPGPU講習会85  GPU上に確保する配列はpycuda.gpuarrayを利用  GPUの初期化やメモリの確保を隠蔽  CPU‐GPU間通信はpycuda.gpuarrayの機能を利用  数学関数をcumath.sinに置換 import numpy as np import pycuda.gpuarray as gpuarray import pycuda.cumath as cumath import pycuda.autoinit N  = 2**20 Lx = 2*np.pi x = np.linspace(0, Lx, N).astype(np.float32) dev_x = gpuarray.to_gpu(x) dev_y = cumath.sin(dev_x) y = dev_y.get() PyCUDA/cumath.py
  86. 86. cumathモジュールを利用したy=sin(x) 2015/11/25GPGPU講習会86  GPU上で変数を確保+CPU→GPUのコピー  gpuarray変数=gpuarray.to_gpu(array変数)  GPU→CPUのコピー  array変数=gpuarray変数.get() import numpy as np import pycuda.gpuarray as gpuarray import pycuda.cumath as cumath import pycuda.autoinit N  = 2**20 Lx = 2*np.pi x = np.linspace(0, Lx, N).astype(np.float32) dev_x = gpuarray.to_gpu(x) #デバイス変数dev_xを動的に確保しつつxをコピー dev_y = cumath.sin(dev_x) #cumathモジュールでdev_xの要素ごとにsin()を計算 y = dev_y.get() #デバイス変数dev_yの内容をyにコピー
  87. 87. PythonからCUDAを利用 2015/11/25GPGPU講習会87  GPUのメモリ上に配列を確保  numpy.arrayの代わりにpycuda.gpuarrayを利用  CPUのデータをGPUへ転送  pycudaの機能を利用(CUDA CのAPIを隠蔽)  GPU上で関数を呼び出して処理を実行  cumathモジュールを利用  GPUから結果を読み出す  pycudaの機能を利用(CUDA CのAPIを隠蔽) 実行したい処理がcumathモジュール に無い場合はどうする?
  88. 88. Elementwise Operation 2015/11/25GPGPU講習会88  配列の全要素に対して同じ処理を行う  ‐wise 名詞や副詞の後ろに付けて,方法や方向を表す  clockwise 時計回り(右回り)  lengthwise 縦方向  Elementwise Operationの利用  ElementwiseKernelをimport  1要素に対する処理と引数を記述したElementwisekernel のオブジェクトを定義  通常の関数のように関数名+実引数(gpuarray)を指定して 実行
  89. 89. Elementwise Operationを利用したy=sin(x) 2015/11/25GPGPU講習会89 import numpy as np import pycuda.gpuarray as gpuarray from pycuda.elementwise import ElementwiseKernel #pycuda.cumathから置き換え import pycuda.autoinit N  = 2**20 Lx = 2*np.pi sin_kernel = ElementwiseKernel( "float *y, float *x", #引数 "y[i] = sin(x[i])", #1要素に対する処理(引数の全要素に適用) "elementwise_sin") #名前 x = np.linspace(0, Lx, N).astype(np.float32) dev_x = gpuarray.to_gpu(x) dev_y = gpuarray.empty_like(dev_x) sin_kernel(dev_y, dev_x) y = dev_y.get() PyCUDA/elementwise1.py
  90. 90. 2変数以上のElementwise Operation 2015/11/25GPGPU講習会90 import numpy as np import pycuda.gpuarray as gpuarray from pycuda.elementwise import ElementwiseKernel #pycuda.cumathから置き換え import pycuda.autoinit N  = 2**20 x = np.ones(N).astype(np.float32) y = np.zeros_like(x) z = np.zeros_like(x) y[:]=2.0 saxpby_kernel = ElementwiseKernel( "float a, float *x, flato b, float *y, float *z", #引数 "z[i] = a*x[i]+b*y[i]", #1要素に対する処理(引数の全要素に適用) "linear_combination") #名前 dev_x = gpuarray.to_gpu(x) dev_y = gpuarray.to_gpu(y) saxpby_kernel(1.0, dev_x, 2.0, dev_y, dev_z) z = dev_z.get() PyCUDA/elementwise2.py
  91. 91. PythonからCUDAを利用 2015/11/25GPGPU講習会91  GPUのメモリ上に配列を確保  numpy.arrayの代わりにpycuda.gpuarrayを利用  CPUのデータをGPUへ転送  pycudaの機能を利用(CUDA CのAPIを隠蔽)  GPU上で関数を呼び出して処理を実行  cumathモジュールを利用  ElementwiseKernelの作成  GPUから結果を読み出す  pycudaの機能を利用(CUDA CのAPIを隠蔽) もっと複雑な処理を実行したり, 既存のCUDAのプログラムを再利 用したいときはどうする?
  92. 92. CUDA Cプログラムの取り込み 2015/11/25GPGPU講習会92  SourceModule  ElementwiseKernelには1要素に対する処理を記述  SourceModuleにはCUDA Cのカーネルそのものを記述  SourceModuleの利用  SourceModuleをimport  CUDA Cカーネルを複数行コメントで記述したSourceModule のオブジェクトを生成  SourceModuleオブジェクトから利用するカーネルを選択  CUDA Cと同様に,カーネル名,実引数,実行時の並列度を指 定して実行
  93. 93. SourceModuleを利用したy=sin(x) 2015/11/25GPGPU講習会93 import numpy as np import pycuda.gpuarray as gpuarray from pycuda.compiler import SourceModule #ElementwiseKernelから置き換え import pycuda.autoinit N  = 2**20 Lx = 2*np.pi module = SourceModule(""" __global__ void sin_kernel(float *y, float *x) { int i = blockIdx.x*blockDim.x + threadIdx.x; y[i] = sin(x[i]); } """) sin = module.get_function("sin_kernel") #実行するカーネルを決定 block = (256, 1, 1) #並列実行時のパラメータを設定 grid  = (N//block[0], 1, 1) #単純な除算を行うとパラメータがfloatになり,エラーが発生 x = np.linspace(0, Lx, N).astype(np.float32) dev_x = gpuarray.to_gpu(x) dev_y = gpuarray.empty_like(dev_x) sin(dev_y, dev_x, grid = grid, block = block) y = dev_y.get() PyCUDA/sourcemdule.py
  94. 94. PythonからCUDAを利用 2015/11/25GPGPU講習会94  GPUのメモリ上に配列を確保  numpy.arrayの代わりにpycuda.gpuarrayを利用  CPUのデータをGPUへ転送  pycudaの機能を利用(CUDA CのAPIを隠蔽)  GPU上で関数を呼び出して処理を実行  cumathモジュールを利用  ElementwiseKernelの作成  SourceModuleを利用してCUDA Cのカーネルを取込  GPUから結果を読み出す  pycudaの機能を利用(CUDA CのAPIを隠蔽) CUDA CのAPIを明 示的に利用したい CUDA CのAPIを明 示的に利用したい CUDA CのAPIを明 示的に利用したい
  95. 95. CUDA APIの利用 2015/11/25GPGPU講習会95 import numpy as np import pycuda.gpuarray as gpuarray from pycuda.compiler import SourceModule import pycuda.autoinit import pycuda.driver as cuda : : (ここは同じなので省略) : x = np.linspace(0, Lx, N).astype(np.float32) dev_x = cuda.mem_alloc(x.nbytes) #CUDAのAPIを利用してメモリ確保 dev_y = cuda.mem_alloc(x.nbytes) #確保された変数dev_x,dev_yはgpuarrayではない cuda.memcpy_htod(dev_x, x) #CUDAのAPIを利用してメモリ転送(host‐>device) sin(dev_y, dev_x, grid = grid, block = block) y = np.empty_like(x) cuda.memcpy_dtoh(y, dev_y) #CUDAのAPIを利用してメモリ転送(host‐>device) print(y) PyCUDA/sourcemdule_api.py
  96. 96. PythonからCUDAを利用 2015/11/25GPGPU講習会96  GPUのメモリ上に配列を確保  numpy.arrayの代わりにpycuda.gpuarrayを利用  CUDA APIでメモリ確保(gpuarrayではないので制限がある)  CPUのデータをGPUへ転送  pycudaの機能を利用  CUDA APIを利用してコピー  GPU上で関数を呼び出して処理を実行  cumathモジュールを利用  ElementwiseKernelの作成  SourceModuleを利用してCUDA Cのカーネルを取込  GPUから結果を読み出す  pycudaの機能を利用  CUDA APIを利用してコピー
  97. 97. CUDA Cプログラムの取り込み 2015/11/25GPGPU講習会97  SourceModuleを利用してCUDA Cカーネルを取り込む  Hello Threads  GPUを使って並列実行できているかを確認  GPGPU関連講義や講習会でもこれまで取り扱ってきた内容 GPU Streaming  Multiprocessor CUDA  Core ハードウェア構成 並列に実行する 処理 スレッドの集 まり スレッド 並列化の階層 Grid Block Thread CUDA
  98. 98. GPUの並列化の階層  グリッド-ブロック-スレッドの3階層  各階層の情報を参照できる変数  x,y,zをメンバにもつdim3型構造体  グリッド(Grid)  gridDim グリッド内にあるブロックの数  ブロック(Block)  blockIdx ブロックに割り当てられた番号  blockDim ブロック内にあるスレッドの数  スレッド(Thread)  threadIdx スレッドに割り当てられた番号 2015/11/25GPGPU講習会98
  99. 99. CUDA Cカーネルの取り込み 2015/11/25GPGPU講習会99  Hello Threads from pycuda.compiler import SourceModule import pycuda.autoinit module = SourceModule(""" #include<stdio.h> //#includeも利用可能 __global__ void hello_thread() { printf("hello thread¥¥n"); //改行は¥¥n } __global__ void hello_threads() { printf("gridDim.x=%d,blockIdx.x=%d,blockDim.x=%d,threadIdx.x=%d¥¥n", gridDim.x, blockIdx.x, blockDim.x, threadIdx.x); } """) hello = module.get_function("hello_threads") block = (4, 1, 1) grid  = (2, 1, 1) hello(grid = grid, block = block) PyCUDA/HelloThreads.py
  100. 100. CUDA Cカーネルの取り込み 2015/11/25GPGPU講習会100  実行結果 gridDim.x=2,blockIdx.x=0,blockDim.x=4,threadIdx.x=0 gridDim.x=2,blockIdx.x=0,blockDim.x=4,threadIdx.x=1 gridDim.x=2,blockIdx.x=0,blockDim.x=4,threadIdx.x=2 gridDim.x=2,blockIdx.x=0,blockDim.x=4,threadIdx.x=3 gridDim.x=2,blockIdx.x=1,blockDim.x=4,threadIdx.x=0 gridDim.x=2,blockIdx.x=1,blockDim.x=4,threadIdx.x=1 gridDim.x=2,blockIdx.x=1,blockDim.x=4,threadIdx.x=2 gridDim.x=2,blockIdx.x=1,blockDim.x=4,threadIdx.x=3
  101. 101. CUDA Cカーネルの取り込み 2015/11/25GPGPU講習会101  #includeが利用可能  別ファイルに記述したカーネルを取り込むことができる(はず)  既存のCUDAのカーネルが書かれたソースの取込  標準のincludeディレクトリ以外にあるファイルをinclude  #include"ファイル名"で指定  SourceModuleの引数include_dirsで場所を指定  ディレクトリパスが長いとエラーがでることがある  includeするファイルの名前がkernel.cuだとエラーが発生  PyCUDAは一時的にkernel.cuというファイルを作るらしい
  102. 102. CUDA Cカーネルの取り込み 2015/11/25GPGPU講習会102 from pycuda.compiler import SourceModule import pycuda.autoinit module = SourceModule(""" #include "hello_kernel.cu" //kernel.cuという名前は利用不可 """,include_dirs=['C:¥Python34¥PyCUDA'])#hello_kernel.cuの場所を指定 hello = module.get_function("hello_threads") #("hello_thread") block = (4, 1, 1) grid  = (2, 1, 1) hello(block = block, grid = grid) #include<stdio.h> __global__ void hello_thread() { printf("hello thread¥n"); } __global__ void hello_threads() { printf("gridDim.x=%d,blockIdx.x=%d,blockDim.x=%d,threadIdx.x=%d¥n", gridDim.x, blockIdx.x, blockDim.x, threadIdx.x); } PyCUDA/HelloThreads_incl.py PyCUDA/hello_kernel.cu
  103. 103. CUDA Cプログラムの取り込み 2015/11/25GPGPU講習会103  ベクトル和C=A+B  配列A, B, Cで参照する配列要素番号iが同じ  各スレッドがある配列添字iを処理 ・・・ ・・・ ・・・c[i] a[i] b[i] + + + + + + スレッド0 スレッド2スレッド1 スレッド3 ・・・
  104. 104. CUDA Cプログラムの取り込み 2015/11/25GPGPU講習会104  VectorAddimport numpy as np import pycuda.gpuarray as gpuarray import pycuda.autoinit from pycuda.compiler import SourceModule #main関数の外で定義 module = SourceModule(""" __global__ void init(float *a, float *b, float *c){ int i = blockIdx.x*blockDim.x + threadIdx.x; a[i] = 1.f; b[i] = 2.f; c[i] = 0.f; } __global__ void add(float *a, float *b, float *c){ int i = blockIdx.x*blockDim.x + threadIdx.x; c[i] = a[i] + b[i]; } """) PyCUDA/vectoradd.py
  105. 105. CUDA Cプログラムの取り込み 2015/11/25GPGPU講習会105  VectorAdddef main(): N  = 2**20 Nt = 2**8 Nb = N//Nt c = np.zeros(N).astype(np.float32)  #配列Cをfloat型で宣言し,0で初期化 dev_a = gpuarray.to_gpu(c) #cを基にGPU上に配列dev_aを宣言し,内容を転送 dev_b = gpuarray.empty_like(dev_a) #dev_aと同じ型,サイズの配列を宣言 dev_c = gpuarray.empty_like(dev_a) #dev_aと同じ型,サイズの配列を宣言 global module #関数外で定義された変数(オブジェクト)を利用するためにglobal宣言を行う init = module.get_function("init") #実行するカーネルを決定 add  = module.get_function("add") #実行するカーネルを決定 init(dev_a, dev_b, dev_c, grid=(Nb,1), block=(Nt,1,1)) add(dev_a, dev_b, dev_c, grid=(Nb,1), block=(Nt,1,1)) c=dev_c.get() print(c) if __name__ == "__main__": main() PyCUDA/vectoradd.py
  106. 106. 実行時間の測定 2015/11/25GPGPU講習会106  関数の実行時間  timeモジュールのtime関数を利用  epochから関数呼び出し時までの経過時間を秒で返す  epochとは  時間計測の基準となる時刻  多くの場合,1970年1月1日午前0時0分0秒 import time start_s = time.time() : 関数呼び出しや他の処理を実行 end_s = time.time() print(end_s‐start_s,"sec")
  107. 107. 実行時間の測定 2015/11/25GPGPU講習会107  カーネルの実行時間  CUDA APIのEvent(cudaEvent)を利用  イベントを生成  プログラムがそのイベントを通過した時間を記録  二つのイベントが記録された時間の差から実行時間(ミリ秒)を測定 import pycuda.driver as cuda start = cuda.Event() end   = cuda.Event() start.record() start.synchronize() : カーネル呼出 end.record() end.synchronize() print(start.time_till(end),"msec")
  108. 108. CPUとGPUの実行時間の比較 2015/11/25GPGPU講習会108 import numpy as np import pycuda.gpuarray as gpuarray import pycuda.autoinit from pycuda.compiler import SourceModule import pycuda.driver as cuda #cudaEventを利用 import time #time()を利用 module = SourceModule(""" __global__ void init(float *a, float *b, float *c){ int i = blockIdx.x*blockDim.x + threadIdx.x; a[i] = 1.f; b[i] = 2.f; c[i] = 0.f; } __global__ void add(float *a, float *b, float *c){ int i = blockIdx.x*blockDim.x + threadIdx.x; c[i] = a[i] + b[i]; } """) PyCUDA/vectoradd_time.py
  109. 109. CPUとGPUの実行時間の比較 2015/11/25GPGPU講習会109 def init(a, b, c,N): for i in range(N): a[i] = 1. b[i] = 2. c[i] = 0. def add(a, b, c,N): for i in range(N): c[i] = a[i] + b[i] def main(): N  = 2**20 Nt = 2**8 Nb = N//Nt a = np.zeros(N).astype(np.float32) b = np.zeros(N).astype(np.float32) c = np.zeros(N).astype(np.float32) init(a,b,c,N) start_s = time.time() add(a,b,c,N) end_s = time.time() print((end_s‐start_s)*1e+3,"msec") #395 msec PyCUDA/vectoradd_time.py
  110. 110. CPUとGPUの実行時間の比較 2015/11/25GPGPU講習会110 dev_a = gpuarray.to_gpu(c) dev_b = gpuarray.empty_like(dev_a) dev_c = gpuarray.empty_like(dev_a) global module gpuinit = module.get_function("init") gpuadd = module.get_function("add") gpuinit(dev_a, dev_b, dev_c,grid=(Nb,1),block=(Nt,1,1)) start = cuda.Event() end   = cuda.Event() start.record() start.synchronize() gpuadd(dev_a, dev_b, dev_c, grid=(Nb,1), block=(Nt,1,1)) end.record() end.synchronize() print(start.time_till(end),"msec") #0.603 msec (CPU実行は395 msec) if __name__ == "__main__": main() PyCUDA/vectoradd_time.py
  111. 111. デバイス変数の確保とメモリ転送の簡略化 2015/11/25GPGPU講習会111  デバイス変数の確保とメモリ転送  カーネルを1回実行する場合でも必要  デバイス変数を複数のカーネルで利用しない場合,確保+転 送を行うのは冗長  カーネル引数の自動転送  pycuda.driver.In  カーネル実行時にメモリをGPUへコピー  pycuda.driver.Out  カーネル終了時にメモリをGPUからコピー  pycuda.driver.InOut  In,Outの両方の動作
  112. 112. デバイス変数の確保とメモリ転送の簡略化 2015/11/25GPGPU講習会112 : (省略) def main(): N  = 2**20 Nt = 2**8 Nb = N//Nt a = np.zeros(N).astype(np.float32) b = np.zeros(N).astype(np.float32) c = np.zeros(N).astype(np.float32) a[:] = 1. b[:] = 2. global module add  = module.get_function("add") #デバイス変数を確保せず,カーネル実行時にメモリを自動で転送 add(cuda.InOut(a), cuda.InOut(b), cuda.InOut(c), grid=(Nb,1),block=(Nt,1,1)) print(c) if __name__ == "__main__": main() PyCUDA/vectoradd_inout.py
  113. 113. デバイス変数の確保とメモリ転送の簡略化 2015/11/25GPGPU講習会113  メモリ転送の指定  a,bはカーネル内で変更されない→copyinのみでよい  cはカーネル実行時に値が不要→copyoutのみでよい  全てInOutとした場合  カーネルの内容に応じてInとOutを選択 add(cuda.InOut(a), cuda.InOut(b), cuda.InOut(c), grid=(Nb,1),block=(Nt,1,1)) #実行時間 875 msec add(cuda.In(a), cuda.In(b), cuda.Out(c), grid=(Nb,1),block=(Nt,1,1)) #実行時間 487 msec #無駄な転送(a,bのcopyout, cのcopyin)が阻止されて高速化
  114. 114. C++の機能(Template)の利用 2015/11/25GPGPU講習会114  コンパイル時にコードを生成する機能  テンプレート仮引数(パラメータ)を利用して処理を記述  テンプレート実引数の情報からコードを生成(実体化)  C言語の関数形式マクロの安全かつ高機能版 template<class T> T add(T a, T b){ return a + b; } int main(void){ int ia=1,ib=2; float fa=1.0f,fb=2.0f; add<int>(ia,ib);   //Tが全てintになる add<float>(fa,fb); //Tが全てfloatになる return 0; }
  115. 115. C++の機能(Template)の利用 2015/11/25GPGPU講習会115 module = SourceModule(""" template <class T> __device__ T add_func(T x, T y){ return (x+y); } extern "C" { __global__ void init(float *a, float * b, float *c){ int i = blockIdx.x*blockDim.x + threadIdx.x; a[i] = 1.0f; b[i] = 2.0f; c[i] = 0.0f; } __global__ void add(float *a, float * b, float * c){ int i = blockIdx.x*blockDim.x + threadIdx.x; c[i] = add_func<float>(a[i],b[i]); } } """, no_extern_c=True) PyCUDA/vectoradd_tmpl.py
  116. 116. C++の機能(Template)の利用 2015/11/25GPGPU講習会116  C以外からC/C++の関数を利用する場合  内部的にCのルールで関数名が表現されていることを前提  C++では関数名の表現がCとは異なる  extern "C"により,関数名の内部表現をCに変換  SourceModuleはCのルールで解釈  C++の機能を利用するため,no_extern_cをTrueに  内部的にはC++で処理を記述  CUDA CのカーネルはC++の機能を利用  Pythonから呼び出すカーネルにはextern "C"を指定
  117. 117. Pythonスクリプト内のパラメータの反映 2015/11/25GPGPU講習会117  CUDA C Kernel内にPythonスクリプトの数値を反映  既存のCUDA C Kernelが#defineでパラメータを設定してい た場合など  メタプログラミング可能なモジュール  Jinja  Cheetah  Pythonの機能のみで同様の事が可能  画面表示の際に書式制御文字列(%s)を指定する機能を流用  %d等が存在していると,それらにも値を指定しなければならない >>> a = '%(dummystring)s' >>> a % {'dummystring':1} '1' >>> b = 'string' >>> a % {'dummystring':b} 'string'
  118. 118. Pythonスクリプト内のパラメータの反映 2015/11/25GPGPU講習会118  ループの回数をPythonスクリプトから決定 from pycuda.compiler import SourceModule import pycuda.autoinit #ここではまだSourceModuleに渡さない kernel_template="""  #include<stdio.h> __global__ void hello_thread() { for(int i=0;i<%(ITERi)s;i++) for(int j=0;j<%(ITERj)s;j++) printf("hello thread¥¥n"); } """ kernel = kernel_template % {'ITERi':2,'ITERj':2} #%sを他の文字に置き換えた文字列を作成 module = SourceModule(kernel) #文字列を基にSourceModuleを作成 hello = module.get_function("hello_thread") block = (2, 1, 1) grid  = (2, 1, 1) hello(block = block, grid = grid) PyCUDA/HellosThread_tmpl.py
  119. 119. 移流方程式の計算 2015/11/25GPGPU講習会119 import numpy as np import pycuda.gpuarray as gpuarray import pycuda.autoinit from pycuda.compiler import SourceModule import pycuda.driver as cuda convection_kernel_template = """ #define Nx (%(Nx)s) //Pythonスクリプト内で設定した値を反映させる #define dx (%(dx)s) // #define C  (%(C)s)  // #define dt (%(dt)s) // __global__ void convection(float *u, float *uNew){ //通常のCUDA Cカーネルを取り込む int i = blockIdx.x*blockDim.x + threadIdx.x; float d_u_dx; if (i == 0){ d_u_dx = (u[i+1] ‐ u[Nx‐2])/(2.0f*dx);     //周期境界条件を設定 } else if (0 < i && i < Nx‐1){ d_u_dx = (u[i+1] ‐ u[ i‐1])/(2.0f*dx); } else if (i == Nx‐1){ d_u_dx = (u[1]   ‐ u[ i‐1])/(2.0f*dx);     //周期境界条件を設定 } uNew[i] = u[i] ‐ C*dt*d_u_dx;      //移流方程式を時間積分 } """ PyCUDA/convection.py
  120. 120. 移流方程式の計算 2015/11/25GPGPU講習会120 def main(): Lx = 2. #計算領域の長さ Nx = 2**10 #離散点の数 dx = Lx/(Nx‐1) #離散点の間隔 C  = 1. #移流速度 dt = 0.01*dx/C #時間積分の間隔 Lt = 2. #計算終了時間 Nt = int(Lt/dt) #積分回数 numThreads = 128 #1ブロックあたりのスレッド数の上限 convection_kernel = convection_kernel_template % {'Nx':Nx, 'dx':dx, 'C':C, 'dt':dt } module = SourceModule(convection_kernel) convection = module.get_function('convection') block = (min((numThreads,Nx)),1,1) #並列実行パラメータの設定 grid  = (Nx//block[0],1,1) # t = 0. #初期値の設定 x = np.linspace(0, Lx, Nx).astype(np.float32) # u = ( 0.5*(1‐np.cos(2.*np.pi*(x‐C*t)/Lx)) )**3 # X = np.linspace(0,Lx, (Nx‐1)*10+1) #解析解の設定 u_analytical = ( 0.5*(1‐np.cos(2.*np.pi*(X‐C*t)/Lx)) )**3 3 2 cos1 2 1                          xL x u  3 exact )(2 cos1 2 1                           xL tCx u  PyCUDA/convection.py
  121. 121. 移流方程式の計算 2015/11/25GPGPU講習会121 uNew = gpuarray.to_gpu(u) uOld = gpuarray.empty_like(uNew) start = cuda.Event() end   = cuda.Event() start.record() start.synchronize() for n in range(0,Nt): uOld = uNew.copy() convection(uOld, uNew, grid=grid, block=block) #移流方程式を計算 end.record() end.synchronize() elapsed_s = start.time_till(end) print('processing time',elapsed_s/Nt,'msec/step') if __name__ == '__main__': main() PyCUDA/convection.py
  122. 122. 実行時間の比較 2015/11/25GPGPU講習会122  Nx(x方向の離散点の点数=配列要素数)  25,210,215,220  1ステップあたりの実行時間を算出  CPUはtime()で測定  GPUはcudaEventで測定  複数step時間進行させ,要した時間/実行step数 Nx CPU実行 [ms/step] GPU実行 [ms/step] 25 0.010 0.102 210 0.024 0.119 215 0.535 0.137 220 24.9 1.82
  123. 123. PythonからCUDAを利用 2015/11/25GPGPU講習会123  GPUのメモリ上に配列を確保  numpy.arrayの代わりにpycuda.gpuarrayを利用  CUDA APIでメモリ確保(gpuarrayではないので制限がある)  CPUのデータをGPUへ転送  pycudaの機能を利用  CUDA APIを利用してコピー  GPU上で関数を呼び出して処理を実行  cumathモジュールを利用  ElementwiseKernelの作成  SourceModuleを利用してCUDA Cのカーネルを取込  Cに加えてC++の機能も利用可能  #include""でソースファイルを取り込む事も可能  Pythonで設定した値をCDUA Cのカーネルに反映させることも可能  GPUから結果を読み出す  pycudaの機能を利用  CUDA APIを利用してコピー

×