26 May 2012

Accessibility modifiers on F# record types

In the note to self category.

type internal Internal = { Si : string }

means that the class Internal will be internal. The constructor and properties are technically public, but that is meaningless since the enclosing class is internal. However, if the Internal type implements public interfaces, then the public methods it implements are visible and usable by clients outside the assembly (provided they get hold of an instance, of course – they can’t construct one since they can’t see the name of the class).

type IGet = abstract Get : string

type internal Internal = 
    { Si : string }
    interface IGet with
        member x.Get = x.Si

If an instance gets out as an obj, clients can still call HashCode and Equals because those are exposed as public overrides from obj or as public interfaces, e.g. IComparable.

type Internal = internal { Si : string }

means that the class Internal is public, but the constructor and properties are internal.

Both can be combined, and private of course works too. Similar accessibility modifiers can be given to discriminated unions – it is not possible to give a different accessibility to individual union cases, just like it isn’t possible to give different modifiers to different fields of a record.

Share this post :

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!