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