protobuf中会严重影响时间和空间损耗的地方
http://blog.chinaunix.net/uid-26922071-id-3723751.html
当前项目中普遍用到GOOGLE 的一个开源大作PROTOBUF,把它作为网络应用层面的传输协议,至于它的诸多优势这里不作多说了!直接入正题!
前几日在PROTOBUF上面有严重的效率和空间开销的问题,没想到这两大难题一下子都来了,来得真是“迅雷不及掩耳之势”!跟踪后发现问题出在了“嵌套的MESSAGE个数过多,时间开销基本上全花在了ADD message上面”,例如:
message B{
标记 类型 变量 = 序列号;
...
}
message A{
repeated B f = 1;
}
A a;
这里message A中有上百万个message B,时间开销基本花在了 a.add_f()上面,而且空间上面开销也比较大。
针对这个头疼的问题,头这几天也在着急,项目都进行到这儿出了这等问题!之前我一直怀疑我们的策略问题,这几天在头儿的指示下,研究了一下PROTOBUF的源码,了解其中部分实现体制。这里简单地跟大家分享一下,大家尽管把砖头拍过来,嘿嘿!^_^
protobuf针对required 标记的字段分了两类,对每类都有相应的处理方式。其一:MESSAGE STRING;其二:非MESSAGE 和STRING,即原始数据类型,如INT32 INT64 FLOAT 等。我们把它们称为:repeated message(或者string)和repeated raw(原始数据类型)两种。PROTOBUF针对前者对内存的管理是,每次ADD时都会NEW一次;而后者先预分配了4个空间,随后成倍地动态增长空间(这个过程包括分配新空间,把原先的数据挪过来,再释放之前的旧空间三个操作);
虽然前都也有预先分配空间和动态增长空间,但分配的是用来存放MESSAGE或者STRING对象地址的空间,每次ADD的时候仍然要为数据分配空间。
下面把其中的一些实现细节贴出来,对比看一下:
repeated message(或者string)类的定义:
点击(此处)折叠或打开
- template <typename Element>
- class RepeatedPtrField : public internal::RepeatedPtrFieldBase {
- public:
- RepeatedPtrField();
- RepeatedPtrField(const RepeatedPtrField& other);
- ~RepeatedPtrField();
RepeatedPtrField继承了RepeatedPtrFieldBase类的一些比较重要的成员如下:
点击(此处)折叠或打开
- static const int kInitialSize = 4;
- void** elements_;
- int current_size_;
- int allocated_size_;
- int total_size_;
- void* initial_space_[kInitialSize];
上面的几个成员的定义在RepeatedPtrFieldBase类里面。
repeated raw(原始数据类型)类的定义:
点击(此处)折叠或打开
- template <typename Element>
- class RepeatedField {
- public:
- RepeatedField();
- RepeatedField(const RepeatedField& other);
- ~RepeatedField();
- 。。。。
点击(此处)折叠或打开
- private:
- static const int kInitialSize = 4;
- Element* elements_;
- int current_size_;
- int total_size_;
- Element initial_space_[kInitialSize];
。。。
elements_就是据说的预分配数组,初始数组元素的个数是kInitialSize,就是4.
current_size_表示当前已经占用的元素个数
total_size_表示当前数组总大小
注意:这两个类中的成员 elements_的类型的区别,前者,用来存放的是message或者string对象空间的地址;后者用来存放的是真正的数据地址。
repeated message(或者string)对ADD的处理方式:
点击(此处)折叠或打开
- template <typename Element>
- inline Element* RepeatedPtrField<Element>::Add() {
- return RepeatedPtrFieldBase::Add<TypeHandler>();
- }
点击(此处)折叠或打开
- template <typename TypeHandler>
- inline typename TypeHandler::Type* RepeatedPtrFieldBase::Add() {
- if (current_size_ < allocated_size_) {
- return cast<TypeHandler>(elements_[current_size_++]);
- }
- if (allocated_size_ == total_size_) Reserve(total_size_ + 1);
- ++allocated_size_;
- typename TypeHandler::Type* result = TypeHandler::New();
- elements_[current_size_++] = result;
- return result;
- }
点击(此处)折叠或打开
- void RepeatedPtrFieldBase::Reserve(int new_size) {
- if (total_size_ >= new_size) return;
- void** old_elements = elements_;
- total_size_ = max(total_size_ * 2, new_size);
- elements_ = new void*[total_size_];
- memcpy(elements_, old_elements, allocated_size_ * sizeof(elements_[0]));
- if (old_elements != initial_space_) {
- delete [] old_elements;
- }
- }
别看这里有动态增长内存空间,它这是做戏给人看的!它这里动态增长的是对象(习惯把message string称作“对象”,把原始类型称作“数据”,其实在C++眼里都是对象,只是本人癖性用C的眼光去看,^_^)地址空间,并不是真正的数据空间!
repeated raw(原始数据类型)对ADD的处理方式:
点击(此处)折叠或打开
- template <typename Element>
- inline Element* RepeatedField<Element>::Add() {
- if (current_size_ == total_size_) Reserve(total_size_ + 1);
- return &elements_[current_size_++];
- }
点击(此处)折叠或打开
- template <typename Element>
- void RepeatedField<Element>::Reserve(int new_size) {
- if (total_size_ >= new_size) return;
- Element* old_elements = elements_;
- total_size_ = max(total_size_ * 2, new_size);
- elements_ = new Element[total_size_];
- MoveArray(elements_, old_elements, current_size_);
- if (old_elements != initial_space_) {
- delete [] old_elements;
- }
- }
这里才是真正的动态增长数据空间。也就是说,只有第5、9、17、33。。。第N次%(2的M次方)==1时,才会重新去分配内存。
到这里,问题已经找到了,那么只能针对这里的两种特性来对我们的“应用协议”(PROTO文件中体现出来的应用架构)做一下协调、更改,可以避免repeated message(或者string)当repeated次数比较多的时候。!
我们相应地修改了一下PROTO文件,最终测试,发现时间上的开销果真缩小到了1/10左右,空间倒是没有减少多少,但多少还是少了一点儿!^_^ 唉!毕竟鱼和熊掌不可兼得,就时间和空间在软件这方面的地位而言,自古就是“难全”的,往往有一个要做出牺牲的!!!
其实去年就PROTOBUF的应用学习过一个月,还学习过一些比较高级的用法,但是并没有深入源码去理解它的实现机制,也没有去过多的去考虑它的性能问题!之后以为,PROTOBUF这个东西比较高级、比较好用,到现在知道再高级实用的东西也会有点瑕疵的,但瑕疵一般不会贴在脸上,还等着我们去发现,去适应它或者索引丢弃它不要!
protobuf中会严重影响时间和空间损耗的地方的更多相关文章
- 测试Protobuffer的定义格式对其时间和空间的影响
测试Protobuffer格式的A命令转换为std::string的的字节个数,分别测试了工程中用到的几种命令: a)AddLayer:108 b)AddSource:209 c)MoveLayer: ...
- protobuf中的编码规则
protobuf中的编码规则 (1)序列化和反序列化: 在开始本部分的内容之前,首先有必要介绍两个基本概念,一个是序列化,一个是反序列化.这两个概念的定义在网上搜一下都很多的,但大多都讲得比较晦涩,不 ...
- google protobuf 中的proto文件编写规则
1. 简单介绍 protobuf文件:就是定义你要的消息(类似Java中的类)和消息中的各个字段及其数据类型(类似java类中的成员变量和他的数据类型) 2. Protobuf消息定义 消息由至少一个 ...
- java性能时间与空间消耗
Java性能时间与空间消耗 一.减少时间消耗 标准代码优化 (1) 将循环不变量的计算移出循环 例如:for (int i=0; i<size()*2; i++) { ... } ------& ...
- 【月光宝盒get√】用时间置换空间,聊聊稀疏数组的那些事儿
背景 数据结构是指带有结构特性的数据元素的集合.在数据结构中,数据之间通过一定的组织结构关联在一起,便于计算机存储和使用.从大类划分,数据结构可以分为线性结构和非线性结构,适用于不同的应用场景. 线性 ...
- 关于web会话中的session过期时间的设置
关于web会话中的session过期时间的设置 1.操作系统: 步骤:开始——〉管理工具——〉Internet信息服务(IIS)管理器——〉网站——〉默认网站——〉右键“属性”——〉主目录——〉配置— ...
- C++中的类所占内存空间总结
C++中的类所占内存空间总结 最近在复习c++的一些基础,感觉这篇文章很不错,转载来,大家看看! 类所占内存的大小是由成员变量(静态变量除外)决定的,成员函数(这是笼统的说,后面会细说)是不计算 ...
- C/C++用匿名数据结构实现时间和空间名利双收
程序的时间和空间,往往是一对矛盾,比如计算CRC32的时候会用到余式表 DWORD *crcTable; // DWORD[256]; 余式表可以用某种规则计算生成,为缩短文章长度就不写出来了,总之要 ...
- javascript小实例,在页面中输出当前客户端时间
时间对象(Date())比较简单,本文旨在为初学者入门使用,大牛可略过! 本文承接基础知识实例,说一下实例的要求: 在页面中输出当前客户端时间(2015年1月1日星期一10:10:10这样的格式),每 ...
随机推荐
- C 字符/字符串经常使用函数
string.h中经常使用函数 char * strchr(char * str ,char ch); 从字符串str中查找首次出现字符ch的位置,若存在返回查找后的地址.若不存在则返回NULL vo ...
- 鸟哥的Linux私房菜-----16、程序与资源管理
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/ ...
- DriverModule_01
最小驱动模块: 最简单的Makefile 无配置文件 最小驱动的四部分 头文件 声明模块信息 模块驱动的入口.出口 功能区 关于这个头文件的分析: linux头文件的位置,例如#include< ...
- 软件——protel 的pcb电路图制作
近期一直在学习PCB板的绘制.
- 使用Docker来运行WebApp
原文:使用Docker来运行WebApp (作者:陈玓玏) 1.加载镜像到容器并运行webapp脚本 先进入管理员模式: su root 然后使用已有的webapp镜像来练习在docker上运行web ...
- Centos7安装.Net Core 2.2环境以及部署.Net Core MVC程序(Apache+Jexus环境)
原文:Centos7安装.Net Core 2.2环境以及部署.Net Core MVC程序(Apache+Jexus环境) 1.双11抢购***VPS.配置如下: CPU:2 核 内存:2048 M ...
- jvm compile
>>>Making sec-files-win @ Thu Oct 17 20:34:02 CST 2013 ... >>>Making jgss-files @ ...
- shell基础之符号与语法
shell脚本如今已经成为了一种非常普遍的脚本语言,之所以如此广泛的被应用,毋庸置疑它是有它的独到之处的.shell脚本语言和其它的语言比方说c/c++有何不同呢?c/c++等语言属于 ...
- AMBA标准
AMBA标准定义了三种不同的总线 高级高性能总线(AHB): 高级系统总线(ASB):-----用的比较少 高级外设总线(APB). 基于AMBA的典型微控制器: 典型的AMBA AHB系统设计包含以 ...
- Maven使用yuicompressor-maven-plugin打包压缩css、js文件
最近项目想使用在maven打包的时间压缩js,css文件,采用yuicompressor-maven-plugin插件进行压缩,但只是压缩减小大小,提高请求速度,并没有对js进行混淆.下面就写一下这个 ...