F# 将两个列表/数组映射到单个列表/数组的不同实现的性能
以下三种实现(F# 将两个列表/数组映射到单个列表/数组的不同实现的性能,f#,F#,以下三种实现(a,b,c)给出了相同的结果 let l1 = [1..10] let l2 = [11..20] let avg1 = fun (x, y) -> (x+y)/2 let avg2 x y = (x+y)/2 let a = l1 |> List.zip l2 |> List.map avg1 let b = List.map2 avg2 l1 l2 let c = (l1, l2) ||> List.map2 avg2 我试图确定哪种实现在速度
a
,b
,c
)给出了相同的结果
let l1 = [1..10]
let l2 = [11..20]
let avg1 = fun (x, y) -> (x+y)/2
let avg2 x y = (x+y)/2
let a = l1 |> List.zip l2 |> List.map avg1
let b = List.map2 avg2 l1 l2
let c = (l1, l2) ||> List.map2 avg2
我试图确定哪种实现在速度方面是最好的
这三种实现真的相同吗
映射是否实际生成了l1
和l2
元素的元组,或者是对l1
和l2
的引用被输入到映射器中
如果
列表
更改为数组
,结果是否会更改?对于b
和c
的计算完全相同。从F#源代码:
let inline (||>) (x1,x2) f = f x1 x2
顾名思义,内联函数在调用时是内联的,在进一步编译之前将c
的表达式转换为b
的表达式
a
在结果方面是等效的,但是当与“实际的”、更大的数据一起使用时,我希望它会更慢。我不希望编译器足够聪明,能够将压缩和投影组合到一个函数中,因此会有两次迭代,可能会分配一个完整的中间列表
对于仅迭代成批数据,数组通常比列表快。但是对于数组,您必须更加小心地将数据传递给谁,因为它们是可变的,不像F#list
可以从in F#interactive中获得粗略的性能评估。启用了[1..1000000]
和[1000001..2000000]
和#time
的列表后,a
的成本如下所示:
Real: 00:00:00.387, CPU: 00:00:00.436, GC gen0: 9, gen1: 4, gen2: 1
和b
确认加速:
Real: 00:00:00.149, CPU: 00:00:00.140, GC gen0: 3, gen1: 2, gen2: 0
与预期的c
相似。请注意,这不是一个非常精确的测量,在随后的运行中,我得到了b
或c
的0.080
秒
要将其放在透视图中,b
带数组:
Real: 00:00:00.009, CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0
由于列表中每个项目的对象开销,数组以很大的优势获胜。因此,这种类型的迭代丢弃缓冲区强烈倾向于使用数组来提高性能。不过,阵列需要一个大的对象分配,并最终取消分配,因此在某些情况下,重新使用现有阵列可以带来另一个加速
但是使用数组,特别是对数组进行变异,会使程序功能降低。即使在这样的情况下,数组的速度要快十几倍,这也可能是帮助编译器优化所付出的高昂代价。永远记住Knuth关于过早优化的观点。在极少数情况下,担心这些事情,因为它们比简洁、易读、健壮等更重要。这非常有用。谢谢