(C#)シリアライズ時の数値の分解

シリアライザUtf8Jsonのソースコードを見ていて、シリアライズ時の数値の分解のあたりが気になりました。
例えば、long→utf8(byte[])に変換するとき、longを桁ごとに分解する必要が有ります。
Utf8Jsonでは、まず4桁毎に分解し、それをさらに1桁に分解します。
その時のコードは、必要な部分だけ抜き出すと以下の感じ。

long div;

result[0] = (byte)('0' + (div = (value * 8389) >> 23));
value -= div * 1000;

result[1] = (byte)('0' + (div = (value * 5243) >> 19));
value -= div * 100;

result[2] = (byte)('0' + (div = (value * 6554) >> 16));
value -= div * 10;

result[3] = (byte)('0' + (value));

なんかややこしいシフト演算していますね。
単純に自分で割り算実装してみると、以下の感じ

int div;

result[0] = (byte)('0' + (div = value / 1000));
value -= div * 1000;

result[1] = (byte)('0' + (div = value / 100));
value -= div * 100;

result[2] = (byte)('0' + (div = value / 10));
value -= div * 10;

result[3] = (byte)('0' + (value));

うん、こっちは簡単ですね。
一応テストコードを書いて、1万未満の数全部で一致することは確認しました。
で、わざわざややこしい事しているのは、当然速度目的だと思われるので、ベンチマークを取ってみました。
ついでに、Utf8Jsonでは、longで実装されていましたので、intバージョンも測定。
(Utf8Jsonでは、intの場合もlongと共有ロジックで処理されていました)

|       Method |     Mean |    Error |   StdDev |
|------------- |---------:|---------:|---------:|
|  Utf8JsonInt | 34.05 us | 0.577 us | 0.540 us |
| Utf8JsonLong | 34.65 us | 0.552 us | 0.516 us |
|       Simple | 45.88 us | 0.763 us | 0.714 us |

やはり、Utf8Jsonのシフト演算系は、速いですね。
しかし、int(Int32)の場合は、longにせずに、intのまま処理した方が、ほんの少しですが速くなりそうです。
せっかくなので、別処理にして、少しでも速くしたいところですね。

投稿日:
カテゴリー: C#

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です