Flash Player 11.3 added a new way to compress and uncompress ByteArray: the LZMA algorithm. This is useful because LZMA typically compresses to much smaller size than the existing zlib and deflate algorithms. But how much of a speed penalty does it incur? Today’s article seeks to find just that!

First of all, it’s incredibly easy to switch to LZMA if you’re interested in doing so. All you need to do is pass CompressionAlgorithm.LZMA to ByteArray.compress or ByteArray.uncompress. Of course you’ll also need to build for Flash Player 11.3 and ensure that your users are using at least that version.

As for the extra compression you’ll get, this will depend on the data you’re compressing but it may be about 50% better than gzip. This could really help with download times! So here’s a test app to check the performance of LZMA versus zlib and Deflate, based on the test app from Compression Speed:

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
 
	public class CompressionSpeedFollowup extends Sprite
	{
		private var logger:TextField = new TextField();
		private function row(...cols): void
		{
			logger.appendText(cols.join(",") + "\n");
		}
 
		public function CompressionSpeedFollowup()
		{
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			row(
				"Size",
				"deflate (compress)",
				"zlib (compress)",
				"LZMA (compress)",
				"deflate (uncompress)",
				"zlib (uncompress)",
				"LZMA (compress)"
			);
			runTests("1 KB", 1024, 1024);
			runTests("1 MB", 1024*1024, 1);
		}
 
		private function runTests(label:String, size:int, reps:int): void
		{
			var beforeTime:int;
			var afterTime:int;
			var emptyTime:int;
			var deflateTimeCompress:int;
			var zlibTimeCompress:int;
			var lzmaTimeCompress:int;
			var deflateTimeUncompress:int;
			var zlibTimeUncompress:int;
			var lzmaTimeUncompress:int;
			var bytes:ByteArray = new ByteArray();
			var originalBytes:ByteArray = new ByteArray();
			var compressedBytes:ByteArray = new ByteArray();
			var i:int;
			var zlib:String = CompressionAlgorithm.ZLIB;
			var deflate:String = CompressionAlgorithm.DEFLATE;
			var lzma:String = CompressionAlgorithm.LZMA;
 
			fillBytes(originalBytes, size);
 
			// Empty
			beforeTime = getTimer();
			for (i = 0; i < reps; ++i)
			{
				copyBytes(originalBytes, bytes);
			}
			afterTime = getTimer();
			emptyTime = afterTime - beforeTime;
 
			// Compress
			beforeTime = getTimer();
			for (i = 0; i < reps; ++i)
			{
				copyBytes(originalBytes, bytes);
				bytes.compress(deflate);
			}
			afterTime = getTimer();
			deflateTimeCompress = afterTime - beforeTime - emptyTime;
 
			beforeTime = getTimer();
			for (i = 0; i < reps; ++i)
			{
				copyBytes(originalBytes, bytes);
				bytes.compress(zlib);
			}
			afterTime = getTimer();
			zlibTimeCompress = afterTime - beforeTime - emptyTime;
 
			beforeTime = getTimer();
			for (i = 0; i < reps; ++i)
			{
				copyBytes(originalBytes, bytes);
				bytes.compress(lzma);
			}
			afterTime = getTimer();
			lzmaTimeCompress = afterTime - beforeTime - emptyTime;
 
			// Uncompress
			copyBytes(originalBytes, compressedBytes);
			compressedBytes.compress(deflate);
			beforeTime = getTimer();
			for (i = 0; i < reps; ++i)
			{
				copyBytes(compressedBytes, bytes);
				bytes.uncompress(deflate);
			}
			afterTime = getTimer();
			deflateTimeUncompress = afterTime - beforeTime - emptyTime;
 
			copyBytes(originalBytes, compressedBytes);
			compressedBytes.compress(zlib);
			beforeTime = getTimer();
			for (i = 0; i < reps; ++i)
			{
				copyBytes(compressedBytes, bytes);
				bytes.uncompress(zlib);
			}
			afterTime = getTimer();
			zlibTimeUncompress = afterTime - beforeTime - emptyTime;
 
			copyBytes(originalBytes, compressedBytes);
			compressedBytes.compress(lzma);
			beforeTime = getTimer();
			for (i = 0; i < reps; ++i)
			{
				copyBytes(compressedBytes, bytes);
				bytes.uncompress(lzma);
			}
			afterTime = getTimer();
			lzmaTimeUncompress = afterTime - beforeTime - emptyTime;
 
			row(
				label,
				deflateTimeCompress,
				zlibTimeCompress,
				lzmaTimeCompress,
				deflateTimeUncompress,
				zlibTimeUncompress,
				lzmaTimeUncompress
			);
		}
 
		private function fillBytes(bytes:ByteArray, size:int): void
		{
			bytes.length = 0;
			bytes.position = 0;
			for (var i:int; i < size; ++i)
			{
				bytes.writeByte(Math.random()*256);
			}
			bytes.position = 0;
		}
 
		private function copyBytes(bytes:ByteArray, into:ByteArray): void
		{
			bytes.position = 0;
			into.position = 0;
			into.length = 0;
			into.writeBytes(bytes);
			bytes.position = 0;
			into.position = 0;
		}
	}
}

I ran this test app in the following environment:

  • Flex SDK (MXMLC) 4.6.0.23201, compiling in release mode (no debugging or verbose stack traces)
  • Release version of Flash Player 11.4.402.287
  • 2.3 Ghz Intel Core i7
  • Mac OS X 10.8.2

And here are the results I got: (the LZMA labels are incorrect)

Performance Chart

Compress-only Performance Chart

Uncompress-only Performance Chart

As the graphs make plain, LZMA is far slower to compress and uncompress data than zlib or Deflate. It’s about 28x slower to compress and and 16x slower to uncompress, at least with the random test data I’ve used. However, as with most tasks in programming, there is still a tradeoff to be had. If you need to speed up slow download times you can now use LZMA compression at the expense of extra processing power to uncompress the data once it’s downloaded. This very well may be a tradeoff you’re willing to make, but you should definitely be aware of the above results: you’re going to hit the CPU hard!

Spot a bug? Have a question or suggestion? Post a comment!