Unity & サーバー間通信でのMessagePack導入奮闘記(Unity編)
前回、MessagePack導入の背景とサーバー側の実装についての記事を紹介しましたが、
今回はクライアント側(Unity)の実装と、そのパフォーマンスについて紹介します。
MePack for CssageLI
Unity(C#)でMessagePackを扱うためのライブラリとして、公式が提供しているMessagePack for CLIを選びました。
オブジェクトのシリアライズとデシリアライズをするためのコードは以下のようになります。
//パック MemoryStream stream = new MemoryStream(); var serializer = MessagePackSerializer.Create<SampleRequest>(); serializer.Pack(stream, mHttpParam.requestParam); data = new byte[(int)stream.Length]; stream.Position = 0; stream.Read(data, 0, (int)stream.Length); //アンパック var serializer = MessagePackSerializer.Create<SampleResponse>(); SampleResponse response = serializer.UnpackSingleObject(bytes);
MessagePack for CLIではデフォルトではリストArray型(リスト)でシリアライズするようになっていますが、これはSerializationContext
で設定を変更することができます。
var context = new SerializationContext(); context.SerializationMethod = SerializationMethod.Map;//Map型 var serializer = MessagePackSerializer.Get<T>(context);
前回の記事でも紹介したとおり、Map型の場合はKey-Valueの情報となるため、ValueのみのArray型と比べてデータ量が多くなってしまうので、今回はArray型を使用することにしました。
Array型では、サーバーとプロパティの順序をあわせる必要があります。
以下のようにアトリビュートを指定することで、その順番を指定できます。
//サンプルAPIのリクエストパラメータ public class SampleRequest { [MessagePackMemberAttribute(0)] public int sampleInt; [MessagePackMemberAttribute(1)] public float sampleFloat; [MessagePackMemberAttribute(2)] public bool sampleFlg; [MessagePackMemberAttribute(3)] public string sampleString; [MessagePackMemberAttribute(4)] public List<SampleSubClass> sampleList; }
パフォーマンス検証
クライアントとネイティブでArray型のMessagePackを扱うことが出来るようになったので、JSON(gzip圧縮)とArray型のMessagePackのパフォーマンスチェックを行いました。
検証にはクライアントにはiPhone5を、実際のモバイル回線を経由してサーバーに接続し、以下の項目を計測しました。
- クライアントでのシリアライズ時間
- クライアント・サーバーでの通信時間
- JSONのgzip圧縮はここに含まれます
- クライアントでのデシリアライズ時間
計測は通信を100回行い、その平均時間としました。
検証には以下のクラスで表現される固定のデータを、リスト形式のデータを用意しました。
import java.io.Serializable; import java.util.Date; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.msgpack.annotation.Ignore; import org.msgpack.annotation.Index; import org.msgpack.annotation.Message; /** * Object4Sample */ @AllArgsConstructor @NoArgsConstructor @Getter @Setter @Builder @Message @EqualsAndHashCode(callSuper = false) public class Object4Sample implements Serializable { /** serialVersionUID */ @Ignore @Getter(value = AccessLevel.NONE) private static final long serialVersionUID = 1L; /** */ @Index(0) private Integer intValue; /** */ @Index(1) private Long longValue; /** */ @Index(2) private Short shortValue; /** */ @Index(3) private Boolean boolValue; /** */ @Index(4) private Float floatValue; /** */ @Index(5) private Double doubleValue; /** */ @Index(6) private String strValue; /** */ @Index(7) private Date dateValue; }
データサイズについて
Object4Sample
をリストで保持し、その要素数を100・1000・10000とした時のJSON(非圧縮)とMessagePack形式のデータのサイズは以下のとおりです(単位はbyteです)。
圧縮率は約3.6倍となりました。
要素数 | JSON | MessagePack(Array) |
---|---|---|
100 | 16458 | 4524 |
1000 | 161358 | 45024 |
10000 | 1610358 | 450024 |
検証結果
検証結果を示します(単位はミリ秒です)。
クライアントでのシリアライズ時間
要素数 | JSON | MessagePack(Array) |
---|---|---|
100 | 25.57 | 2.75 |
1000 | 238.53 | 17.06 |
10000 | 2261.18 | 156.12 |
クライアントでのデシリアライズ時間
要素数 | JSON | MessagePack(Array) |
---|---|---|
100 | 34.34 | 5.33 |
1000 | 333.72 | 47.83 |
10000 | 3483.91 | 505.3 |
クライアント・サーバー間の通信時間
要素数 | JSON | MessagePack(Array) |
---|---|---|
100 | 340.61 | 418.08 |
1000 | 571.48 | 502.81 |
10000 | 2494.57 | 1493.67 |
検証結果のまとめ
上記の結果を加味して、プロジェクトではArray型のMessagePackの導入を決定しました。
- JSONとくらべてMessagePackの方がデータサイズが小さい点
- クライアントでのシリアライズ・デシリアライズ時間が、要素数に応じて大きく短縮できている点
- クライアント・サーバー間通信の時間は、要素数に応じて短縮される点
まとめ
簡単にですがUnity(C#)でMessagePackの導入例とクライアント・サーバーでの各フォーマットのパフォーマンスチェックの結果を示しました。
また機会があれば、以下のように実際に使用する上で苦労していることや、工夫していることについて記載していきたいと思います。
・Array型で苦労する点とその対策(サーバーとクライアント側の定義自動化)
・iOS実機でのAOT問題
この記事へのコメントはありません。