Java Forum / General / November 2007
Why is Java so slow????
Java Performance Export - 19 Nov 2007 19:28 GMT Hi all,
I'm wondering if anyone can help me understand why my Java is being very slow compared to an equivalent program written in "C".
I'm simply trying to print out the first N integers like
"This is line <nnnn>"
as a simple benchmark.
My Java version is over 60 times slower than my "C" version and I would like to establish a lower bound on how long the very fastest Java version could take, by applying every possible performance speedup availalbe in the Java environment.
I've profiled with "-Xrunhprof" and looked at the output (below) and was surprised by what I saw. Over 50 different methods are involved before I arrive at the point where 80% of the cumulative CPU usage for the run is accounted for! What the heck is this stuff?????
Is this really happening, and is there a way to get around it?
My client is threatening to implement in "C" and I am trying to talk him out of it.
I'd be very curious to see how this equivalent benchmark peforms on others' environments.
Thanks,
Larry
MY ENVIRONMENT
Machine: Intel Core 2 Duo 2 GHz processor, 8GB of ram O/S: Linux kernel V. 2.6.18-8.1.14.el5, x86_64 architecture
Java: >> java -version java version "1.6.0_03" Java(TM) SE Runtime Environment (build 1.6.0_03-b05) Java HotSpot(TM) Server VM (build 1.6.0_03-b05, mixed mode)
MY BENCHMARK
This "C" program takes about 11.5 seconds to print 50,000,000 lines or 4.3 million lines / sec... output is to /dev/null
Compiled with: gcc -O4 -o t t.c
======================== t.c #include <stdio.h> int main(int argc, char ** argv) { int lim = atoi(argv[1]); int i; for (i=1; i <= lim; i++) printf("this is line %d\n", i); } ======================== t.c
This Java class takes about 15.6 seconds to print 5,000,000 lines or 63,700 lines / sec ... 67 times slower!
======================= t.java class t { static public void main(String[] argv) { int lim = new Integer(argv[0]); int i; for (i=0; i < lim; i++) System.out.println("This is line " + i); }
} ======================= t.java
Here is the result of running with -Xrunhprof:cpu=times and # interations argument of 10,000
============== java.hprof.txt
JAVA PROFILE 1.0.1, created Mon Nov 19 13:40:52 2007 Header for -agentlib:hprof (or -Xrunhprof) ASCII Output (JDK 5.0 JVMTI based) ... ... rank self accum count trace method 1 10.64% 10.64% 20000 300932 sun.nio.cs.US_ASCII $Encoder.encodeArrayLoop 2 5.59% 16.23% 20000 300958 sun.nio.cs.StreamEncoder.writeBytes 3 4.22% 20.45% 20000 300939 sun.nio.cs.StreamEncoder.implWrite 4 3.00% 23.45% 20000 300936 java.nio.charset.CharsetEncoder.encode 5 2.97% 26.42% 20000 300956 java.io.PrintStream.write 6 2.84% 29.26% 20000 300933 sun.nio.cs.US_ASCII $Encoder.encodeLoop 7 2.23% 31.49% 10000 300985 java.io.PrintStream.newLine 8 2.15% 33.64% 20000 300955 java.io.BufferedOutputStream.flush 9 2.10% 35.74% 10000 300964 java.io.PrintStream.write 10 2.01% 37.75% 20013 300087 java.nio.Buffer.<init> 11 1.96% 39.71% 1 301017 t.main 12 1.70% 41.40% 60027 300305 java.nio.ByteBuffer.arrayOffset 13 1.61% 43.01% 60027 300301 java.nio.CharBuffer.arrayOffset 14 1.51% 44.53% 10000 300922 java.io.BufferedWriter.write 15 1.41% 45.94% 10000 300912 java.lang.AbstractStringBuilder.append 16 1.37% 47.31% 10000 300971 java.io.BufferedWriter.write 17 1.35% 48.66% 20000 300926 java.nio.CharBuffer.<init> 18 1.34% 50.00% 20000 300928 java.nio.CharBuffer.wrap 19 1.29% 51.29% 20000 300952 java.io.FileOutputStream.write 20 1.26% 52.55% 20000 300927 java.nio.HeapCharBuffer.<init> 21 1.25% 53.80% 20000 300953 java.io.BufferedOutputStream.flushBuffer 22 1.23% 55.03% 10000 300960 sun.nio.cs.StreamEncoder.flushBuffer 23 1.17% 56.20% 10000 300986 java.io.PrintStream.println 24 1.13% 57.33% 40018 300306 java.nio.Buffer.position 25 1.13% 58.46% 10000 300977 java.io.BufferedWriter.flushBuffer 26 1.12% 59.58% 10000 300923 java.io.Writer.write 27 1.08% 60.66% 40018 300303 java.nio.Buffer.limit 28 1.07% 61.73% 40018 300302 java.nio.Buffer.position 29 1.06% 62.80% 10000 300979 sun.nio.cs.StreamEncoder.implFlushBuffer 30 1.06% 63.85% 10000 300942 java.io.BufferedWriter.flushBuffer 31 1.03% 64.88% 10000 300972 java.io.Writer.write 32 1.00% 65.88% 10000 300980 sun.nio.cs.StreamEncoder.flushBuffer 33 0.98% 66.87% 10000 300959 sun.nio.cs.StreamEncoder.implFlushBuffer 34 0.97% 67.84% 10000 300908 java.lang.AbstractStringBuilder.append 35 0.97% 68.81% 10000 300975 sun.nio.cs.StreamEncoder.write 36 0.95% 69.76% 10000 300984 java.io.BufferedOutputStream.flush 37 0.94% 70.69% 10000 300940 sun.nio.cs.StreamEncoder.write 38 0.87% 71.56% 10000 300941 java.io.OutputStreamWriter.write 39 0.75% 72.32% 10000 300914 java.util.Arrays.copyOfRange 40 0.72% 73.03% 9000 300988 java.util.Arrays.copyOf 41 0.71% 73.74% 10000 300915 java.lang.String.<init> 42 0.68% 74.42% 10000 300905 java.lang.StringBuilder.<init> 43 0.66% 75.08% 10000 300963 java.lang.String.indexOf 44 0.66% 75.74% 10000 300981 java.io.OutputStreamWriter.flushBuffer 45 0.65% 76.39% 10000 300976 java.io.OutputStreamWriter.write 46 0.63% 77.03% 10000 300909 java.lang.StringBuilder.append 47 0.63% 77.66% 10000 300916 java.lang.StringBuilder.toString 48 0.63% 78.29% 20000 300947 java.nio.Buffer.position 49 0.63% 78.93% 10000 300961 java.io.OutputStreamWriter.flushBuffer 50 0.61% 79.54% 20000 300930 java.nio.CharBuffer.hasArray 51 0.61% 80.15% 10000 300913 java.lang.StringBuilder.append ... 100 0.02% 99.51% 4 300254 sun.net.www.ParseUtil.decode 101 0.02% 99.53% 4 300257 java.io.UnixFileSystem.normalize 102 0.02% 99.55% 1 300619 sun.net.www.protocol.file.Handler.createFileURLConnection 103 0.02% 99.57% 2 300717 java.io.FilePermission$1.run CPU TIME (ms) END
Mark Thornton - 19 Nov 2007 19:38 GMT > Hi all, > [quoted text clipped - 6 lines] > > as a simple benchmark. Your Java example flushes the output buffer on every line while the C example does not. However even if you fixed that difference it is hard to see what useful conclusion could be drawn from such a benchmark.
Mark Thornton
Mike Schilling - 19 Nov 2007 19:38 GMT > Hi all, > [quoted text clipped - 6 lines] > > as a simple benchmark. Your program does nothing besides convert integers to strings and write to standard output. Is this a good simulation of what your application will do? If not, the benchmark tells you nothing useful.
Ramon F Herrera - 19 Nov 2007 19:51 GMT On Nov 19, 3:28 pm, Java Performance Export <java.performance.exp...@gmail.com> wrote:
> Hi all, > > I'm wondering if anyone can help me understand why my Java is being > very slow compared to an equivalent program written in "C". Larry:
I am as much of a Java fan and advocate as the next Java developer, but let's face it: Java is slower than native code. It has to be.
Your short answer is: that's the price we have to pay for portability, interoperability and freedom.
-Ramon
Daniel Pitts - 19 Nov 2007 19:54 GMT > On Nov 19, 3:28 pm, Java Performance Export > <java.performance.exp...@gmail.com> wrote: [quoted text clipped - 12 lines] > > -Ramon 60 times slower seems like an error in the benchmark though, not a fundamental issue with Java performance.
My suggestion would be to write a program that counts the number of primes between 1 and 1000000 (or something like) Don't forget, there is a start-up cost with Java, so you should start your timer before the work loop, not before the program starts.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Lew - 19 Nov 2007 20:25 GMT >> On Nov 19, 3:28 pm, Java Performance Export >> <java.performance.exp...@gmail.com> wrote: [quoted text clipped - 20 lines] > Don't forget, there is a start-up cost with Java, so you should start > your timer before the work loop, not before the program starts. Java is not always slower than "native" code. For one thing, significant amounts of Java programs run as native code. For another, the JVMs have gotten *very* smart about optimization.
Java takes a long time to start up because you have to load the JVM. As Daniel pointed out, a valid benchmark will kick off its timing after the program and all the library classes load.
Java is not designed for quick little utilities. Its greatest strength is in full-scale programs that run for a while. Under those conditions, some benchmarks run faster in Java than in statically-compiled languages.
To give a blanket assertion that Java "has to be" slower than "native" code betrays a lack of knowledge of JVM techniques and current benchmarks.
Even when Java tests slower than C++ (say), as it often does, it's by a factor of about 2 or 3 to 1, not 60 to 1. Again, this is after the program and JVM have loaded. There's no denying that load time for the JVM (on most PCs) is a large overhead for Java programs.
 Signature Lew
