performance - Why are some functions in the Seq module optimized whilst others were not in F#? -
this follow previous question regarding seq module's iter , map functions being slower compared array , list module equivalents.
looking @ source, can see functions such isempty , length performs simple type check optimize arrays , lists before resorting using ienumerator.
[<compiledname("isempty")>] let isempty (source : seq<'t>) = checknonnull "source" source match source | :? ('t[]) -> a.length = 0 | :? list<'t> -> a.isempty | :? icollection<'t> -> a.count = 0 | _ -> use ie = source.getenumerator() not (ie.movenext()) [<compiledname("length")>] let length (source : seq<'t>) = checknonnull "source" source match source | :? ('t[]) -> a.length | :? ('t list) -> a.length | :? icollection<'t> -> a.count | _ -> use e = source.getenumerator() let mutable state = 0 while e.movenext() state <- state + 1; state in case of iter same approach can done vastly improve performance, when shadowed iter function presented significant gains on built-in version:
[<compiledname("iterate")>] let iter f (source : seq<'t>) = checknonnull "source" source use e = source.getenumerator() while e.movenext() f e.current; my question is, given of functions in seq module optimized use specific collection types (arrays, list< t>, etc.) how come other functions such iter , nth not optimized in similar way?
also, in case of map function, @mausch pointed out, not possible employ similar approach enumerable.select (see below) , build specialized iterators different collection types?
public static ienumerable<tresult> select<tsource, tresult>(this ienumerable<tsource> source, func<tsource, tresult> selector) { if (source == null) throw error.argumentnull("source"); if (selector == null) throw error.argumentnull("selector"); if (source enumerable.iterator<tsource>) return ((enumerable.iterator<tsource>) source).select<tresult>(selector); if (source tsource[]) return (ienumerable<tresult>) new enumerable.whereselectarrayiterator<tsource, tresult>((tsource[]) source, (func<tsource, bool>) null, selector); if (source list<tsource>) return (ienumerable<tresult>) new enumerable.whereselectlistiterator<tsource, tresult>((list<tsource>) source, (func<tsource, bool>) null, selector); else return (ienumerable<tresult>) new enumerable.whereselectenumerableiterator<tsource, tresult>(source, (func<tsource, bool>) null, selector); } many in advance.
in case of iter same approach can done vastly improve performance
i think answer question is. test artificial, , doesn't test real world examples of these methods. tested 10,000,000 iterations of these methods in order differences in timing in ms.
converted per item costs, here are:
array list seq.iter 4 ns 7 ns seq.map 20 ns 91 ns these methods typically used once per collection, meaning cost additional linear factor algorithms performance. in worst case losing less 100 ns per item in list (which shouldn't using if care performance much).
contrast case of length linear in general case. adding optimization provide enormous benefit forgot manually cache length luckily given list.
similarly may call isempty many times, , adding object creation silly if can ask directly. (this 1 isn't strong argument)
another thing keep in mind neither of methods looks @ more 1 element of output. expect following code (excluding syntax errors or missing methods)
type custom() = interface ienumerable member x.getenumerator() = return seq { yield 1 yield 2 } interface ilist member x.item get(index) = index member x.count = 12 let = custom() |> seq.iter (v -> printfn (v.tostring()))
Comments
Post a Comment