为什么Seq模块中的某些功能已经优化而其他功能不在F#中?

时间:2012-06-04 21:53:33

标签: performance collections f#

这是我之前question关于Seq模块的itermap函数与Array和{{{{{}}相比慢得多的后续行动1}}模块等价物。

查看源代码,我可以看到ListisEmpty等函数执行非常简单的类型检查,以便在使用length之前优化数组和列表。< / p>

IEnumerator

[<CompiledName("IsEmpty")>] let isEmpty (source : seq<'T>) = checkNonNull "source" source match source with | :? ('T[]) as a -> a.Length = 0 | :? list<'T> as a -> a.IsEmpty | :? ICollection<'T> as a -> a.Count = 0 | _ -> use ie = source.GetEnumerator() not (ie.MoveNext()) [<CompiledName("Length")>] let length (source : seq<'T>) = checkNonNull "source" source match source with | :? ('T[]) as a -> a.Length | :? ('T list) as a -> a.Length | :? ICollection<'T> as a -> a.Count | _ -> use e = source.GetEnumerator() let mutable state = 0 while e.MoveNext() do state <- state + 1; state 的情况下,可以采用相同的方法来大大提高其性能,当我shadowed iter函数时,它比内置版本显着提升:

iter

我的问题是,鉴于[<CompiledName("Iterate")>] let iter f (source : seq<'T>) = checkNonNull "source" source use e = source.GetEnumerator() while e.MoveNext() do f e.Current; 模块中的某些函数已针对特定集合类型(数组,列表&lt; T&gt;等)进行了优化,如何使用其他函数,例如{{1} }和Seq没有以类似的方式进行优化?

此外,在iter函数的情况下,正如@mausch指出的那样,是否不可能对nth使用类似的方法(见下文)并为不同的集合类型构建专门的迭代器?

map

非常感谢提前。

2 个答案:

答案 0 :(得分:4)

  

在iter的情况下,可以采用相同的方法来大大提高其性能

我认为这是你问题的答案所在。你的测试是人为的,并没有真正测试这些方法的任何现实世界的例子。您测试了10,000,000次迭代这些方法,以便在ms中获得时间差异。

转换回每件商品的费用,这里是:

          Array   List
Seq.iter   4 ns    7 ns
Seq.map   20 ns   91 ns

这些方法通常每个集合使用一次,这意味着此成本是算法性能的另一个线性因素。在最糟糕的情况下,您在列表中的每个项目丢失的次数少于100 ns(如果您非常关心性能,则不应使用该项目。)

将此与length的情况进行对比,isEmpty在一般情况下始终是线性的。通过添加此优化,您可以为忘记手动缓存长度的人提供巨大的好处,但幸运的是总是给出一个列表。

同样,您可以多次调用type Custom() = interface IEnumerable with member x.GetEnumerator() = return seq { yield 1 yield 2 } interface IList with member x.Item with get(index) = index member x.Count = 12 let a = Custom() a |> Seq.iter (v -> printfn (v.ToString())) ,如果您可以直接询问,添加另一个对象创建是愚蠢的。 (这不是一个强有力的争论)

要记住的另一件事是,这些方法都不会实际查看输出的多个元素。您希望以下代码做什么(排除语法错误或缺少方法)

{{1}}

答案 1 :(得分:1)

从表面上看,Seq.length / isEmpty中的类型检查似乎是错误的。我假设大多数Seq函数不对正交性执行此类检查:List / Array模块中已存在特定于类型的版本。为什么要复制它们?

这些检查在LINQ中更有意义,因为它只直接使用IEnumerable