Showing posts with label Expression Tree. Show all posts
Showing posts with label Expression Tree. Show all posts

Wednesday, December 25, 2013

Introducing CloneExtensions .NET cloning library


Blog has been moved to new address: marcinjuraszek.com. You'll be redirected to new site in few seconds. If you don't want to wait click here: Introducing CloneExtensions .NET cloning library

I've spent last two days working on my first open source .NET library named CloneExtensions. It gives you a smart way to clone your object instances without implementing any interface writing any additional Clone method at all. It uses Expression Tree to compile that Clone method for you right before you're trying to use GetClone<T> for given type T for the first time.

Project is in early phase but even now you can use it to clone plenty of different types:
  • Primitive (int, uint, byte, double, char, etc.), known immutable types (DateTime, TimeSpan, String) and delegates (including Action<T>, Func<T1, TResult>, etc)
  • Nullable<T>
  • T[] arrays
  • Custom classes and structs, including generic classes and structs.
Following class/struct members are cloned internally:
  • Values of public, not readonly fields
  • Values of public properties with both get and set accessors
  • Collection items for types implementing ICollection<T>
If you're interested in how it works internally look at documentation, where Expression Tree creation logic is described with additional samples.

Because Expression once generated and compiled is used like it was written by you, starting from the second time method is used with the same T, it has the same performance as if you'd write the logic by yourself. That's why CloneExtensions is definitely faster then reflection-based solutions and is faster then serialization-based solutions when you clone more then just a couple instances of the same time.

Take a look at CloneExtensions and feel free to provide feedback to both existing solution and plans for future developments.


Thursday, August 15, 2013

Convert class, IConvertible interface and Generics - deep dive into performance

Blog has been moved to new address: marcinjuraszek.com. You'll be redirected to new site in few seconds. If you don't want to wait click here: Convert class, IConvertible interface and Generics – deep dive into performance




First non LINQ-related question on my blog, but another one in response to great StackOverflow question: Casting generic parameter to and from integer. Every day you can find something that inspires you to do your own research and think hard why things work like they do. That's exciting, isn't it?

Getting back to question. Author posted following code and simply asked, how can it be done better?
class Test<T> where T : struct, IConvertible
{
    public static T TestFunction(T x)
    {
        int n = Convert.ToInt32(x);
        T result = (T)Convert.ChangeType(n, typeof(T));
        return result;
    }
}
But there are some assumptions that have to be made before any work can be done on the question:

  • The class is only intended to work with build-in simple types, like byte, uint, etc.
  • The main goal here is not to make code clean, but to make it fast.
My first thought was, let's use Expression Tree to compile proper conversion code at runtime. The response I come with is:

class Test<T> where T : struct, IConvertible
{
    private static Func<int, T> _getInt;

    static Test()
    {
        var param = Expression.Parameter(typeof(int), "x");
        UnaryExpression body = Expression.Convert(param, typeof(T));
        _getInt = Expression.Lambda<Func<int, T>>(body, param).Compile();
    }

    public static T TestFunction(T x)
    {
        int n = Convert.ToInt32(x);
        T result = _getInt(n);
        return result;
    }
}

It works much better then the one from question. However, I started thinking how much better it is, and can it be done any better? Challenging question, so let's try to face it. Just to make the performance test results comparable, I decided to test all things against three T types: byte, ushort, and uint.

I decided to split tests in two: one part for T to int and another one for the opposite direction. Things I've come up with and gave them a try:
  1. x.ToInt32() instead of Convert.ToInt32(x)
  2. Expression Tree with (int)x to convert T to int and (T)n as int to T conversion.
Code for every testes conversion method can be found on pastebin. Result for particular cases presented on charts below:

int to T conversion time in ms

T to int conversion time in ms

As you can see, the results are quite clear. The fastest way to convert int to T is definitely the one using Expression Tree:
private static class TestClass<T> where T : IConvertible
{
    static Func<T, int> _getInt;
    static Func<int, T> _getT;

    static TestClass()
    {
        var param = Expression.Parameter(typeof(int), "x");
        UnaryExpression body = Expression.Convert(param, typeof(T));
        _getT = Expression.Lambda<Func<int, T>>(body, param).Compile();

        // (...)
    }

    public static int GetInt(T x)
    {
        return _getInt(x);
    }

    public static T GetT(int x)
    // (...)
}
However, the opposite direction conversion is faster when standard ToInt32 method is being called:
private static class TestClass<T> where T : IConvertible
{
    public static int GetInt(T x)
    {
        return x.ToInt32(null);
    }

    public static T GetT(int x)
    // (...)
}
A couple conclusions about test results:
  • Convert tests take so long, because of boxing/unboxing operations from byte/uint/ushort to IConvertible interface instance.
  • T to int conversion on both Convert and ExpressionTree tests do actually the same things under the hood: both use implicit conversion. However, the first one uses standard method calls when second one uses Func delegate call. That's why the second one is a little bit slower.
  • There is one think, that is not measured but actually requires some work before ExpressionTest methods are called for the first time - static constructor code, which actually creates and compiles the expression trees. But because that happens only once, I decided to skip it from tests
And just to be sure: this is the first time I've tried to perform low-level performance tests, so any questions and concerns about the results are really welcome.