Steve Sobol - 19 Nov 2007 20:29 GMT > Java takes a long time to start up because you have to load the JVM. Yes. And that's something Java is trying to fix. Google "Consumer JRE"
 Signature Steve Sobol, Victorville, CA PGP:0xE3AE35ED www.SteveSobol.com Geek-for-hire. Details: http://www.linkedin.com/in/stevesobol
Chronic Philharmonic - 19 Nov 2007 21:06 GMT > On Nov 19, 3:28 pm, Java Performance Export > <java.performance.exp...@gmail.com> wrote: [quoted text clipped - 10 lines] > Your short answer is: that's the price we have to pay for portability, > interoperability and freedom. A lot hinges on good software design. About 10 years ago, I worked for a now defunct mainframe terminal emulation company. They had some widely-touted Active-X controls that ran in a browser. They were supposed to be the smallest, fastest implementation possible. My team was assigned the task of porting them to Java.
After looking at the Active-X design, and looking at the Java language (for the first time), we elected not to port the code, since it did not fit the Java paradigm, and we would have abused the Java language in many respects. When we finished, we had a terminal emulator that was smaller, faster, and more correct (relative to real Mainframe terminals) than the Active-X controls that we started with. This was on Java 1.2 with AWT, before JIT, before Swing.
Since that time, I have worked on several projects to build massively scalable server applications in Java. So when someone tells me that Java is too slow, I just roll my eyes. I'm sure people can find edge cases where Java just isn't fast enough. One thing's for sure: A direct port from C/C++ is almost guaranteed to fail in nearly every way imaginable.
Daniel Dyer - 19 Nov 2007 19:57 GMT > This "C" program takes about 11.5 seconds to print 50,000,000 lines or > 4.3 million lines / sec... output is to /dev/null [quoted text clipped - 29 lines] > } > ======================= t.java Try the Java printf so that you are at least comparing something similar (it should avoid a lot of the String allocations):
<http://java.sun.com/j2se/1.5.0/docs/api/java/io/PrintStream.html#printf(java.lan g.String, java.lang.Object...)>
Also, try the -server switch on the JVM. It may well inline a lot of those method calls that you are seeing.
Finally, disregard your results until you have considered the following Java micro-benchmarking advice posted here by Chris Uppal a while back:
<http://groups.google.com/group/comp.lang.java.programmer/browse_frm/thread/79219 945c16fa272/7e96da29cca14efc?lnk=st>
Dan.
 Signature Daniel Dyer https://watchmaker.dev.java.net - Evolutionary Computation for Java
