pbc 库的 lua binding

pbc 库的 lua binding,第1张

概述http://blog.codingnow.com/2011/12/pbc_lua_binding.html 前几天写的 pbc 初衷就是想可以方便的 binding 到动态语言中去用的。所以今天花了整整一天自己写了个简单的 lua binding 库,就是很自然的工作了。 写完了之后,我很好奇性能怎样,就写了一个非常简单的测试程序测了一下。当然这个测试不说明很多问题,因为测试用的数据实在是太简单 http://blog.codingnow.com/2011/12/pbc_lua_binding.html

前几天写的 pbc 初衷就是想可以方便的 binding 到动态语言中去用的。所以今天花了整整一天自己写了个简单的 lua binding 库,就是很自然的工作了。

写完了之后,我很好奇性能怎样,就写了一个非常简单的测试程序测了一下。当然这个测试不说明很多问题,因为测试用的数据实在是太简单了,等明天有空再弄个复杂点的来跑一下吧。我很奇怪,为什么 Google 官方的 C++ 版性能这么差。

我的 lua 测试代码大约是这样的:

local protobuf = require "protobuf"addr = io.open("../../build/addressbook.pb","rb")buffer = addr:read "*a"addr:close()protobuf.register(buffer)for i=1,1000000 do    local person = {        name = "Alice",ID = 123,}    local buffer = protobuf.encode("tutorial.Person",person)    local t = protobuf.decode("tutorial.Person",buffer)end

100 万次的编码和解码在我目前的机器上,耗时 3.8s 。

为了适应性能要求极高的场合,我还提供了另一组高性能 API 。他们可以把数据平坦展开在 lua 栈上,而不构成 table 。只需要把循环里的代码换成

    local buffer = protobuf.pack(        "tutorial.Person name ID","Alice",123)    protobuf.unpack("tutorial.Person name ID",buffer)

就可以了。这个版本只需要耗时 0.9s 。

一个月前,我曾经自己用 luajit + ffi 实现过一个纯 lua 的版本(没有开源),我跑了一下这个 case ,那个版本也很给力,达到前面的接口的功能,只需要 2.1s 。

不过我相信我新写的 binding 慢主要还是慢在 lua 上, 我换上了 luajit 跑以后,果然快了很多。

table 版本的耗时 1.7s,平坦展开版是 0.57s.

看来 luajit 的优化力度很大。

btw,我去年早些时候还写过一个 lua binding ,今天也顺便测了一下,在 luajit 下跑的时间是 1.2s 。没有这次写的这个版本快。

最后,我随手写了一个 C++ 的版本。应该有不少优化途径。不过我想这也是某中常规用法。

#include <iostream><sstream<string"addressbook.pb.h"using namespace std;int main(int argc, char* argv[]) {  Google_PROTOBUF_VERIFY_VERSION;  for int i=0;i<1000000+{      tutorial::Person person;      person.set_name("Alice");      person.set_ID(123;      stringstream output.SerializetoOstream(&output;      output.str;      tutorial::Person person2;      person2.ParseFromIstream.name.ID;  }  Google::protobuf::ShutdownProtobuflibrary;  return ;}

这段代码在开了 -O2 编译后,在我的机器上依旧需要时间 1.9s。若是这么看,那简直是太慢了 (比 luajit + c binding 还慢)。很久没研究 C++ 的细节,也懒得看了,如果谁有兴趣研究一下为什么 C++ 这么慢,我很有兴趣知道原因。

12 月 16 日

留言中 lifc0 说这段 C++ 代码中开销最大的是 stringstream 的构造和销毁,所以我改了一段代码:

stringstream output;stringstream input;{    output.clear;    output"";    tutorial;    person;    person;    input;    input(output);    person2&input;    person2}

这样更符合现实应用,每次初始化 stringstream 而不构造新的出来.

这样运行时间就从 1.90s 下降到 1.18s 了.

云风 提交于 December 14,2011 10:44 PM | 固定链接

@H_502_260@ COMMENTS

不支持扩展吗?

Posted by: kudoo | (34) September 6,2013 12:27 PM

坏处就是要附带.proto 文件,协议容易被破解

Posted by: fdsaf | (33) June 24,2013 05:10 PM

坏处就是要附带.proto 文件,协议容易被破解

Posted by: fdsaf | (32) 

