2012-03-27 17 views
20

(; बात नैनोसेकंड; Sometimes हमारे मेजबान गलत है)अजगर (और जावा) में डेटा की सबसे तेजी से पैकिंग

मैं एक अजगर मुड़ सर्वर है कि कुछ जावा सर्वर और रूपरेखा से बात की है पता चलता का खर्च ~ 30% JSON एन्कोडर/डिकोडर में इसका रनटाइम; इसका काम प्रति सेकंड हजारों संदेश हैंडलिंग कर रहा है।

This talk यूट्यूब द्वारा दिलचस्प लागू अंक को जन्म देती है:

  • क्रमबद्धता प्रारूपों - कोई फर्क नहीं पड़ता जो आप उपयोग, वे सब महंगे हैं। का आकलन करें। अचार का प्रयोग न करें। एक अच्छी पसंद नहीं है। प्रोटोकॉल बफर धीमे हो गए। उन्होंने अपना स्वयं का बीएसओएन कार्यान्वयन लिखा जो आपके द्वारा डाउनलोड किए जा सकने वाले 10-15 गुना तेज है।

  • आपको मापना होगा। विटास ने HTTP कार्यान्वयन के लिए अपने प्रोटोकॉल को बदल दिया। हालांकि यह सी में था यह धीमा था। इसलिए उन्होंने HTTP से बाहर फिसल दिया और पाइथन का उपयोग करके प्रत्यक्ष सॉकेट कॉल किया और यह वैश्विक CPU पर 8% सस्ता था। HTTP के लिए लिफाफा वास्तव में महंगा है।

  • मापन। पाइथन माप में चाय की पत्तियों को पढ़ने की तरह है। पाइथन में बहुत सी चीजें हैं जो प्रतिद्वंद्वी काउंटरसिटन की लागत जैसे सहज ज्ञान युक्त हैं। उनके ऐप्स के अधिकांश भाग अपने समय क्रमबद्ध करने में व्यतीत करते हैं। प्रोफाइलिंग सीरियलाइजेशन पर निर्भर करता है जो आप डाल रहे हैं। इनट्सलाइजिंग इनट्स से बड़े ब्लब्स को क्रमबद्ध करने से बहुत अलग है।

वैसे भी, मैं अपने संदेश-पासिंग एपीआई के पाइथन और जावा दोनों सिरों को नियंत्रित करता हूं और JSON की तुलना में एक अलग क्रमबद्धता चुन सकता हूं।

मेरे संदेशों की तरह लग रहे:

  • देशांतर के परिवर्तनशील; उनमें से 1 और 10 के बीच कहीं भी
  • और दो पहले से ही यूटीएफ 8 टेक्स्ट स्ट्रिंग्स; दोनों के बीच 1 और 3KB

क्योंकि मैं उन्हें एक सॉकेट से पढ़ रहा हूँ, मैं पुस्तकालयों कि धाराओं के साथ शान से निपटने कर सकते हैं - अपने परेशान करता है, तो यह मुझे एक बफर यह खपत का कितना, उदाहरण के लिए नहीं बताता है।

इस स्ट्रीम का दूसरा सिरा जावा सर्वर है, बेशक; मैं कुछ ऐसा नहीं चुनना चाहता जो पाइथन अंत के लिए बहुत अच्छा है लेकिन जावा अंत में समस्याएं हल करता है उदा। प्रदर्शन या कष्टप्रद या flaky एपीआई।

मैं स्पष्ट रूप से अपनी खुद की प्रोफाइलिंग कर रहा हूं। मैं यहां आशा करता हूं कि आप उन दृष्टिकोणों का वर्णन करेंगे जिन्हें मैं नहीं सोचूंगा। struct का उपयोग करके और सबसे तेज़ प्रकार के तार/बफर क्या हैं।

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 

bson, msgpack और ujson सभी easy_install

