07 May 2012

Making F# reflection faster

I don’t really believe I’m the first one to do this, but I couldn’t find anything that’s publically available. If you know about a mature implementation, please leave a comment!

I put up some code on https://bitbucket.org/kurt/fsreflect/wiki/Home that has a small API, mirroring FSharpValue’s PreComputeXXX reflective construction and deconstruction methods for unions and records. It does the exact same thing as the original methods, only faster.

As explained on the project page, the code uses two techniques. The first is on-the-fly IL code generation using DynamicMethod. This is used for the fast record and union construction code. The second is using delegates instead of MethodInfo.Invoke for the record and union readers, using a great trick introduced by Jon Skeet. The former is explained in a lot of places – the latter is explained perfectly by Mr Skeet already.

Anyway the code is pretty short and sweet, so if you’re interested please do have a browse.

Here are some gratuitous micro-benchmarks.

> repeat (fun i -> fastRecordCtor [| "2"; i; 3. |] :?> MyRecord)
Real: 00:00:00.198, CPU: 00:00:00.202, GC gen0: 73, gen1: 2, gen2: 1
val it : unit = ()
> repeat (fun i -> standardRecordCtor [| "2"; i; 3. |] :?> MyRecord) 
Real: 00:00:02.811, CPU: 00:00:02.808, GC gen0: 115, gen1: 0, gen2: 0
val it : unit = ()
> repeat (fun i -> fastUnionCtor [| "3"; i |] :?> MyUnion) 
Real: 00:00:00.150, CPU: 00:00:00.156, GC gen0: 50, gen1: 0, gen2: 0
val it : unit = ()
> repeat (fun i -> standardUnionCtor [| "3"; i |] :?> MyUnion) 
Real: 00:00:02.551, CPU: 00:00:02.542, GC gen0: 72, gen1: 0, gen2: 0
val it : unit = ()
> repeat (fun i -> fastRecordReader { S = "2"; i = i; f = 3.0 }) 
Real: 00:00:00.209, CPU: 00:00:00.218, GC gen0: 76, gen1: 0, gen2: 0
val it : unit = ()
> repeat (fun i -> standardRecordReader { S = "2"; i = i; f = 3.0 }) 
Real: 00:00:05.390, CPU: 00:00:05.397, GC gen0: 77, gen1: 1, gen2: 0
val it : unit = ()
> repeat (fun i -> fastUnionReader (Two ("s",i))) 
Real: 00:00:00.160, CPU: 00:00:00.171, GC gen0: 50, gen1: 0, gen2: 0
val it : unit = ()
> repeat (fun i -> standardUnionReader (Two ("s",i))) 
Real: 00:00:03.477, CPU: 00:00:03.478, GC gen0: 49, gen1: 0, gen2: 0
val it : unit = ()
Technorati Tags: ,,
Share this post : MSDN! Technet! Del.icio.us! Digg! Dotnetkicks! Reddit! Technorati!

1 comment:

  1. Hi Kurt, I cant find it at the links you give. Is this part of FSharpx.Extras now ?

    ReplyDelete