Seq.iter比原生映射慢:任何通用解决方案?

时间:2012-11-28 11:45:29

标签: generics compiler-construction f# inline functor

此问题已经出现过,其理由由John Palmer解答: why is Seq.iter and Seq.map so much slower?

let ar = Array.zeroCreate<int> (64*1024*1024)
#time
//Real: 00:00:00.171, CPU: 00:00:00.171, GC gen0: 0, gen1: 0, gen2: 0
for i in 0 .. ar.Length - 1 do
    ar.[i] <- ar.[i]*3

//Real: 00:00:00.327, CPU: 00:00:00.328, GC gen0: 0, gen1: 0, gen2: 0
ar |> Array.iteri(fun i _ -> ar.[i] <- ar.[i]*3)

//Real: 00:00:02.249, CPU: 00:00:02.250, GC gen0: 0, gen1: 0, gen2: 0
ar |> Seq.iteri(fun i _ -> ar.[i] <- ar.[i]*3)

我想知道是否存在某种“内联”或其他可以映射的通用机制,比如序列(最后已知的?)具体类型以加速这些行为。 例如,我有静态保证,我将迭代一个数组。

您是否知道理论上存在令人满意的解决方案? (那里有什么花哨的名字?)

是否有一些能够很好地承认和解决这个问题的语言?

3 个答案:

答案 0 :(得分:4)

你确实在F#中没有高级绑定类型,但你仍然可以使用内联函数并模拟一些类型类行为。 通过定义Functor,您可以获得原生映射的通用解决方案:

type Fmap = Fmap with
    static member ($) (_Functor:Fmap, x:List<_> ) = fun f -> List.map    f x  
    static member ($) (_Functor:Fmap, x:array<_>) = fun f -> Array.map   f x
    static member ($) (_Functor:Fmap, x:_ [,]   ) = fun f -> Array2D.map f x
    static member ($) (_Functor:Fmap, x:_ [,,]  ) = fun f -> Array3D.map f x
    static member ($) (_Functor:Fmap, x:_ [,,,] ) = fun f ->
        Array4D.init (x.GetLength 0) (x.GetLength 1) (x.GetLength 2) (x.GetLength 3) (fun a b c d -> f x.[a,b,c,d])

let inline fmap f x = (Fmap $ x) f

以fmap为例,这个函数是内联的,它将接受在重载中定义的任何类型,并且它将像直接调用函数一样快地执行(这是幕后实际发生的事情)。 为此,您可以使用fmap并丢弃结果,在您的情况下,您可能需要定义类似fmapi的内容以使索引可用。

请记住,如果您传递Seq&lt;'a&gt;,则应始终使用具体类型调用这些函数。它会失败,你不应该混合使用两种方法:subtype和ad-hoc多态。

答案 1 :(得分:2)

  

您是否知道理论上存在令人满意的解决方案? (那里有什么花哨的名字?)

你不能吃蛋糕并且吃它。

  

是否有一些能够很好地承认和解决这个问题的语言?

F#承认了这个问题并很好地解决了这个问题。您可以拥有清晰的性能配置文件和快速编译,如F#,或者您可能会有一个不可预测的性能配置文件和缓慢的编译,因为大量复杂的编译器负载优化传递,如此特殊情况。 F#选择了前(实用)解决方案。

答案 2 :(得分:0)

对于iter,您可以检查集合的类型并分派到最佳实现。遇到问题的地方是map,有些T<'b>需要返回T<'a>。这需要更高的kinded类型,F#不直接支持。以下是map尝试说明问题:

module AnySeq =
  let map<'a, 'b, 'c, 'd when 'a :> seq<'b> and 'd :> seq<'c>> (f: 'b -> 'c) (s: 'a) : 'd =
    let (!) x = unbox (box x)
    let tyDef = s.GetType().GetGenericTypeDefinition()
    if tyDef = typedefof<list<_>> then !(List.map f !s)
    elif tyDef = typedefof<array<_>> then !(Array.map f !s)
    else !(Seq.map f !s)

如果您尝试使用它将int list转换为float list

let ints = List.init 10000000 id
let floats = ints |> AnySeq.map float

您收到了值限制错误:

  

值'float'被推断为具有泛型类型的val浮点数:'_ a when'_a:&gt; SEQ

如果您向floats添加类型注释,它会起作用,但之后您还没有完成任何操作。