zoukankan      html  css  js  c++  java
  • 学习protobuf

    一、认识Protobuf

    ref:http://blog.csdn.net/program_think/article/details/4229773
    摘要:
    1. protobuf是一个开源项目。
    2. 用于把某种数据结构的信息,以某种格式保存起来。主要用于数据存储、传输协议格式。
    3. 优点:◇性能好/效率高◇代码生成机制◇支持“向后兼容”和“向前兼容”◇支持多种编程语言
    4. 缺点:◇应用不够广◇二进制格式导致可读性差,定位问题难◇缺乏自描述

    使用它的最大理由应该是“代码生成”,也就是只要写好描述脚本,它可以自动生成c++,java,或其他语言的类,自带序列化和反序列化功能,极大的方便了开发者,不再需要手动写数据存储类。另外就是序列化的密度和压缩比大;以及不同语言的序列化结果一致,使得不同语言可一致处理。

    二、安装Python Protobuf

    1. pip install protobuf
    这将安装python对protobuf的支持库,正常情况下可能只是安装pure python的支持,效率不如c扩展。为了安装c扩展,需要执行:
    PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
    pip install protobuf
    会编译安装c扩展,如果有编译错误,需要解决。

    2. 安装protobuf 代码生成器 -- protoc
    在官网(http://code.google.com/p/protobuf/)下载安装包或源代码包,直接安装或者编译(按readme提示)安装。

    三、示例
    建立addressbook.proto文件:
    package tutorial;

    message Person {
      required string name = 1;
      required int32 id = 2;
      optional string email = 3;

      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }

      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }

      repeated PhoneNumber phone = 4;
    }

    message AddressBook {
      repeated Person person = 1;
    }
    执行命令:
    /usr/local/bin/protoc -I=./ --python_out=./ ./addressbook.proto
    将在同目录下生成addressbook_pb2.py文件,建立protobuf_test.py文件:
    def testProto():
        import addressbook_pb2
        person = addressbook_pb2.Person()
        person.id = 1234
        person.name = "John Doe"
        person.email = "jdoe@example.com"
        phone = person.phone.add()
        phone.number = "555-4321"
        phone.type = addressbook_pb2.Person.HOME

        print 'src:', person
        str = person.SerializeToString()
        person2 = addressbook_pb2.Person()
        person2.ParseFromString(str)

        print 'Serialize len:%d' % (len(str))
        print 'dst:',person2

    if __name__ == '__main__':
        testProto()

    运行,显示如下:
    src: name: "John Doe"
    id: 1234
    email: "jdoe@example.com"
    phone {
      number: "555-4321"
      type: HOME
    }

    Serialize len:45
    dst: name: "John Doe"
    id: 1234
    email: "jdoe@example.com"
    phone {
      number: "555-4321"
      type: HOME
    }

    可见序列化和反序列化成功。

    四、提高

    ref:http://yz.mit.edu/wp/fast-native-c-protocol-buffers-from-python/
    摘要:
    1.写了一段python测试protobuf序列化和反序列化的时间消耗。
    2.使用c扩展和纯python库执行的性能对比,序列化15倍,反序列化8倍。
    3.使用protoc直接生成proto的c++代码,封装c++代码生成c扩展,安装c扩展。在测试代码中import这个c扩展,执行测试代码将自动使用c扩展版本进行数据存储和处理,性能将得到进一步提高,相比纯python库,序列化68倍,反序列化13倍。

    例:
    /usr/local/bin/protoc -I=./ --cpp_out=./ ./addressbook.proto
    将生成addressboo.pb.h和addressboo.pb.cc文件。

    建立addressbook.c文件:
    #include <Python.h>

    static PyMethodDef podMethods[] = {
      {NULL, NULL, 0, NULL}        /* Sentinel */
    };

    PyMODINIT_FUNC
    initpodpb(void)
    {
      PyObject *m;

      m = Py_InitModule("podpb", podMethods);
      if (m == NULL)
        return;
    }

    建立setup.py文件:
    import os
    import platform

    from setuptools import setup, find_packages
    from distutils.core import setup, Extension

    setup(
        ext_modules=[Extension('podpb',
    sources=['./addressbook.c','./addressbook.pb.cc'], libraries=['protobuf'])]
        )

    执行python setup.py build命令,有以下输出:
    g++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions build/temp.linux-x86_64-2.6/./addressbook.o build/temp.linux-x86_64-2.6/./addressbook.pb.o -lprotobuf -o build/lib.linux-x86_64-2.6/podpb.so
    表示生成了对应的c扩展podpb.so,将这个so文件复制到当前目录下。

    在protobuf_test.py加入测试代码:
    import os, random
    import timeit
    #import podpb

    def ser(xs):
        return [x.SerializeToString() for x in xs]

    def parse(ys):
        import addressbook_pb2
        for y in ys: addressbook_pb2.Person().ParseFromString(y)

    def benchTest():
        import addressbook_pb2

        nruns = 1000
        nwarmups = 100

        xs = [] # your protobufs
        for i in range(10):
            rId = random.randint(100000, 999999)
            person = addressbook_pb2.Person()
            person.id = rId
            person.name = "John Doe"
            person.email = "jdoe@example.com"
            phone = person.phone.add()
            phone.number = "555-4321"
            phone.type = addressbook_pb2.Person.HOME

            xs.append(person)

        t = timeit.Timer(lambda:None)
        t.timeit(nwarmups)
        print 'noop:', t.timeit(nruns) / nruns

        t = timeit.Timer(lambda: ser(xs))
        t.timeit(nwarmups)
        print 'ser:', t.timeit(nruns) / nruns / len(xs)

        ys = ser(xs)
        t = timeit.Timer(lambda: parse(ys))
        t.timeit(nwarmups)
        print 'parse:', t.timeit(nruns) / nruns / len(xs)

        print 'msg size:', sum(len(y) for y in ys) / len(ys)

    if __name__ == '__main__':
        testProto()
        benchTest()
       
    未使用podpb及PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION,有:
    noop: 9.29832458496e-08
    ser: 2.62283086777e-05
    parse: 3.05251121521e-05
    msg size: 46

    使用PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION有:
    noop: 9.08374786377e-08
    ser: 2.59931087494e-06
    parse: 7.11431503296e-06
    msg size: 46

    使用podpb及PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION有:
    noop: 9.20295715332e-08
    ser: 7.13109970093e-07
    parse: 4.77969646454e-06
    msg size: 46

    和文章对比性能差别不那么大,但趋势是符合的。

  • 相关阅读:
    Tomcat server.xml中配置的connectionTimeout参数无效?客户端等待超时时间
    org.joda.time.datetime.plusMonths 添加月数,缺少天数。
    微信小程序苹果手机请求地址 报错404,安卓可以正常请求
    在Ubuntu 18.04 Desktop图形中配置静态和动态IP
    Ubuntu16.04设置静态IP或动态ip(DHCP)
    linux监控命令
    lsof命令详解
    通过程序名称kill掉所有的进程
    linux目录和Windows目录对比
    Linux系统下查找安装包所在目录
  • 原文地址:https://www.cnblogs.com/lwis_webgis/p/3254915.html
Copyright © 2011-2022 走看看