Compression Speed Followup
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)
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!
#1 by Smily on October 29th, 2012 ·
Perhaps you could try it with low-entropy data (non-random, like uncompressed images, xml files, books from Project Gutenberg), to see if/how the execution speed is affected by entropy. It could be that random (high-entropy) data is slower to compress? With highly compressible source material, you could also roughly compare compression ratios.
Also, graph titles could be a bit misleading if you don’t read other stuff, since “compression speed” implies that larger values are better. Perhaps “execution time” might fit better?
Anyway, keep up the good work! :)
#2 by jackson on October 29th, 2012 ·
It’s definitely possible, but really hard to test. There are so many types and sizes of data that could be tested that this article would be humongous if I tested a good set. I could do all zeroes, all ones, random data, PNG data, JPEG data, XML data, JSON data, and so on and so on. The test data link I provided gives times for various data sets along with compression ratios, so the overall times are probably similar given that there’s a high likelihood that Flash Player is using off-the-shelf zlib, deflate, and LZMA libraries.
Good point about the graph titles. I’ll try to be clearer next time.
#3 by Tronster on October 30th, 2012 ·
Great article!
Quick naming bug: I believe “compress” should be “uncompress” next to LZMA for the labels on the (2) charts and in the source code.
#4 by jackson on October 30th, 2012 ·
Thanks, I’ve pointed out the incorrect labels in the article.
#5 by Gary Paluk on November 19th, 2012 ·
Great information, these quick info, real world examples are perfect for me. Thanks for the data that allows me to hit the ground running.
#6 by Bill on November 25th, 2013 ·
Hello, I’d like to be able to download flash and extract the plugin file for my browser without admin rights on osx. The lzma thing has stumped me for a while now. So my question is… can you tell me how to extract and decompress the plugin from the downloaded install dmg from adobe using popular utilities?
#7 by jackson on November 26th, 2013 ·
Sorry, I don’t know how to do this. Perhaps you could ask whoever has admin rights to install a new version of Flash for you. Or you could develop with a standalone version of Flash, which probably doesn’t need admin rights.