4

I want to write generic class which is intended to work with built-in types like byte and ushort. In internal computations I need to cast generic type to integer and back to generic type. I found the way to compile such code, for example:

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;
    }
}

I think that using such conversions may significantly reduce performance, if they are used in computation loops. Is there a better way to make these conversions?

Alex F
  • 42,307
  • 41
  • 144
  • 212
  • 5
    What are your benchmarks for the performance of this, otherwise what are people to improve against? Not to mention how you know it's even a real problem at all. – Grant Thomas Aug 12 '13 at 08:18
  • @GrantThomas - both `Convert.ToInt32` and `Convert.ChangeType` accept `Object` parameter. This means, this code requires boxing. I can ask this by another way: is it possible to avoid boxing? – Alex F Aug 12 '13 at 08:37
  • You could, and could even make it _quicker_, but that still doesn't make it a problem. – Grant Thomas Aug 12 '13 at 08:39
  • .net optimizes for 32bit integers anyway. See [link](http://stackoverflow.com/questions/129023/net-integer-vs-int16) – stepandohnal Aug 12 '13 at 08:54

2 Answers2

2

int to T conversion is a bit tricky. I think you could use Expression class here.

Test<T> class should can look like that:

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 prepares _getInt = x => (T)x for you in static constructor and uses it later, to convert int to T.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • Thanks, using precompiled lambda improves the function performance. – Alex F Aug 12 '13 at 12:03
  • 1
    I mate precompiled lambda also in another direction (T to int) and got significant performance boost on histogram calculation. – Alex F Aug 12 '13 at 13:11
  • This cuts more than 50% of execution time! Histogram function wasted most of its time on boxing, which is part of Convert.ToInt32 and Convert.ChangeType functions. – Alex F Aug 12 '13 at 13:23
  • That's really interesting! I'm gonna perform some tests myself later! What type is `T` in your test suite? Because `Convert.ToInt32` has an overload which takes `byte` as parameter, so there should be no boxing there. – MarcinJuraszek Aug 12 '13 at 13:27
  • OK, I see what's happening here. Actually, `Convert.ToInt32(byte)` is much better than `byte.ToInt32`! – MarcinJuraszek Aug 12 '13 at 13:32
  • Made a benchmarking on byte and ushort types on real histogram equalization algorithm. The best results: using precompiled lambda for casting in both directions. – Alex F Aug 12 '13 at 14:55
  • @AlexFarber I've just posted a [blog post](http://mjuraszek.blogspot.com/2013/08/convert-class-iconvertible-interface.html) about that question and performance of suggested solutions. Feel free to read and comment! – MarcinJuraszek Aug 15 '13 at 12:21
  • Thanks. I made additional tests, with the same results. The fastest way is: T to int - IConvertible.ToInt32, int to T - precompiled lambda. – Alex F Aug 18 '13 at 09:44
1

After some thinking, I'm happy that thanks to the question and some answers I have resolved an old problem of mine: using operations on generic T:

First the example with Cast (as requested by the OP)

public static class Cast<T, U>
{
    public static readonly Func<T, U> Do;

    static Cast()
    {
        var par1 = Expression.Parameter(typeof(T));

        Do = Expression.Lambda<Func<T, U>>(Expression.Convert(par1, typeof(U)), par1).Compile();
    }
}

And then an example with multiplication:

public static class Multiply<T>
{
    public static readonly Func<T, T, T> Do;

    static Multiply()
    {
        var par1 = Expression.Parameter(typeof(T));
        var par2 = Expression.Parameter(typeof(T));

        Do = Expression.Lambda<Func<T, T, T>>(Expression.Multiply(par1, par2), par1, par2).Compile();
    }
}

The use is quite simple:

int x = Conv<T, int>.Do(someTValue);

in the end a static class is created, with a single field that is a readonly static property named Do that is a delegate that "points" to an operation built with an Expression tree.

The multiplication is similar:

T res = Multiply<T, T>.Do(someTValue1, someTValue2);

The multiplication is somewhat 3x slower in the general case than a direct multiplication (in Release mode, no debugging).

Clearly doing the other operations is simple by starting from Multiplication

(it's interesting that I knew quite well of Expression trees, but I hadn't ever thought of using static classes as "dictionaries" for containing the various types. I always did something like Dictionary<Type, Delegate> instead of letting the .NET "handle" the Dictionary through generic class specialization.)

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • Very interesting. Dynamic beats boxing? I am testing this now, thanks. – Alex F Aug 12 '13 at 08:42
  • @AlexFarber I haven't done any pure boxing method. You can't purely unbox to another type. If `T is long`, and `T x`, `(int)(object)x` is InvalidCastException. Try `int res = (int)(object)5L` – xanatos Aug 12 '13 at 08:46
  • It was really interesting. Surprisingly, using `dynamic` gives the same performance, as using Convert functions. Using expressions gives better performance, thanks. – Alex F Aug 12 '13 at 11:56
  • I wish to accept your answer, especially after strange downvotes, but you replaced it with the answer to your own question :) I give an upvote and accept another answer. Hopefully, this discussion will help to SO users. – Alex F Aug 12 '13 at 12:00