JVM言語の動き方・動かし方 /make-jvm-lang
by
Miyakawa Taku
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
JVM言語の動き方・動かし方 2019-02-27 JJUGナイトセミナー ハッシュタグ: #jjug 宮川 拓
Slide 2
Slide 2 text
⚫ @miyakawa_taku ⚫ JJUG幹事です ⚫ Salesforceで働いてます ⚫ 奄美出身の力士を応援しています☆ ⚫ オレオレJVM言語Kinkを作っています http://doc.kink-lang.org/ 自己紹介 #jjug 2/127
Slide 3
Slide 3 text
背景 Graal+Truffleは素敵みたい Graal+Truffleの素敵さを納得するため、 言語処理系のかんどころを分かっときたい #jjug 3/127
Slide 4
Slide 4 text
演目 言語処理系の構成 コンピュータはどのように プログラムを動かすのか JVMはどのように プログラムを動かすのか JVM言語処理系の構成: Kinkを題材に #jjug 4/127
Slide 5
Slide 5 text
演目 言語処理系の構成 コンピュータはどのように プログラムを動かすのか JVMはどのように プログラムを動かすのか JVM言語処理系の構成: Kinkを題材に #jjug 5/127
Slide 6
Slide 6 text
そもそも 「言語処理系」ってなに? #jjug 6/127
Slide 7
Slide 7 text
本セッションにおける用語定義 書かれた プログラム くりかえし 三回: 念仏.唱える コンピュータ上での 実行 南無阿弥陀仏 南無阿弥陀仏 南無阿弥陀仏 この間に必要なソフトウェア一式を 「言語処理系」と呼ぶことにする #jjug 7/127
Slide 8
Slide 8 text
言語処理系の二段階 翻訳系 *1 実行系 *1 書かれたプログラム 実行に適した表現: 機械語 or バイトコード or ... 実行に適した表現 を コンピュータ上で 実行 #jjug 8/127
Slide 9
Slide 9 text
翻訳系 ここはザックリ行きます #jjug 9/127
Slide 10
Slide 10 text
翻訳系 書かれたプログラム 実行に適した表現: 機械語 or バイトコード or ... つまりコンパイラ #jjug 10/127
Slide 11
Slide 11 text
翻訳系の中身 書かれたプログラム トークン列 抽象構文木 (AST) 実行に適した表現 字句解析 構文解析 シンボル解決, 型検査, 最適化,,, " く り か え し ", " 三 回 ", " : ", , , (loop 3 (call (var "念仏") "唱える")) #jjug 11/127
Slide 12
Slide 12 text
つまり翻訳系は多段階のデータ変換処理 プログラマの多くは、似たような処理を 作った経験があるはず #jjug 12/127
Slide 13
Slide 13 text
データ投入バッチ タブ区切りテキスト トークン列 抽象構文木 (AST) データベース 字句解析 構文解析 マスタ突合, バリデーション,,, "中島敦", "悟浄歎異", 1942, 改行,,, [{"author": "中島敦", "title": "悟浄歎異", "year": 1942},,,] 中島敦 悟浄歎異 1942 深沢七郎 風流夢譚 1960 ,,, やってることは翻訳系と同じ! #jjug 13/127
Slide 14
Slide 14 text
翻訳系でやっていることは、 とても馴染み深いデータ変換処理 ただし、型検査や最適化などは、それぞれが 一大トピック #jjug 14/127
Slide 15
Slide 15 text
実行系 こっちが今日のメイン #jjug 15/127
Slide 16
Slide 16 text
実行系 実行に適した表現 を コンピュータ上で 実行 #jjug 16/127
Slide 17
Slide 17 text
誰が実行する? 実行対象 実行の担い手 機械語の プログラム CPUとOS つまりコンピュータそのもの 機械語以外: バイトコード、 木構造など 実行系のプログラム: JVM, V8, YARVなど コンピュータの真似をする “Java Virtual Machine”! #jjug 17/127
Slide 18
Slide 18 text
ここから先は、 ◼ コンピュータがどのように プログラムを動かすのか ◼ JVMやJVM言語処理系が、どのように コンピュータを真似するのか を見ていきます #jjug 18/127
Slide 19
Slide 19 text
演目 言語処理系の構成 コンピュータはどのように プログラムを動かすのか JVMはどのように プログラムを動かすのか JVM言語処理系の構成: Kinkを題材に #jjug 19/127
Slide 20
Slide 20 text
サンプルプログラム N = M + 42 ld x5, &M addi x6, x5, 42 sd x6, &N 書かれた プログラム RISC-Vの 機械語 プログラム x5, x6: レジスタ &M, &N: 変数の番地 #jjug 20/127
Slide 21
Slide 21 text
OSの仕事: ローダ #jjug 21/127
Slide 22
Slide 22 text
OS(ローダ)の仕事 1/2 プログラムが使う仮想アドレス空間を確保 機械語命令列の領域 グローバル変数の領域 100番地 200番地 #jjug 22/127
Slide 23
Slide 23 text
OS(ローダ)の仕事 2/2 プログラムをアドレス空間にロード *2 #jjug ld x5, &M addi x6, x5, 42 sd x6, &N ... M N ... 100番地 200番地 204番地 23/127
Slide 24
Slide 24 text
CPUの仕事: 計算 #jjug 24/127
Slide 25
Slide 25 text
CPU CPUの概念図 *3 レ ジ ス タ 群 演 算 装 置 ( ALU ) メ モ リ デ ー タ 用 ポ ー ト 命 令 用 ポ ー ト Program Counter +4 番 地 命 令 番 地 入 力 演 算 結 果 デ ー タ 次 の 命 令 へ レ ジ ス タ : 計 算 に 使 う 数 値 を 置 い て お く 場 所 Program Counter ( PC ) : 実 行 す る 命 令 の 番 地 を 表 す 特 殊 な レ ジ ス タ 演 算 装 置 ( ALU ) : 加 算 器 と か 除 算 器 と か の 回 路 の か た ま り #jjug 25/127
Slide 26
Slide 26 text
CPU ld x5, &M レ ジ ス タ 群 演 算 装 置 ( ALU ) メ モ リ デ ー タ 用 ポ ー ト 命 令 用 ポ ー ト Program Counter +4 1) 100 6) x5 7) +4 3) &M=200 4) &M=200 5) M=3 1) 実行する命令の番地 2) 命 令 5) M =3 をロード #jjug 2) 命令をロード 3, 4) M の番地 6) 3 をx5 レジスタに書き込み 7) +4 して次の命令の番地へ 26/127
Slide 27
Slide 27 text
CPU addi x6, x5, 42 レ ジ ス タ 群 演 算 装 置 ( ALU ) メ モ リ デ ー タ 用 ポ ー ト 命 令 用 ポ ー ト Program Counter +4 1) 104 3) x5 8) +4 5) 42 1) 実行する命令の番地 2) 命 令 5) 即 値 をALU に入力 4) x5=3 6) 和=45 7) x6 #jjug 3, 4) x5 =3 をALU に入力 2) 命令をロード 6, 7) 和をx6 レジスタに 8) +4 して次の命令の番地へ 27/127
Slide 28
Slide 28 text
CPU sd x6, &N レ ジ ス タ 群 演 算 装 置 ( ALU ) メ モ リ デ ー タ 用 ポ ー ト 命 令 用 ポ ー ト Program Counter +4 1) 108 5) x6 7) +4 3) &N=204 4) &N=204 6) x6=45 1) 実行する命令の番地 2) 命 令 5, 6) x6 =45 をN に書き込み #jjug 3, 4) N の番地 2) 命令をロード 7) +4 して次の命令の番地へ 28/127
Slide 29
Slide 29 text
CPUの仕事: 手続き呼び出し #jjug 29/127
Slide 30
Slide 30 text
手続き呼び出し 高度なプログラムでは、手続きの呼び出しに よって処理があちこちにジャンプする foo { ... bar() ... } bar { ... return; } call return #jjug 30/127
Slide 31
Slide 31 text
手続きbarを呼び出す jal x1, &bar jal命令は次の2つを実行する 1. PC+4をx1に入れる ⚫ PC+4は、jal命令の次の命令の番地 ⚫ あとでこの戻り番地に戻ってくる 2. PCに&barを入れる ⚫ つまり、barの命令列にジャンプする #jjug 31/127
Slide 32
Slide 32 text
手続きbarから戻る jalr x0, x1 #jjug jalr命令は次の2つを実行する 1. PC+4をx0に入れる ⚫ x0は、入れた値が捨てられる特殊なレジスタ ⚫ つまりここでは、PC+4は捨てられる 2. PCにx1を入れる ⚫ x1には、呼び出し元で設定された 戻り番地が入っている ⚫ つまり、呼び出し元にジャンプ=returnする 32/127
Slide 33
Slide 33 text
OSの仕事: スタックの確保 #jjug 33/127
Slide 34
Slide 34 text
呼び出し先でレジスタの値が変えられると、 元の処理が続けられないので困る 呼び出し前後で、レジスタの元の値を 退避しておく領域が必要 → これがスタック領域 #jjug 34/127
Slide 35
Slide 35 text
スタックの確保 スレッドXの スタック OSはスレッドごとにスタックを確保する #jjug スレッドYの スタック スレッドZの スタック 35/127
Slide 36
Slide 36 text
スタックの中身 fooの フレーム barの フレーム callしたら呼び出しの作業領域として 「フレーム」を確保する returnしたらフレームを捨てる #jjug スタックの中身のやりくりはプログラムの責任 36/127
Slide 37
Slide 37 text
OSの仕事: 動的メモリ確保 #jjug 37/127
Slide 38
Slide 38 text
動的メモリ確保 OSは実行中のプログラムの要求に応じて、 仮想アドレス空間を動的に割り当てる #jjug 38/127
Slide 39
Slide 39 text
小まとめ: コンピュータはどのように プログラムを動かすのか #jjug 39/127
Slide 40
Slide 40 text
プログラムを動かすためにコンピュータは ◼ プログラムのロード ◼ 計算 ◼ 手続き呼び出し&スタック管理 ◼ 動的メモリ確保 などを行います #jjug 40/127
Slide 41
Slide 41 text
演目 言語処理系の構成 コンピュータはどのように プログラムを動かすのか JVMはどのように プログラムを動かすのか JVM言語処理系の構成: Kinkを題材に #jjug 41/127
Slide 42
Slide 42 text
Java処理系の二段階 翻訳系: javac 実行系: JVM Javaソースコード Javaバイトコード Javaバイトコード を コンピュータ上で 実行 #jjug 42/127
Slide 43
Slide 43 text
実行系はコンピュータの真似をする でも、まったく同じことをする必要はない! ◼ 抽象度の高い枠組みを提供しても良い ◼ コンピュータの機能が使えるのであれば、 そのまま使えば良い ◆ 加算器の回路をシミュレートするより、 add命令を使う方が良い #jjug 43/127
Slide 44
Slide 44 text
コンピュータ, JVM, WebAssemblyの比較 #jjug 44/127
Slide 45
Slide 45 text
WebAssembly 各種言語をブラウザ上で動かすための実行系 の仕様 JVMと位置づけは似ているが、 構成はかなり異なる #jjug 45/127
Slide 46
Slide 46 text
オブジェクトシステム コンピュータ WebAssembly JVM オブジェクトシステムは 存在しない Javaクラス&インスタンス #jjug 46/127
Slide 47
Slide 47 text
メモリ管理 コンピュータ WebAssembly JVM フラットなアドレス領域を 割り当て GCは存在しない オブジェクトの生成を管理 参照関係を追跡して、 不要なオブジェクトをGC #jjug 47/127
Slide 48
Slide 48 text
手続きの呼び出し コンピュータ WebAssembly JVM アドレスへのジャンプ クラスの仮想関数テーブルを ルックアップ&ジャンプ またはinvokedynamicで 実行時に呼び出し方法を規定 #jjug 48/127
Slide 49
Slide 49 text
スタック コンピュータ WebAssembly JVM 他の呼び出しのフレームも 読み書き可能 現在の呼び出しの フレーム以外は 読み書きできない #jjug 49/127
Slide 50
Slide 50 text
計算 コンピュータ WebAssembly JVM PC, ALU, レジスタ, ... 仕様は項書換え系 実装は様々 バイトコードインタプリタ &JITコンパイラ 計算にはさまざまな実現方法がある #jjug 50/127
Slide 51
Slide 51 text
計算: 項書換え系 #jjug 51/127
Slide 52
Slide 52 text
項書換え系 ルールに従って項(式の一部)を書き換える (1+2) * 3 3 * 3 9 項書換え系ですべてのプログラムが表せる! → ラムダ計算、SKIコンビネータ計算 #jjug 52/127
Slide 53
Slide 53 text
項書換え系 利点 ◼ データと演算をひとまとめにするので 理論的に扱いやすい ◼ 数学っぽくてかっこいい 欠点 ◼ 素直に実装すると遅い #jjug 53/127
Slide 54
Slide 54 text
計算: Metacircular Interpreter #jjug 54/127
Slide 55
Slide 55 text
Meta-circular Interpreter ASTやそれに類する木構造を再帰的に 手繰って計算を適用する 「ASTを直接評価」という場合はこれのこと #jjug 55/127
Slide 56
Slide 56 text
Meta-circular Interpreter 利点 ◼ ホスト言語(実行系を記述する言語)の 制御構造が使えるため、実装が楽 欠点 ◼ ホスト言語の制御構造に制約される #jjug 56/127
Slide 57
Slide 57 text
計算: バイトコードインタプリタ #jjug 57/127
Slide 58
Slide 58 text
バイトコードインタプリタ 命令列の実行をホスト言語上でエミュレート 命令の抽象度は機械語よりも高い ◼ JVMの場合: ◆ new命令: インスタンスを生成 ◆ getfield命令: オブジェクトから フィールドの値を取得 #jjug 58/127
Slide 59
Slide 59 text
バイトコードインタプリタ 実装のイメージ for (int pc = 0; pc < instructions_len; ++pc) { long insn = instructions[pc]; switch (get_opcode(insn)) { case GETFIELD: obj *obj = datastack[--sp]; sym field_sym = get_operand(insn); datastack[sp++] = obj->get_field(field_sym); break; case SETFIELD: ... } } #jjug 59/127
Slide 60
Slide 60 text
バイトコードインタプリタ 利点 ◼ Javaの場合、クラスファイルの内容が 直接実行できる ◼ スタックを自前で管理する場合、 ホスト言語の制御構造ではできないこと も実現できる 欠点 ◼ コンピュータよりは遅い #jjug 60/127
Slide 61
Slide 61 text
計算?: JITコンパイラ #jjug 61/127
Slide 62
Slide 62 text
JITコンパイラ 実行系に内蔵された翻訳系のプログラム JVM バイトコード インタプリタ Javaバイトコード を コンピュータ上で 実行 JITコンパイラ *4 Javaバイトコード 機械語の命令列 #jjug 62/127
Slide 63
Slide 63 text
JITコンパイラ 実行系の実行時に ◼ 機械語の命令列を生成して ◼ コード領域を動的確保して ◼ コード領域に機械語命令列を書き込んで ◼ CPUに実行させる! #jjug 63/127
Slide 64
Slide 64 text
JITコンパイラ 利点 ◼ 生成されたコードの実行は速い 欠点 ◼ JITコンパイラの実行自体は重い → よく使われる所だけを対象とする #jjug 64/127
Slide 65
Slide 65 text
小まとめ: JVMはどのように プログラムを動かすのか #jjug 65/127
Slide 66
Slide 66 text
JVMはコンピュータよりも抽象度の高い 枠組みを提供している ◼ オブジェクトシステム ◼ GC ◼ 仮想関数テーブル経由の呼び出し ◼ スタックフレームの保護 JVMの計算はバイトコードインタプリタと JITコンパイラの組み合わせで実現される #jjug 66/127
Slide 67
Slide 67 text
演目 言語処理系の構成 コンピュータはどのように プログラムを動かすのか JVMはどのように プログラムを動かすのか JVM言語処理系の構成: Kinkを題材に #jjug 67/127
Slide 68
Slide 68 text
そもそも JVM言語処理系ってなに? #jjug 68/127
Slide 69
Slide 69 text
JVM言語処理系 本セッションでは ◼ 実行系がJVM上で動く言語処理系、 ◼ または実行系がJVMそのものである 言語処理系、 ◼ ただしJava以外のもの としておきます #jjug 69/127
Slide 70
Slide 70 text
#jjug JVM言語処理系 Scala, Kotlin, Groovy, JRuby, Jython, Clojure, Kawa, Frege, Eta, BeanShell, Pnuts, ... and Kink! 70/127
Slide 71
Slide 71 text
なぜJVM上で 処理系を作るの? #jjug 71/127
Slide 72
Slide 72 text
#jjug GC JVMにGCが任せられる マルチスレッドの処理系で実用的に動く GCを作ることは、とてもむずかしい ◼ CRubyやCPythonは、各スレッドを 排他的に動かして問題を回避している (Global Interpreter Lock) ◼ Luaの処理系は単一スレッドで動く 72/127
Slide 73
Slide 73 text
#jjug 移植性 つくった処理系がWindowsでもLinuxでも macOSでも動くのは嬉しい 73/127
Slide 74
Slide 74 text
#jjug 性能 プログラムをJavaバイトコードに翻訳する 場合、JITコンパイルのおかげで速い 74/127
Slide 75
Slide 75 text
#jjug Java API リッチなJava APIが使える リフレクションのおかげで、 JVM言語の側からJava APIを操作することも 簡単 75/127
Slide 76
Slide 76 text
JVM言語処理系の構成 #jjug 76/127
Slide 77
Slide 77 text
基本的な考え方 JVMの性能面の利点を活かすため、 JVMの仕組みをできればそのまま使いたい そのまま使えない場合は ◼ JVMの仕組みとのマッピングを頑張るか、 ◆ invokedynamicはこれを楽にする命令 ◼ それも難しい場合は自前の仕組みを作る #jjug 77/127
Slide 78
Slide 78 text
JVM言語処理系の構成の傾向 Scala Kotlin Groovy JRuby Kink 実行系が JVMそのもの 実行系に 自前の要素が多い #jjug 78/127
Slide 79
Slide 79 text
ここから先はKink処理系の実装の中で、 JVMの仕組みのどの部分が ◼ 使えるか ◼ 使えないか ◼ 使える可能性があるか を見ていきます #jjug 79/127
Slide 80
Slide 80 text
Kinkとその処理系 #jjug 80/127
Slide 81
Slide 81 text
Kinkってこんな言語 :new_dog <- { new_val( 'bark' { 'Bow' } 'howl' { 'Wow' } ) } :Dog <- new_dog stdout.print_line(Dog.bark) # => Bow stdout.print_line(Dog.howl) # => Wow :Another_dog <- new_dog Another_dog:howl <- { 'Grrr' } stdout.print_line(Another_dog.bark) # => Bow stdout.print_line(Another_dog.howl) # => Grrr #jjug Kink言語マニュアル: 特徴 81/127
Slide 82
Slide 82 text
Kinkの処理系 kinkコマンド 翻訳系 実行系 Qcode Kinkソースコード Qcode: 自前のバイトコード を Javaで実装された インタプリタで実行 ひとつのコマンドで翻訳系・実行系をまかなう RubyやPythonなどと同様 #jjug 82/127
Slide 83
Slide 83 text
Kinkの処理系: 翻訳系 *5 ソースコード トークン列 AST Qcode Itree Wire 最適化 最適化 木構造の中間表現 フラットな中間表現 #jjug 83/127
Slide 84
Slide 84 text
Kinkの処理系: 実行系 オブジェクト システム メモリ管理 計算 スタック 自前 継承のないオブジェクトシステム JVMのGCに依存 自前のバイトコードインタプリタ 自前のスタック管理 #jjug 84/127
Slide 85
Slide 85 text
Kink: オブジェクトシステムと メモリ管理 #jjug 85/127
Slide 86
Slide 86 text
Kinkは継承のないオブジェクトシステムを 採用しています 最初のオブジェクト指向言語である Simula Iと同様! #jjug 86/127
Slide 87
Slide 87 text
クラスベースの継承 値 データ X データ Y クラス 親クラス メソッド a メソッド b メソッド b メソッド c instance-of is-a 例: ⚫ Java ⚫ Smalltalk ⚫ Ruby ⚫ C++ #jjug 87/127
Slide 88
Slide 88 text
プロトタイプベースの継承 値 データ X データ Y プロト タイプ プロト の親 メソッド a メソッド b メソッド b メソッド c delegate-to delegate-to 例: ⚫ JavaScript ⚫ SELF ⚫ Io ※ Luaは変わり種 #jjug 88/127
Slide 89
Slide 89 text
delegate-to関係、必要? 半端な値としての「プロトタイプ」の 存在が気持ち悪い 言語仕様に現れるべきものじゃなくない? 処理系の実装が裏で頑張ればよくない? #jjug 89/127
Slide 90
Slide 90 text
Kinkのオブジェクトシステム 値 データ X データ Y メソッド a メソッド b メソッド c #jjug 90/127
Slide 91
Slide 91 text
#jjug 値をつくる 単にnew_val関数を呼び出して値を作る # 色 new_val( 'R' 64 'G' 128 'B' 255 ) 91/127
Slide 92
Slide 92 text
#jjug トレイト 複数の値で共有するフィールドのかたまりを トレイトと呼ぶ(実体はただの配列) # 色のトレイト :Color_trait <- [ 'lightness' {[:C] (C._max + C._min) // 2 } '_max' {[:C] [C.R C.G C.B].fold(0 COMPARE$max) } '_min' {[:C] [C.R C.G C.B].fold(255 COMPARE$min) } ] 92/127
Slide 93
Slide 93 text
#jjug トレイトを使う トレイトはnew_val関数の引数として 展開して使う new_val( # 色のトレイトを展開 ... Color_trait 'R' 64 'G' 128 'B' 255 ) 93/127
Slide 94
Slide 94 text
意味は下記と同じ #jjug new_val( # トレイトが展開された結果 'lightness' { 省略 } '_max' { 省略 } '_min' { 省略 } 'R' 64 'G' 128 'B' 255 ) このとおり実装すると無駄が多いので、 そこは実行系が工夫する 94/127
Slide 95
Slide 95 text
どう実装する? Javaのクラスベースのオブジェクトシステム へのマッピングはしんどい → 自前実装 #jjug 95/127
Slide 96
Slide 96 text
オブジェクトシステムの実装 Kinkの値: Valクラス commonVarMapping traitVarMapping ownVarMapping 全ての値に共通の フィールド群(不変): k1→v1, k2→v2,,, 複数の値に共有される フィールド群(不変): k1→v1, k2→v2,,, 値固有の フィールド群(可変): k1→v1, k2→v2,,, #jjug 96/127
Slide 97
Slide 97 text
メモリ管理 Kinkの値: Valクラス commonVarMapping traitVarMapping ownVarMapping k1→v1, k2→v2,,, k1→v1, k2→v2,,, k1→v1, k2→v2,,, 値同士の参照関係はJavaオブジェクトの参照関係に マッピングされている → メモリ管理はJavaのGCに丸投げできる #jjug 97/127
Slide 98
Slide 98 text
Kink: 計算とスタック管理 #jjug 98/127
Slide 99
Slide 99 text
Kinkは限定継続を使って 例外やコルーチンを実現しています #jjug 99/127
Slide 100
Slide 100 text
限定継続とは スタックを切り貼りする仕組み Kinkではreset関数とshift関数の組み合わせで 限定継続を操作 #jjug 100/127
Slide 101
Slide 101 text
reset & shift: 継続の保存 reset: スタック上にタグを打つ tag resetブロック内の処理 tag shift: タグまでの切片を関数(=継続)に保存 tag tag kont = #jjug 101/127
Slide 102
Slide 102 text
継続は呼び出せる kont(Val): 継続の呼び出し: 継続に保存されたスタック切片を貼り付ける よその処理 tag #jjug 102/127
Slide 103
Slide 103 text
スタックをどう実装する? JVMのスタックをそのまま使うのは難しい ◼ JVMにはスタックをプログラムから 操作する方法がない ※ でも世の中には無茶する人がいる ※ Quasarライブラリは例外とバイト コード変換で無理矢理操作 ※ Project Loomで状況が変わるかも → Kinkは自前のスタックを実装 #jjug 103/127
Slide 104
Slide 104 text
計算をどう実装する? KinkプログラムをJavaバイトコードに 翻訳するのは苦労が多そう ◼ 自前のオブジェクトシステム ◆ putfield/getfield命令が使えない ◼ 自前の呼び出しスタック ◆ invoke*命令が使えない → 自前のバイトコードインタプリタ #jjug 104/127
Slide 105
Slide 105 text
Kink: 性能 #jjug 105/127
Slide 106
Slide 106 text
ベンチマーク: たらいまわし関数 #jjug :tarai <- {(:X :Y :Z) (X <= Y).if_else( { Y } { tarai( tarai(X - 1 Y Z) tarai(Y - 1 Z X) tarai(Z - 1 X Y) ) } ) } tarai(13 6 0) 106/127
Slide 107
Slide 107 text
#jjug tarai(13 6 0) 3.8 秒 7.7 秒 12.0 秒 30.3 秒 43.5 秒 Ruby 2.5.1 Python 3.6.7 GNU awk 4.1.4 GNU bc 1.07.1 Kink @ Xeon E5-2699 v4 107/127
Slide 108
Slide 108 text
プロファイリング #jjug 紫色の部分がスタック操作 バイトコードインタプリタと合わせて、4割~を占める → 自前実行系の限界? 108/127
Slide 109
Slide 109 text
性能改善の余地 計算をJVMにプッシュバックすれば 性能改善が期待できる、かも ◼ JRuby方式: ◆ 自前バイトコードから JavaバイトコードへのJITコンパイル ◆ 最も実行される部分は、さらにJVMが 機械語にJITコンパイルする ◼ Graal+Truffleに期待できる? #jjug 109/127
Slide 110
Slide 110 text
まとめ #jjug 110/127
Slide 111
Slide 111 text
JVM言語処理系の構成にはさまざまな バリエーションがある JVMの仕組みがそのまま使えると嬉しい ◼ GC: 自前で作るのは大変! ◼ JITコンパイラ: 計算が速くなる JVMの仕組みへのマッピングはときに大変 #jjug 111/127
Slide 112
Slide 112 text
演目 言語処理系の構成 コンピュータはどのように プログラムを動かすのか JVMはどのように プログラムを動かすのか JVM言語処理系の構成: Kinkを題材に #jjug 112/127
Slide 113
Slide 113 text
参考文献 #jjug 113/127
Slide 114
Slide 114 text
中田育夫 『コンパイラ: つくりながら学ぶ』, 2017 ◼ コンパイラと実行系をつくる ◼ 易しくて良い本 #jjug 114/127
Slide 115
Slide 115 text
Abelson, Sussman, Sussman 『Structure and Interpretation of Computer Programs』, 1996 ◼ いわゆる「SICP」 ◼ Scheme言語の上で、Scheme言語の 実行系を作ろう!という本 ◼ 5章がバイトコードインタプリタ ◆ 自前のメモリ管理も扱われています #jjug 115/127
Slide 116
Slide 116 text
Patterson, Hennessy 『Computer Organization and Design: RISC-V Edition』, 2017 ◼ いわゆる「パタヘネ」 ◆ 「ヘネパタ」は別書なので注意! ◼ CPUがプログラムをどうやって動かすか、 という本 #jjug 116/127
Slide 117
Slide 117 text
Pierce『型システム入門』, 訳書2013 ◼ いわゆる「TAPL」 ◼ 型システムの教科書 ◼ 序盤の3章~7章が特にオススメ ◆ 型を導入する準備として、型のない 項書換え系が解説されている ◆ 型なしラムダ計算はこれでバッチリ! #jjug 117/127
Slide 118
Slide 118 text
中田育夫 『コンパイラの構成と最適化』, 2版, 2009 ◼ 主に最適化の本 ◼ むずかしい #jjug 118/127
Slide 119
Slide 119 text
浅井健一 「shift/resetプログラミング入門」, 2011 ◼ 限定継続を紹介する文書 #jjug 119/127
Slide 120
Slide 120 text
Gasbichler, Sperber 「Final Shift for call/cc: Direct Implementation of Shift and Reset」, 2002 ◼ Scheme処理系に限定継続を実装したよ、 という論文 ◼ Kinkにおける限定継続の実装は、3節の 「Direct Implementation」に該当する #jjug 120/127
Slide 121
Slide 121 text
Chambers, Ungar, Lee 「An Efficient Implementation of SELF」, 1991 ◼ SELF言語の実行系をどうやって高速化 したか ◼ ChromeのJSエンジンであるV8でも参考 にされています: Fast Property Access #jjug 121/127
Slide 122
Slide 122 text
脚注 #jjug 122/127
Slide 123
Slide 123 text
*1 翻訳系、実行系 次の分け方が普通(中田 (2017)) ◼ 変換系 = Translator ◼ 翻訳系 = Compiler ◼ 通訳系 = Interpreter 変換は翻訳の亜種/一部と割り切りました 「通訳」は分かりづらい、「インタプリタ」 はソースを直接動かすものと誤解されがち、 ということで「実行系」としました #jjug 123/127
Slide 124
Slide 124 text
*2 プログラムをロード 実際は、プログラムの実行ファイルを、 プロセスの仮想アドレス空間にメモリマップ します ◼ @akachochin 「Linuxのユーザプロセスのメモリマッ プについて」, 2017 #jjug 124/127
Slide 125
Slide 125 text
*3 CPUの概念図 Patterson, Hennessy (2017) のFigure 4.11を 参考にしました #jjug 125/127
Slide 126
Slide 126 text
*4 JITコンパイラ Graalの場合、JITコンパイラの入力は Javaバイトコードに限りません #jjug 126/127
Slide 127
Slide 127 text
*5 Kinkの翻訳系 ソース: FunHelper#compile #jjug 127/127