Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法

46.551 Aufrufe

Veröffentlicht am

https://github.com/neuecc/ZeroFormatter

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法

  1. 1. Work C# Unity Private http://neue.cc/ @neuecc https://github.com/neuecc/UniRx
  2. 2. using
  3. 3. ZeroFormatter
  4. 4. 秘訣は...
  5. 5. 評価を遅らせる
  6. 6. 無限大高速なシリアライザ https://github.com/neuecc/ZeroFormatter/ シリアライズも速い
  7. 7. 無限大高速なシリアライザ https://github.com/neuecc/ZeroFormatter/ シリアライズも速い
  8. 8. Benchmark
  9. 9. 本当に速いの?
  10. 10. はい。
  11. 11. 辻ベンチマーク
  12. 12. Serialization Process
  13. 13. 例えばint(999)をbyte[]にシリアライズ var bytes = BitConverter.GetBytes(999); unsafe { var bytes = new byte[4]; fixed (byte* ptr = bytes) { *((int*)ptr) = 999; } }
  14. 14. // ふつーのシリアライザのAPIの例 byte[] Serialize<T>(T obj) { // 1. 内部での書き込みストリーム作りのためにnew MemoryStream using(var stream = new MemoryStream()) // 2. データ生成時の内部ステートを保持するためのWriterのnew var writer = new XxxWriter(stream); // 3. Int用子シリアライザの取得あるいはprimitiveの場合はswitch var 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(); }
  15. 15. // ふつーのシリアライザのAPIの例 byte[] Serialize<T>(T obj) { // 1. 内部での書き込みストリーム作りのためにnew MemoryStream using(var stream = new MemoryStream()) // 2. データ生成時の内部ステートを保持するためのWriterのnew var writer = new XxxWriter(stream); // 3. Int用子シリアライザの取得あるいはprimitiveの場合はswitch var 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(); } そりゃ遅い!!! し、ゴミも発生しまくる
  16. 16. // 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; }
  17. 17. // 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; } 汎用シリアライザながら 理論上最速とほぼ同等! ゴミも発生しない!
  18. 18. 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); }
  19. 19. 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])); }
  20. 20. Conclusion
  21. 21. バイナリだから速いということはない シリアライザのパフォーマンスは実装が大事 よく出来たJSONシリアライザはその辺のバイナリ系より速い (実際 Jil はめちゃくちゃすごくよく出来た実装) 汎用シリアライザのチューニング可能箇所は無数 特にEnumは取扱注意で、語ると長くなりまうす それら無数に存在する全てを潰しきったのがZeroFormatter (パフォーマンス優先で潰し切ることが可能な設計になってる) というわけで、なので自信もってC#最速だといえます
  22. 22. 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変換へのオーバーヘッド低減

×