Java Performance Expert - 19 Nov 2007 20:29 GMT > On Mon, 19 Nov 2007 19:28:17 -0000, Java Performance Export > [quoted text clipped - 3 lines] > Try the Java printf so that you are at least comparing something similar > (it should avoid a lot of the String allocations): tried it, make it run in over 40 secs instead of 15 seconds.
> Also, try the -server switch on the JVM. It may well inline a lot of > those method calls that you are seeing. had no effect.
> Finally, disregard your results until you have considered the following > Java micro-benchmarking advice posted here by Chris Uppal a while back: > > <http://groups.google.com/group/comp.lang.java.programmer/browse_frm/t...> Followed these instructions, moving the "loop" into a separate method, then called that a number of times. no improvement. Rewritten test class below. I presume putting the meat of it in a separate method allows the JIT to recognize it as a bottlneck and use inlining or native compliation on subsequent invocations? But it did not do so even after 100 invocations. Can't I just ask Java to compile the very first one?
W/ the revised class I am also still seeing over 50 methods calling each other before 80% of the CPU time is accounted for (see origional post). I would have expected there to be some very small handful of places where most of the work was done.
Mostly what i am wondering is what the heck are these methods and why is all this necessary, or is it? and how can I get this simple program to run as quickly as possible in Java.
Also, I saw comments this is "not a valid benchmark." Actually this script is being used to pipe test input to a database processing stream, and it was thought the component of the pipe that simply emits the test data would be so negligible as to be ignored. However the factor of ~ 70 slowdown w/ Java version is causing that to be an issue.
The question I don't want to have to answer is "Why, again, can you not make this program run fast?"
Thanks,
Larry
Mark Space - 19 Nov 2007 20:37 GMT > Also, I saw comments this is "not a valid benchmark." Actually > this script is being used to pipe test input to a database processing > stream, and it was thought the component of the pipe that simply emits > the test data would be so negligible as to be ignored. However the > factor of ~ 70 slowdown w/ Java version is causing that to be an > issue. Did you try not flushing the stream, as indicated above?
Andreas Leitgeb - 19 Nov 2007 20:46 GMT >> Also, I saw comments this is "not a valid benchmark." Actually >> this script is being used to pipe test input to a database processing [quoted text clipped - 4 lines] > > Did you try not flushing the stream, as indicated above? If that prog was indeed not just a benchmark, but *the* program as he needs it, then using the C version seems like a reasonable answer. Of course, this answer shouldn't be misapplied to other domains. Always use the right tool for a job. For that trivial prog, C is the right tool.
Java Performance Expert - 19 Nov 2007 21:09 GMT > > Also, I saw comments this is "not a valid benchmark." Actually > > this script is being used to pipe test input to a database processing [quoted text clipped - 4 lines] > > Did you try not flushing the stream, as indicated above? OK, that helped a lot. I can now generate 50m lines in 28 seconds, which is only 2.5 X slower than in "C". We can probably live with that. I am wondering if I have "taken it to the limit" tho. Revised class is below.
I still don't think I'm getting the benefits of native compilation.
Another poster remarked:
> Your Java example flushes the output buffer on every line while the C > example does not. However even if you fixed that difference it is hard > to see what useful conclusion could be drawn from such a benchmark. The useful conclusion that can be drawn is that a test generator written in "C" will run faster, and so be more desirable to use, than one written in Java, to the extent that it is desirable for each and every component to run as fast as possible, and to the extent that it is useful to identify those aspects of our decision making that produce desirable results.
Let me know if anything is unclear..
Thx
Larry
import java.io.BufferedOutputStream; import java.util.Date;
class t1 { static public void main(String[] argv) { int lim = new Integer(argv[0]); int nbench = new Integer(argv[1]); int b; for (b=0; b < nbench; b++) { System.err.println("Bench " + b); Date start = new Date();
try { mytest(lim); } catch ( Exception e) { }
Date now = new Date(); System.err.println("Took " + ((now.getTime() - start.getTime())/1000) + " seconds"); } }
static public void mytest(int lim) throws Exception { int i; BufferedOutputStream bos = new BufferedOutputStream(System.out, 1000000); for (i=0; i < lim; i++) { String s = "This is line " + i; byte[] barr = s.getBytes(); bos.write(barr, 0, barr.length); } } }
Lew - 19 Nov 2007 21:40 GMT > The useful conclusion that can be drawn is that a test generator > written in "C" will run faster, and so be more desirable to use, than > one written in Java, to the extent that it is desirable for each and > every component to run as fast as possible, and to the extent that it > is useful to identify those aspects of our decision making that > produce desirable results. A more useful conclusion is that a Java test harness should perhaps just be kept running in order to amortize the startup time.
 Signature Lew
Mark Thornton - 19 Nov 2007 22:38 GMT >> Your Java example flushes the output buffer on every line while the C >> example does not. However even if you fixed that difference it is hard [quoted text clipped - 6 lines] > is useful to identify those aspects of our decision making that > produce desirable results. The main reason your Java code was so slow relative to C is that you had not taken account of the special characteristics of System.out. It is a line buffered PrintStream. To avoid this characteristic try writing to a file instead or creating a new stream on FileDescriptor.out like this:
PrintStream out = new PrintStream(new BufferedOutputStream(new FileOuputStream(FileDescriptor.out)));
Whenever you see a big difference in text IO performance between Java and C, differences in buffering are almost always the cause.
In addition, Java is always at a disavantage in such tests because its character handling is always unicode, while the C comparison invariably uses a single byte character set (and no conversion of character encoding).
If you want to write worthwhile tests you need a better understanding of the languages and libraries involved.
Mark Thornton
Bent C Dalager - 19 Nov 2007 23:07 GMT >The main reason your Java code was so slow relative to C is that you had >not taken account of the special characteristics of System.out. I tried running the code Lew posted, redirected, with the following results:
JAVA:
$ time java -server -classpath . TimePrin > out.txt real 0m31.306s user 0m15.430s sys 0m14.705s
$ time java -server -classpath . TimePrin > /dev/null real 0m20.474s user 0m14.085s sys 0m6.233s
C:
$ time ./a.out > out.txt real 0m4.680s user 0m1.302s sys 0m0.650s
$ time ./a.out > /dev/null real 0m1.366s user 0m1.248s sys 0m0.010s
> It is a >line buffered PrintStream. To avoid this characteristic try writing to a >file instead or creating a new stream on FileDescriptor.out like this: > >PrintStream out = new PrintStream(new BufferedOutputStream(new >FileOuputStream(FileDescriptor.out))); Trivially rewriting it to use a wrapped FileOutputStream yields:
kandidat:~/tmp bcd$ time java -server -classpath . TimePrin Elapsed: 5.223 secs.
real 0m5.410s user 0m4.679s sys 0m0.521s
This value of 5.41s is reasonably close to C's 4.68s, but the Java program needed to be written specifically to output to file whileas C achieved this performance by simple redirection.
The code used is:
import java.util.Date; import java.io.*; public class TimePrin { private static final int LIM = 5000000;
public static void main( String [] args) throws Exception { int lim; if ( args.length < 1 ) { lim = LIM; } else { try { lim = Integer.parseInt( args [0] ); } catch ( NumberFormatException ex ) { lim = LIM; } }
long start = new Date().getTime(); PrintStream out = new PrintStream( new BufferedOutputStream( new FileOutputStream("out.txt"))); for ( int i=0; i < lim; i++) { out.print( "This is line " + i +"\n" ); } out.close(); long end = new Date().getTime();
double elapsed = (end - start) / 1000.0; System.out.println( "Elapsed: "+ elapsed +" secs." ); } }
Cheers, Bent D
 Signature Bent Dalager - bcd@pvv.org - http://www.pvv.org/~bcd powered by emacs
Lew - 19 Nov 2007 23:19 GMT >> The main reason your Java code was so slow relative to C is that you had >> not taken account of the special characteristics of System.out. [quoted text clipped - 5 lines] > > $ time java -server -classpath . TimePrin > out.txt The benchmark I provided specifically sought to avoid including program load time in the result. Using UNIX 'time' utility defeats that.
 Signature Lew
Bent C Dalager - 19 Nov 2007 23:39 GMT >> $ time java -server -classpath . TimePrin > out.txt > >The benchmark I provided specifically sought to avoid including program load >time in the result. Using UNIX 'time' utility defeats that. The impact is negligible in the case at hand.
Cheers, Bent D
 Signature Bent Dalager - bcd@pvv.org - http://www.pvv.org/~bcd powered by emacs
