Racket和Chez Scheme一样都有time函数,用来打印函数的执行时间,所不同的地方在于,Chez Scheme的time函数会把分配的字节数也显示出来,这对于我们调试和优化代码很有帮助,不过Matthew Flatt并没有打算将Racket的time函数添加这一功能,尽管如此,Matthew Flatt提供了一个思路,只是稍微麻烦一些。
Chez Scheme的time函数打印的格式如下:
19 collections
0.201254290s elapsed cpu time, including 0.183228711s collecting
0.201678000s elapsed real time, including 0.183665000s collecting
160012160 bytes allocated, including 113015168 bytes reclaimed
一下是改进的time函数,名字叫time2:
(define (time2 proc lst)
(let ([mem-before (current-memory-use 'cumulative)])
(let-values ([(r cpu-time real-time gc-time) (time-apply proc lst)])
(let ([mem-now (current-memory-use 'cumulative)])
(values
r
(format "~ams elapsed cpu time" cpu-time)
(format "~ams elapsed real time" real-time)
(format "~ams elapsed gc time" gc-time)
(format "~a bytes allocated" (- mem-now mem-before)))))))
> (time2 make-list '(10000000 0))
"177ms elapsed cpu time"
"178ms elapsed real time"
"153ms elapsed gc time"
"160100112 bytes allocated"
(time2 make-bytes '(10000))
"0ms elapsed cpu time"
"0ms elapsed real time"
"0ms elapsed gc time"
"10544 bytes allocated"
分配的字节数不会非常精确,比如没有考虑多线程的情况,不过在REPL中倒是可以使用,基本上不会有太大的偏差。
current-memory-use和custodian
current-memory-use函数的参数有3种模式:
(current-memory-use [mode]) → exact-nonnegative-integer?
mode : (or/c #f ‘cumulative custodian?) = #f
第三种mode叫做 custodian? , 这是Racket提供的一种监护机制.
custodian 可以用来管理一组线程、文件流端口、TCP端口、TCP监听器、UDP sockets、字节转换器、place。当线程或者端口等等被创建时,它就置于(current-custodian)的监护之下,除了root custodian, 每一个custodian都处于其父级的custodian的管理之下,这样就形成了一个层次结构。
当一个custodian被( custodian-shutdown-all )关闭时,它会立即且强制终结其下管理的线程、端口等等。
custodian在很多场景中很有用,比如用于释放资源:
#lang racket
(define done-chan (make-channel))
(define (print-hello)
(define my-cust (make-custodian)) ;; 定义监护人,便于清理所有相关资源
(parameterize ([current-custodian my-cust])
(thread
(lambda ()
(let loop ()
(displayln "Hello")
(sleep 3)
(loop))))
(thread
(lambda ()
(let loop ()
(define item (channel-get done-chan))
(custodian-shutdown-all my-cust))))))
(print-hello)
;; (channel-put done-chan #t)
这段代码创建了一个custodian, 并启动了两个线程,线程由这个custodian管理((parameterize ([current-custodian my-cust])))。一个线程每隔3秒钟打印一个“Hello”,永不停止。另一个线程等待done-chan 消息,如果有消息,则关闭custodian, 同时关闭这个custodian下的两个线程,也就释放了资源。
custodian还可以用来限制内存的使用量:
(custodian-limit-memory
(current-custodian) (* 2 1024))
(define x (make-bytes (* 4 1024)))
运行这段代码,会提示out of memory(在REPL无效)。
欢迎加入Racket 隐修会 :731859928(QQ)