.Netプラットフォームでのプロトコルバッファーの使用(パート1)

ディスカッション形式での.Netプラットフォームでのプロトコルバッファーの使用の概要を紹介します。 それが何であるか、そして.Net開発者がそれを必要とする理由を説明します。 このトピックを読むには、読者がC#言語とSVNバージョン管理システムの初期コマンドを持っている必要があります。 素材の量は、ハブ上のトピックの統計的な平均量を超えています。これは、ブラウザを憧れに駆り立てたり、コメントにスクロールさせたりしないため、2つの部分に分割することにしました。 最初の部分では、基本に精通し、多くのコードを記述します(あまり記述しません)!


こんにちは、プロトコルバッファとは何ですか?


公式ページの定義によると、 Protocol Buffers (protobuf)は、Googleがほぼすべての製品で使用する効率的で拡張可能な形式で構造化データをエンコードする方法です。 .Netを含むほとんどのプラットフォームでは、このプロセスはシリアル化と呼ばます。

Googleサービスを使用していますが、どのようにprotobufを使用して.Netアプリケーションを開発できますか?


はい、そうです、Googleは.Net開発者向けの特別なライブラリを作成していません。 ただし、protobufを.Netで使用できるようにするprotobuf-netプロジェクト(プラットフォーム用のprotobufのいくつかの実装の1つ)があります。 stackoverflow.comのレギュラーであり、他の多くの素晴らしいプロジェクトのメンバーでもあるMarc Gravellが主導しています 。 したがって、いつでも質問をすることができ、彼は喜んで答えます(トピックの著者が悪用しました)。

組み込みツールの代わりにこのライブラリを使用する必要があるのはなぜですか?


.Netでのシリアル化に関しては、通常、誰もがバイナリフォーマッタxmlシリアライザの存在を覚えています。 開発者が次に注意することは、最初のものは高速で圧縮率が高いことですが、.Netプラットフォーム内でのみ機能します。 2つ目は、人間が読める形式でデータを提示し、 SOAPの基盤として機能します。SOAPは 、クロスプラットフォームサポートを提供します。 実際、速度と移植性のどちらかを常に選択する必要があるという主張は、公理に基づいています! しかし、protobufを使用すると、両方の問題を一度に解決できます。

protobufはバイナリシリアル化に劣らず、ポータブルでもあるとおっしゃいますか?


はい、それだけです。 小さな例を見て、同時にprotobuf-netの使用方法を学びましょう。 次のエンティティがあるとします。
using System;
using ProtoBuf;

namespace Proto.Sample
{
public enum TaskPriority
{
Low,
Medium,
High
}

[ Serializable ] // <-- BinaryFormatter
[ProtoContract]
public class Task
{
[ProtoMember(1)]
public int Id { get ; set ; }

[ProtoMember(2)]
public DateTime CreatedAt { get ; set ; }

[ProtoMember(3)]
public string CreatedBy { get ; set ; }

[ProtoMember(4)]
public TaskPriority Priority { get ; set ; }

[ProtoMember(5)]
public string Content { get ; set ; }
}
}


* This source code was highlighted with Source Code Highlighter .

Protobuf-netでは、フィールドの順序に応じて、形式の主な機能から直接続く特別な属性を使用する必要があります。 パフォーマンスと圧縮率のテストを作成します。
using System;
using System.Collections. Generic ;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using ProtoBuf;

