Why's Java in this case faster (and slower) than C?
Some fellow just started learning C by reading K&R and came up with its fahrenheit-to-celcius conversion loop printed down on the first pages:
#include <stdio.h>
main ()
{
int fahr;
for (fahr = 0; fahr<= 200000000; fahr = fahr + 20)
printf("%d\t%6.2f\n", fahr, (5.0 / 9.0) * (fahr-32));
}
He was told Java to be slow. So, told him that Java's very competitive these days but that C will in this simple case probably be faster. Wanted to proof him and basically added "System.out." in front of printf().
It was more than 10x slower. Way too much. I was baffled. Thought about String object creation, GC, -server, yada, yada, yada.
I was even more baffled when I figured out that nearly 100% of the time was actually spent in printf() (PrintSteam.write()
, output piped to /dev/null).
After some fiddling I came up with this (doesn't do %f's 开发者_运维百科rounding for now):
public static void main(String... args) throws Exception {
int fahr=0;
PrintWriter out = new PrintWriter(Channels.newWriter(Channels.newChannel(System.out), "US-ASCII") );
int max = 2000000000;
for (fahr = 0; fahr<= max; fahr = fahr + 20)
// out.printf("%d\t%6.2f\n", fahr, (5.0 / 9.0) * (fahr-32));
out.println( fahr + "\t" + f(((5.0 / 9.0) * (fahr-32)) ));
out.close();
}
private static final String f(double d) {
return (int)d + "." + (int)((d - (int)d)*100);
}
}
So, this uses NIO. And it outperforms gcc -O2 on two machines tested.
Questions:
- why is the literal transscript from C to Java (i.e.
PrintStream
) so slow? - (why is commented
out.printf()
so slow [maybe performance degrades over time]?) - and finally: why is my solution faster than C (incl. JVM startup time)?
why is the literal transscript from C to Java (i.e. PrintStream) so slow?
You are benchmarking System.out and the various stdio (C vs Java) implementations
(why is commented out.printf() so slow [maybe performance degrades over time]?)
Converting a floating point number to string (and vice versa) is a very complex and slow operation. Have a look at the glibc source code.
and finally: why is my solution faster than C (incl. JVM startup time)?
Because you are benchmarking and comparing different things :
- a java code which does conversion of integer to string (which is much faster than floating point for obvious reasons) and some trivial string operations which hopefully the java VM can do pretty well, plus out.println()
- against a C code which runs an interpreted language on each loop iteration (yes, printf is an interpreter for a small language of which "%d\t%6.2f\n" is a program) and does a floating point to string conversion (which is itself many times slower than integer), plus stdio.
It is not clear wether the C version uses single or double precision, either.
Basically your experiments show that the calculation here is completely irrelevant and the vast majority of the time is spent in formatting and printing the output. Thus, you are not testing the performance of C vs. Java as languages, but of the different string formatting library code (C's seems to be much better optimized) and how stdout is wired to the actual console (Java wins out here).
精彩评论