庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

Clojure世界: STM的统计

Posted on 2012-02-09 20:55 dennis 阅读(3137) 评论(2)  编辑  收藏 所属分类: my open-sourceClojure
    年前一篇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测试是可以的)。希望有兴趣的朋友加入进来一起完善。

转载请注明出处:http://www.blogjava.net/killme2008/archive/2012/02/09/369694.html

评论

# re: clojure STM的统计  回复  更多评论   

2012-02-10 09:31 by 电脑K歌
原来是个写代码的高手啊

# re: Java编程打开运行exe程序  回复  更多评论   

2012-02-11 10:55 by tb
恩不错呀

只有注册用户登录后才能发表评论。


网站导航: