5.2.4 監控機器的效能
5.2.4 監控機器的效能
模擬是有用的,不僅對於一個假定的機器的設計的正確性進行驗證,而且
對於度量機器的效能。例如,在我們的模擬程式中,我們能夠安裝一個
測量器,它能度量一個計算中的棧操作所使用的次數。為了做到這一點,我們
修改了我們的模擬的棧,來跟蹤被儲存在棧上的次數暫存器的值,和棧達到
的最大深度,並且加一個訊息到棧的介面,來打印出棧的統計資訊,如下所示。
我們也加一個操作到基本的機器模型中來列印棧的統計資訊。通過在make-new-machine中
把 the-ops 初始化到如下的結構中。
(list (list 'initialize-stack (lambda () (stack 'initialize)))
(list 'print-stack-statistics (lambda () (stack 'print-statistics))))
這裡有make-stack的新版本的程式:
(define (make-stack)
(let ((s '())
(number-pushes 0)
(max-depth 0)
(current-depth 0))
(define (push x)
(set! s (cons x s))
(set! number-pushes (+ 1 number-pushes))
(set! current-depth (+ 1 current-depth))
(set! max-depth (max current-depth maxdepth)))
(define (pop)
(if (null? s)
(error "Empty stack --POP")
(let ((top (car s)))
(set! s (cdr s))
(set! current-depth (- current-depth 1))
top)))
(define (initialize)
(set! s '())
(set! number-pushes 0)
(set! max-depth 0)
(set! current-depth 0)
'done)
(define (print-statistics)
(newline)
(display (list 'total-pushes '= number-pushes
'maximum-depth '= max-depth)))
(define (dispatch message)
(cond ((eq? message 'push) push)
((eq? message 'pop) (pop))
((eq? message 'initialize) (initialize))
((eq? message 'print-statistics) (print-statistics))
(else (error "Unknown request --STACK" message)))
)
dispatch
)
)
練習5.14到練習5.19描述了其它的有用的監控與呼叫特性,它能被新增到
暫存器機器的模擬器中。
練習5.14
使用在圖5.11中顯示的階乘機器,對於明顯的很小的n值,度量為了計算n的階乘,
需要的棧的最大深度和壓棧的次數。從你的資料,來確定壓棧操作的總次數和棧的最大深度的
以n為自變數的公式,在計算n的階乘時,任何一個n都大於1。注意,這些中的任何一個都是n
的線性函式,因此能被兩個常數確定。為了得以統計資訊的列印,你將不得不給階乘的機器
以實際 的引數,以帶有初始化棧和列印統計資訊的指令。你可能也要修改機器,讓它來重複地讀取n的值,計算階乘,然後列印結果。(正如我們在圖5.4中的求最大公約數的機器中的做法那樣),所以你將不必重複地呼叫 get-register-contents,set-register-contents!,和start.
練習5.15
新增一個指令的計算到暫存器機器的模擬之中。也就是讓機器的模型保持著對已執行的指令的數量進行跟蹤。擴充套件機器的模型的介面來接受一個新的訊息,它打印出指令的數量和重設為零。
練習5.16
為了對指令進行跟蹤,對模擬器設定一個實際引數。也就是在任何一個指令執行之前,
模擬器應該打印出指令的文字。讓機器模型接受trace-on 和trace-off訊息,來把跟蹤
開啟或者關閉。
練習5.17
擴充套件練習5.16中的指令跟蹤,為了在列印一個指令之前,模擬器打印出在控制器序列中的指令
與之相關聯的標籤。以一種方式實現這個任務時要小心了,不要把它和練習5.15中的指令計數
弄混亂了。你將不得不讓模擬器提供必要的標籤資訊。
練習5.18
修改5.2.1部分中的make-register程式,為了讓暫存器能被跟蹤。暫存器應該接受開啟或者關閉
跟蹤的訊息。當一個暫存器被跟蹤時,賦一個值給暫存器時應該列印暫存器的名稱,暫存器的舊值,被賦的新的值。擴充套件機器模型的介面,允許你能夠開啟與關閉對特定的暫存器的跟蹤。
練習5.19
阿麗莎要一個斷點的功能特性存在於模擬器中,為了能夠幫助她除錯機器設計。
你被安排為她做這個功能。當模擬器停下來時,她要能夠指定在控制器序列中的一個地方,
並且允許她檢查機器的狀態。你將要實現一個程式
(set-breakpoint <machine> <label> <n>)
這是在給定的標籤後面的第n條指令之前設定一個斷點。例如:
(set-breakpoint gcd-machine 'test-b 4)
這是在對暫存器a賦值之前在gcd-machine中安裝了一個斷點。當模擬器到達了斷點
處,它應該打印出標籤和斷點的偏移量並且停止執行指令。然後阿麗莎能夠使用
get-register-contents和set-register-contents!來操縱被模擬的機器的狀態。她應該
能繼續執行指令通過發出如下的指令:
(proceed-machine <machine>)
她應該也能夠取消一個特定的斷點,通過如下的程式:
(cancel-breakpoint <machine> <label> <n>)
或者是取消所有的斷點,通過如下的程式:
(cancel-all-breakpoints <machine>)