[Ⅱ]表計算とアルゴリズム...全12-Ⅱ-1 【1】 次の表は,ある全国チェーンのペットショップの本部における子猫の取扱いに関するものである。ワーク
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
-
Upload
yoshifumi-kawai -
Category
Technology
-
view
5.681 -
download
1
Transcript of ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
Work
C#
Unity
Private
http://neue.cc/
@neuecc
https://github.com/neuecc/UniRx
using
ZeroFormatter
秘訣は...
評価を遅らせる
無限大高速なシリアライザ
https://github.com/neuecc/ZeroFormatter/
シリアライズも速い
無限大高速なシリアライザ
https://github.com/neuecc/ZeroFormatter/
シリアライズも速い
Benchmark
本当に速いの?
はい。
辻ベンチマーク
Serialization Process
例えばint(999)をbyte[]にシリアライズ
var bytes = BitConverter.GetBytes(999);
unsafe{
var bytes = new byte[4];fixed (byte* ptr = bytes){
*((int*)ptr) = 999;}
}
// ふつーのシリアライザのAPIの例byte[] Serialize<T>(T obj){
// 1. 内部での書き込みストリーム作りのためにnew MemoryStreamusing(var stream = new MemoryStream())
// 2. データ生成時の内部ステートを保持するためのWriterのnewvar writer = new XxxWriter(stream);
// 3. Int用子シリアライザの取得あるいはprimitiveの場合はswitchvar serializer = serializerCacheDictionary[typeof(T)];
// 4. (意外と内部では入ってることがある)objectへのボクシングserializer.WriteObject(writer, (object)obj);
// 5. 可変長整数へのエンコードif(x <10) write... else if(x < 150) write...
// 6. WriteByte呼び出しの連打(内部では幾つかのifやインクリメント)stream.WriteByte(byte >> 0); stream.WriteByte(byte >> 8) ...
// 7. MemoryStreamのToArrayはbyte[]コピーmemoryStream.ToArray();
}
// ふつーのシリアライザのAPIの例byte[] Serialize<T>(T obj){
// 1. 内部での書き込みストリーム作りのためにnew MemoryStreamusing(var stream = new MemoryStream())
// 2. データ生成時の内部ステートを保持するためのWriterのnewvar writer = new XxxWriter(stream);
// 3. Int用子シリアライザの取得あるいはprimitiveの場合はswitchvar serializer = serializerCacheDictionary[typeof(T)];
// 4. (意外と内部では入ってることがある)objectへのボクシングserializer.WriteObject(writer, (object)obj);
// 5. 可変長整数へのエンコードif(x <10) write... else if(x < 150) write...
// 6. WriteByte呼び出しの連打(内部では幾つかのifやインクリメント)stream.WriteByte(byte >> 0); stream.WriteByte(byte >> 8) ...
// 7. MemoryStreamのToArrayはbyte[]コピーmemoryStream.ToArray();
}
そりゃ遅い!!!し、ゴミも発生しまくる
// ZeroFormatterのばやいbyte[] Serialize<T>(T obj){
// 1. Static変数からの最速での子シリアライザの取得var formatter = Formatter<DefaultResolver, T>.Default;
// 2. 当然ボクシングは全くなく、内部構造も全てジェネリクスで統一されてるformatter.Serialize(T value);
// 3. 長さが既知の場合は長さを取って生成できるvar bytes = new byte[formatter.GetLength()];
// 4. バイト配列に直接書く(WriteInt32の中身は *((int*)b) = value; だけ)BinaryUtil.WriteInt32(ref bytes, value);
// 5. 出来たバイト配列をそのまま返すだけreturn bytes;
}
// ZeroFormatterのばやいbyte[] Serialize<T>(T obj){
// 1. Static変数からの最速での子シリアライザの取得var formatter = Formatter<DefaultResolver, T>.Default;
// 2. 当然ボクシングは全くなく、内部構造も全てジェネリクスで統一されてるformatter.Serialize(T value);
// 3. 長さが既知の場合は長さを取って生成できるvar bytes = new byte[formatter.GetLength()];
// 4. バイト配列に直接書く(WriteInt32の中身は *((int*)b) = value; だけ)BinaryUtil.WriteInt32(ref bytes, value);
// 5. 出来たバイト配列をそのまま返すだけreturn bytes;
}
汎用シリアライザながら理論上最速とほぼ同等!ゴミも発生しない!
1階層だけの抽象化
public abstract class Formatter<TTypeResolver, T>where TTypeResolver : ITypeResolver, new()
{public abstract int? GetLength();public abstract int Serialize(ref byte[] bytes, int offset, T value);public abstract T Deserialize(ref byte[] bytes, int offset, out int byteSize);
}
internal class Int32ArrayFormatter : Formatter<Int32[]>{
public override int Serialize(ref byte[] bytes, int offset, Int32[] value){
var writeSize = value.Length * 4;BinaryUtil.EnsureCapacity(ref bytes, offset, writeSize + 4);BinaryUtil.WriteInt32Unsafe(ref bytes, offset, value.Length);Buffer.BlockCopy(value, 0, bytes, offset + 4, writeSize);
return writeSize + 4;}
}
// 例えばint[]の場合、普通はintの要素一個一個を処理するコードになるfor (int i = 0; i < values.Length; i++){
stream.Write(serialize(values[i]));}
Conclusion
バイナリだから速いということはないシリアライザのパフォーマンスは実装が大事
よく出来たJSONシリアライザはその辺のバイナリ系より速い
(実際 Jilはめちゃくちゃすごくよく出来た実装)
汎用シリアライザのチューニング可能箇所は無数特にEnumは取扱注意で、語ると長くなりまうす
それら無数に存在する全てを潰しきったのがZeroFormatter
(パフォーマンス優先で潰し切ることが可能な設計になってる)
というわけで、なので自信もってC#最速だといえます
C#における潮流の変化競合相手の変化、GoなどのBetter C勢と戦わなければならない
富豪的プログラミングでOKで済ませられる局面は、特にフレームワークレイヤーでは厳しくなってきている(今まで以上に、ね)
言語(C# 7.0)とフレームワーク両面で性能に強い意識が働いている
抽象化をいかに薄くするかや、LINQを使わないということはLINQをどう使うか、と同じぐらいの重要性を持った選択肢になってくる
Corefxlab https://github.com/dotnet/corefxlab/
System.Slices -より効率的な配列やメモリ相互変換の取扱い
System.IO.Pipelines -より高速なStream抽象
System.Text.Utf8 - UTF8変換へのオーバーヘッド低減