O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

GPGPU Seminar (PyCUDA)

5.643 visualizações

Publicada em

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

Publicada em: Engenharia
  • Dating direct: ♥♥♥ http://bit.ly/369VOVb ♥♥♥
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • Sex in your area is here: ♥♥♥ http://bit.ly/369VOVb ♥♥♥
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui

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を利用してコピー

×