JVM言語の動き方・動かし方 /make-jvm-lang
by
Miyakawa Taku
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
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