Pythonの理理解を試みる
〜~バイトコードインタプリタ
を作成する〜~
柏原秀蔵
Preferred  Networks,  Inc.
2016/04/28
⾃自⼰己紹介
l  柏原秀蔵 (@suma90h) (Preferred Networks)
l  最近:Dockerfileは友達
l  最近の活動
–  お菓⼦子作り(前回の発表と同じ写真ですが、たまに焼いてます)
–  年年1回くらいバイクでツーリング&キャンプに⾏行行ってます
2
去年年は佐渡島へ  キャンプしたり
3
去年年は佐渡島へ  登⼭山したり
4
Pythonについて、実世界で役に⽴立立ちづらい話をします
l  今⽇日のネタはPython
–  Python⾖豆知識識
–  CPythonを理理解するためにバイトコードインタプリタを作る話(本題)
l  ここ最近の私のセミナー発表は何かの紹介が多かった
–  2015/07/09  先取り  Go  1.5
–  2014/12/18  systemdを始めよう
5
なぜPython?
l  10年年以上使われている(メンテされ続けている)プログラミング⾔言語
l  個⼈人的に注⽬目したいソフトウェアがPythonを利利⽤用している
–  Chainer  (PFI/PFNによる開発)
–  Ansible  (最近2.0出ました)
u  マシンの構成管理理ツール
l  息が⻑⾧長い話し
–  Python  3が出ていても、いまだにPython  2.7も台頭している
–  Ubuntu  16.04ではPython  3標準であるとか
6
Pythonの⾖豆知識識:クイズ
l  以前、⼈人前で話したことがあるので知っている⼈人も多いかもしれません
l  Q:Pythonは何の⽬目的で作られたでしょう?
l  解答候補
–  A:テキスト処理理の⽬目的で作られた
–  B:分散OSのプラットフォームで使う⽬目的で作られた
–  C:業務時間にほかにやることがないから作られた
7
Pythonの起源クイズ:  クイズの答え
l  正解はB:分散OSのプラットフォームで使う⽬目的で作られた
l  Amoebaプロジェクト(分散OS:アンドリュー・タネンバウム先⽣生)のプラ
ットフォーム向けに作られた
–  WikipediaやPython公式FAQにも載ってます
l  ちなみに他の解答の⾔言語は?
–  A:テキスト処理理の⽬目的で作られた
–  Perl。awkだと⼒力力不不⾜足だったらしい
–  C:業務時間にほかにやることがないから作られた
–  Ruby。割と有名な話し
8
CPythonを理理解するために
バイトコードインタプリタを
作成する
9
モチベーション
l  プログラミング⾔言語の、コンパイラかインタプリタを作りたかった
–  ⾞車車輪輪の再発明でも構わない。⾃自分の学習のため
l  ⾔言語⾃自作は考えない。⽂文法(Syntax)考えるのが⼤大変なため
l  ⾔言語VM(Virtual  Machine)を作って⾒見見たい気持ちもあった
–  コンパイラの書籍では中々解説されない部分で、知らないことが多い
–  Java  VMやCLR(.NET  Framework)  →  タスクとして重たすぎる
–  JavaScript  も上と同様、また⾼高速化⾯面で重たそうな印象
–  Ruby  →  内部実装よく知らない、中間コードのファイル⽣生成しない
–  Python  →  バイトコード(中間コード)⽣生成するため、丁度度良良いかもしれない!
u  参考にできるプログラムを発⾒見見できた(GoやPython製のインタプリタ)
10
注意
l  CPythonの理理解を試みた
–  のですが、バイトコードインタプリタの理理解が深まるばかりで、Pythonインタプ
リタ(全体)の理理解については浅い気がします
11
Pythonバイトコードインタプリタを作る!
l  バイトコードとは
–  実⾏行行可能なプログラムバイナリ
–  狭義にはJavaのように1バイト(8ビット)単位が命令令コード(Operation  Code)だ
ったためバイトコードと呼ばれる
–  Pythonの中間コードも1バイト単位
–  1バイト  →  256通りまでの命令令を表現できる
l  コードリーディング
–  Python-‐‑‒2.7.10/Include/opcode.h  では147までを定義
–  Python-‐‑‒3.5.1/Include/opcode.h  では154までを定義
12
Pythonのフロー(いわゆるインタプリタ処理理系のフロー)
l  通常は字句句解析などが必要
l  フロントエンドの処理理(字句句・構⽂文解析)からバイトコード⽣生成までは
CPythonに任せる作戦
–  バイトコードインタプリタ開発の場合
–  バイトコードのファイル読み込み  →  実⾏行行  となる
13
source
(test.py)
lexical analyzer
字句句解析
parser
構⽂文解析
etc execute
Pythonや一般的な処理系のフロー	
result
バイトコードインタプリタ作成の流流れ
l  公式情報から作る場合、CPython本家を調査する努⼒力力が必要
–  CPythonのCソースコードを読み、バイトコードインタプリタを解読する
–  公式ドキュメントを読み、バイトコードの意味を把握する
u  http://docs.python.jp/2.7/library/dis.html
l  今回はGo製のインタプリタGoPyをD⾔言語で再実装(移植)することにした
–  ⼀一部の命令令が実装されている
l  GoPyの⾏行行数
–  wc  -‐‑‒l  *.go  →  1643  ⾏行行
–  短い?  1000⾏行行くらいなら、気合いで1週間程度度で動くものが作れると判断
14
なぜD⾔言語か?
l  バイナリファイルの扱いや、ビット演算などが簡単に書けると思った
l  better  C++として、C++よりも簡単にVMの実装を書けると期待した
l  D⾔言語初⼼心者なので、D⾔言語で1000⾏行行程度度書いてみたかった
–  (感想)Goと⽐比較しても、⾔言語⾃自体は書きやすく悪くない感じ
–  Goから移ると、公式の標準ライブラリや周辺ツールの少なさが⽬目⽴立立つ
l  書籍「The  D  Programming  Language」はわかりやすい
–  ただし2010年年の本なので、古い箇所がある
–  ライブラリ名など、修正が必要なサンプルコードもある
15
Pythonバイトコード.pyc概要
l  .pyc  がバイトコード(中間コード)の拡張⼦子。Pythonによって⽣生成される
l  file.pyとfile.pycがあったとき、file.pyc  を優先して実⾏行行する
–  pycはタイムスタンプを持っており、.pyと⽐比較して新しい⽅方を読み込む
–  参考:  python  -‐‑‒  When  are  .pyc  files  refreshed?  -‐‑‒  Stack  Overflow
u  http://stackoverflow.com/questions/15839555/when-‐‑‒are-‐‑‒pyc-‐‑‒files-‐‑‒
refreshed
l  pycはいつ⽣生成されるのか
–  importで他のファイルの.pyを読み込んだとき
–  pythonで  –m  compileall  もしくは  –m  py_̲compile  オプションで実⾏行行されたとき
l  pycの効果
–  ファイル読み込みのキャッシュの効果
–  プログラム実⾏行行が⾼高速になるわけではない!
16
Pythonバイトコード.pyc内容
l  ローカル変数の数、スタックのサイズ、フラグ、命令令コード…など
l  Python-‐‑‒2.7.10/Include/code.h  の  PyCodeObject構造体が実体
–  https://hg.python.org/cpython/file/2.7/Include/code.h#l10
l  pycファイルのバイナリ読み込みにあたって
–  数値はリトルエンディアンで格納されている
–  16/32ビット数値、⽂文字列列(もしくはバイナリ)
–  ※  バイナリのパースについては複雑なので⾶飛ばします
l  このデータをもとにVM(バイトコードインタプリタ)が⾛走ることになる
–  Pythonのバイトコードインタプリタはスタックマシン型
–  Opode:  https://hg.python.org/cpython/file/2.7/Include/opcode.h
17
スタックマシン型のVM、Pythonのインタプリタとは?
l  ⼀一般的なスタックマシンの解説(省省略略します)
–  https://ja.wikipedia.org/wiki/スタックマシン
–  https://en.wikipedia.org/wiki/Stack_̲machine
–  例例:  Java  VMやCLR(.NET  Framework)のVMでもスタックマシンが採⽤用
l  Pythonでは(例例)
–  BINARY_̲ADD:  スタック上のトップ2つの値をPopして加算し、スタックにPushする
–  LOAD_̲NAME:    指定された名前の変数の値をスタックにPushする
–  STORE_̲NAME:  スタック上のトップ1つの値をPopし、指定した名前の変数へ格納する
–  http://docs.python.jp/2.7/library/dis.html
18
バイトコードインタプリタ開発の記録
l  ⽇日記と⾏行行数の記録を書いていた
–  ⽇日記はそのままコピペします
l  数⾏行行のコメントと、twitterで⾏行行数(進捗)をつぶやく
19
バイトコードインタプリタ開発の記録
l  2016/04/20  
–  D⾔言語まともに書き始めた  
20
バイトコードインタプリタ開発の記録
l  2016/04/21
–  pycのパーサ書いた、途中で4バイト読み込みするのが抜けたバグではまった。  
–  printfデバッグして⽐比較最中、⾜足りない箇所が発覚して無事解決。  
–  PyTupleとか基本データ型を実装。attributeとかは⾶飛ばした。後で実装する。  
–  Bytecodeのopcodeのswitchを書くところまで進んだ。  
21
バイトコードインタプリタ開発の記録
l  2016/04/21  アクシデントも
–  D⾔言語の標準ライブラリにStackがない!
–  →  kinabaさんのウェブサイトにあったコードを借りてしのいだ
22
バイトコードインタプリタ開発の記録
l  2016/04/22
–  Opcodeのインタプリタを作成中  
–  加減乗算あたりの基礎を実装  
23
バイトコードインタプリタ開発の記録
l  2016/04/24
–  STORE_̲NAMEの名前取得がバグってた
24
バイトコードインタプリタ開発の記録
l  2016/04/25
–  関数定義と関数呼び出し(引数有り・無し)ができるようになった
25
デモ
l  GoPy付属のテスト⽤用スクリプト
–  組み込みモジュールtimeのtime.time()から、Unix  timeを取得する
–  同⼀一ファイル内の関数を呼び出す
l  今回作成したテスト⽤用のスクリプト
–  数値の演算(加減算)をして、printする
26
Pythonを理理解できたであろうか?
l  Pythonの理理解は深まったのか?  という問い
–  ⼀一部機能などに関して個⼈人的に深まったと⾔言える。しかし、広く伝えるのは難しい
–  pycの16進数/ASCIIダンプやopcodeを⾒見見て⼀一部の命令令が解読できるようになった
l  バイトコードインタプリタ作成を通じて得られたもの
–  Pythonのような⾔言語のバイトコードインタプリタの実装⽅方法の知⾒見見
–  D⾔言語でのちょっとしたスタックマシン型VMの開発経験(D⾔言語良良かった)
–  Pythonのfunctionなどがどんな実体で管理理されてるか知る機会となった
–  Pythonで  `̀type(funcname)`̀  とすると関数はオブジェクトだとわかる
l  注意:  インタプリタが作れるからといって、Python⾔言語そのものに
詳しくなれるとは限らない!
27
よりPythonを知るには(実装という意味で)
l  CPythonをはじめとし、⾔言語の実装の各⽅方⾯面からアプローチすると良良さそう
–  ⾔言語の⽂文法を知る・使い⽅方を知る
–  組み込みの標準機能(標準のデータ型)や構⽂文(ifやforなど)の実装を知る
–  標準のランタイムライブラリを知る
–  処理理系(CPythonならバイトコードインタプリタ)にあわせた実⾏行行⽅方法を知る
l  よりディープな所に触れる
–  マルチスレッドの実装
–  GIL(Global  Interpreter  Lock)
–  GC(Garbage  Collection)
–  CPythonのC拡張
28
参考
l  https://github.com/flosch/GoPy
l  https://github.com/nedbat/byterun
l  http://akaptur.com/blog/2013/11/15/introduction-‐‑‒to-‐‑‒the-‐‑‒python-‐‑‒
interpreter/
29
Copyright  ©  2016
Preferred  Networks  All  Right  Reserved.

Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