मैं करूंगा प्यार के माध्यम से स्थापित दिखाए जाने के लिए मैं मैं गलत कर रहा हूँ; कि मुझे cStringIO इंटरफेस का उपयोग करना चाहिए या फिर आप इसे सब कुछ तेज करें!

इस डेटा को क्रमबद्ध करने का एक तरीका होना चाहिए जो निश्चित रूप से तीव्रता का क्रम है?

+0

पायथन अंत में क्रमबद्ध करने के लिए, आप सीपीकल का उपयोग कर सकते हैं क्योंकि यह अचार की तुलना में 10X से अधिक तेज है। सर्वर की तरफ, आप स्ट्रिंगबिल्डर का उपयोग कर सकते हैं (यदि आप ऑप्टिमाइज़ेशन की तलाश में हैं, और समवर्ती एक्सेस की आवश्यकता नहीं है) –

+0

मुझे यकीन नहीं है कि जेसन को किसी और चीज़ के साथ बदलना अच्छा विचार है। यदि पाइथन अंत ज्योति में होता, तो जावा में क्रमबद्धता निष्पादित विचार होता। –

+0

क्यों एक सरल सीमांकित स्ट्रिंग का प्रयास न करें। "1 | 2 | 3 | 4 | foo | bar" यदि आप एक डिलीमीटर ढूंढ सकते हैं जो आपके स्ट्रिंग मानों में कभी नहीं दिखाई देता है तो स्ट्रिंग का उपयोग करके। स्प्लिट सबसे तेज़ 'deserialization' होगा –

उत्तर

6

अंत में, हमने msgpack का उपयोग करना चुना।

जावा पर, http://blog.juicehub.com/2012/11/20/benchmarking-web-frameworks-for-games/ का कहना है:: जब तक हम JSON लिब बदली

प्रदर्शन बिल्कुल नृशंस था

आप JSON के साथ जाना है, तो अजगर और जावा पर पुस्तकालय की अपनी पसंद प्रदर्शन के लिए महत्वपूर्ण है (जेसन-सरल) जैकॉन के ऑब्जेक्टमैपर के लिए। इससे 35 से 300+ के लिए आरपीएस लाया गया - 10x वृद्धि

6

जबकि JSON लचीला है, यह जावा में सबसे धीमी क्रमबद्धता स्वरूपों में से एक नैनो सेकंड में (संभव अजगर के साथ-साथ) बात मैं देशी बाइट क्रम में एक द्विआधारी प्रारूप (होने की संभावना बहुत कम endian)

का प्रयोग करेंगे है

यहां एक लाइब्रेरी है जो मैं ठीक करता हूं AbstractExcerpt और UnsafeExcerpt एक सामान्य संदेश क्रमबद्ध करने और भेजने या पढ़ने और deserialize करने के लिए 50 से 200 एनएस लेता है।

+1

हम्मम सबसे धीमा? मुझे नहीं लगता कि एक्सएमएल तेज है - मेरे अनुभव से यह धीमा और अत्यधिक संसाधन उपभोग करने वाला है। –

+1

एक्सएमएल धीमा है यदि आप दस्तावेज़ मॉडल का उपयोग करते हैं, लेकिन यदि आप इवेंट मॉडल का उपयोग करते हैं तो मुझे तेज़ी से मिल गया है। यह जेएसओएन एन्कोडर, डीकोडर का उपयोग कर रहा था। ;) –

+2

दिलचस्प, हो सकता है कि आपने जेएसओएन लाइब्रेरी का उपयोग किया हो जो डीओएम जैसी संरचनाओं पर काम कर रहा था, मुझे लगता है कि इवेंट प्रोसेसिंग जेएसओएन तेज होगा, मुझे कुछ परीक्षण करना होगा जब मेरे पास समय लगेगा। –

2

Protocol Buffers बहुत तेज़ हैं और जावा और पायथन दोनों के लिए बाइंडिंग हैं। यह काफी लोकप्रिय पुस्तकालय है और Google के अंदर उपयोग किया जाता है, इसलिए इसे परीक्षण और अनुकूलित किया जाना चाहिए।

