(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

(:3[kanのメモ帳]


本ブログの運営者kan.kikuchiが個人で開発したゲームです!


Messagepackをさらに圧縮する方法と、圧縮が有効なデータ【Unity】【Messagepack】


このエントリーをはてなブックマークに追加


この記事でのバージョン
Unity 6000.2.6f2


はじめに

以前、JSONと同じように扱えて、より高速かつコンパクトなMessagePack

Unityに導入する方法と使い方を紹介しました。


今回はそのMessagePackをさらに圧縮する方法の紹介です!



Messagepackをさらに圧縮

といってもシリアライズ時にオプションを追加するだけです。(デシリアライズも同じオプションを使う)

//シリアライズオプション
var lz4  = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);

//圧縮してMessagePack化(シリアライズ)
var bytesLz4 = MessagePackSerializer.Serialize(data, lz4);

//復元(デシリアライズ)
var restored = MessagePackSerializer.Deserialize<IntListContainer>(bytesLz4, lz4);


例えば以下のようにただintの配列もってるだけのクラスを、

using System;
using System.Collections.Generic;
using MessagePack;
using UnityEngine;

[MessagePackObject, Serializable]
public class IntListContainer{
  [Key(0)] public List<int> Numbers = new List<int>();
}


大量のintの配列でインスタンス化した後、

それをMessagepack化して非圧縮と圧縮で比較してみると(ついでにJsonも)、

//生成するデータの設定
int   elementCount   = 500000;//生成する整数の個数
float nonZeroDensity = 0.5f;//0以外を置く確率(小さいほど0が多くなり、圧縮が効きやすい)
    
//整数列を作成
var list = new List<int>(elementCount);
for (int i = 0; i < elementCount; i++){
  if (Random.value < nonZeroDensity){
    list.Add(1 + Random.Range(0, Mathf.Max(1, int.MaxValue)));
  }
  else{
    list.Add(0);
  }
}

var data = new IntListContainer{
  Numbers = list
};

//圧縮オプション(非圧縮/LZ4)
var noComp = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.None);
var lz4    = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);

//シリアライズ
var bytesJson = System.Text.Encoding.UTF8.GetByteCount(JsonUtility.ToJson(data));//Jsonも比較用に
var bytesNo   = MessagePackSerializer.Serialize(data, noComp);
var bytesLz4  = MessagePackSerializer.Serialize(data, lz4);

//見やすいようにMB換算
var mbJson = bytesJson / (1024f * 1024f);
var mbNo   = bytesNo.Length / (1024f * 1024f);
var mbLz4  = bytesLz4.Length / (1024f * 1024f);
var rate   = 100f * bytesLz4.Length / Mathf.Max(1, bytesNo.Length);

//結果出力
Debug.Log("=== MessagePack int配列(LZ4有無) サイズ比較 ===");
Debug.Log($"要素数: {elementCount}, 非ゼロ密度: {nonZeroDensity:P0}");
Debug.Log($"Jsonサイズ : {mbJson:F2} MB");
Debug.Log($"MessagePack 非圧縮サイズ: {mbNo:F2} MB");
Debug.Log($"MessagePack 圧縮サイズ  : {mbLz4:F2} MB");
Debug.Log($"圧縮率     : {rate:F1}%(小さいほど良い)");

//一応、復元確認
var restored = MessagePackSerializer.Deserialize<IntListContainer>(bytesLz4, lz4);
Debug.Log($"復元確認: Numbers.Count={restored.Numbers.Count}");


圧縮によってサイズが大幅に減少してるのが分かります。


ただし、上記のデータは0以外の数字が出る確率(nonZeroDensity)が5%、

同じようなパターンを多く含むデータだったため、ここまで圧縮されています。


逆に0以外が出る確率(nonZeroDensity)が50%という、

同じようなパターンが少ないデータだと、圧縮率が悪くなってしまいます。