到目前为止,在我使用 Racket 的经验中,我没有过多考虑向量,因为我发现它们的主要好处——对元素的恒定时间访问——在你使用大量元素之前并不重要。
然而,这似乎不太准确。即使元素数量很少,向量也具有性能优势。例如,分配一个列表比分配一个向量要慢:
#lang racket
(time (for ([i (in-range 1000000)]) (make-list 50 #t)))
(time (for ([i (in-range 1000000)]) (make-vector 50 #t)))
>cpu time: 1337 real time: 1346 gc time: 987
>cpu time: 123 real time: 124 gc time: 39
并且检索元素也更慢:
#lang racket
(define l (range 50))
(define v (make-vector 50 0))
(time (for ([i (in-range 1000000)]) (list-ref l 49)))
(time (for ([i (in-range 1000000)]) (vector-ref v 49)))
>cpu time: 77 real time: 76 gc time: 0
>cpu time: 15 real time: 15 gc time: 0
顺便说一句,如果我们增加到 1000 万,这个性能比仍然成立:
#lang racket
(define l (range 50))
(define v (make-vector 50 0))
(time (for ([i (in-range 10000000)]) (list-ref l 49)))
(time (for ([i (in-range 10000000)]) (vector-ref v 49)))
>cpu time: 710 real time: 709 gc time: 0
>cpu time: 116 real time: 116 gc time: 0
当然,这些是综合示例,大多数程序不分配结构或使用
list-ref
。一百万次循环。 (是的,我故意捕获第 50 个元素来说明性能差异。)但它们也不是,因为在一个依赖列表的整个程序中,每次触摸这些列表时都会产生一些额外的开销,所有这些小效率低下都会导致整体运行时间变慢程序。
因此我的问题是:为什么不一直使用向量?在什么情况下,我们应该期望列表有更好的性能?
我最好的猜测是因为从 中取出元素的速度一样快。正面 列表,例如:
#lang racket
(define l (range 50))
(define v (make-vector 50 0))
(time (for ([i (in-range 1000000)]) (list-ref l 0)))
(time (for ([i (in-range 1000000)]) (vector-ref v 0)))
>cpu time: 15 real time: 16 gc time: 0
>cpu time: 12 real time: 11 gc time: 0
...列表在递归情况下是首选,因为您主要使用
cons
和 car
和 cdr
,并且它节省了处理列表的空间(向量不能在不复制整个向量的情况下被破坏并重新组合在一起,对吗?)但是在您存储和检索数据元素的情况下,无论长度如何,向量似乎都有优势。
最佳答案
由于list-ref
使用与索引线性的时间,除非用于短列表,否则很少可以使用。但是,如果访问模式是顺序的并且元素的数量可以变化,那么列表就可以了。看到一个对 50 个元素长的 fixnums 列表的元素求和的基准会很有趣。
但是,对数据结构的访问模式并不总是顺序的。
以下是我如何选择在 Racket 中使用的数据结构:
DATA STRUCTURE ACCESS NUMBER INDICES
List: sequential Variable not used
Struct: random Fixed names
Vector: random Fixed integer
Growable vector: random Variable integer
Hash: random Variable hashable
Splay: random Variable non-integer, total order
关于list - 在 Racket 中,列表相对于向量的优势是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27584416/