+0

Google के स्रोतों में उद्धृत सवाल कहते हैं कि उन्हें प्रोटोकॉल बफर धीमा पाया गया; क्या आपके पास प्रोटोकॉल बफर प्रदर्शन के आंकड़े हैं? – Will

+0

@Will "फास्ट" रिश्तेदार है। मैंने प्रोटोकॉल बफर को सरल एक्सएमएल से तेज़ पाया है, और यह मेरे उपयोग के मामले के लिए काफी अच्छा था। कुछ अन्य उपयोग के मामले के लिए, आप पाते हैं कि यह पर्याप्त नहीं है। यह सब इस बात पर निर्भर करता है कि आपको वास्तव में क्या करना है और आपके पास क्या अपेक्षाएं हैं। –

+0

फास्ट के रूप में तेज़ "एक ही काम के लिए कम से कम समय" तेजी से;) – Will

0

चूंकि आपका प्रेषण पहले से ही अच्छी तरह परिभाषित, गैर-पुनरावर्ती और गैर-घोंसला वाला डेटा है, क्यों न केवल एक सरल सीमांकित स्ट्रिंग का उपयोग करें। आपको केवल एक डिलीमीटर की आवश्यकता है जो आपके स्ट्रिंग वेरिएबल्स में 'n 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])); 

टिप्पणी ऊपर वेब ब्राउज़र और untested तो सिंटैक्स त्रुटि मौजूद हो सकता है में लिखा गया था।

एक पाठ आधारित के लिए; मुझे लगता है कि उपरोक्त सबसे तेज़ तरीका है।

3

आप struct मामले

def encode_struct(longs,str1,str2): 
    return struct.pack(">iii%dq"%len(longs),len(longs),len(str1),len(str2),*longs)+str1+str2 
  1. एक द्विआधारी स्ट्रिंग में अपने देशांतर कन्वर्ट करने के लिए अजगर सरणी मॉड्यूल और विधि toString उपयोग करने का प्रयास तेजी लाने के लिए सक्षम हो सकता है। फिर आप इसे जोड़ सकते हैं जैसे आपने स्ट्रिंग्स
  2. struct.Struct ऑब्जेक्ट बनाएं और इसका उपयोग करें। मेरा मानना ​​है कि यह और अधिक कुशल

है आप भी देख सकते हैं में:

http://docs.python.org/library/xdrlib.html#module-xdrlib

आपका सबसे तेजी से विधि 0.1222 सेकंड में 1000 तत्वों encodes। यह 12 तत्व मिलीसेकंड में 1 तत्व है। यह बहुत तेज़ है। मुझे संदेह है कि आप भाषा स्विच किए बिना बेहतर प्रदर्शन करेंगे।

+0

वास्तव में, धन्यवाद, धन्यवाद! मैं थोड़ा संकोच कर रहा हूं, मुझे लगता है कि मैं पोर्टेबल, भरोसेमंद, परिभाषित प्रारूप में होने के लिए array.tostring पर भरोसा नहीं करता – Will

3

मुझे पता है कि यह एक पुराना सवाल है, लेकिन यह अभी भी दिलचस्प है। मेरी सबसे हालिया पसंद Cap’n Proto का उपयोग करना था, जो उसी व्यक्ति ने लिखा था जिसने Google के लिए प्रोटोबफ किया था। मेरे मामले में, जो जैक्सन के JSON एन्कोडर/डिकोडर (सर्वर से सर्वर, जावा दोनों तरफ जावा) की तुलना में 20% के आसपास दोनों समय और मात्रा में कमी का कारण बनता है।

+0

भी https://github.com/google/flatbuffers देखें। इस तरह की चीज करने के लिए बहुत ही दिलचस्प तरीका है – frmdstryr

संबंधित मुद्दे