Mark Thornton - 20 Nov 2007 20:11 GMT > Trivially rewriting it to use a wrapped FileOutputStream yields: > [quoted text clipped - 8 lines] > program needed to be written specifically to output to file whileas C > achieved this performance by simple redirection. That time comparison is what I would expect. As to the Java's default buffering behaviour, it isn't unreasonable it just produces better results in different circumstances. The C code output, viewed in an IDE, may provide its output in 512 byte chunks which can equally annoying as the slower speed for the Java default. I would say that the Java default is less surprising than the C case. Both need changes when the default is not what you want.
Mark Thornton
bugbear - 20 Nov 2007 14:41 GMT > The useful conclusion that can be drawn is that a test generator > written in "C" will run faster, and so be more desirable to use, than > one written in Java, to the extent that it is desirable for each and > every component to run as fast as possible Unlikely.
I would recommend identifying bottlenecks, and putting any analysis and optimization effort, either in environment, algorithm, communication protocol,, implementation langugage etc into those bottlenecks.
In a finite developement schedule, there is not enough time to mindlessly make every component run as fast as possible.
I remember someone bragging that he'd optimised some startup code (in a daemon module) to be 5 times faster. He was confused when I wasn't impressed.
BugBear
Java Performance Expert - 20 Nov 2007 15:27 GMT > I would recommend identifying bottlenecks, > and putting any analysis and optimization effort, > either in environment, algorithm, communication protocol,, > implementation langugage > etc into those bottlenecks. hi BugBear,
what I'm tyring to do is convince management that there isn't anything we cannot do as fast in Java as in "C" and this initial exercise was a simple example. It is more about managing the risk of being "up against a wall" with regard to the ability to speed up any arbitrary portion of the system.
I understand I have the JNI and native methods at my disposal as well and this will probably be the life-saver.
FYI, I've rid my example of all references to String and it is faster again by two. My latest version is below. Still far from the "C" version in speed but it I think it is good enough.
Thx
Larry (a.k.a. the Java Hound)
import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.util.Date;
class t1 { static byte[] prompt = "This is line ".getBytes(); static byte[] prompt2 = "\n".getBytes();
static public void main(String[] argv) { int lim = new Integer(argv[0]); int nbench = new Integer(argv[1]); int b; for (b=0; b < nbench; b++) { System.err.println("Bench " + b); Date start = new Date();
try { mytest(lim); } catch ( Exception e) { System.err.println("Exception occurred"); System.err.println(e.toString()); }
Date now = new Date(); System.err.println("Took " + ((now.getTime() - start.getTime())/1000) + " seconds"); } }
static public void mytest(int lim) throws Exception { int i; BufferedOutputStream bos = new BufferedOutputStream(System.out, 1000000); DataOutputStream dos = new DataOutputStream(bos); for (i=0; i < lim; i++) {
// byte[] ibytes = new Integer(i).toString().getBytes();
writebytes(prompt, dos); writebytes(iconv(i), dos); writebytes(prompt2, dos); } dos.flush(); }
static public void writebytes(byte[] arr, DataOutputStream dos) throws Exception { int n = arr.length; int i; for ( i=0; i < n; i++ ) { dos.writeByte(arr[i]); } }
static byte[] iconv(int i) { byte[] digs = new byte[20]; int ndig = 0; while ( i >= 10 ) { digs[ndig] = (byte) (48 + i % 10); ndig++; i = i / 10; } digs[ndig] = (byte) (48 + i); ndig++;
byte[] result = new byte[ndig]; int dig; int j = 0; for (dig=ndig-1; dig>= 0; dig--) { result[j] = digs[dig]; j++; } return result; } }
Patricia Shanahan - 20 Nov 2007 15:57 GMT ...
> what I'm tyring to do is convince management that there isn't anything > we cannot do as fast in Java as in "C" and this initial exercise was > a simple example. It is more about managing the risk of being "up > against a wall" with regard to the ability to speed up any arbitrary > portion of the system. ...
I'm afraid you have undertaken a mission that is doomed to failure. It is vanishingly rare for programming language X to be at least as fast as programming language Y for all applications.
In this case, I think part of the problem is a test I vaguely remember seeing in a C runtime support library, back in the mists of the 1980's when I was a compiler writer. The I/O library changed its buffering strategy for standard out depending on whether it was going to a TTY or not.
Patricia
Mark Space - 20 Nov 2007 16:52 GMT > static byte[] iconv(int i) > { [quoted text clipped - 18 lines] > } > } Did this code buy you any speed improvements? I got very little performance hit when I added converting an int to a string (and the resultant encoding for going from a String to a byte[]) using the regular Integer.toString(i).getBytes() method.
Just curious if it's worth it to use optimizations like this.
Mark Space - 20 Nov 2007 19:47 GMT > static byte[] iconv(int i) > { > byte[] digs = new byte[20]; I poked around the Java sources and did some experimentation. Removing object creation does seem to help. So does removing casting at runtime, including casting primitives.
Here's my own version, based loosely on the Integer.toString(int) method source.
/** Converts a POSITIVE integer to a byte [], with an emphasis on speed. * * @param buff The start, length and ASCII values are stored in this * buffer. * The buffer must be therefore at least the size of Integer.MAX_VALUE + * 2. * buff.start stores the offset of the first ASCII character. * buff.length * stores * the length of the ASCII character string. Strings are written to the * end of the buffer.ascii array. * @param i MUST BE POSITIVE. This is not tested by the method. Stuff * will explode in spectacular ways if you pass this routine a negative * integer. */ static void fastItoS2( AsciiByteBuff buff, int i ) { int index = buff.ascii.length - 1; int q = i; int r; for(;;) { r = q % 10; q = q / 10; buff.ascii[index--] = digits[r]; if( q == 0 ) break; } buff.start = (index + 1); buff.slength = (buff.ascii.length - 1 - index); }
private static class AsciiByteBuff { public int start; public int slength; public byte [] ascii; }
private static byte [] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
Here is the driver for this routine:
static public void write3fastItoS2( String[] args ) throws IOException {
int lim = DEFAULT_LINES;
if( args != null && args.length > 0 ) { try { lim = Integer.parseInt( args[0] ); } catch( NumberFormatException ex ) { System.err.print( ex + "\nUsing default of " + DEFAULT_LINES ); } if( lim < 0 ) { lim = DEFAULT_LINES; System.err.println( "Lim less than 0.\n" + "Using defalut of " + DEFAULT_LINES ); } }
BufferedOutputStream os = new BufferedOutputStream( System.out );
String message2 = "This is line "; byte[] mbuff = message2.getBytes(); int mlength = mbuff.length;
AsciiByteBuff ibuff = new AsciiByteBuff(); ibuff.ascii = new byte [Integer.toString(Integer.MIN_VALUE).length() + 2 ];
for( int i = 0; i < lim; i++ ) { // os.write( mbuff ); os.write( mbuff, 0, mlength ); fastItoS2( ibuff, i ); os.write(ibuff.ascii, ibuff.start, ibuff.slength ); os.write('\n'); } os.close(); }
I think that's right, I had to repair a couple of lines after the email editor wrapped them.
Good luck.
Steve Wampler - 20 Nov 2007 20:35 GMT > I poked around the Java sources and did some experimentation. Removing > object creation does seem to help. So does removing casting at runtime, > including casting primitives. > > Here's my own version, based loosely on the Integer.toString(int) method > source. Sweet! Here's the timings using this code (put into the Java benchmark framework posted earlier by Java Performance Expert), on my machine, for 50,000,000 lines of output. The timing for the C version are included for comparision:
->time timepr 50000000 | cat >/dev/null Elapsed: 16.930000 secs. timepr 50000000 16.44s user 0.50s system 95% cpu 17.650 total cat > /dev/null 0.10s user 0.57s system 3% cpu 17.649 total
->time java -server TimePrin3 50000000 1 | cat >/dev/null Bench 0 Took 10.608 seconds java -server TimePrin3 50000000 1 9.26s user 0.60s system 91% cpu 10.737 total cat > /dev/null 0.17s user 0.68s system 7% cpu 10.699 total
C: gcc 4.1.1, -O4 Java: 1.6.0u3 CentOS4: dual 2GHz Opteron, 2GB ram
 Signature Steve Wampler -- swampler@noao.edu The gods that smiled on your birth are now laughing out loud.
Lew - 21 Nov 2007 01:57 GMT > Sweet! Here's the timings using this code (put into the Java benchmark > framework posted earlier by Java Performance Expert), on my machine, [quoted text clipped - 16 lines] > Java: 1.6.0u3 > CentOS4: dual 2GHz Opteron, 2GB ram Take that! You naysayers who assert, "Java can never be as fast as C!"
 Signature Lew
Java Performance Expert - 19 Nov 2007 20:50 GMT On Nov 19, 3:29 pm, Java Performance Expert <java.performance.exp...@gmail.com> wrote:
> Followed these instructions, moving the "loop" into a separate method, > then called that a number of times. no improvement. Rewritten test > class below. Oops forgot to paste in the class. Also below is the output.
It was pointed out:
>> Java is not designed for quick little utilities. Its greatest strength is in >> full-scale programs that run for a while. Under those conditions, some >> benchmarks run faster in Java than in statically-compiled languages. Not sure if my test data generator is a "quick utility" It is part of a larger system, which is a full-scale program. What I would like is to make a full-scale program that DOESN'T run for a long while; hope that makes sense.
I suspect what is happening is that I am not gaining the benefits of JIT native compilation. Is there a way to simply tell the Java environment to compile it from the get-go? It seem if I have to wait 'til my method becomes a bottlneck, then my compilation is not "just in time" -- it is by definition too late.
Am I missing something obvious here?
Thx
Larry
=========================================================================
import java.util.Date;
class t1 { static public void main(String[] argv) { int lim = new Integer(argv[0]); int nbench = new Integer(argv[1]); int b; for (b=0; b < nbench; b++) { System.err.println("Bench " + b); Date start = new Date(); mytest(lim); Date now = new Date(); System.err.println("Took " + ((now.getTime() - start.getTime())/1000) + " seconds"); } }
static public void mytest(int lim) { int i; for (i=0; i < lim; i++) System.out.println("This is line " + i); } }
=========================================================================
Bench 0 Took 15 seconds Bench 1 Took 15 seconds Bench 2 Took 15 seconds Bench 3 Took 15 seconds Bench 4 Took 15 seconds Bench 5 Took 15 seconds Bench 6 Took 15 seconds Bench 7 Took 15 seconds Bench 8 Took 15 seconds Bench 9 Took 15 seconds
Jack Marsh - 19 Nov 2007 23:50 GMT >> ========================================================================= > [quoted text clipped - 24 lines] > } > } try this instead version of mytest, it wont be as fast as C but it should be much faster than your code. On my XP computer it was a little over 10x faster than your version. I used StringBuffer rather than StringBuilder because StringBuffer has a length() method.
static public void mytest(int lim) { final int bufferSize = 100000; StringBuffer b = new StringBuffer(bufferSize+100);
for (int i=0; i < lim; i++) { b.append("This is line " + i +"\n"); if ( b.length() >bufferSize) { System.out.print(b.toString()); b = new StringBuffer(bufferSize+100); } } // tidy up if ( b.length() > 0) { System.out.print(b.toString()); }
}
> ========================================================================= Daniel Dyer - 19 Nov 2007 20:57 GMT >> On Mon, 19 Nov 2007 19:28:17 -0000, Java Performance Export >> [quoted text clipped - 5 lines] > > tried it, make it run in over 40 secs instead of 15 seconds. Yep, my mistake. I should have realised it would create a new formatter for every call. I guess the other option is to create and reuse your own message format instance. That is if the String allocations are a significant part of the overhead.
>> Also, try the -server switch on the JVM. It may well inline a lot of >> those method calls that you are seeing. > > had no effect. None at all? I would expect it to make some difference, even if it made it worse.
> Followed these instructions, moving the "loop" into a separate method, > then called that a number of times. no improvement. Rewritten test [quoted text clipped - 3 lines] > so even after 100 invocations. Can't I just ask Java to compile > the very first one? There is a switch that does do that. It's one of the "unsupported" -XX options. I forget which one, I've never used it.
> W/ the revised class I am also still seeing over 50 methods calling > each other before 80% of the CPU time is accounted for (see origional > post). I would have expected there to be some very small handful of > places where most of the work was done.
> Mostly what i am wondering is what the heck are these methods > and why is all this necessary, or is it? and how can I get > this simple program to run as quickly as possible in Java. Looking at your profiling output (which I ignored first time round, sorry), a lot of the time is spent converting Java's Unicode Strings into US ASCII. Your C version doesn't do Unicode, so it has a lot less work to do.
Dan.
 Signature Daniel Dyer https://watchmaker.dev.java.net - Evolutionary Computation for Java
Ming - 19 Nov 2007 21:06 GMT I think it is because Java is designed to be slow :P
> On Mon, 19 Nov 2007 20:29:44 -0000, Java Performance Expert > [quoted text clipped - 50 lines] > -- > Daniel Dyerhttps://watchmaker.dev.java.net- Evolutionary Computation for Java Lew - 19 Nov 2007 21:41 GMT > I think it is because Java is designed to be slow :P What a load of hooey.
In my version of the benchmark, Java took about 94 seconds to do what C did in 88.
 Signature Lew
Daniel Pitts - 19 Nov 2007 21:08 GMT >> On Mon, 19 Nov 2007 19:28:17 -0000, Java Performance Export >> [quoted text clipped - 46 lines] > > Larry If you're simply trying to pipe data, use "cat" :-)
Like someone else mentioned, Java shines for full applications, not small utilities, and this is mostly to do with the startup costs.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Java Performance Expert - 19 Nov 2007 21:16 GMT On Nov 19, 4:08 pm, Daniel Pitts <newsgroup.spamfil...@virtualinfinity.net> wrote:
> Like someone else mentioned, Java shines for full applications, not > small utilities, and this is mostly to do with the startup costs. I'm calcualting and outputing my timing stats well into the main() routine, so startup costs do not apply to me.
Also, I am not getting around Unicode conversion which after I eliminate it may bring me down to the fastest popssible
thanks
Mark Space - 19 Nov 2007 21:28 GMT > The question I don't want to have to answer is "Why, again, can you > not make this program run fast?" I'm doing some testing for you (for free!!) but just switching from println to print gave me a 28% speed improvement.
Next step: get rid of those calls to encoders.
package microbenchmarks;
public class Main {
static public void main(String[] argv) { int lim = new Integer(argv[0]); int i; for (i=0; i < lim; i++) { System.out.print( "This is line " + i + "\n"); } } }
Lew - 19 Nov 2007 21:38 GMT > I'm wondering if anyone can help me understand why my Java is being > very slow compared to an equivalent program written in "C". [quoted text clipped - 22 lines] > I'd be very curious to see how this equivalent benchmark peforms on > others' environments. I modified the Java benchmark in accordance with others' suggestions and for five million lines of output came up with:
Java: $ java -server -cp build/classes testit.TimePrin Elapsed: 93.966 secs.
C program: $ ./timepr Elapsed: 88.000000 secs.
A far cry from 60:1, eh?
AMD-64 ~2 GHz, 1 GB RAM, Linux Fedora 7, the usual mix of other programs running. 32-bit Java.
Code with my variations follows. <sscce name="TimePrin.java"> package testit; public class TimePrin { private static final int LIM = 5000000;
public static void main( String [] args) { int lim; if ( args.length < 1 ) { lim = LIM; } else { try { lim = Integer.parseInt( args [0] ); } catch ( NumberFormatException ex ) { lim = LIM; } }
long start = new Date().getTime(); for ( int i=0; i < lim; i++) { System.out.print( "This is line " + i +"\n" ); } long end = new Date().getTime();
double elapsed = (end - start) / 1000.0; System.out.println( "Elapsed: "+ elapsed +" secs." ); } } </sscce>
<sscce name="timepr.c" build="gcc -O4 -o timepr timepr.c" > #include <stdio.h> #include <time.h>
#define LIM 5000000
int main(int argc, char ** argv) { int lim; if ( argc < 2 ) { lim = LIM; } else { lim = atoi(argv[1]); if ( lim <= 1000 ) { lim = LIM; } }
int i; time_t start = time( NULL ); for ( i=1; i <= lim; i++ ) { printf("this is line %d\n", i); } time_t end = time( NULL );
printf( "Elapsed: %f secs.\n", difftime( end, start )); } </sscce>
 Signature Lew
Java Performance Expert - 19 Nov 2007 21:52 GMT > I modified the Java benchmark in accordance with others' suggestions and for > five million lines of output came up with: [quoted text clipped - 8 lines] > > A far cry from 60:1, eh? Lew, On my system, it takes 9.21 seconds (Java) and 1.1 seconds (C)
Any ideas?
thanks JH
Lew - 19 Nov 2007 22:00 GMT >> I modified the Java benchmark in accordance with others' suggestions and for >> five million lines of output came up with: [quoted text clipped - 11 lines] > Lew, > On my system, it takes 9.21 seconds (Java) and 1.1 seconds (C) Running the code that I posted?
If so, I am mystified.
I only have access to my machine, which gave the results from the command line as posted. The numbers are fairly consistent from run to run, too, about 94 s. for Java and about 88 or 89 for C, emitting five million lines of output in each test loop. As you can see, the test loop was set up to eliminate startup time in the measurement.
I used GCC 4.1.2 and Java 6u3 (32-bit).
 Signature Lew
Ming - 19 Nov 2007 22:04 GMT For web application, FastCGI/C is the fastest, then FastCGI/Perl or Mod_Perl. Java is way way slower than FastCGI/C
> >> I modified the Java benchmark in accordance with others' suggestions and for > >> five million lines of output came up with: [quoted text clipped - 26 lines] > -- > Lew Steve Wampler - 19 Nov 2007 22:29 GMT > Running the code that I posted? > [quoted text clipped - 7 lines] > > I used GCC 4.1.2 and Java 6u3 (32-bit). I made a couple of modifications:
(1) time output to stderr, so I could redirect stdin (I didn't want to wade through X-zillion output messages!) (2) I had to import java.util.Date to get Date() - I don't understand why you don't have to!
With 5,000,000 strings:
Java (-server) : 8.192 C++ (4.1.1 -O4): 2.000
with 50,000,000 strings:
Java (-server) : 80.421 C++ (4.1.1 -O4): 17.000
both have the same ratio. I suspect (without any attempt to profile anything!) that Java is, in the simple example, paying the price for multiple object creations in the inner loop.
Your counts for 5,000,000 seem pretty high, but are likely I/O bound costs of not tossing stdout.
 Signature Steve Wampler -- swampler@noao.edu The gods that smiled on your birth are now laughing out loud.
Steve Wampler - 19 Nov 2007 22:40 GMT > Your counts for 5,000,000 seem pretty high, but are likely I/O bound > costs of > not tossing stdout. Yep. In fact, the tests become dominated by I/O costs if stdout isn't tossed (note the use of fewer lines - just a sign of impatience):
Java (50,000 lines): 3.683 s C++ (50,000 lines): 3.000 s (hmm, *always* integral?)
CentOS 4, Dual 2GHz Opteron, 32-bit everything.
 Signature Steve Wampler -- swampler@noao.edu The gods that smiled on your birth are now laughing out loud.
Lew - 19 Nov 2007 23:04 GMT > (1) time output to stderr, so I could redirect stdin (I didn't want > to wade through X-zillion output messages!) > (2) I had to import java.util.Date to get Date() - I don't understand > why you don't have to! Oops. I did, but forgot to include that line in the post.
> With 5,000,000 strings: > [quoted text clipped - 11 lines] > object creations > in the inner loop. Much more likely that it's the IO costs alluded to by others. Object creation in Java tends to be pretty fast (roughly 10 machine instructions, according to Sun), and often optimized away.
> Your counts for 5,000,000 seem pretty high, but are likely I/O bound > costs of > not tossing stdout. No doubt, but I was trying the simple case just to see what resulted. As it happened, the differences here were on the order of 6-7%, favoring C, without the optimizations of elimination of stdout or string encoding in Java.
I was also paying the price for String encoding conversion, as others have mentioned.
 Signature Lew
Java Performance Expert - 20 Nov 2007 11:00 GMT > >>Java: > >> $java-server -cp build/classes testit.TimePrin [quoted text clipped - 3 lines] > >> $ ./timepr > >> Elapsed: 88.000000 secs. Hey Lew, could you do me a favor and run the above as:
$ time java-server -cp build/classes testit.TimePrin $ time ./timepr
and post the results. Note the prepending of "time"
This will show the mix of user CPU and real time and I think this will clear up our mystery.
Thx.
JH
Lew - 20 Nov 2007 14:03 GMT >>>> Java: >>>> $java-server -cp build/classes testit.TimePrin [quoted text clipped - 13 lines] > This will show the mix of user CPU and real time and I think this will > clear up our mystery. $ time java-server -cp build/classes testit.TimePrin Elapsed: 88.367 secs.
real 1m29.279s user 0m7.269s sys 0m8.294s
$ time ./timepr Elapsed: 80.000000 secs.
real 1m19.347s user 0m2.652s sys 0m8.116s
Still not the 60:1 the OP reported.
If String encoding really does involve 40:1 performance degradation for Java, that renders this particular benchmark moot. I'm going to recode it not to use Strings and run again.
 Signature Lew
Bent C Dalager - 19 Nov 2007 22:23 GMT >On my system, it takes 9.21 seconds (Java) and 1.1 seconds (C) > >Any ideas? As an experiment, I would try to increase the workload tenfold (i.e. increase to 50 mill lines) just to see if the ratios remain the same.
Cheers, Bent D
 Signature Bent Dalager - bcd@pvv.org - http://www.pvv.org/~bcd powered by emacs
Mark Space - 19 Nov 2007 22:49 GMT > On my system, it takes 9.21 seconds (Java) and 1.1 seconds (C) > > Any ideas? It might have something to do with your locale or your system libraries. Even though you are using a BufferedOutputStream writer, each call to getBytes() on the string still runs an encoder.
The following is much faster than any of my previous tests on my system. Give it a shot. It does not (yet) count, but it does spam IO so we can at least see if it might yield some improvements.
static public void rawTest( String [] args ) throws IOException {
int lim = new Integer( args[0] );
String message = "This is line "; String sed = "0000000\n";
OutputStream os = new BufferedOutputStream(System.out);
byte [] mbuff = message.getBytes(); byte [] sedBuff = sed.getBytes(); int mlength = mbuff.length; int sedLength = sedBuff.length;
for( int i = 0; i < lim; i++ ) { os.write( mbuff, 0, mlength ); os.write( sedBuff, 0, sedLength ); }
}
Note: not complete, just paste it in and have main() call it.
Mark Space - 19 Nov 2007 23:16 GMT > String sed = "0000000\n"; This *still* causes the output to be flushed. Remove the \n and I get a 50% speed increase (halves runtime). Is there no way to set the autoflush for System.out to false?
Mark Space - 19 Nov 2007 23:26 GMT >> String sed = "0000000\n"; > > This *still* causes the output to be flushed. Remove the \n and I get a > 50% speed increase (halves runtime). Is there no way to set the > autoflush for System.out to false? Now I'm just kind of making notes as I go.
BufferedOutputStream's write( b[], int, int ) method does a range check on its arguments, then just does this:
for (int i = 0 ; i < len ; i++) { write(b[off + i]); }
That's cut and paste from the source code.
You'd be faster by a bit just to call write(byte) in a for loop.
Mark Space - 19 Nov 2007 23:40 GMT > BufferedOutputStream's write( b[], int, int ) method does a range check > on its arguments, then just does this: [quoted text clipped - 6 lines] > > You'd be faster by a bit just to call write(byte) in a for loop. But this doesn't work. It's an order of magnitude *slower* to call write(byte) in a loop.
Also, call write( byte []) is also about 50% slower than the three argument write(). Both write(byte[]) and write(byte) resolve according to NetBeans to the superclass of BufferedOuputStream, OutputStream.
Is this virtual method overhead?
Or has Sun pre-optimized their own libraries so they run faster?
Bent C Dalager - 19 Nov 2007 23:37 GMT >> String sed = "0000000\n"; > >This *still* causes the output to be flushed. Remove the \n and I get a >50% speed increase (halves runtime). This could be because your console window needs to scroll less?
> Is there no way to set the >autoflush for System.out to false? System.setOut(new PrintStream(System.out)); might work, after a fashion :-)
From the APIdoc:
PrintStream
public PrintStream(OutputStream out)
Create a new print stream. This stream will not flush automatically.
Parameters: out - The output stream to which values and objects will be printed See Also: PrintWriter.PrintWriter(java.io.OutputStream)
Cheers, Bent D
 Signature Bent Dalager - bcd@pvv.org - http://www.pvv.org/~bcd powered by emacs
Mark Space - 20 Nov 2007 00:35 GMT >>> String sed = "0000000\n"; >> This *still* causes the output to be flushed. Remove the \n and I get a [quoted text clipped - 7 lines] > System.setOut(new PrintStream(System.out)); > might work, after a fashion :-) I don't think this works the way you think it will.
First, System.out already is a PrintStream. When a PrintStream is created, the constructor just takes the existing object, and stores it in its "out" instance variable. So you end up with a new PrintStream, that contains another PrintStream (the original System.out object) which already has the autoflush variable set to true.
I haven't tried it, but I bet that is what the debugger would say.
Note that this is weird too: OutputStream os = new BufferedOutputStream( System.out );
Guess what this makes?
Same as your idea, it makes a BufferedOutputStream that simply wraps the existing PrintStream which is System.out (autoflush still set to true!). System.out already wraps a BufferedOutputStream, however. And that BufferedOutputStream wraps a FileOutputStream. Finally, something that doesn't wrap something else! I think FileOutputStream handles all the real work of writing characters to the OS.
Why is wrapping with a second BufferedOutputStream so much faster? I dunno, but I'm guessing it does buffer, and it does cut down the calls to PrintStream. PrintStream will still autoflush though, just perhaps less often.
Bent C Dalager - 20 Nov 2007 09:39 GMT >> System.setOut(new PrintStream(System.out)); >> might work, after a fashion :-) [quoted text clipped - 6 lines] >that contains another PrintStream (the original System.out object) which >already has the autoflush variable set to true. From reading PrintStream's javadoc, what the wrapping PrintStream ought to be doing is handle its own buffer and its own (absence of) autoflush so that whatever data goes through to the wrapped one comes in much larger chunks. That the wrapped one then autoflushes these large chunks shouldn't really matter.
Of course, this may not actually be what happens :-)
>Why is wrapping with a second BufferedOutputStream so much faster? I >dunno, but I'm guessing it does buffer, and it does cut down the calls >to PrintStream. PrintStream will still autoflush though, just perhaps >less often. The problem is that System.out's PrintStream autoflushes most things, so the trick would be to wrap it into something that does /not/ autoflush but waits until some reasonably-sized buffer is full before passing it on. My assumption was that a default PrintStream would do this because it says that it defaults to not auto-flushing. A wrapping BufferedOutputStream would basically do the same thing.
Of course, I haven't actually tried either of them so it's all speculation.
Cheers, Bent D
 Signature Bent Dalager - bcd@pvv.org - http://www.pvv.org/~bcd powered by emacs
