使用序列长度,序列参考,序列映射等是否有任何缺点,而不是列表(长度列表-ref等),字符串(字符串长度,字符串-ref等),向量等的不同函数球拍?
答案 0 :(得分:4)
<强>性能。强>
考虑这个微小的基准:
#lang racket/base
(require racket/sequence)
(define len 10000)
(define vec (make-vector len))
(collect-garbage)
(collect-garbage)
(collect-garbage)
(time (void (for/list ([i (in-range len)])
(vector-ref vec i))))
(collect-garbage)
(collect-garbage)
(collect-garbage)
(time (void (for/list ([i (in-range len)])
(sequence-ref vec i))))
这是我机器上的输出:
; vectors (vector-ref vs sequence-ref)
cpu time: 1 real time: 1 gc time: 0
cpu time: 2082 real time: 2081 gc time: 0
是的,这是 3个数量级的差异。
为什么呢?好吧,racket/sequence
不是一个非常“智能”的API,即使向量是随机访问,sequence-ref
也不是。结合Racket优化器大量优化基本操作的能力,序列API是一个相当差的接口。
当然,这有点不公平,因为矢量是随机访问,而列表之类的东西则不是。但是,执行与上面完全相同的测试,但使用列表而不是向量仍会产生相当严峻的结果:
; lists (list-ref vs sequence-ref)
cpu time: 113 real time: 113 gc time: 0
cpu time: 1733 real time: 1732 gc time: 0
序列API 慢,主要是因为间接性很高。
现在,单凭性能并不是完全拒绝API的理由,因为在更高的抽象层次上工作具有明显的优势。也就是说,我认为序列API不是一个好的抽象,因为它:
...在实现中是不必要的有状态,这给接口的实现者带来了不必要的负担。
...不包含与列表不相似的内容,例如随机访问向量或哈希表。
如果您想使用更高级别的API,一种可能的选择是使用collections
package,它会尝试提供类似于racket/sequence
的API,但同时适应更多种类的数据结构拥有更完整的功能集。 免责声明:我是collections
包的作者。
再次考虑上述基准测试,性能仍然比直接使用底层函数更差,但它至少更易于管理:
; vectors (vector-ref vs ref)
cpu time: 2 real time: 1 gc time: 0
cpu time: 97 real time: 98 gc time: 10
; lists (list-ref vs ref)
cpu time: 104 real time: 103 gc time: 0
cpu time: 481 real time: 482 gc time: 0
你是否能负担得起的开销取决于你究竟在做什么,而且由你来自己打电话。只要执行某种动态调度,专门操作将始终比推迟它们的操作快一些。一如既往,请记住性能优化规则:不要猜测,测量。