Does boxing cause performance issues? How can I prevent boxing?

380 views Asked by At

Does boxing cause performance issues in my code? How can I prevent boxing?

void Main()
{
    AreEqual<int>(12, 13);
}

public static bool AreEqual<T>(T a, T b)
{
    return a.Equals(b);
}

IL:

IL_0000:  nop         
IL_0001:  ldc.i4.s    0C 
IL_0003:  ldc.i4.s    0D 
IL_0005:  call        UserQuery.AreEqual
IL_000A:  pop         
IL_000B:  ret         

AreEqual:
IL_0000:  nop         
IL_0001:  ldarga.s    00 
IL_0003:  ldarg.1     
IL_0004:  box         01 00 00 1B 
IL_0009:  constrained. 01 00 00 1B 
IL_000F:  callvirt    System.Object.Equals
IL_0014:  stloc.0     // CS$1$0000
IL_0015:  br.s        IL_0017
IL_0017:  ldloc.0     // CS$1$0000
IL_0018:  ret       
4

There are 4 answers

0
CodeCaster On BEST ANSWER

Does Boxing cause performance issues in my code?

Only you can answer that. It's your code, it's your code's performance. To me, that isn't a problem, to you, it might be.

How can I prevent boxing?

By forcing the compiler to choose the correct overload. Your initial code calls object.Equals(object) (overridden in Int32.Equals(object)), because that's the only method that works for every unconstrained T, which requires boxing of the int argument you pass.

Int32.Equals(Int32) implements IEquatable<T>.Equals(T), so put that as constraint:

private static bool AreEqualEquatable<T>(T a, T b)
    where T : IEquatable<T>
{
    return a.Equals(b);
}

Which compiles to this IL:

IL_0000: nop
IL_0001: ldarga.s a
IL_0003: ldarg.1
IL_0004: constrained. !!T
IL_000a: callvirt instance bool class [mscorlib]System.IEquatable`1<!!T>::Equals(!0)
IL_000f: stloc.0
IL_0010: br.s IL_0012

IL_0012: ldloc.0
IL_0013: ret

Because the compiler will try to find the most specialized overload, in this case IEquatable<T>.Equals(T).

0
Hamid Pourjam On

Any additional operation that the computer do to perform a task will decrease performance, decrease in performance is one thing and performance issue is another thing. If you are trying to implement a high speed data structure with millions of data in it, it may cause performance issues but if you are just writing an information system which performs many queries on database and network then I don't think boxing cause performance issues for you.

You should always profile your application and see where is the part that exactly hurts the performance.

So if you are trying to compare two integers then the operation is (a == b). If you write a function for this then an additional function call is needed. If you add additional generic methods, anonymous types, boxing, unboxing ... then all of these additional operation will decrease performance.

as mentioned here you can prevent boxing of integers.

and here is a benchmark

private static void Main(string[] args)
{
    var sw1 = new Stopwatch();
    bool b1 = true;
    sw1.Start();
    for (int i = 0; i < 10 * 1000 * 1000; i++)
    {
        b1 = b1 ^ AreEqual(i, i + 1);
    }
    sw1.Stop();
    Console.WriteLine(b1);
    Console.WriteLine(sw1.ElapsedTicks);


    var sw2 = new Stopwatch();
    bool b2 = true;
    sw2.Start();
    for (int i = 0; i < 10 * 1000 * 1000; i++)
    {
        b2 = b2 ^ AreEqualEx(i, i + 1);
    }
    sw2.Stop();
    Console.WriteLine(b2);
    Console.WriteLine(sw2.ElapsedTicks);
}

public static bool AreEqual<T>(T a, T b)
{
    return a.Equals(b);
}

public static bool AreEqualEx<T>(T a, T b) where T:IEquatable<T>
{
    return a.Equals(b);
}

and the result is

True
254379
True
35514

0
zmbq On

Sometimes it does, usually it doesn't.

You can't really predict performance problems this way. Do you have a specific program that is too slow and you suspect the problem is with boxing?

0
Binkan Salaryman On

See this hidden feature which could be used as a workaround. But - as always - be sure this isn't premature optimization.

Example Code:

   static void foo<T>(ref T value) {
        //This is the ONLY way to treat value as bool, without boxing/unboxing objects
        if(value is bool) {
            TypedReference reference = __makeref(value); //get reference
            bool boolVal = __refvalue(reference,bool);   //get primitive value
            __refvalue(reference, bool) = !boolVal;      //set primitive value
        } else {
            value = default(T);
        }
    }

I took the code partially from this thread