Search code examples
c#implementationilmscorlib

Possible to Inspect Innards of Core C# Functionality


I was struck today, with the inclination to compare the innards of Buffer.BlockCopy and Array.CopyTo. I am curious to see if Array.CopyTo called Buffer.BlockCopy behind the scenes. There is no practical purpose behind this, I just want to further my understanding of the C# language and how it is implemented. Don't jump the gun and accuse me of micro-optimization, but you can accuse me of being curious!

When I ran ILdasm on mscorlib.dll I received this for Array.CopyTo

.method public hidebysig newslot virtual final 
    instance void  CopyTo(class System.Array 'array',
                          int32 index) cil managed
{
  // Code size       0 (0x0)
} // end of method Array::CopyTo

and this for Buffer.BlockCopy

.method public hidebysig static void  BlockCopy(class System.Array src,
                                            int32 srcOffset,
                                            class System.Array dst,
                                            int32 dstOffset,
                                            int32 count) cil managed internalcall
{
  .custom instance void System.Security.SecuritySafeCriticalAttribute::.ctor() = ( 01 00 00 00 ) 
} // end of method Buffer::BlockCopy

Which, frankly, baffles me. I've never run ILdasm on a dll/exe I didn't create. Does this mean that I won't be able to see how these functions are implemented? Searching around only revealed a stackoverflow question, which Marc Gravell said

[Buffer.BlockCopy] is basically a wrapper over a raw mem-copy

While insightful, it doesn't answer my question if Array.CopyTo calls Buffer.BlockCopy. I'm specifically interested in if I'm able to see how these two functions are implemented, and if I had future questions about the internals of C#, if it is possible for me to investigate it. Or am I out of luck?


Solution

  • I am going to answer my own question. I realize that this is bad style, but I could not have formulated the answer if it were not for the wonderful resources that previous answers have supplied me. Thank you.

    First off, those coming here and wondering how to, in general, inspect the innards of C# functions, Tim posted a wonderful resource, ILSpy. This works in the cases where the methods are not defined externally. When they are defined externally, it seems like the only hope for getting an answer is if you download the SSCLI 2.0. Since this is targeted for .Net 2.0 and not 4.0, the information I present may be dated. However, I will continue with the assumption that the methods in question have not changed much. After looking through the source files, I believe I can answer the question “does Array.CopyTo call Buffer.BlockCopy behind the scenes?”

    Before I get to the heart of the matter, others have pointed out that CopyTo calls Array.Copy. Array.Copy is defined externally, so I will change my query to “does Array.Copy call Buffer.BlockCopy behind the scenes?” A little tidbit that I found interesting is from the documentation of Array.CopyTo on MSDN

    If implementing System.Collections.ICollection is not explicitly required, use [Array.]Copy to avoid an extra indirection.

    Let me distinguish the types of checks that must be true for each function to perform:

    BlockCopy:

    1. Destination and Source cannot be null
    2. Destination and Source array are composed of primitives or strings but no objects.
    3. The lengths and offsets must be valid

    Array.Copy:

    1. Destination and Source cannot be null
    2. Destination and Source must be an array
    3. Several checks with method tables and ranks
    4. Slightly more in-depth length and offset checks
    5. Types must match somehow (either with un/boxing, casting, or widening)

    After these checks, BlockCopy calls m_memmove, which is self-explanatory, yet highly interesting. m_memmove is architecture dependent; forgoing traditional memmove on 32-bit machines in favor for a hand rolled 16 bytes at a time copy.

    Arrays, as can be imagined, hold more than just primitives.

    • If the source and destination array are of the same type, Copy calls m_memmove. Then, if the underlying type is not a primitive, a garbage collection action is enacted.
    • Else, depending on the type of elements passed in, Copy will unbox, box, cast, or widen each element as it transfers them. These functions, as far as I can tell, do not call m_memmove, and instead convert each element one at a time.

    Therefore, to answer my original question, “does Array.Copy call Buffer.BlockCopy behind the scenes?” Sort of, if we consider the similarities the two methods have in common dealing with m_memmove. BlockCopy will always call m_memmove whereas Copy will only call it if it is dealing with arrays of exactly the same type. It is then my recommendation, if one wanted to copy an array of bytes to ints, or something similar where they just cared about the raw data, to use BlockCopy as it will be able to take advantage of m_memmove.

    For reference: what .Net defines as a primitive (taken from cortypeinfo.h)

    1. Void
    2. Bool
    3. Char
    4. Signed and Unsigned Byte
    5. Signed and Unsigned short
    6. Signed and Unsigned int
    7. Signed and Unsigned long
    8. Float and double
    9. Signed and Unsigned IntPtr
    10. ELEMENT_TYPE_R (not sure what this is)