Successfully reported this slideshow.
Your SlideShare is downloading. ×

debug_profile.pdf

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
L1 apgd
L1 apgd
Loading in …3
×

Check these out next

1 of 50 Ad
Advertisement

More Related Content

Recently uploaded (20)

Advertisement

debug_profile.pdf

  1. 1. デバッグとプロファイル 2022-11−08 立岩斉明
  2. 2. Introduction
  3. 3. バグとは プログラムが何らかの事情で処理を行えなくなる、もしくは挙動が意図した ものとずれること。 算術、浮動小数点演算 ゼロ割/ オーバーフロー/ 桁落ち メモリアクセス 解放済みのメモリや配列の定義域外にアクセス メモリ解放 メモリ解放忘れ(memory leak) / 二回メモリを解放(double free) プログラムミス 平均と中央値を間違える/ 無駄なメモリの使用/ etc... 3
  4. 4. バグとは システムがエラーを返さないバグもあり、それらはユーザーが頑張って見つ ける必要がある。 テストを作成して実行する 計算結果に違和感を感じる、など... 発覚したバグについてはユーザーは バグの根本的原因箇所の発見 修正と再発防止、を行う 4
  5. 5. 本スライドの目的 研究(仮説)とプログラミング(検証)は密接に関わっており、バグの少な いコードは実験のやり直しを最小限にし、手法の検討に必要な正しい情報の 取得に貢献する しかしながら、テストを書いたり、バグの原因を見つける作業は労力を要す るもの。ただ幸いなことに先人のノウハウやツールを我々は利用することが できる。 本スライドでは、テスト、デバッグ、バグ原因特定に関する汎用的なテクニ ックをそれぞれに対応するPythonツールの使い方とともに紹介する。 5
  6. 6. 本スライドの構成 紹介順序 対応ソフト 1 例外について 2 バグの根本的箇所の発見 pdb 3 バグの発見 pytest + 4 再発防止 assertion 5 プロファイル cProfile α 6
  7. 7. 例外(Exception)
  8. 8. 例外 例外とは、エラーが生じたときに実行を強制終了させるのではなく、エラー に対して処理(例外処理)を行い実行を継続できるようにするもの。 Pythonだと組み込みクラスとしてExceptionクラスが存在し 、それを継承し ていくつかの具体的な例外クラスが実装されている。 >>> Exception <class 'Exception'> >>> RuntimeError <class 'RuntimeError'> >>> RuntimeError.__mro__ # 親クラスを確認 (<class 'RuntimeError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>) 1 1: C++ではstd::exception。だいたいこの辺はどのプログラミング言語も同じような用語を用いている。 8
  9. 9. 例外 エラー発生時にそれに適したExceptionが返される。 >>> 1 / 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero 9
  10. 10. 例外 自分でExceptionクラスをカスタマイズすることもできる。 >>> class OneDivisionError(Exception): ... pass >>> def divide(a, b): ... if b == 1: ... raise OneDivisionError ... else: ... return a / b >>> divide(3, 1) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in divide __main__.OneDivisionError: division by one 10
  11. 11. 例外処理 発生したエラーを捕まえて(catch)、そのエラーに対して処理を行うことを例 外処理(もしくはエラーハンドリング)と呼ぶ。 try ~ except 構文で記述できる。 >>> try: ... x = divide(3, 1) ... except OneDivisionError as e: ... print(type(e), e) ... x = 1 <class '__main__.OneDivisionError'> division by one >>> x 1 11
  12. 12. デバッガ
  13. 13. デバッガ プログラムの実行を任意のタイミングで停止させて、変数の値や関数の呼び だし経路を確認したり、停止地点から実行を少しずつ進めることで、プログ ラム実行が意図したものに沿っているかをチェックできる。 言語 デバッガ Python pdb C/C++ gdb Docker buildg 13
  14. 14. デバッガ プログラムの実行は プログラマが指定した箇所(breakpoint) エラーが発生した瞬間 警告が発生した瞬間 で停止させることができる。 14
  15. 15. pdb Pythonの標準対話型デバッガ(python debugerの略だと思う)。 Pythonスクリプト実行に-mオプションを付けてpdbを起動したり python -m pdb test.py ソースコードに、直接breakpointを書き込むと、その地点に到達した瞬間に pdbが起動する。 a = 4 breakpoint() a += 3 15
  16. 16. pdb > 例 test.py ; x = ["apple"], y = [10]の二つのリストを作成するプログラム def generate_empty_list(x=[]): return x # create list x as ["apple"] x = generate_empty_list() x.append("apple") # create list y as [0] y = generate_empty_list() y.append(0) z = y[0] + 10 print(z) 16
  17. 17. pdb > 例 実行してみる。 python test.py Traceback (most recent call last): File "/Users/tateiwa/test.py", line 10, in <module> z = y[0] + 10 TypeError: can only concatenate str (not "int") to str エラー検出。 17
  18. 18. pdb > 例 python -m pdb test.py で実行すると > /Users/tateiwa/test.py(1)<module>() -> def generate_empty_list(x=[]): 対話型モードが起動される。コマンドを打ち込んで実行を進めよう。 18
  19. 19. pdb > コマンド コマンド 説明 c continue 実行を続ける s step ステップ実行(関数の中に入る) n next 次の行へ l list ソースコードの表示 b breakpoint ブレークポイントの設定 p print 変数の表示 q quit 終了 19
  20. 20. pdb > 実演
  21. 21. pdb > フレーム移動 コマンド 説明 bt backtrace バックトレースの表示 u up 1つ上のフレームへ d down 1つ下のフレームへ w where 現在のフレームの表示 u 3 とすると、3つ上のフレームへ移動する。 d 2 とすると、2つ下のフレームへ移動する。 21
  22. 22. pdb > 実演
  23. 23. Tips for Python Debugger
  24. 24. ipdb pdbの拡張版パッケージ。出力がちょっとリッチになったり、追加の情報表示 機能がある。便利そう。 pip install ipdb python -m ipdb test.py github: https://github.com/gotcha/ipdb 解説記事: https://qiita.com/Kobayashi2019/items/98e74110d74e4c60f617 24
  25. 25. watchpoints 変数の変更を表示できるパッケージ。変数が変更されたことを表示したり、 pdbで対話モードに移ることが可能。 pip install watchpoints import watchpoints watchpoints.watch.config(pdb=True) # 指定した変数に変更があればpdbに移行する a = 0 watchpoints.watch(a) # 変数の監視を開始 a = 1 github: https://github.com/gaogaotiantian/watchpoints 解説記事: https://opensource.com/article/21/4/monitor-debug-python 25
  26. 26. Warningのcatch tmp.py import numpy as np x = np.ones((2, 2), dtype=np.float16) x[0, 0] = 1e4 y = x ** 2 python -W error -m pdb tmp.py と実行することで、warningの部分で実行を止めることができる。 解説記事: https://inarizuuuushi.hatenablog.com/entry/2022/06/06/090000 26
  27. 27. Test
  28. 28. テスト バグの発見だけでなく、プログラムの変更がこれまで正常に動作した機能を 壊していないかの確認(リグレッションテスト)にも使える。 それぞれのテストはなるべく最小限の構成で行う。 小さな入力(行列サイズを最小限にするとか) 関数単位など小さい構成 これらはテスト失敗時の原因箇所の特定の容易化や計算時間の短縮に貢献。 28
  29. 29. テストケース設計 testは(入力, 出力)のペアを用いて、モジュールの処理(入力) == 出力、になる かを確認する 。テスターはどのようなテストケース(入力、出力)を設計すれ ば良いのだろうか? テストケース設計技法は、大きく次の二つに分類される。 ブラックボックステスト ホワイトボックステスト 1 1: ただ単に処理(入力)が問題なく終わることを確認する場合は出力は不要。また処理(入力)が出力に対して小さい、大きい、あ る程度の誤差を許容して等しいことを確認したり、というケースもある。 29
  30. 30. テストケース設計> ブラックボックス モジュール(プログラムのかたまり)をブラックボックスとみなす、すなわ ちモジュールの内部構造には全く着目せずにテストを設計する。 同値分割 入力データをいくつかのクラスに分割して、各クラスを代表する値をテストデ ータとする 限界値分析(境界値分析) それぞれのクラスの境界値をテストデータとして設定する 1 2 1: 例: の入力 を[-inf, 0), [0, 100], (100, inf)の区間を分けてそれぞれから-1, 50, 150を選択する。 2: 1の例を用いると、-1, 0, 100, 101が選択される。 f (x) x ∈ Z 30
  31. 31. テストケース設計> ホワイトボックス プログラム内部に着目してテストを設計する。 命令網羅 全ての命令を少なくとも1回は実行 判定条件網羅(分岐網羅) 判定条件で真となる場合と偽となる場合をそれぞれ少なくとも1回は実行 条件網羅 条件判定が複合的(A or B)の場合に、それぞれの条件の真偽の組み合わせを網羅 (この場合はAが真、偽の場合、Bが真、偽の場合を組み合わせて4通り) 1 2 3 1: 要するにプログラムの全ての行が実行する 2: if x < 3: の条件文の場合はこの条件文において x < 3とx >= 3のどちらのケースも実行する 3: if x < 3 or y > 4の条件文の場合はこの条件文において (x < 3, y > 4), (x < 3, y <= 4), (x >= 3, y > 4), (x >= 3, y <= 4)をテストす る 31
  32. 32. pytest-cov pytestの拡張パッケージ。テスト実行時のプログラムの網羅性を可視化。 pip install pytest-cov pytest -v --cov=<directory> --cov-report=html githu: https://github.com/pytest-dev/pytest-cov 解説記事: https://qiita.com/kg1/items/e2fc65e4189faf50bfe6 32
  33. 33. pytest-cov 実演
  34. 34. CI/CD Continuous Integration/Continuous Delivery(CI/CD)とは、ソフトウェアの 変更を常にテストして自動で本番環境にリリース可能な状態にしておく、ソ フトウェア開発の手法のこと。 こまめにテストを実行して常にクリーンな状態でソースコードを管理しまし ょう、ということ。 参考: https://codezine.jp/article/detail/11083 34
  35. 35. CI/CD > Act Github actionをローカルで実行できるツール。仮想環境を作成してテストで きるので、様々な環境での動作検証に有用。パッケージを配布する前のテス トにも使える。 githu: https://github.com/nektos/act 解説記事: https://qiita.com/wwalpha/items/6c303dcf04e236238315 35
  36. 36. CI/CD > Act 実演
  37. 37. Tips for Prevention of Bugs
  38. 38. assertion 成立してほしい条件を確認するための構文。もし条件を満たしていない場合 はAssertionErrorをメッセージと共にraiseする。 メッセージの部分にバグの原因などを書く 。ソースコードに情報が残るのが 良い。記法は下記。 assert 条件式, "メッセージ" # "メッセージ"は省略可能 1 1: 入力される変数の型の違いを教えてあげたり、より直接的な要因"入力ファイルのフォーマットはcsv出なければならない"な どを教えてあげる。 2: NASAのコーディング規約では関数ごとに最低2つのassertionが義務付けられている。 https://qiita.com/tmokmss/items/1b5625d429ea4aa1ac32, Rule 5。 38
  39. 39. assertion > 例 def generate_empty_list(x=[]): return x # create list x as ["apple"] x = generate_empty_list() x.append("apple") assert x == ["apple"] # create list y as [0] y = generate_empty_list() y.append(0) # assert y == [0] assert y == [0], f"y must be [0], but got {y}" 39
  40. 40. ロギング 実行中のさまざまな情報をログとして出力することで、プログラムの挙動を 追跡できるようにすることで、バグの検出を容易にする。 printとの違い 情報の出力場所(ファイル名、関数、行数)や時刻を一緒に表示できる 複数のファイルに分けてログを保存できる レベル(DEBUG, INFO, WARNING, ERROR, CRITICAL)ごとに色分けで 表示できる(colorlogを使えば) 解説記事: https://inarizuuuushi.hatenablog.com/entry/2020/12/12/225907 40
  41. 41. 型ヒント 型ヒントを書くことで、ユーザーに変数の型を教える。また、linterを用いた 静的解析もできる。 def generate_empty_list(x: list = []) -> list: return x https://docs.python.org/ja/3/library/types.html 41
  42. 42. Profile
  43. 43. プロファイル プログラム実行時の関数ごとの実行時間やメモリ使用量を計測すること。プ ログラム改善の目安やその改善度合いの確認に使用。 言語 用途 プロファイラ Python 実行速度 cProfile Python メモリ使用量 memray, tracemalloc C/C++ 実行速度 gprof C/C++ メモリ使用量 valgrind 43
  44. 44. cProfile python標準のプロファイラ test3.py ; フィボナッチ数列計算 def fib(n): if n == 0: return 0 elif n == 1: return 1 else: return fib(n - 1) + fib(n - 2) print("fib(35)", fib(35)) 44
  45. 45. cProfile python -m cProfile -o stat test3.py 結果の可視化 pip install snakeviz snakeviz stat 解説記事: https://kazuhira-r.hatenablog.com/entry/2019/04/13/173643 45
  46. 46. cProfile > 実演
  47. 47. cProfile > 実演 test4.py ; lru cacheを用いたフィボナッチ数列計算の高速化 import functools @functools.cache def fib(n): if n == 0: return 0 elif n == 1: return 1 else: return fib(n - 1) + fib(n - 2) print("fib(35)", fib(35)) 47
  48. 48. cProfile > 実演
  49. 49. メモリプロファイル> memray 実行中の使用メモリの推移や関数のどの部分でメモリを生成しているのかを 確認できる。 49
  50. 50. メモリプロファイル> memray 実演

×