1. 程式人生 > >Java壓縮演算法效能比較

Java壓縮演算法效能比較

作者:Dreawer
連結:https://zhuanlan.zhihu.com/p/24379501
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

前言

遊戲開發中,經常在玩家進入遊戲的時候進行必要的資訊初始化,往往這個初始化資訊資料包是相對來說還是比較大的,一般在30-40kb左右,還是有必要進行壓縮一下再發送訊息,剛好前段時間看過,裡面列舉了一些常用的壓縮演算法,如下圖所示:

是否可切分表示是否可以搜尋資料流的任意位置並進一步往下讀取資料,這項功能在Hadoop的MapReduce中尤其適合。
下面對這幾種壓縮格式進行簡單的介紹,並進行壓力測試,進行效能比較

DEFLATE

DEFLATE是同時使用了LZ77演算法與哈夫曼編碼(Huffman Coding)的一個無損資料壓縮演算法,DEFLATE壓縮與解壓的原始碼可以在自由、通用的壓縮庫zlib上找到,zlib官網:

http://www.zlib.net/

jdk中對zlib壓縮庫提供了支援,壓縮類Deflater和解壓類Inflater,Deflater和Inflater都提供了native方法

private native int deflateBytes(long addr, byte[] b, int off, int len,
int flush);
private native int inflateBytes(long addr, byte[] b, int off, int len)
throws DataFormatException;

所有可以直接使用jdk提供的壓縮類Deflater和解壓類Inflater,程式碼如下:

 public static byte[] compress(byte input[]) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Deflater compressor = new Deflater(1);
        try {
            compressor.setInput(input);
            compressor.finish();
            final byte[] buf = new byte[2048];
            while
(!compressor.finished()) { int count = compressor.deflate(buf); bos.write(buf, 0, count); } } finally { compressor.end(); } return bos.toByteArray(); } public static byte[] uncompress(byte[] input) throws DataFormatException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); Inflater decompressor = new Inflater(); try { decompressor.setInput(input); final byte[] buf = new byte[2048]; while (!decompressor.finished()) { int count = decompressor.inflate(buf); bos.write(buf, 0, count); } } finally { decompressor.end(); } return bos.toByteArray(); }

可以指定演算法的壓縮級別,這樣你可以在壓縮時間和輸出檔案大小上進行平衡。可選的級別有0(不壓縮),以及1(快速壓縮)到9(慢速壓縮),這裡使用的是以速度為優先。

gzip

gzip的實現演算法還是deflate,只是在deflate格式上增加了檔案頭和檔案尾,同樣jdk也對gzip提供了支援,分別是GZIPOutputStream和GZIPInputStream類,同樣可以發現GZIPOutputStream是繼承於DeflaterOutputStream的,GZIPInputStream繼承於InflaterInputStream,並且可以在原始碼中發現writeHeader和writeTrailer方法:

private void writeHeader() throws IOException {
     ......
}
private void writeTrailer(byte[] buf, int offset) throws IOException {
     ......
}

具體的程式碼實現如下:

public static byte[] compress(byte srcBytes[]) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(srcBytes);
            gzip.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return out.toByteArray();
    }

    public static byte[] uncompress(byte[] bytes) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[2048];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return out.toByteArray();
    }

bzip2

bzip2是Julian Seward開發並按照自由軟體/開源軟體協議釋出的資料壓縮演算法及程式。Seward在1996年7月第一次公開發布了bzip2 0.15版,在隨後幾年中這個壓縮工具穩定性得到改善並且日漸流行,Seward在2000年晚些時候釋出了1.0版。更多wikibzip2

bzip2比傳統的gzip的壓縮效率更高,但是它的壓縮速度較慢。

jdk中沒有對bzip2實現,但是在commons-compress中進行了實現,maven引入:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.12</version>
</dependency>

具體的程式碼實現如下:

public static byte[] compress(byte srcBytes[]) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        BZip2CompressorOutputStream bcos = new BZip2CompressorOutputStream(out);
        bcos.write(srcBytes);
        bcos.close();
        return out.toByteArray();
    }

    public static byte[] uncompress(byte[] bytes) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            BZip2CompressorInputStream ungzip = new BZip2CompressorInputStream(
                    in);
            byte[] buffer = new byte[2048];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return out.toByteArray();
    }

