我在应用程序中有一个函数foo
,其目的是conj
一个元素到一个顺序集合的末尾。这些系列在其他功能上很远。有时集合是向量,有时它们是列表或惰性序列,方便了它们的功能。 Perforce,foo
返回一个同样由其他下游函数使用的向量,这些函数可能会或可能不会关心它们获得什么样的顺序集合。
在foo
新数据之前,将xs
的参数(vec xs)
强制转换为conj
的向量很容易。我的问题是,如果xs
已经成为一个向量,我是否支付了可避免的(并且可能是多项式!)价格,或者vec
及其被调用者“智能”是否足以绕过冗余分配?
如果clojure.lang.LazilyPersistentVector/create
的参数是vec
而不是vector?
,source for vec
会显示最终调用clojure.lang.IObj
。在我挖掘答案的那一刻,我认为在SO中提出一个关于设计意图的问题可能更聪明,而不是深入研究未来可能会改变的实施内容。
答案 0 :(得分:4)
我找到了一个涵盖该主题的interesting blog post,似乎是由Clojure语言开发人员撰写的。重要的部分是向下的方式:
在...上调用vec
时
IPersistentVector - 如果它已经是一个向量,则删除meta并返回一个新实例。如上所述,这种情况发生了很多,现在是快速恒定时间操作,而不是线性时间操作。
除非我错过了一些上下文,否则这似乎表明vec
已经被优化(截至2015年1月),对已经是向量的集合进行了恒定时间操作。
为了完整起见,我使用Criterium运行测试(感谢@Thumbnail的想法)。我在带有M3处理器的Surface Pro 4上运行它,这是非常弱的。期待你的时间缩短:
(let [small-v (into [] (range 1000))
large-v (into [] (range 1000000))]
(do
(c/bench (vec small-v))
(println "-----")
(c/bench (vec large-v))))
Evaluation count : 2902458480 in 60 samples of 48374308 calls.
Execution time mean : 18.612868 ns ; <----------
Execution time std-deviation : 1.153643 ns
Execution time lower quantile : 17.731625 ns ( 2.5%)
Execution time upper quantile : 22.199789 ns (97.5%)
Overhead used : 3.054999 ns
Found 6 outliers in 60 samples (10.0000 %)
low-severe 6 (10.0000 %)
Variance from outliers : 46.7497 % Variance is moderately inflated by outliers
-----
Evaluation count : 3122779260 in 60 samples of 52046321 calls.
Execution time mean : 16.303825 ns ; <----------
Execution time std-deviation : 0.614467 ns
Execution time lower quantile : 15.727943 ns ( 2.5%)
Execution time upper quantile : 17.949363 ns (97.5%)
Overhead used : 3.054999 ns
Found 5 outliers in 60 samples (8.3333 %)
low-severe 1 (1.6667 %)
low-mild 4 (6.6667 %)
Variance from outliers : 23.8541 % Variance is moderately inflated by outliers
时间几乎相同(彼此相差2ns)。这似乎证实了上述引用。