contributed by <LanKuDot
>, <ktvexe
>
這邊我不是很懂,需要再閱讀些資料,研究其實作方式與相關應用劉亮谷
我參考這個LanKuDot"in time" 的意義要先搞懂,引述 dictionary.com 的解說:
Before a time limit expires, early enough, as in His speech begins at eight, so we've arrived in time. It is often put as in time for, as in Please come in time for dinner.
所以 just-in-time compiler 的意思是,在真正執行特定功能之際或期限超過前,才去編譯程式碼 jserv
>
:移動 data pointer 到下一個 cell<
:移動 data pointer 到上一個 cell+
:加一 data pointer 指向的 byte-
:減一 data pointer 指向的 byte.
:輸出 data pointer 所指向的 byte,
:輸入 1 byte 的資料到 data pointer 所指向的 cell[
:如果 data pointer 所指向的資料為 0,就跳到 ]
之後繼續執行,否則往下執行]
:跳回對應的[
繼續執行上課中問到BrainF*ck可否實現if-else,不知道如果回答Turing machine的定義有沒有切重要害劉亮谷
instruction pointer and data pointer李昆憶
[
,所以將 b
初始化為 1,如果遇到 ]
就減一,遇到[
就加一。當b
為 0 時,就代表找到成對的中括號。]
,改成0
,為了就是讓 data pointer 偵測迴圈終點。recursive call,進入迴圈執行。等迴圈結束後,再將0
改回]
。
==
的 precedence 比+=
、-=
高。李昆憶
[P16]:copy:x=y
,$y[-$x+$y]
。假設 x 存在 cell 0,y 存在 cell 1,data pointer 目前指向 x:那就會是 >[-<+>]
:
$y[-$t+$y]$t[-$x+$y+$t]
,另一個方法是將 y 的值存到 t 中,然後減掉 t,同時將 x y 加回來。假設 t 在 cell 2,data pointer 目前指向 x:>[->+<]>[-<<+>+>]
[P17]:if:if (x) { <foobar> };
[-]
。用一個 while loop 把那個 cell 減到 0。+[,.]
。先將目前的 cell 加一,確保他非 0,然後用一個 while loop,一直讀輸入,再輸出,直到遇到\0
。原本想直接跑 make bench-jit-x64
但出現缺少檔案的訊息 (我環境是 64 bit OS)
lua
:再跑 make
會出現 bit
module not found 的訊息
到官網下載 bitop
module 的 source code
解壓縮後執行 make
,可能會出現 include 的 header file 不在的訊息。檢查它所需的 header file 是否在 /usr/include/lua<ver>
,通常安裝 library 的 header file 會在 /usr/include
或 /usr/local/include
。找到後,修改 Makefile 中的 include 路徑。編完會產生一個 .so 檔。
sudo make install
,可能會出現 install: 無法建立普通檔案
這樣的訊息,可以直接建立對應的資料夾解決。
lua
的預設 module 路徑,然後修改 make
中對應的安裝路徑
*.dasc
dynasm.lua
,經過處理的 *.dasc
會輸出為 *.h
*.h
與 dynasm-driver.c
一起編譯成 jit-x64
dynasm-driver.c
包含 JIT 初始化,配置記憶體空間,及釋放記憶體空間專案中預設執行的 benchmark 有三:awib.b
、mandelbort.b
、hanoi.b
,執行的 compiler 為 jit-x64
將連續的 pointer 移動事件及連續對同一個 byte 的計算,整理起來計算有多少次,直接使用add
及sub
。
之前修課有寫過這個作業,jserv 老師提過增加新功能要以模組化的方式設計,提高可攜性及修改彈性LanKuDot
因為這些都牽涉到迴圈的運作,所以可以一併分析。觀察 bf 的原始碼,歸類一些特性 ($x
為 move to cell x
):
[-]
:清空目前的 data cell[-{move y cell}]
:包含目前的 cell,每隔 y cells 減 1,直到遇到值為 0 的 cell[- $1- $2]
:$1 = $1 - $2, than $2 = 0 (cell data 沒有負數,推測 $1 > $2)[- $1+ $2]
:$1 = $1 + $2, than $2 = 0[- $1- $2+ $victim]
:要注意 $1, $2, $victim 的移動量加總為 0
[- $1++++++ $2]
:$1 = $1 + 6 * $2, than $2 = 0[- $1------ $2]
:$1 = $1 - 6 * $2, than $2 = 0[- $1+++ $2---- $victim]
:
為了實現上面 section 的想法,必須處理 single loop:
紀錄 loop_end
可以在做完 single loop 簡化操作後,直接讓 p
指向 loop_end
。
這些都有幾個共通點:
[(減)+ (移動 (加)+/(減)+)+ 移動]
:()+
為出現1個或多個的意思設計一個 structure 將分析到的資訊包起來:
vaild_length
:紀錄 cell_move
與 victim_multiplier
的有效長度,或是其他類型的 single loop
.
,
victim_divisor
:victim 的除數。因為可能會出現 [---->+<]
這種情況,如果 victim 原為 12,則每次減 4 的話,下一個 cell 只會被加 3 (=12/4) 次。cell_move
:紀錄到下個 cell 的距離 (不是相對於 victim 的距離)victim_multiplier
:針對這個 cell 的操作,victim 要乘上的值呼叫 handle_single_loop()
會得到分析結果,接下來是使用流程:
victim_divisor
$cur_cell
+ victim_multiplier
* $victim
loop_end
jit-x64
讓我可以得到 data cell 的值並存到變數中?
-
→ 連續移動到第一個位址 <
或>
→ +
→ 連續移動到第二個位址 <
或>
→ +
→ 移動回原本的位址。
dst_1
儲存第一個位址要被歸零的位址差距:<
為 -1,>
為 +1dst_2
儲存第二個位址要被歸零的位址差距mul_1
、mul_2
:儲存第一二個乘數的值跟昆憶哥討論過後,他給我提示說,讀取 brainfunck 原始碼是在 util.h
的 read_file
,不過他就是整坨讀近來,沒有處理換行或是非有效字元等等問題。
所以開始處理在讀檔時所遇到的非有效 brainfuck 程式碼,不過我思考許久,我想不到確定可以比 jit-x64.dasc
中的 switch case 效能還要快的 parser 方式,因為原先 util.h
中是使用 fread
,只進行一次讀取,不過如果要在讀檔階段做 parse 那就必須一個字元一個字元判斷,我認為這會比起原先的讀檔還費時,所以我最後的作法有點像是把jit-x64.dasc
中的 switch case 搬到讀檔時處理,感覺不是很好的方式,所以我預測這樣的改寫效能可能不會提升。
但是實驗結果非常奇怪,其實我認為這組實驗數據應該是有問題的。
awib.b
的效能提升相當有限,這也是蠻奇怪的,如果說事前的 parser 真的能夠對效能提升有所幫助,那其提升的比例應該會與其 code size 的改變有關,見下表比較 code size 的改變再對照效能分析,發現 code size 減少最多的 awib.b
效能提升的比例卻不如 hanoi.b
。Origin Size | Parsed Size | |
---|---|---|
awib.b | 44463 | 31514 |
mandelbrot.b | 11669 | 11451 |
hanoi.b | 54593 | 53884 |
read_file
程式的 parse 應該與 jit-x64.dasc
中的 switch case 合併,這樣效能會比現在這個方式還要更佳。但因為我處理優化都是透過 function call,所以一開始就傳入處理好的字串會比較簡單。讓優化函式可以專注在目標 pattern 的判斷,而不用再處理非有效字元。LanKuDot
jit-x64.dasc
:
可以發現 hanoi.b
有很多 clear loop
注意這邊的測試是使用不同的電腦
Only Contraction:
Parse but don't duplicate memory:
Duplicate correct memory:
sysprog21
sysprog2016