I was playing with Disruptor framework and its port for .NET platform and found an interesting case. May be I completely miss something so I’m looking for help from almighty Community.
long iterations = 500*1000*1000;
long testValue = 1;
//.NET 4.0. Release build. Mean time - 26 secs;
object lockObject = new object();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
lock (lockObject)
{
testValue++;
}
}
sw.Stop();
//Java 6.25. Default JVM params. Mean time - 17 secs.
Object lock = new Object();
long start = System.currentTimeMillis();
for (int i = 0; i < iterations; i++)
{
synchronized (lock)
{
testValue++;
}
}
long stop = System.currentTimeMillis();
It seems that acquiring the lock in the scenario with a signle thread in .NET costs just 50% more than in Java. At first I was suspicious at timers but I’ve ran the same test for a few times with results just around mentioned above mean values. Then I was suspicious at synchronized block of code but it does no more than just monitorenter / monitorexit byte code instructions – the same thing as lock keyword in .NET. Any other ideas why taking a lock is so expensive in .NET vs Java?
Yes, it looks like taking an uncontended lock is more expensive in .NET than in Java. (The results on my netbook are slightly more dramatic still.)
There are various aspects to performance which will be faster on one platform than another, sometimes to this extent. The HotSpot JIT and the .NET JIT are pretty radically different in various ways – not least because the .NET JIT only runs once on IL, whereas HotSpot is able to optimize more and more as a particular piece of code is run more and more often.
The important question is whether this is really significant. If your real life application spends really acquires an uncontented lock 500 million times every minute, it probably is significant – and you should probably redesign your app somewhat. If your real life application actually does real work within the lock (or between acquisitions of the lock) then it’s unlikely to be a real bottleneck.
I recently found two .NET gotchas (part one; part two) which I’m having to work round as I’m writing a “system level library” and they would have made a significant difference when an application did a lot of date/time parsing – but this sort of micro-optimization is rarely worth doing.