下面的介紹的lzo,lz4以及snappy這3中壓縮演算法,均已壓縮速度為優先,但壓縮效率稍遜一籌

lzo

LZO是致力於解壓速度的一種資料壓縮演算法,LZO是Lempel-Ziv-Oberhumer的縮寫。這個演算法是無損演算法,更多wikiLZO

需要引入第三方庫,maven引入:

<dependency>
    <groupId>org.anarres.lzo</groupId>
    <artifactId>lzo-core</artifactId>
    <version>1.0.5</version>
</dependency>

具體實現程式碼:

public static byte[] compress(byte srcBytes[]) throws IOException {
        LzoCompressor compressor = LzoLibrary.getInstance().newCompressor(
                LzoAlgorithm.LZO1X, null);

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        LzoOutputStream cs = new LzoOutputStream(os, compressor);
        cs.write(srcBytes);
        cs.close();

        return os.toByteArray();
    }

    public static byte[] uncompress(byte[] bytes) throws IOException {
        LzoDecompressor decompressor = LzoLibrary.getInstance()
                .newDecompressor(LzoAlgorithm.LZO1X, null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
        LzoInputStream us = new LzoInputStream(is, decompressor);
        int count;
        byte[] buffer = new byte[2048];
        while ((count = us.read(buffer)) != -1) {
            baos.write(buffer, 0, count);
        }
        return baos.toByteArray();
    }

lz4

LZ4是一種無損資料壓縮演算法,著重於壓縮和解壓縮速度更多wikilz4
maven引入第三方庫:

<dependency>
    <groupId>net.jpountz.lz4</groupId>
    <artifactId>lz4</artifactId>
    <version>1.2.0</version>
</dependency>

具體程式碼實現:

public static byte[] compress(byte srcBytes[]) throws IOException {
        LZ4Factory factory = LZ4Factory.fastestInstance();
        ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
        LZ4Compressor compressor = factory.fastCompressor();
        LZ4BlockOutputStream compressedOutput = new LZ4BlockOutputStream(
                byteOutput, 2048, compressor);
        compressedOutput.write(srcBytes);
        compressedOutput.close();
        return byteOutput.toByteArray();
    }

    public static byte[] uncompress(byte[] bytes) throws IOException {
        LZ4Factory factory = LZ4Factory.fastestInstance();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        LZ4FastDecompressor decompresser = factory.fastDecompressor();
        LZ4BlockInputStream lzis = new LZ4BlockInputStream(
                new ByteArrayInputStream(bytes), decompresser);
        int count;
        byte[] buffer = new byte[2048];
        while ((count = lzis.read(buffer)) != -1) {
            baos.write(buffer, 0, count);
        }
        lzis.close();
        return baos.toByteArray();
    }

snappy

Snappy(以前稱Zippy)是Google基於LZ77的思路用C++語言編寫的快速資料壓縮與解壓程式庫,並在2011年開源。它的目標並非最大壓縮率或與其他壓縮程式庫的相容性,而是非常高的速度和合理的壓縮率。更多wikisnappy

maven引入第三方庫:

<dependency>
    <groupId>org.xerial.snappy</groupId>
    <artifactId>snappy-java</artifactId>
    <version>1.1.2.6</version>
</dependency>

具體程式碼實現:

    public static byte[] compress(byte srcBytes[]) throws IOException {
        return  Snappy.compress(srcBytes);
    }

    public static byte[] uncompress(byte[] bytes) throws IOException {
        return Snappy.uncompress(bytes);
    }

壓力測試

以下對35kb玩家資料進行壓縮和解壓測試,相對來說35kb資料還是很小量的資料,所有以下測試結果只是針對指定的資料量區間進行測試的結果,並不能說明哪種壓縮演算法好與不好。

測試環境:


對35kb資料進行2000次壓縮和解壓縮測試,測試程式碼如下:

public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream(new File("player.dat"));
        FileChannel channel = fis.getChannel();
        ByteBuffer bb = ByteBuffer.allocate((int) channel.size());
        channel.read(bb);
        byte[] beforeBytes = bb.array();

        int times = 2000;
        System.out.println("壓縮前大小:" + beforeBytes.length + " bytes");
        long startTime1 = System.currentTimeMillis();
        byte[] afterBytes = null;
        for (int i = 0; i < times; i++) {
            afterBytes = GZIPUtil.compress(beforeBytes);
        }
        long endTime1 = System.currentTimeMillis();
        System.out.println("壓縮後大小:" + afterBytes.length + " bytes");
        System.out.println("壓縮次數:" + times + ",時間:" + (endTime1 - startTime1)
                + "ms");

        byte[] resultBytes = null;
        long startTime2 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            resultBytes = GZIPUtil.uncompress(afterBytes);
        }
        System.out.println("解壓縮後大小:" + resultBytes.length + " bytes");
        long endTime2 = System.currentTimeMillis();
        System.out.println("解壓縮次數:" + times + ",時間:" + (endTime2 - startTime2)
                + "ms");
    }