Mark Space - 20 Nov 2007 16:39 GMT > From reading PrintStream's javadoc, what the wrapping PrintStream > ought to be doing is handle its own buffer and its own (absence of) > autoflush so that whatever data goes through to the wrapped one comes > in much larger chunks. That the wrapped one then autoflushes these > large chunks shouldn't really matter. Right. The new PrintStream doesn't autoflush. But the old one that it wraps does. I don't think a PrintStream does any buffering itself, so the first PrintStream will just pass the writes down to the the wrapped PrintStream, which will do auto-flushes.
Set this up in an IDE and then run it with the debugger. Poke the internal variables (especially the inherited one "out") and you'll see. It's actually quite simple and un-mystical.
> Of course, this may not actually be what happens :-) > [quoted text clipped - 9 lines] > this because it says that it defaults to not auto-flushing. A wrapping > BufferedOutputStream would basically do the same thing. Yes. Somewhere above there's a discussion on using the FileDescriptors, which I didn't know where available.
PrintStream System.out = new PrintStream( new BufferedOutputStream( FileDescriptor.out), true );
This is how I think the System.out variable gets set up. Obviously you can do the same thing with FileDescriptor.out just setting the auto flush parameter to false.
However testing this, for me the above code (with auto-flush set to false) was a tad slower than:
BufferedOutputStream os = new BufferedOutputStream( System.out );
I don't know why this is. Possibly the JVM is doing some optimization tricks on it's own PrintStream to make it faster. So using the special optimized one is faster than double buffering all IO. Or it could be bad testing on my part, other random factors, etc. Not really sure.
Also important to remember: println() is very, very slow. print() is a little faster, even when printing newlines and with autoflush enabled. All versions of write() are much faster by almost an order of magnitude, and the fastest still is the three argument version, in some cases by a factor of 2x over the other write()'s. I'm not sure why, but I think it may have to do with virtual method overhead.
That's my one nickel summary of what's been discussed here.
Stefan Ram - 19 Nov 2007 23:45 GMT >This *still* causes the output to be flushed. Remove the \n and I get a >50% speed increase (halves runtime). Is there no way to set the >autoflush for System.out to false? I have written the following attempt. But I have not tested whether it works. (The exception handling still can be simplified.)
/* NB: The "false" below is intended to turn autoflush off. */ public class Main { public static java.io.PrintStream newOutPrintWithAutoflushTurnedOff() { java.io.PrintStream outPrint = null; java.lang.Exception ex = null; final java.io.FileDescriptor outDescriptor = java.io.FileDescriptor.out; try{ java.io.FileOutputStream outStream = new java.io.FileOutputStream( outDescriptor ); outPrint = new java.io.PrintStream( outStream, false ); } catch( final java.lang.SecurityException e ){ ex = e; } if( ex != null )throw new java.lang.RuntimeException( ex ); return outPrint; }
public static void setOutWithAutoflushTurnedOff() { java.io.PrintStream outPrint; java.lang.Exception ex = null; try{ outPrint = newOutPrintWithAutoflushTurnedOff(); try{ java.lang.System.setOut( outPrint ); } catch( final java.lang.SecurityException e ){ ex = e; }} catch( final java.lang.RuntimeException e ){ ex = e; } if( ex != null )throw new java.lang.RuntimeException( ex ); }
public static void main( final java.lang.String[] args ) { setOutWithAutoflushTurnedOff(); java.lang.System.out.println( "Is autoflush\noff now?" ); }}
massiccio - 23 Nov 2007 14:07 GMT On Nov 19, 9:52 pm, Java Performance Expert <java.performance.exp...@gmail.com> wrote:
> > I modified the Java benchmark in accordance with others' suggestions and for > > five million lines of output came up with: [quoted text clipped - 16 lines] > thanks > JH Try to pass the "-Xcomp" option to the JVM (it will compile all the code at startup -- by default at the beginning the VM runs in interpreted mode). Furthermore, you should run the same test a few times (let say inside a for loop): at the beginning the JVM "warms up" and optimizes the code it executes, then it will run at full speed.
Michele
Bent C Dalager - 19 Nov 2007 22:22 GMT >I modified the Java benchmark in accordance with others' suggestions and for >five million lines of output came up with: [quoted text clipped - 11 lines] >AMD-64 ~2 GHz, 1 GB RAM, Linux Fedora 7, the usual mix of other programs >running. 32-bit Java. On my MacBook Pro 2.33GHz with OSX 10.4.10, using java version "1.5.0_07" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-164) Java HotSpot(TM) Client VM (build 1.5.0_07-87, mixed mode, sharing) - and - gcc version 4.0.1 (Apple Computer, Inc. build 5367)
I get:
$ java -classpath . TimePrin Elapsed: 86.424 secs.
$ java -server -classpath . TimePrin Elapsed: 83.093 secs.
$ gcc test.c $ ./a.out Elapsed: 52.000000 secs.
$ gcc -O4 test.c $ ./a.out Elapsed: 52.000000 secs.
Cheers, Bent D
 Signature Bent Dalager - bcd@pvv.org - http://www.pvv.org/~bcd powered by emacs
Roedy Green - 20 Nov 2007 02:15 GMT On Mon, 19 Nov 2007 11:28:17 -0800 (PST), Java Performance Export <java.performance.expert@gmail.com> wrote, quoted or indirectly quoted someone who said :
>I'm wondering if anyone can help me understand why my Java is being >very slow compared to an equivalent program written in "C". But it isn't. It is slower to start. It has a bigger footprint, but with Jet, Java optimisation beats hand coded assembler. Java is a much more orderly language than C. So many of C's seat-of-the-pants tricks with freewheeling pointers and overlaying different data in the same cells inhibit optimisation.
see http://mindprod.com/jgloss/jet.html
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Nigel Wade - 20 Nov 2007 09:49 GMT > Hi all, > [quoted text clipped - 24 lines] > I'd be very curious to see how this equivalent benchmark peforms on > others' environments. There may well be other issues with a benchmark which, on the surface, appears to be a very simple text manipulation program.
As a simple test on a mainstream Linux installation (RedHat 4) running the standard GNU diff utility with the default UTF8 character encoding is 40X slower than the same diff if LANG=C is set first. Java uses UTF, C uses ASCII. Not basing the entire benchmark on character/string manipulation might be a good starting point.
There is no such animal as a simple benchmark, the simpler it is (or appears to be) the more likely it is to be misleading or downright wrong. At the end of the day the only meangingful benchmark is the actual code you need to run, or as near an approximation to that code as you can manage.
 Signature Nigel Wade, System Administrator, Space Plasma Physics Group, University of Leicester, Leicester, LE1 7RH, UK E-mail : nmw@ion.le.ac.uk Phone : +44 (0)116 2523548, Fax : +44 (0)116 2523555