在游戏服务器的io序列化中使用protobuf是低效的,测试结果显示更简单直接的序列化代码可以比protobuf快5-10倍。而这些序列化代码,通过协议数据结构定义可以自动生成,很容易就写出这样的工具脚本。如果追求效率,游戏服务器处理消息并不适合使用probobuf。惟一潜在用途是数据库。因为数据结构频繁改动,使得读取老数据有问题,而probobuf则很适合这个场合。

Posted by: pirunxi | (31) May 26,2012 09:30 AM

推荐使用显式的销毁 buffer 的接口。 gc 那个只是 5.2 支持顺手加上的。

Posted by: cloud | (30) February 18,2012 01:29 PM

@H_306_301@

一个小项目需要lua+protobuf,研究了纯c的nanopb、protobuf-c、upb和lua的lua-pb、lua-protobuf、protoc-gen-lua都不太合适。upb只能decode没encode,其他一些要么需要代码生成,要么没有lua绑定,纯lua的方案则不方便和c代码交换消息。
本打算花点时间给nanopb加上lua绑定,后来在Google groups上搜索lightweight、fast等关键字时无意看到http://comments.gmane.org/gmane.comp.lib.protocol-buffers.general/7972,去github研究之后觉得几乎就是我理想中的模式(和nanopb思路差不多,但实现了lua绑定),仔细看原来是云风大侠的作品。再搜相关资料来到这个页面,居然去年还过来踩过一脚。

给pbc提个小建议:lua绑定有几处用setMetatable给table绑定__gc元方法,但lua 5.1似乎只处理userdata的__gc,不知是否5.2做了相关调整,等有空再去研究。

Posted by: lifc0 | (29) 

for (int i = 0; i < 1000000; i++)
{
Person person;
person.set_name("Alice");
person.set_ID(123);
std::string s = person.SerializeAsstring();
person.ParseFromString(s);
}

Posted by: Anonymous | (28) December 22,2011 02:28 PM

要源码的同学就是不肯去 github 自己取?难道要人打包好 email 才行么?

Posted by: Cloud | (27) 

请问能否将你的bind库开源呢?

Posted by: Anonymous | (26) 

@tearshark

把一小短测试代码,针对性的优化是没有意义的.

实际不可能这样连着用,因为这样的代码段其实什么事情都没有做.

对于任何语言的任何代码片断,都是以最舒适和直观的写出来为最常规的用法.

对于一个通用库来说尤其如此,因为它是给许多不同的人在不同的场合用的.

就这段代码而言. 一个常规的用法 :

比如在数据编码阶段是,准备一个输出流,准备一个待序列化的结构,装填结构的数据,序列化到流,流输出.

这些步骤在实际用的时候是在不同流程,不同时机去做的,甚至不是一个人来维护,在同一模块里出现.

测试代码应体现这些流程和步骤,而不是想办法放在一起,再看看哪里可以优化,这样得到的优化结果没有太多意义.

Posted by: Cloud | (25) December 18,2011 02:08 AM

for (int i=0;i<1000000;i++) {
output.clear();
output.str(""); //构造/拷贝/析购string,释放内存,分配内存

tutorial::Person person; //构造person
person.set_name("Alice");
person.set_ID(123);

person.SerializetoOstream(&output);

input.clear();
input.str(output.str()); //构造/拷贝/析购string,分配内存

tutorial::Person person2; //构造person2

person2.ParseFromIstream(&input);

person2.name();
person2.ID();
}

这段代码测试什么的呢?内存分配?
input和output用相同的对象,然后通过seek *** 作重用数据不更好?
另外,stringstream笨拙的可以,还不如vector<>.clear()----至少我见过的vector<>的实现,clear()都不会真正的删除内存.
如果把stringstream替换成vector<>的实现,则我心目中理想的写法是

vectorstream<char> input;
tutorial::Person person;
tutorial::Person person2;

for (int i=0;i<1000000;i++) {
input.clear();
person.set_name("Alice");
person.set_ID(123);

person.SerializetoOstream(&input);

input.seek(0);
person2.ParseFromIstream(&input);

person2.name();
person2.ID();
}

这样可以尽量避免input的反复内存分配导致的效率低下. C++真不是适合新手使用的库,到处都是陷阱,特别的STL的IO实现部分.还不如C.

总结

以上是内存溢出为你收集整理的pbc 库的 lua binding全部内容,希望文章能够帮你解决pbc 库的 lua binding所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1264068.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存