Python(和Java)中最快的数据打包
(我们的主机错了;纳秒很重要;) 我有一个PythonTwisted服务器,它与一些Java服务器进行对话,分析显示,在JSON编码器/解码器中花费了大约30%的运行时间;它的工作是每秒处理数千条消息 youtube提出了有趣的适用点:Python(和Java)中最快的数据打包,java,python,performance,optimization,serialization,Java,Python,Performance,Optimization,Serialization,(我们的主机错了;纳秒很重要;) 我有一个PythonTwisted服务器,它与一些Java服务器进行对话,分析显示,在JSON编码器/解码器中花费了大约30%的运行时间;它的工作是每秒处理数千条消息 youtube提出了有趣的适用点: 序列化格式-无论使用哪种格式,它们都是 昂贵的。量。不要用泡菜。这不是个好选择。建立 协议缓冲区速度慢。他们编写了自己的BSON实现 比您可以下载的速度快10-15倍 你必须测量。Vitess将其一个协议替换为HTTP协议 实施虽然是C语言,但速度很慢。所以他
- 序列化格式-无论使用哪种格式,它们都是 昂贵的。量。不要用泡菜。这不是个好选择。建立 协议缓冲区速度慢。他们编写了自己的BSON实现 比您可以下载的速度快10-15倍
- 你必须测量。Vitess将其一个协议替换为HTTP协议 实施虽然是C语言,但速度很慢。所以他们撕开了 使用python进行了直接套接字调用,这是8% 更便宜的全球CPU。HTTP的封装非常昂贵
- 测量。在Python中,测量就像阅读茶叶。 Python中有很多东西是违反直觉的,比如 掠夺大学的成本。他们的大部分应用程序都花了 他们的时间序列。分析序列化非常依赖于 你在放什么。序列化int与 序列化大blob
- 数量可变的长形;在1到10公里之间的任何地方
- 和两个已经存在的UTF8文本字符串;都在1到3KB之间
import time, random, struct, json, sys, pickle, cPickle, marshal, array
def encode_json_1(*args):
return json.dumps(args)
def encode_json_2(longs,str1,str2):
return json.dumps({"longs":longs,"str1":str1,"str2":str2})
def encode_pickle(*args):
return pickle.dumps(args)
def encode_cPickle(*args):
return cPickle.dumps(args)
def encode_marshal(*args):
return marshal.dumps(args)
def encode_struct_1(longs,str1,str2):
return struct.pack(">iii%dq"%len(longs),len(longs),len(str1),len(str2),*longs)+str1+str2
def decode_struct_1(s):
i, j, k = struct.unpack(">iii",s[:12])
assert len(s) == 3*4 + 8*i + j + k, (len(s),3*4 + 8*i + j + k)
longs = struct.unpack(">%dq"%i,s[12:12+i*8])
str1 = s[12+i*8:12+i*8+j]
str2 = s[12+i*8+j:]
return (longs,str1,str2)
struct_header_2 = struct.Struct(">iii")
def encode_struct_2(longs,str1,str2):
return "".join((
struct_header_2.pack(len(longs),len(str1),len(str2)),
array.array("L",longs).tostring(),
str1,
str2))
def decode_struct_2(s):
i, j, k = struct_header_2.unpack(s[:12])
assert len(s) == 3*4 + 8*i + j + k, (len(s),3*4 + 8*i + j + k)
longs = array.array("L")
longs.fromstring(s[12:12+i*8])
str1 = s[12+i*8:12+i*8+j]
str2 = s[12+i*8+j:]
return (longs,str1,str2)
def encode_ujson(*args):
return ujson.dumps(args)
def encode_msgpack(*args):
return msgpacker.pack(args)
def decode_msgpack(s):
msgunpacker.feed(s)
return msgunpacker.unpack()
def encode_bson(longs,str1,str2):
return bson.dumps({"longs":longs,"str1":str1,"str2":str2})
def from_dict(d):
return [d["longs"],d["str1"],d["str2"]]
tests = [ #(encode,decode,massage_for_check)
(encode_struct_1,decode_struct_1,None),
(encode_struct_2,decode_struct_2,None),
(encode_json_1,json.loads,None),
(encode_json_2,json.loads,from_dict),
(encode_pickle,pickle.loads,None),
(encode_cPickle,cPickle.loads,None),
(encode_marshal,marshal.loads,None)]
try:
import ujson
tests.append((encode_ujson,ujson.loads,None))
except ImportError:
print "no ujson support installed"
try:
import msgpack
msgpacker = msgpack.Packer()
msgunpacker = msgpack.Unpacker()
tests.append((encode_msgpack,decode_msgpack,None))
except ImportError:
print "no msgpack support installed"
try:
import bson
tests.append((encode_bson,bson.loads,from_dict))
except ImportError:
print "no BSON support installed"
longs = [i for i in xrange(10000)]
str1 = "1"*5000
str2 = "2"*5000
random.seed(1)
encode_data = [[
longs[:random.randint(2,len(longs))],
str1[:random.randint(2,len(str1))],
str2[:random.randint(2,len(str2))]] for i in xrange(1000)]
for encoder,decoder,massage_before_check in tests:
# do the encoding
start = time.time()
encoded = [encoder(i,j,k) for i,j,k in encode_data]
encoding = time.time()
print encoder.__name__, "encoding took %0.4f,"%(encoding-start),
sys.stdout.flush()
# do the decoding
decoded = [decoder(e) for e in encoded]
decoding = time.time()
print "decoding %0.4f"%(decoding-encoding)
sys.stdout.flush()
# check it
if massage_before_check:
decoded = [massage_before_check(d) for d in decoded]
for i,((longs_a,str1_a,str2_a),(longs_b,str1_b,str2_b)) in enumerate(zip(encode_data,decoded)):
assert longs_a == list(longs_b), (i,longs_a,longs_b)
assert str1_a == str1_b, (i,str1_a,str1_b)
assert str2_a == str2_b, (i,str2_a,str2_b)
给出:
encode_struct_1 encoding took 0.4486, decoding 0.3313
encode_struct_2 encoding took 0.3202, decoding 0.1082
encode_json_1 encoding took 0.6333, decoding 0.6718
encode_json_2 encoding took 0.5740, decoding 0.8362
encode_pickle encoding took 8.1587, decoding 9.5980
encode_cPickle encoding took 1.1246, decoding 1.4436
encode_marshal encoding took 0.1144, decoding 0.3541
encode_ujson encoding took 0.2768, decoding 0.4773
encode_msgpack encoding took 0.1386, decoding 0.2374
encode_bson encoding took 55.5861, decoding 29.3953
,并且都是通过easy_install安装的
我希望被证明我做错了;我应该使用cStringIO接口,否则你会加快速度
肯定有一种方法可以将这些数据序列化快一个数量级?虽然JSon是灵活的,但它是Java中最慢的序列化格式之一(也可能是python),在纳秒级,我会使用本机字节顺序的二进制格式(可能是小端)
这是一个库,我正是这样做的,一条典型的消息需要50到200纳秒的时间来序列化和发送,或者读取和反序列化。非常快,并且对Java和Python都有绑定。这是一个非常流行的库,在Google内部使用,因此应该对其进行测试和优化。既然您发送的数据已经定义良好、非递归且非嵌套,为什么不使用简单的分隔字符串呢。您只需要一个未包含在字符串变量中的分隔符,可能是“\n”
"10\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\nFoo Foo Foo\nBar Bar Bar"
然后使用一个简单的字符串拆分方法
string[] temp = str.split("\n");
ArrayList<long> longs = new ArrayList<long>(long.parseLong(temp[0]));
string string1 = temp[temp.length-2];
string string2 = temp[temp.length-1];
for(int i = 1; i < temp.length-2 ; i++)
longs.add(long.parseLong(temp[i]));
string[]temp=str.split(“\n”);
ArrayList longs=新的ArrayList(long.parseLong(temp[0]);
字符串string1=温度[温度长度-2];
字符串string2=温度[温度长度-1];
对于(int i=1;i
注意:以上内容是在web浏览器中编写的,未经测试,因此可能存在语法错误
以文本为基础;我认为上面的方法是最快的方法。您可以加快struct case的速度
def encode_struct(longs,str1,str2):
return struct.pack(">iii%dq"%len(longs),len(longs),len(str1),len(str2),*longs)+str1+str2
struct.struct
对象并使用它。我相信这样更有效率您最快的方法在.1222秒内编码1000个元素。这是1222毫秒内的1个元素。很快。我怀疑如果不切换语言,您会做得更好。最后,我们选择使用msgpack 如果使用JSON,那么选择Python和Java上的库对性能至关重要: 关于Java,他说: 在我们将JSON库(JSON simple)替换为Jackon的ObjectMapper之前,性能绝对糟糕。这使RPS从35增加到300+——增加了10倍
我知道这是一个老问题,但它仍然很有趣。我最近的选择是使用,作者是为谷歌制作protobuf的同一个人。在我的例子中,这导致与Jackson的JSON编码器/解码器(服务器到服务器,两边都是Java)相比,时间和容量都减少了约20%。对于python端的序列化,您可以使用cpickle,因为它比pickle快10倍多。在服务器端,您可以使用StringBuilder(如果您正在寻找优化,并且不需要并发访问),我不确定用其他东西替换json是否是一个好主意。如果python端在jython中,那么java中的序列化将是一个可执行的想法。“1 | 2 | 3 | 4 | foo | bar”如果您可以找到一个从未出现在字符串值中的分隔符,那么使用string.Split将是最快的“反序列化”pickle/cPickle,如果您使用二进制协议,它实际上比您的测试结果显示的要好得多。只需将.dumps(args)更改为.dumps(args,-1)或.dumps(args,cPickle.HIGHEST_协议)。当我通过这些修改重新运行基准测试时,cPickle实际上能够在组合编码/解码时间上击败msgpack。我意识到,对于Java互操作性来说,pickle不是一个选项,但是如果您只使用Python并且可以信任您的数据(因为pickle可以执行任意代码),那么我