Mark Rafn - 20 Nov 2007 19:20 GMT Funny username for this question ;)
>I'm wondering if anyone can help me understand why my Java is being >very slow compared to an equivalent program written in "C". Because the program is not equivalent.
>I'm simply trying to print out the first N integers like > "This is line <nnnn>" >as a simple benchmark. Too simple. There are different assumptions C and Java make about character encoding, stdout buffering, and likely other things that mean the programs are not equivalent.
>My Java version is over 60 times slower than my "C" version and I >would like to establish a lower bound on how long the very fastest >Java version could take, by applying every possible performance >speedup availalbe in the Java environment. For the simple version of this program, I get about 10:1 when I replace system.out with 'new PrintStream("/dev/null")' to get similar buffering. I suspect I could get closer to parity if I wanted to deal with bytes instead of unicode characters, as C does.
>My client is threatening to implement in "C" and I am trying to talk >him out of it. If your client is printing millions of lines of pure ASCII to /dev/null, then C is probably the better choice. If she wants something else, then a different evaluation would be perhaps desired.
If you want to show performance strength of Java, pick a benchmark that the VM can optimize better. I like the recursive fibonacci calculator, where the VM's ability to do dynamic optimization at runtime beats out gcc's static compiler optimation. (Yes, I know the algorithm is silly, but so is any micro-benchmark):
[dagon tmp]$ time java -server -cp . Fib 45 fibonacci 45 is 1134903170 real 0m8.729s user 0m8.400s sys 0m0.000s
[markrafn tmp]$ time ./Fib 45 fibonacci 45 is 1134903170 real 0m13.650s user 0m13.530s sys 0m0.000s
Fib.java: public class Fib { /** get fibonacci number N */ public static long fib(int fibNum) { if (fibNum < 3) return 1; return (fib(fibNum-1) + fib(fibNum-2)); }
public static void main(String[] args) { int fibNum = Integer.parseInt(args[0]); System.out.println("fibonacci " + fibNum + " is " + fib(fibNum)); } }
Fib.c: #include<stdio.h>
long fib(int num) { if (num < 3) return 1; return (fib(num-1) + fib(num-2)); }
int main(int argc, char **argv, char **envv) { int num = atoi(argv[1]); printf("fibonacci %d is %ld\n", num, fib(num)); } -- Mark Rafn dagon@dagon.net <http://www.dagon.net/>
Crouchez - 22 Nov 2007 03:10 GMT Why is Java so slow?
It's not usually that slow - it depends how you use it.
It's usually going to be a bit slower than C and C++ because it uses C and C++ code to get access to the OS core libraries.
In the example you gave it is doing some peripheral stuff even before it passes the bytes to the native display print so inherently it is going to add an overhead.
Java is a bridge to the native OS, it takes a small extra amount of time to cross the bridge but it's negligible most of the time and it makes up for the fact because it's intended to be ported to other platforms.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|