Search code examples
eventsf#abstract-classperformance-testingdowncast

F# Downcasting Slow?


I have a situation where I need to downcast twice in one procedure using :?>. I have a custom EventArgs class (which inherits System.EventArgs), and an instance of an abstract class within that custom EventArgs. Upon receiving the event, I need to downcast twice. Once for the custom EventArgs, and once for the abstract class within that custom EventArgs. I have to do this potentially millions of times a day, so I'm wondering if there's anything inherently slow about downcasting.


Solution

  • For grins, I put together the following little function:

    let castToStream (o:Object) = o :?> Stream
    

    and called it with the following code:

    [<EntryPoint>]
    let main argv = 
        let stm1 = new FileStream("output.tmp", FileMode.Create, FileAccess.ReadWrite, FileShare.Read)
        let obj = stm1 :> Object
        let stm2 = castToStream obj
        0 // return an integer exit code
    

    When it is compiled, castToStream turns into this IL:

    .method public static class [mscorlib]System.IO.Stream 
        castToStream(object o) cil managed
    {
      // Code size       8 (0x8)
      .maxstack  8
      IL_0000:  nop
      IL_0001:  ldarg.0
      IL_0002:  unbox.any  [mscorlib]System.IO.Stream
      IL_0007:  ret
    } // end of method Program::castToStream
    

    which in this case is effectively 1 real instruction, unbox.any. Unbox.any for a reference type is equivalent to a castclass instruction. From the description, you'll take a one-time initial hit to load the type if it's not already loaded, then it's going to be a whatever magic is necessary to determine if the types are equivalent (likely using Type.IsAssignableFrom(), but I don't know for sure). However, unless your class hierarchy is super deep (and it shouldn't be), I would expect this to take microseconds on a typical machine.

    For the curious, I initially had the code for castToStream inline, but the f# compiler saw through my shenanigans and removed all the casting entirely.