2009-09-30 17 views
7

我想知道什么是存储一些通用数据类型的最佳方式,这些通用数据类型不包含在协议缓冲区支持的列表中。使用协议缓冲区使用小数和日期时间的最佳方法是什么?

  • 日期时间(秒精度)
  • 日期时间(毫秒精度)
  • 小数的固定精度
  • 小数的可变精度
  • 大量的布尔值(如果你有很多他们的样子由于它们的标签,每个标签都会有1-2个字节的开销

此外,还有一个想法是将它们映射到相应的地方查找C++/Python/Java数据类型。

回答

3

protobuf设计原理最可能将数据类型支持尽可能保留为“原生”,以便将来采用新语言很容易。我想他们可以提供内置消息类型,但是你在哪里画线?

我的解决方案是创建两个消息类型:

DateTime 
TimeSpan 

这仅仅是因为我来自一个C#的背景下,这些类型是理所当然的。

回想起来,TimeSpanDateTime可能已经过分了,但它是避免从h/m/s到s的转换的一种“廉价”方式,反之亦然;那么只需实现一个实用功能就简单了,如:

int TimeUtility::ToSeconds(int h, int m, int s) 

Bklyn指出堆内存用于嵌套消息;在某些情况下,这显然非常有效 - 我们应该始终知道如何使用内存。但是,在其他情况下,这可能不太受关注,我们更担心实现的简易性(这是我认为的Java/C#哲学)。

使用非内在类型与protobuf TextFormat::Printer也有一个小缺点;你不能指定其所显示的格式,所以它会是这个样子:

my_datetime { 
    seconds: 10 
    minutes: 25 
    hours: 12 
} 

...这是一些过于冗长。也就是说,如果它是以秒为单位的话,它会更难阅读。

总之,我会说:

  • 如果你担心内存/解析效率,使用秒/毫秒。
  • 但是,如果易于实现是目标,请使用嵌套消息(DateTime等)。
2

对不起,不是完整的答案,而是“我也是”。

我认为这是一个很好的问题,我很乐意回答自己。无法自然地描述基本类型,如日期时间和(对于财务应用程序)定点小数,或将它们映射到语言指定或用户定义的类型对我来说是一个真正的杀手。它或多或少地阻止了我能够使用图书馆,我认为这很棒。

在原语法中声明自己的“DateTime”或“FixedPoint”消息并不是一个真正的解决方案,因为您仍然需要手动将平台的表示转换为/从生成的对象转换,这很容易出错。此外,这些嵌套的消息被存储为指向C++中堆分配对象的指针,当底层类型基本上只是一个64位整数时,这是非常低效的。

具体来说,我希望能够写出这样的事情在我的原文件:

message Something { 
    required fixed64 time = 1 [cpp_type="boost::posix_time::ptime"]; 
    required int64 price = 2 [cpp_type="fixed_point<int64_t, 4>"]; 
    ... 
}; 

而且我会被要求提供任何胶水是必要的,这些类型从fixed64转换成/和int64,以便序列化工作。也许通过像adobe::promote这样的东西?

3

这里有一些基于我的经验与协议缓冲区类似的有线协议的想法。

日期时间(秒精度)

日期时间(毫秒精度)

我想答案这两个是相同的,你只是通常被处理的是一个更小范围的数字在秒精度的情况下。

使用sint64/sfixed64存储从某些知名时代(如午夜格林威治标准时间1/1/1970)以秒/毫秒为单位的偏移量。 Date对象的这种方式是internally represented in Java。我相信在Python和C++中有类似的东西。

如果您需要时区信息,请根据UTC传递您的日期/时间并将相关时区作为单独的字符串字段进行建模。为此,您可以使用Olson Zoneinfo database中的标识符,因为它已经变得有点标准。

这样你就有了日期/时间的规范表示,但你也可以本地化到任何时区是相关的。

小数的固定精度

我首先想到的是用类似于一个如何构建从Python的十进制小数包对象的字符串。我想这可能是相对于某些数字表示而言效率低下的。

根据您使用的是哪个域,可能会有更好的解决方案。例如,如果您正在为货币价值建模,那么也许您可以使用uint32/64以美分的价格进行交易,而不是分数美元的金额。

this thread也有一些有用的建议。

小数的可变精度

不Protocol Buffers的已经与浮点/双精度标量类型支持呢?也许我误解了这个要点。无论如何,如果您需要绕过这些标量类型,则可以使用IEEE-754编码为uint32或uint64(分别为float vs double)。例如,来自Float/Double对象的Java allows you to extract the IEEE-754 representationvice versa。在C++/Python中有类似的机制。

大量的布尔值(如果你有很多他们的 它看起来像你必须因 他们的标签1-2 字节为他们每个人的开销。

如果你是关注线路上的浪费字节,你可以使用bit-masking techniques将许多布尔变量压缩成单个uint32或uint64。

因为在协议缓冲区中没有一流的支持,所有这些技术都需要一点点的“代理之间的合同。也许在字段中使用命名约定(如“_dttm”或“_mask”)将有助于在给定字段具有超出Protocol Buffers默认行为的额外编码语义时进行通信。

1

对于具有毫秒分辨率的日期时间,我使用了日期时间为YYYYMMDDHHMMSSmmmint64。这使得它既简洁又可读,而且令人惊讶的是,它将持续很长时间。

对于小数,我使用byte[],知道没有更好的表示,不会有损。