程式碼中的GZIPUtil根據不同的演算法進行替換,測試結果如下圖所示:

分別對壓縮前大小、壓縮後大小、壓縮時間、解壓縮時間、cpu高峰進行了統計

總結

從結果來看,deflate、gzip和bzip2更關注壓縮率,壓縮和解壓縮時間會更長;lzo,lz4以及snappy這3中壓縮演算法,均已壓縮速度為優先,壓縮率會稍遜一籌;lzo,lz4以及snappy在cpu高峰更低一點。因為在容忍的壓縮率之內,我們更加關注壓縮和解壓縮時間,以及cpu使用,所有最終使用了snappy,不難發現snappy在壓縮和解壓縮時間以及cpu高峰都是最低的,並且在壓力率上也沒有太多的劣勢。

相關推薦

Java壓縮演算法效能比較

作者:Dreawer連結:https://zhuanlan.zhihu.com/p/24379501來源:知乎著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。前言遊戲開發中,經常在玩家進入遊戲的時候進行必要的資訊初始化,往往這個初始化資訊資料包是相對來說還

大資料聚類演算法效能比較及實驗報告

在大資料領域這個聚類演算法真是起到了十分重要的作用,只有通過有效地聚類才能得到非常直觀的結果。 有一個實驗要求對比兩種大資料聚類演算法的效能,具體的程式碼也不是由我實現的,我只是改了一部分,主要還是部落格大佬們的程式碼,我這裡借用了一下~~ 具體的實驗報告和

java return throw效能比較