namespace Proto.Sample
{
internal class Program
{
private static void Main( string [] args)
{
var tasks = new List <Task>
{
new Task
{
Id = 1,
CreatedBy = "Steve Jobs" ,
CreatedAt = DateTime .Now,
Priority = TaskPriority.High,
Content = "Invent new iPhone"
},
new Task
{
Id = 2,
CreatedBy = "Steve Ballmer" ,
CreatedAt = DateTime .Now.AddDays(-7),
Priority = TaskPriority.Low,
Content = "Install own Skype"
}
};

Console .WriteLine( "The test of binary formatter:" );

const string file1 = "tasks1.bin" ;

TestBinaryFormatter(tasks, file1, 1000);
TestBinaryFormatter(tasks, file1, 2000);
TestBinaryFormatter(tasks, file1, 3000);
TestBinaryFormatter(tasks, file1, 4000);
TestBinaryFormatter(tasks, file1, 5000);

Console .WriteLine( "\nThe test of protobuf-net:" );

const string file2 = "tasks2.bin" ;

TestProtoBuf(tasks, file2, 1000);
TestProtoBuf(tasks, file2, 2000);
TestProtoBuf(tasks, file2, 3000);
TestProtoBuf(tasks, file2, 4000);
TestProtoBuf(tasks, file2, 5000);

Console .WriteLine( "\nThe comparision of file size:" );

Console .WriteLine( "The size of {0} is {1} bytes" , file1, ( new FileInfo(file1)).Length);
Console .WriteLine( "The size of {0} is {1} bytes" , file2, ( new FileInfo(file2)).Length);

Console .ReadKey();
}

private static void TestBinaryFormatter(IList<Task> tasks, string fileName, int iterationCount)
{
var stopwatch = new Stopwatch();
var formatter = new BinaryFormatter();
using ( var file = File .Create(fileName))
{
stopwatch.Restart();

for ( var i = 0; i < iterationCount; i++)
{
file.Position = 0;
formatter.Serialize(file, tasks);
file.Position = 0;
var restoredTasks = ( List <Task>)formatter.Deserialize(file);
}

stopwatch.Stop();

Console .WriteLine( "{0} iterations in {1} ms" , iterationCount, stopwatch.ElapsedMilliseconds);
}
}

private static void TestProtoBuf(IList<Task> tasks, string fileName, int iterationCount)
{
var stopwatch = new Stopwatch();
using ( var file = File .Create(fileName))
{
stopwatch.Restart();

for ( var i = 0; i < iterationCount; i++)
{
file.Position = 0;
Serializer.Serialize(file, tasks);
file.Position = 0;
var restoredTasks = Serializer.Deserialize< List <Task>>(file);
}

stopwatch.Stop();

Console .WriteLine( "{0} iterations in {1} ms" , iterationCount, stopwatch.ElapsedMilliseconds);
}
}
}
}


* This source code was highlighted with Source Code Highlighter .

結果:
The test of binary formatter:
1000 iterations in 423 ms
2000 iterations in 381 ms
3000 iterations in 532 ms
4000 iterations in 660 ms
5000 iterations in 814 ms

The test of protobuf-net:
1000 iterations in 1056 ms
2000 iterations in 76 ms
3000 iterations in 129 ms
4000 iterations in 152 ms
5000 iterations in 202 ms

The comparision of file size:
The size of tasks1.bin is 710 bytes
The size of tasks2.bin is 101 bytes


* This source code was highlighted with Source Code Highlighter .

ご覧のとおり、速度だけでなく圧縮率もバイナリシリアル化を上回っています。 唯一の欠点は、protobuf-netが「コールドスタート」に時間がかかったことです。 ただし、次のヘルパーコードを使用してこの問題を解決できます。
var model = TypeModel.Create();
model.Add( typeof (Task), true );
var compiledModel = model.Compile(path);
compiledModel.Serialize(file, tasks);


* This source code was highlighted with Source Code Highlighter .

他のテストと結果はここで見つけることができます

わかった 速度と圧縮に関して、あなたは私を納得させましたが、移植性はどのように解決されますか?


必要なプラットフォームの実装があれば、ほとんどの場合移植性の問題は取り除かれます。 また、protobufの実装は20以上の言語に対応しています。 完全なリストはここで見ることができます 。 一部の言語では複数の実装があることに注意してください。 したがって、常に選択肢があります。

UPD: パート2

Source: https://habr.com/ru/post/J119503/


All Articles