みなさんこんにちは、私はすごいです。
最近、私たちのプロジェクトでは、データを保存するためのキャリアとして protobuf 形式を使用しています。うっかり大きな穴を自分で埋めてしまいましたが、発見するまでに時間がかかりました。
protobuf の概要
protobuf の正式名は Protocol Buffers で、Google によって開発され、クロス言語、クロスプラットフォーム、スケーラブルなシリアル化データ メカニズムです。 XML に似ていますが、XML よりも小さく、高速で、シンプルです。データの構造を一度定義するだけで、その生成ツールを使用して、シリアル化および逆シリアル化操作を含むソース コードを生成できます。構造化データは、さまざまなプログラミング言語を使用して、さまざまなデータ ストリームに対して簡単に書き込みおよび読み取りを行うことができます。
proto2 バージョンは、Java、Python、Objective-C、および C でのコード生成をサポートしています。新しい proto3 言語バージョンでは、Kotlin、Dart、Go、Ruby、PHP、C# など多くの言語も使用できます。 #########どうやって分かったの?
新しいプロジェクトでは、protobuf 形式を使用して実行されたプロジェクトのデータを保存します。このようにして、デバッグ プロセス中に、現場で記録されたデータに基づいてローカル デバッグを実行することがあります。 message ImageData {
// ms
int64 timestamp = 1;
int32 id = 2;
Data mat = 3;
}
message PointCloud {
// ms
int64 timestamp = 1;
int32 id = 2;
PointData pointcloud = 3;
}
message State {
// ms
int64 timestamp = 1;
string direction = 2;
}
message Sensor {
repeated PointCloud point_data = 1;
repeated ImageData image_data = 2;
repeated State vehicle_data = 3;
}
ログイン後にコピー
このようなデータセットを定義し、保存する際、Sensor の 3 つのデータソースのフレームレートが異なるため、実際には 1 つの Sensor には 1 セットのデータしか含まれません。 , 2種類のデータは付属しておりません。
単一パックのみを録音した場合には問題は発生しませんでした。単一のパケットを長時間記録できないと感じるまでは、パケットを分割する解決策を見つける必要があります。
その時は「これは簡単だろう」と思い、パッケージが 500M に達したら、それ以降のデータを新しいパッケージに保存するように設定しました。とてもスムーズに書き終えて、データ記録のために現場に置きました。しばらく録音した後、パッケージを持ち帰り、新しいプログラムのテストをシミュレートしました。一部のパッケージのデータ解析に問題があることが判明しました。プログラムが実行途中で停止してしまいます。多くのテストを行った結果、一部のパッケージにこの問題があることが判明しました。
当初私たちが疑ったのは、ファイルサイズの判断方法が間違っており、外注先に影響を及ぼしたのではないかということでした。ファイルサイズを判断する際にファイルが開かれるためです。しかし、ファイルを開けない他の方法がいくつかあると判断した結果、分割が実行されました。記録されたパッケージの一部で依然として問題が発生しました。
そのとき初めて、protobuf にはデータを保存するための特別な要件があるのではないかと思いました。その後、いくつかの記事を読んで、protobuf では複数のデータセットを 1 つのファイルに保存するために識別子が必要であることを知りました。そうしないと、ファイルから解析を戻すときに、protobuf は単一データのストップ キャラクタがどこにあるのか認識できず、データ解析エラーが発生します。
ここで、この穴が現れます。
一連のデータを区切り操作を行わずに 1 つのパッケージに保存します。 protobuf が解析すると、ファイル内のすべての内容が 1 つのセンサーに解析されます。センサーにはすべてのデータが含まれており、protobuf は保存されているすべてのデータをアクティブにマージします。
このとき、過去にシングルパケットを記録したときはすべて正しいデータだったことがわかり、本当に幸運でした。 protobuf はたまたま正常に解析されました。
どうすれば解決できますか?
protobuf がこのように動作することがわかったので、あとは protobuf を分割する方法を知る必要があるだけです。この方法を使用する私たちのような人が少なすぎるため、この方法を見つけるのは本当に困難です。中国語の検索ではこのコンテンツがまったく見つかりません。おそらく誰もがデータの保存に protobuf を使用していないでしょう。誰もが使用する方法は、複数のサービス間の対話のシナリオであるはずです。
stackoverflow のいくつかの回答を通じて最終的に答えを見つけました。回答から、このソリューションは protobuf 3.3 に正式にマージされただけであることがわかりました。この機能は実際にはあまり使われないようです。
bool SerializeDelimitedToOstream(const MessageLite& message,
std::ostream* output);
bool ParseDelimitedFromZeroCopyStream(
MessageLite* message, io::ZeroCopyInputStream* input, bool* clean_eof);
ログイン後にコピー
この 2 つのメソッドにより、データ フローに従ってファイルを 1 つずつ保存し、読み取ることができます。データのマージや読み取りについて心配する必要はもうありません。
もちろん、この方法で保存されたデータは、元の解析方法では解析できず、ストレージの形式は完全に変更されています。このメソッドは、最初にバイナリ データのサイズを保存し、次にバイナリ データを保存します。
結論
何度も試行錯誤した結果、最終的にこのセグメンテーションの落とし穴を解決することができました。利用シーンが比較的ニッチなため、まったく見つからない情報が多くなる可能性があります。私はソースコードを自分で調べてこれらの問題を発見しました。 C のソース コードは非常に読みにくく、テンプレート メソッドやテンプレート クラスが多数あるため、細部を見落としがちです。最後にC#のコードを見て最終的に確認しました。
以上が元の言葉を書き直しました: 予期せぬ発見は、当初バグとみなされていたものが、実際には Protobuf の設計上の機能であるということです。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。