Google プロトコル バッファーで sint32 の代わりに int32 を使用するのに適した時期はありますか?
-
12-09-2019 - |
質問
私はずっと読んできました Google プロトコル バッファー 最近では、さまざまなスカラー値タイプをメッセージで使用できるようになりました。
によると 彼らの文書, 、可変長整数プリミティブには 3 種類あります - int32
, uint32
, 、 そして sint32
. 。彼らは文書の中で次のように述べています。 int32
は「負の数値をエンコードするには非効率的です。フィールドに負の値が含まれる可能性がある場合は、 sint32
しかし、負の数値を含まないフィールドがある場合は、uint32 の方が使用するタイプとして適していると思います。 int32
とにかく(余分なビットと負の数を処理するための CPU コストの削減のため)。
それで、いつになるでしょう int32
使いやすいスカラーですか?ドキュメントには、負の数値がほとんど得られない場合にのみ最も効率的であることが示唆されていますか?それとも常に使用することが好ましいのでしょうか? sint32
そして uint32
, 、分野の内容によっては?
(同じ質問がこれらのスカラーの 64 ビット バージョンにも当てはまります。 int64
, uint64
, 、 そして sint64
;ただし、読みやすくするために、問題の説明からそれらを省きました。)
解決
私は Google プロトコル バッファーについては詳しくありませんが、ドキュメントの私の解釈は次のとおりです。
- 使用
uint32
値を負にできない場合 - 使用
sint32
値が負である可能性が高い場合とそうでない場合 (「である可能性が高い」のあいまいな定義について) - 使用
int32
値が負である可能性があるが、値が正である可能性よりもはるかに低い場合 (たとえば、アプリケーションがエラーまたは「不明な」値を示すために -1 を使用することがあり、これは比較的まれな状況である場合)
エンコーディングについてドキュメントに記載されている内容は次のとおりです (http://code.google.com/apis/protocolbuffers/docs/encoding.html#types):
signed int 型の間には重要な違いがあります (
sint32
そしてsint64
) と「標準」int 型 (int32
そしてint64
) 負の数をエンコードする場合。使用する場合int32
またはint64
負の数の型として、結果としてvarint
は常に 10 バイトの長さです。事実上、非常に大きな符号なし整数のように扱われます。署名付き型のいずれかを使用すると、結果としてvarint
ZigZag エンコーディングを使用するため、はるかに効率的です。ZigZag エンコードでは、絶対値が小さい数値 (-1 など) の値が小さくなるように、符号付き整数を符号なし整数にマップします。
varint
エンコードされた値も。これは、正と負の整数を「ジグザグに」行ったり来たりする方法で実行され、-1 は 1 としてエンコードされ、1 は 2 としてエンコードされ、-2 は 3 としてエンコードされます...
したがって、負の数値の使用がまれであっても、プロトコルで渡す数値 (負でない数値を含む) の大きさが小さい限り、使用したほうが良いようです。 sint32
. 。よくわからない場合は、プロファイリングを行うとよいでしょう。
他のヒント
これまで*というよりも、SINT * int型を使用するには非常に少ない良い理由があります。これらの余分なタイプの存在は、プロトコルバッファも、独自のプロトコル・バージョン間で維持しようとする歴史的、後方互換性の理由から、最も可能性の高いです。
私の最高の推測では、最も古いバージョンでは、彼らはdumbly 9バイト(余分なタイプのバイトを数えていない)の最大サイズvarintエンコーディングを必要と2の補数表現に負の整数をコードしていることです。すでにそれを使用して古いコードとシリアライズを壊さないように、そして、彼らはそのエンコーディングで立ち往生しました。だから、彼らは既存のコードを壊すずに負の数のためのより良い可変サイズのエンコーディングを取得するには、* SINT、新しいエンコードタイプを追加する必要がありました。設計者は私を超え全くありGET-、外出先から、この問題を認識していませんでしたどのようにます。
(1以上のバイトを必要とするタイプの仕様、なし)varintエンコーディングはバイトの次数の符号なし整数値をエンコードすることができます:
0、2 ^ 7):1バイト
[2 ^ 7、2 ^ 14):2バイト
[2 ^ 14、2 ^ 21):3バイト
[2 ^ 21、2 ^ 28):4バイト
[2 ^ 28、2 ^ 35):5バイト
[2 ^ 35、2 ^ 42):6バイト
[2 ^ 42、2 ^ 49):7バイト
[2 ^ 49、2 ^ 56):8バイト
[2 ^ 56、2 ^ 64):9バイト
あなたは同様にコンパクトに小さな大きさの負の整数をエンコードしたい場合は、あなたは記号を示すために1ビットを「使い切る」する必要があります。あなたは明示的に(一部の予約位置)符号ビットと絶対値表現を介してこれを行うことができます。それとも、あなたが効果的に左1ビットによって大きさをシフトし、負の数に1を減算することにより、同じことを行いジグザグエンコーディングを、行うことができます(そう最下位ビットは符号を示し:追いついたが非負あり、オッズは否定しています)。
いずれにせよ、をする点を超えるカットが正の整数がより多くのスペースを必要とし、今2倍早います:
0、2 ^ 6):1バイト
[2 ^ 6、2 ^ 13):2バイト
[2 ^ 13、2 ^ 20):3バイト
[2 ^ 20、2 ^ 27):4バイト
[2 ^ 27、2 ^ 34):5バイト
[2 ^ 34、2 ^ 41):6バイト
[2 ^ 41、2 ^ 48):7バイト
[2 ^ 48、2 ^ 55):8バイト
[2 ^ 55、2 ^ 63):9バイト
のSINT *上* int型を使用するケースを作るために、負の数は非常にまれな、しかし可能でなければならないであろう、および/またはあなたがコードすることが予想される最も一般的な正の値が右のカットオーバーの1の周りに落ちなければならないだろうint型とは対照的に(例えば、 - サイズをコードする2倍に至る2 ^ 6対2 ^ 7)* SINTに大きな符号化につながる点
基本的に、あなたはいくつかの*は、デフォルトの使用SINTによって* intではなく、負でもよい数字を持ってしようとしている場合。 int型*非常にまれに優れていないだろうし、通常も、あなたはそれが私見それだけの価値があるかどうか判断に向けて専用にする必要があり、余分な思考の価値ではありません。