Каков наилучший способ представления System.Десятичное число в буферах протокола?
-
21-08-2019 - |
Вопрос
Следуя дальше от это вопрос, каков наилучший способ представления System.Десятичный объект в буфере протокола?
Решение
Что ж, protobuf-net просто справится с этим за вас;он использует свойства типов и имеет полную поддержку decimal
.Поскольку нет прямого способа выразить decimal
в proto он не будет (в настоящее время) генерировать decimal
свойство из файла ".proto", но было бы неплохо настроить распознавание некоторого общего типа ("BCL.Decimal" или аналогичного) и интерпретировать его как десятичный.
Что касается представления этого - у меня был дискуссионный документ об этом (как я подозреваю, уже устаревшем) в вики-разделе protobuf-net;теперь в protobuf-net есть рабочая версия, которая просто делает это за вас.
Без сомнения, мы с Джоном обсудим это позже сегодня ;-p
Версия protobuf-net этого (в .proto) выглядит примерно так (из здесь):
message Decimal {
optional uint64 lo = 1; // the first 64 bits of the underlying value
optional uint32 hi = 2; // the last 32 bis of the underlying value
optional sint32 signScale = 3; // the number of decimal digits, and the sign
}
Другие советы
У нас с Марком есть очень смутные планы создать библиотеку "common PB message", такую, чтобы вы могли представлять довольно распространенные типы (дата / время и десятичная дробь, мгновенно приходящие на ум) обычным способом, с преобразованиями, доступными в .NET и Java (и все остальное, что кто-либо захочет внести).
Если вы с удовольствием придерживаетесь .NET и ищете компактность, я бы, возможно, выбрал что-то вроде:
message Decimal {
// 96-bit mantissa broken into two chunks
optional uint64 mantissa_msb = 1;
optional uint32 mantissa_lsb = 2;
required sint32 exponent_and_sign = 3;
}
Знак может быть просто представлен знаком exponent_and_sign, причем показатель является абсолютным значением.
То, что обе части мантиссы являются необязательными, означает , что представлено 0 очень компактно (но все еще различаясь между 0 м и 0,0000 м и т.д.).exponent_and_sign также мог бы быть необязательным, если бы мы действительно захотели.
Я не знаю о проекте Марка, но в моем порту я генерирую частичные классы, так что вы можете выполнить преобразование между System.Decimal и Protobuf.Обычный.Decimal (или что-то еще) в частичный класс.
Я собрал патч для protobuf-csharp-port с перехватами, который генерирует классы protobuf с собственными структурами Decimal и DateTime.С точки зрения проводного формата, они представлены двумя "встроенными" протосообщениями.
Немного более простой в реализации подход, чем у Джона или Марка, заключается в сохранении его как 4 sint32
значения, которые удобно тривиально сопоставляются с выводом Десятичный.GetBits().
Протофайл будет выглядеть следующим образом:
message ProtoDecimal {
sint32 v1 = 1;
sint32 v2 = 2;
sint32 v3 = 3;
sint32 v4 = 4;
}
И преобразователь будет:
public decimal ConvertToDecimal(AbideDecimal value)
{
return new decimal(new int[] { value.V1, value.V2, value.V3, value.V4 });
}
public ProtoDecimal ConvertFromDecimal(decimal value)
{
var bits = decimal.GetBits(value);
return new ProtoDecimal
{
V1 = bits[0],
V2 = bits[1],
V3 = bits[2],
V4 = bits[3]
}
}
Это может быть не так просто на других языках, но если вам нужно настроить таргетинг только на C #, то это займет тот же максимум 16 байт, что и при другом подходе (хотя значения типа 0 могут храниться не так компактно - я недостаточно разбираюсь в сложных деталях того, как protobuf хранит целые числа), и при этом будет намного понятнее для тупых программистов вроде меня :)
Очевидно, вам придется участвуйте в гонках на лошадях если вы хотите протестировать производительность, но я сомневаюсь, что в этом много чего есть.