Advertisement

why are people using c# over java

Started by June 01, 2017 11:39 AM
9 comments, last by TheChubu 7 years, 7 months ago

I'll add something to this.

HotSpot, which is OpenJDK's JVM, is friggin nice. It lacks a bunch of stuff the CLR does well, like more fine grained control over GC and reified generics, *much* nicer native interop, value types, and so on, but it does quite a bunch more than what the CLR (or CoreCLR, or Mono) does.

For instance, check this blog article: http://www.nimaara.com/2016/03/06/beware-of-the-idictionary-tkey-tvalue/

If you run that on VS (or LINQPad which is pretty cool I recommend it), it behaves much like the blogger posted. Takes a good while (1 second on a Haswell i5 IIRC last time I tried) on that empty foreach, and takes even more and generates garbage if you use IDictionary because of the mentioned reasons (IEnumerator vs Enumerator, struct vs class, yadda yadda).

If you try to do a benchmark like that in Java (more specifically, with HotSpot), the equivalent code should be something like this:


public static void main ( String[] args )
{
        // Look ma! No value types! No reified generics!
	HashMap<Integer, Integer> dic = new HashMap<>();
        // Stopwatch in C# is also much nicer to use than this.
	long start = System.nanoTime();
	for ( int i = 0; i < 100000000; i++ )
	{
                // Also C#'s KeyValuePair is a struct, much more efficient than HashMap's Entry objects.
		for ( Entry<Integer, Integer> item : dic.entrySet() )
		{
			;
		}
	}
	long diff = System.nanoTime() - start;

	StringBuilder result = new StringBuilder();
        // To milliseconds.
	result.append( diff / 1000000.0d );
	result.append( System.lineSeparator() );

	for ( GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans() )
	{
                // Never cared for printf so I wont use it :3
		result.append( gc.getCollectionCount() ).append( System.lineSeparator() );
	}

	System.out.println( result.toString() );
}

If you run that on Java/HotSpot it takes a whooping amount of 90ms on my Sandy Bridge i5, with zero collections. If you replace that "HashMap<Integer,Integer>" with it's interface counterpart Map<Integer,Integer>, like the blogger did, it takes a whooping 90ms again, with zero collections again. You might think the bytecode compiler is optimizing something but it isn't, it has those interator.next() calls and so on.

CLR is a literal JIT. It compiles methods as you call them, and doesn't bothers to think (too much) about what your method is actually doing, nor how it is used. It JITs all the methods the first time you use the, stores them in a cache and goes its merry way. This actually makes it a very fast JIT to use, startup times are pretty good in .NET.

HotSpot on the other hand inspects what your code is doing, and in this case, it knows it is actually doing nothing, so it optimizes it away. EDIT: On further testing, it doesn't optimizes away the loop, but it does do the following: It doesn't cares if you define your local variable as HashMap or Map or whatever, it knows that you're actually using a HashMap instance, so it can do nifty things like inlining the HashMap Iterator implementation, or better yet, do escape analysis on the Iterator instance since it knows it wont live outside the method's scope, it wont even be allocated in the heap.

EDIT: More specifically, it inlines HashMap's methods, EntrySet methods and EntrySet's Iterator methods, and performs escape analysis on the iterator itself so it's stack allocated. The only thing not inlined is HashMap's constructor since it is called only once.

For HotSpot, it doesn't matters if you're using interfaces, concrete classes or abstract classes, if your inheritance tree is 2 level deep or 10, it only cares for what you actually use, so if you're only using one actual concrete implementation of a method in a particular call site (ie, most of the cases), it is perfectly capable of de-virtualize it, inline it and re-optimize it.

This is why a benchmarking framework like JHM exists for Java, because it is actually pretty hard to measure things in HotSpot. If you're making naive benchmarks like you see in C# which have short methods, long but empty loops, or don't do anything with the instanced objects they're measuring, and so on, it will most certainly optimize it all out and inline the fuck out of all there is left, skewing your benchmarks a lot.

Have in mind CoreCLR might be in the path of re-creating all these tricks in it, given I've seen discussions in GitHub about implementing tiered compilation like HotSpot's for instance, that's a long way to go though.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

This topic is closed to new replies.

Advertisement