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关于过早优化的观点。在极少数情况下,担心这些事情,因为它们比简洁、易读、健壮等更重要。

这非常有用。谢谢