Performance regression in java.util.zip.Deflater

Martin Buchholz Martin.Buchholz at Sun.COM
Thu Dec 20 21:25:03 UTC 2007


A thorny problem.
Here is a list of related bugs "fixed" in 5.0 updates:

6348045: REGRESSION: serious performance degradation as GZIPInputStream
is slower
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6348045

6206933: GZipOutputStream/InputStream goes critical(calls
JNI_Get*Critical) and causes slowness
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6206933

6364346: GZIPOutputStream is slower on 1.4.2_11-b02 than on 1.4.2_09
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6364346

Sun engineers have tried to get reasonable performance
without using JNI_Get*Critical, since that introduces other
serious performance problems.  It was my belief that any
pathological n^2 performance problems had been truly fixed.

Make sure you are running at least 5.0u8 to get all of the above.

Martin

Clemens Eisserer wrote:
> Hello,
> 
> Sombody posted at
> http://forums.java.net/jive/thread.jspa?messageID=251006 that he has
> problems with the performance of java.util.zip.Deflater starting with
> version 1.5.0_07.
> I did a very dumb micro-benchmark and it seems to confirm it, with
> small buffers (the original author used a 1000 byte buffer), 1.4.2
> took ~1000ms whereas 6.0/7.0b23 take 11000ms. Even when using a 32kb
> buffer 1.4.2 is still twice as fast.
> I played a bit with oprofile and it clearly shows up that memcopy eats
> all the memory.
> 
> The problem is that every time the whole input-buffer is copied to the
> native side, assuming that every call 2000bytes (ratio 50%) of input
> data are compressed "away" from the input, the method copies every
> call to deflateBytes 5000k, 4998k, 4996k , ....
> This can't be solved easily because we don't know how many bytes zlib
> may consume from the input-data.
> 
> I would have a few ideas how this issue could be solved:
> 
> 1.) Using DirectByteBuffers for data-transfer.
> pros: Array-Like access from the native side, no negative inpact on GC.
> cons: Data has to be copied, wasted RAM (because we have two copies,
> one in the byte[] supplied by the user, and one outside the heap in
> the DirectByteBuffer, possible OOMs because out-of-native memory.
> 
> 2.) Use GetPrimitiveArrayCritical:
> pros: no copying involved at all, no redundant copies of data arround.
> cons: quite harsh to the GC (blocked until compression is finished) -
> maybe even scalability limiter.
> I've modified Deflate.c to use GetPrimitiveArrayCritical, and it now
> compresses in 100ms instead of 11000, even twice as fast as 1.4.2.
> Although this solution looks quite cool, I doubt its behaviour does
> comply with Sun's quality expectations.
> 
> 3.) Limit the amount of byted trasfereed to the native side:
> pros: no redundant copies of input-data
> cons: still a lot of copying (however not n^2), maybe more JNI calls
> to get same work done.
> 
> I would be happy about suggestions and thoughts in general. Maybe
> somebody knows why the old JVMs performed so much better here?
> 
> Thank you in advance, lg Clemens
> 
> 
> 
> Test-Case:
> public class DeflaterTest
> {
> 
> public static byte[] compresserZlib(byte[] donnees)
> 	{
> 	ByteArrayOutputStream resultat = new ByteArrayOutputStream();
> 	byte[] buffer = new byte[1000];
> 	int nbEcrits;
> 
> 	Deflater deflater = new Deflater();
> 	deflater.setInput(donnees);
> 	deflater.setLevel(0);
> 	deflater.finish();
> 
> 	while (!deflater.finished())
> 	{
> 		nbEcrits = deflater.deflate(buffer);
> 		resultat.write(buffer, 0, nbEcrits);
> 	}
> 
> 		return resultat.toByteArray();
> 	}
> 	
> 	public static void main(String[] args)
> 	{
> 		Random r = new Random();
> 		byte[] buffer = new byte[5000000];
> 		for(int i=0; i < buffer.length; i++)
> 		{
> 			buffer[i] = (byte) (r.nextInt()%127);
> 		}
> 		
> 		for(int i=0; i < 100; i++)
> 		{
> 			long start = System.currentTimeMillis();
> 			byte[] result = compresserZlib(buffer);
> 			long end = System.currentTimeMillis();
> 			
> 			System.out.println("Run took: "+(end-start)+" "+result[Math.abs(buffer[0])]);
> 		}
> 		
> 	}
> }



More information about the core-libs-dev mailing list