閱讀460 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Clojure世界: STM的統計

   年前一篇blog提過,寫了一個stm-profiler用於統計clojure STM的運行狀況,放在了github上:
https://github.com/killme2008/stm-profiler

   STM的事務在遇到寫衝突(多個事務寫同一個ref的時候)就會回滾事務並重試,通過stm-profiler你可以查看事務的重試次數,重試原因,以及每個reference的使用情況。使用很簡單,在lein的project.clj引用stm-profiler:
[stm-profiler "1.0.2-SNAPSHOT"]

注意,目前stm profiler僅支持clojure 1.3。

我們寫一個簡單例子:
(use 'stm)
(def a (ref 1))
(def b (ref 2))

(dotimes [_ 100] (future (dosync (alter a + 1) (alter b - 1))))
(Thread/sleep 1000)
(prn @a)
(prn @b)
(Thread/sleep 1000)
(prn "stm statistics" (stm-stats))
(prn "reference a statistics" (ref-stats a))
(prn "reference b statistics" (ref-stats b))

定義了兩個ref:a和b,然後用future啟動100個線程並發地發起同一個事務操作,對a加一,對b減一。最後打印a和b的值,使用stm-stats函數獲取stm的統計信息並打印,使用ref-stats獲取a和b兩個reference的統計信息並打印。

運行這個例子,在啟動的時候會有些警告信息,忽略即可(主要是因為stm profiler重新定義了一些跟STM相關的函數和宏,如dosync等,但是僅僅是添加了統計功能,並沒有修改他們原本的功能)。

在我機器上的一次輸出:
101
-98
"stm statistics" {"(alter a + 1)(alter b - 1)" {:not-running 11, :average-retry 5, :total-cost 1233, :get-fault 44, :barge-fail 224, :change-committed 227, :total-times 100, :average-cost 12}}
"reference a statistics" {"(alter a + 1)(alter b - 1)" {:alter 609, :get-fault 44, :barge-fail 224, :change-committed 227}}
"reference b statistics" {"(alter a + 1)(alter b - 1)" {:alter 114, :not-running 11}}

a和b的結果都沒問題。重點看打印的統計信息,(stm-stats)的輸出結果是:
{"(alter a + 1)(alter b - 1)" {:not-running 11, :average-retry 5, :total-cost 1233, :get-fault 44, :barge-fail 224, :change-committed 227, :total-times 100, :average-cost 12}}

這個結果是一個map,key是事務的form,而value就是該form的統計信息,也是一個map,具體各項的含義如下:
total-cost
所有事務的總耗時
100個事務耗時1233毫秒
total-times
事務運行次數
100次
average-cost
平均每個事務耗時
平均一個事務耗時12毫秒
average-retry
平均每個事務的重試次數  平均每個事務重試了5次才成功
not-running  當前事務不處於running狀態,可能是被其他事務打斷(barge),需要重試  因為not-running的原因重試了11次
get-fault
 讀取ref值的時候沒有找到read point之前的值,被認為是一次讀錯誤,需要重試
 因為讀ref錯誤重試了44次
barge-fail  打斷其他事務失敗次數,需要重試  嚐試打斷其他事務失敗而重試了224次
change-committed  在本事務read point之後有ref值獲得提交,則需要重試
 因為ref值被其他事務提交而重試了227次

    從輸出結果來看,這麼簡單的一個事務操作,每次事務要成功平均都需要經過5次的重試,最大的原因是因為ref的值在事務中被其他事務更改了,或者嚐試打斷其他正在運行的事務失敗而重試。關於clojure STM的具體原理推薦看這篇文章《Software transactional memory》。STM不是完美的,事務重試和保存每個reference的曆史版本的代價都不低。

    再看(ref-stats a)的輸出:
{"(alter a + 1)(alter b - 1)" {:alter 609, :get-fault 44, :barge-fail 224, :change-committed 227}}
    可以看到a在所有事務中的統計信息,返回的結果同樣是個map,key是使用了a的事務,value是具體的統計信息。各項的含義類似上表,不過這裏精確到了具體的reference。其中alter項是指對a調用alter函數了609次。ref-stats會輸出所有在事務中調用了a的函數的調用次數。

    通過stm profiler你可以分析具體每個事務的執行狀況,甚至每個reference的運行狀況,查找熱點事務和熱點reference等。stm-profiler還不完善,目前還不支持1.2(1.4測試是可以的)。希望有興趣的朋友加入進來一起完善。
文章轉自莊周夢蝶  ,原文發布時間2012-02-09

最後更新:2017-05-18 20:36:04

  上一篇:go  《KAFKA官方文檔》入門指南(四)
  下一篇:go  storm常見問題解答