-Xss16m /** * fileName Test * author zhangx * date 2018/10/12 16:05 * description */ public class Test { public static void main(St

五種內部排序演算法效能比較——C語言

五種內部排序演算法效能比較 1.直接插入排序演算法 將一個待排序的記錄插入到若干個已排好序的有序記錄中的適當位置,從而得到一個新的、記錄數增加1的有序資料序列,直到插入完成。在最開始,整個有序資料序列只有一個數據,當全部資料插入完畢後,整個資料序

各種排序演算法效能比較

原帖 http://www.cnblogs.com/wangjiahong/p/3570465.html?utm_source=tuicool 下面是我直接做成的原始碼,直接可以執行。 大家可以根據需要測試 using System; using System.Col

常見加密/簽名/雜湊演算法效能比較 (多平臺 AES/DES, DH, ECDSA, RSA等)

常見加密/簽名/雜湊演算法效能比較 (多平臺) 各CPU平臺處理數值單位是seconds of cpu time, 通過計算執行該程序的CPU時間。 1、 AES/DES: 2、Hashs 3、AEAD 4、Diffie-Hel

【視覺-立體視覺】立體匹配幾種演算法效能比較

OpenCV三種立體匹配求視差圖演算法總結 對OpenCV中涉及的三種立體匹配演算法進行程式碼及各自優缺點總結: 首先我們看一下BM演算法: 該演算法程式碼: CvStereoBMState *BMState = cvCreateStereoBMSta

五中排序演算法效能比較總結

1 概述 本文對比較常用且比較高效的排序演算法進行了總結和解析,並貼出了比較精簡的實現程式碼,包括選擇排序、插入排序、歸併排序、希爾排序、快速排序等。演算法效能比較如下圖所示: 2 選擇排序 選擇排序的第一趟處理是從資料序列所有n個數據中選擇一個最小的資料作為有

crypto++ 中DES AES RC5 RC6 TEA XTEA XXTEA加密演算法效能比較

加密是網際網路資料安全的保證,但是現在感覺多數網頁遊戲和手遊都已經拋棄了資料加密,幾乎都是直接明文傳送。不過現在遊戲爛大街,除非很牛的遊戲才會有人去破解,垃圾遊戲我想沒有那個蛋疼的會去破解。本著學習的精神下載了crypto++ 對常用的對稱加密演算法進行效能比較,由於遊戲資

A*,Dijkstra,BFS演算法效能比較及A*演算法的應用

                    一之續、A*,Dijkstra,雙向BFS演算法效能比較及A*演算法的應用 作者:July   二零一一年三月十日。 出處:http://blog.csdn.net/v_JULY_v ----------------------

python(三)6種排序演算法效能比較(冒泡、選擇、插入、希爾、快速、歸併)

# coding:utf-8 import random import time def bubble_sort(alist): """冒泡""" n = len(alist) for j in range(n-1): count

八大排序演算法Java效能比較

選擇排序—堆排序(Heap Sort) 堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。 基本思想: 堆的定義如下:具有n個元素的序列(k1,k2,...,kn),當且僅當滿足 時稱之為堆。由堆的定義可以看出,堆頂元素(即第一個元素)必為最小項(小頂堆)。 若以一維陣列

Java 之 檔案讀寫及效能比較總結

Java 之 檔案讀寫及效能比較總結 2014年05月12日 17:56:49 閱讀數:21765  幹Java這麼久,一直在做WEB相關的專案,一些基礎類差不多都已經忘記。經常想得撿起,但總是因為一些原因,不能如願。 其實不是沒有時間,只是有些時候疲於總結,今得空,下定決心

java java中subString、split、stringTokenizer三種擷取字串方法的效能比較

面試的時候,string  基本上是必須問的知識   突然想起面試的時候曾經被人問過:都知道在大資料量情況下,使用String的split擷取字串效率很低,有想過用其他的方法替代嗎?用什麼替代?我當時的回答很斬釘截鐵:沒有。 google了一下,發現有2中替代方法,於

Java 字串拼接 五種方法的效能比較分析 “+”、contact、join、append

一、五種方法分析: 1. 加號 “+” 2. String contact() 方法 3. StringUtils.join() 方法 4. StringBuffer append() 方法 5. StringBuilder append() 方法 二、優劣勢分析 開

Java 中不同的並行實現的效能比較

Fork/Join框架在不同配置下的表現如何? 正如電影星球大戰那樣,Java 8的並行流也是譭譽參半。並行流(Parallel Stream)的語法糖就像預告片裡的新型光劍一樣令人興奮不已。現在Java中實現併發程式設計存在多種方式,我們希望瞭解這麼做所帶來的效能提升及風險是什麼。從經過260多次測試之後

高併發程式設計系列:4種常用Java執行緒鎖的特點,效能比較、使用場景

高併發程式設計系列:4種常用Java執行緒鎖的特點,效能比較、使用場景 http://youzhixueyuan.com/4-kinds-of-java-thread-locks.html   在Java併發程式設計中,經常遇到多個執行緒訪問同一個 共享資源 ,這時候作為開發者

【 模擬 】基於TOA的定位演算法效能分析(不同接收站數量下的比較

研究接收機數量對SNR = 30 dB的非線性和線性方法的MSPE效能的影響。 從最小數量的感測器開始,即L = 3,它們的位置是(0,0),(10,0)和(10,10)。 然後將具有座標(0,10),(0,5),(5,0),(10,5)和(5,10)的接收器連續新增到L = 8。未知源位於 (

【 模擬 】基於TOA的定位演算法效能分析(不同信噪比下的比較

Comparison of Nonlinear and Linear Approaches with CRLB for TOA - Based Positioning for Different SNRs 上篇博文:【 筆記 】定位演算法效能分析 給出了各種定位演算法效能分析的理論

Java容器——ArrayList VS LinkedList(Java9) 效能比較

ArrayList和LinkedList都繼承自List, 內在實現機制有所不同,關於區別方面已經有很多優秀的文章進行了介紹。本文從實踐角度出發,對比兩種List在不同操作中的效能,便於讀者在特定場景中參考選型。由於電腦配置,JDK版本,IDE等會導致測試結果又出入甚至有結論不太一致的結果,本文的試