Skip to content

Instantly share code, notes, and snippets.

@theburningmonk
Created June 4, 2012 16:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save theburningmonk/2869351 to your computer and use it in GitHub Desktop.
Save theburningmonk/2869351 to your computer and use it in GitHub Desktop.
Shadow the Seq module's iter and map functions to include type test
module Seq
// Shadowing the iter function is probably ok.
let iter f (seq : seq<'a>) =
match seq with
| :? array<'a> -> Array.iter f (seq :?> array<'a>)
| :? list<'a> -> List.iter f (seq :?> list<'a>)
| _ -> Seq.iter f seq
// Though you probably shouldn't shadow the map function!!
let map f (seq : seq<'a>) =
match seq with
| :? array<'a> -> Array.map f (seq :?> array<'a>) :> seq<'a>
| :? list<'a> -> List.map f (seq :?> list<'a>) :> seq<'a>
| _ -> Seq.map f seq
let toArray (seq : seq<'a>) =
match seq with
| :? array<'a> -> seq :?> array<'a>
| _ -> Seq.toArray seq
let toList (seq : seq<'a>) =
match seq with
| :? list<'a> -> seq :?> list<'a>
| _ -> Seq.toList seq
#load "Module1.fs"
let n = 10000000
let doNothing _ = ()
let incr x = x + 1
let arr = Array.init n (fun _ -> 0)
let rec buildList n acc i = if i = n then acc else buildList n (0 :: acc) (i + 1)
let lst = buildList n [] 0
#time
arr |> Array.iter doNothing // 14ms
arr |> Seq.iter doNothing // 17ms
arr |> Collections.Seq.iter doNothing // 56ms
lst |> List.iter doNothing // 19ms
lst |> Seq.iter doNothing // 19ms
lst |> Collections.Seq.iter doNothing // 89ms
arr |> Array.map incr // 31ms
arr |> Seq.map incr |> Seq.toArray // 21ms
arr |> Seq.map incr
|> Collections.Seq.toArray // 39ms
arr |> Collections.Seq.map incr
|> Collections.Seq.toArray // 233ms
lst |> List.map incr // 776ms
lst |> Seq.map incr |> Seq.toList // 768ms
lst |> Seq.map incr
|> Collections.Seq.toList // 853ms
lst |> Collections.Seq.map incr
|> Collections.Seq.toList // 1683ms
@mausch
Copy link

mausch commented Jun 4, 2012

Some functions, like Seq.isEmpty or Seq.length, already do reflection on the argument to optimize. But some others don't, like Seq.head, even though Enumerable.First has this optimization.
And Enumerable.Select does check for arrays and lists... maybe we should add such optimized shadowed versions to FSharpx?

@mausch
Copy link

mausch commented Jun 4, 2012

BTW Enumerable.Select does this optimization by creating specific enumerators for arrays and lists, so it doesn't kill lazy evaluation.

@mausch
Copy link

mausch commented Jun 4, 2012

It would be interesting to see if the F# 3.0 runtime has these optimizations.

@mausch
Copy link

mausch commented Jun 28, 2012

Just checked and FSharp.Core 4.3.0.0 does not specialize Seq.head or Seq.map

@theburningmonk
Copy link
Author

That's a shame, but easy enough to shadow those operations anyway, think it's worth adding to fsharpx?

@mausch
Copy link

mausch commented Jul 8, 2012

I think it's definitely worth checking if these optimizations really improve the performance in most cases.And if they do, then of course they would be a great match to add to FSharpx!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment