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問題

この記事へのコメントはありません。