为什么在Racket的gvector中保留容量会使性能变差?

时间:2016-10-02 20:11:55

标签: performance vector profiling racket

在Racket 6.6中使用以下简单基准:

#lang racket
(require data/gvector)
(define (run)
  ;; this should have to periodically resize in order to incorporate new data
  ;; and thus should be slower
  (time (define v (make-gvector)) (for ((i (range 1000000))) (gvector-add! v i)) )
  (collect-garbage 'major)
  ;; this should never have to resize and thus should be faster
  ;; ... but consistently benchmarks slower?!
  (time (define v (make-gvector #:capacity 1000000)) (for ((i (range 1000000))) (gvector-add! v i)) )
  )

(run)

正确预留容量的版本会持续恶化。为什么?这肯定不是我期望的结果,并且与您在C ++(std :: vector)或Java(ArrayList)中看到的结果不一致。我是否以某种方式错误地进行基准测试?

示例输出:

cpu time: 232 real time: 230 gc time: 104
cpu time: 228 real time: 230 gc time: 120

2 个答案:

答案 0 :(得分:3)

一个基准测试评论:在您的微基准测试中使用in-range而不是range;否则,您需要在测量中包含构建百万元素列表的成本。

我在微基准测试中添加了一些额外的循环,以使其完成更多工作(并修复了range问题)。以下是一些结果:

#:capacity用于大容量会更慢。

== 5 iterations of 1e7 sized gvector, measured 3 times each way
with #:capacity
cpu time: 9174 real time: 9169 gc time: 4769
cpu time: 9109 real time: 9108 gc time: 4683
cpu time: 9094 real time: 9091 gc time: 4670
without
cpu time: 7917 real time: 7912 gc time: 3243
cpu time: 7703 real time: 7697 gc time: 3107
cpu time: 7732 real time: 7727 gc time: 3115

#:capacity用于小容量会更快。

== 20 iterations of 1e6 sized gvector, measured three times each way
with #:capacity
cpu time: 2167 real time: 2168 gc time: 408
cpu time: 2152 real time: 2152 gc time: 385
cpu time: 2112 real time: 2111 gc time: 373
without
cpu time: 2310 real time: 2308 gc time: 473
cpu time: 2316 real time: 2315 gc time: 480
cpu time: 2335 real time: 2334 gc time: 488

我的假设:它的GC开销。当支持向量发生突变时,Racket的世代GC会记住该向量,因此它可以在下一个次要集合中扫描它。当后备向量非常大时,在每个次要GC上扫描整个向量都会超过重新分配和复制的成本。使用更精细的记忆集粒度的GC(而不是......权衡)不会产生开销。

顺便说一下,看一下gvector代码,我发现了一些改进的机会。但他们并没有改变大局。

答案 1 :(得分:1)

使用因子10增加矢量大小我在DrRacket中得到以下内容 (关闭所有调试功能):

cpu time: 5245 real time: 5605 gc time: 3607
cpu time: 4851 real time: 5136 gc time: 3231

注意:如果第一个基准测试中遗留了垃圾,它可能会影响下一个基准测试。因此在再次使用之前使用收集垃圾(三次)。

另外......不要像我一样在DrRacket中制作基准 - 使用命令行。