I was curious whether boxing impacts performance in a long chain of IObservable<T>
operators where T
is a value type, and whether performance can be improved by changing T
to Box<T>
from CommunityToolkit.HighPerformance
. I tested this in the form of some unit tests. I also included a test without the long chain of operators to establish how much of the runtime is due to those operators.
The results I got were so surprising that I question whether I'm testing what I think I'm testing. The test where T
is a bare value type runs fastest. This is the opposite of what I expected. Using Box<T>
actually hurts performance, with runtime similar to an ordinary reference type. I get similar results when I run all the tests in a batch or run each one cold.
Do my tests actually demonstrate that T
being a value type is faster, at least for these types?
[TestClass]
public class PerformanceTests
{
private const int RepeatCount = 10000000;
private const int DoCount = 100;
[TestMethod]
public void TestReferenceType()
{
Observable.Repeat(new object(), RepeatCount).EmptyDo(DoCount).Subscribe(o => { });
}
[TestMethod]
public void TestValueTypeBaseline()
{
Observable.Repeat(0, RepeatCount).Subscribe(o => { });
}
[TestMethod]
public void TestValueType()
{
Observable.Repeat(0, RepeatCount).EmptyDo(DoCount).Subscribe(o => { });
}
[TestMethod]
public void TestBoxT()
{
Observable.Repeat(Box<int>.GetFrom(0), RepeatCount).EmptyDo(DoCount).Subscribe(o => { });
}
}
internal static class RxExtensions
{
public static IObservable<T> EmptyDo<T>(this IObservable<T> source, int count)
{
for(int i = 0; i < count; ++i)
{
source = source.Do(o => { });
}
return source;
}
}
Results:
Test | Time |
---|---|
TestValueTypeBaseline |
1.7s |
TestValueType |
7.2s |
TestReferenceType |
8.8s |
TestBoxT |
8.8s |
TestValueTypeBaseline
lacksEmptyDo(DoCount)
like the others are using. Is that intentional? \$\endgroup\$struct
type, which runs in ~8s instead of ~7.2s, still consistently ~12% faster than a reference type with the same properties. This seems to contradict Microsoft's advice to avoid using structs as method parameters, and the entire reason for the existence ofBox<T>
. \$\endgroup\$