2011-10-10 12 views
8

हम एक आवेदन जिसका सार्वजनिक प्रोटोकॉल Google प्रोटोकॉल बफ़र पर आधारित है में क्रमबद्धता और संदेशों के अक्रमांकन के लिए Protobuf शुद्ध उपयोग कर रहे हैं। पुस्तकालय उत्कृष्ट है और इसके अलावा हमारी सभी आवश्यकताओं को शामिल करता है: संदेश वास्तव में धारावाहिक होने से पहले हमें बाइट्स में क्रमबद्ध संदेश लंबाई को खोजने की आवश्यकता है।Protobuf शुद्ध संदेश धारावाहिक आकार संपत्ति

The question पहले से डेढ़ साल पहले ही पूछा जा चुका है और मार्क के अनुसार, ऐसा करने का एकमात्र तरीका मेमोरीस्ट्रीम को क्रमबद्ध करना था और .Length संपत्ति को बाद में पढ़ना था। यह हमारे मामले में स्वीकार्य नहीं है, क्योंकि मेमोरीस्ट्रीम दृश्यों के पीछे एक बाइट बफर आवंटित करता है और हमें इससे बचना होगा।

आप स्पष्ट यदि यूज-केस है, मुझे यकीन है कि हम इसे आसानी से उपलब्ध करा सकते हैं (यदि:

एक ही प्रतिक्रिया से यह पंक्ति हमें आशा है कि यह सब के बाद संभव हो सकता है देता है यह पहले से नहीं है)।

यहाँ हमारे उपयोग है। हमारे पास संदेश हैं जिनके आकार में कई बाइट्स और दो मेगाबाइट्स के बीच भिन्नता है। आवेदन पूर्व-आवंटित बाइट बफर सॉकेट ऑपरेशंस के लिए उपयोग किया जाता है और सीरियलाइजिंग/deserializing के लिए और एक बार गर्म अप चरण खत्म हो जाने के बाद, कोई अतिरिक्त बफर बनाया जा सकता है (संकेत: जीसी और ढेर विखंडन avoding)। बाइट बफर अनिवार्य रूप से पूल किए जाते हैं। हम जितना संभव हो बफर/धाराओं के बीच बाइट कॉपी करने से बचना चाहते हैं।

हम दो संभव रणनीतियों के साथ आए हैं और उन दोनों को संदेश आकार अग्रिम की आवश्यकता होती है: (बड़े) निश्चित-आकार बाइट बफ़र्स

  1. उपयोग और सभी संदेशों को एक बफर में फिट कर सकते हैं क्रमानुसार; Socket.Send का उपयोग कर बफर की सामग्री भेजें। हमें पता होना चाहिए कि अगला संदेश बफर में फिट नहीं हो सकता है और धारावाहिक को रोक सकता है। संदेश आकार के बिना, इसे प्राप्त करने का एकमात्र तरीका Serialize के दौरान होने वाले अपवाद की प्रतीक्षा करना है।
  2. (छोटे) परिवर्तनीय आकार बाइट बफर का उपयोग करें और प्रत्येक संदेश को एक बफर में क्रमबद्ध करें; Socket.Send का उपयोग कर बफर की सामग्री भेजें। पूल से उपयुक्त आकार के साथ बाइट बफर को देखने के लिए, हमें यह जानने की जरूरत है कि एक धारावाहिक संदेश कितने बाइट करता है।

क्योंकि प्रोटोकॉल पहले ही परिभाषित है (हम इसे नहीं बदल सकते हैं) और Varint32 होने के लिए संदेश लंबाई उपसर्ग की आवश्यकता है, हम SerializeWithLengthPrefix विधि का उपयोग नहीं कर सकते हैं।

तो यह एक तरीका है कि एक धारा में क्रमांकन के बिना संदेश आकार का अनुमान जोड़ने के लिए संभव है? यदि यह ऐसा कुछ है जो लाइब्रेरी के मौजूदा फीचर सेट और रोडमैप में फिट नहीं है, लेकिन यह करने योग्य है, तो हम पुस्तकालय को विस्तारित करने में रुचि रखते हैं। यदि कोई है तो हम वैकल्पिक दृष्टिकोण की भी तलाश कर रहे हैं।

उत्तर

4

जैसा कि ध्यान दिया गया है, यह तुरंत उपलब्ध नहीं है, क्योंकि कोड जानबूझकर डेटा पर एक ही पास करने की कोशिश करता है (विशेष रूप से IEnumerable<T> आदि)। , अपने डेटा पर निर्भर करता है, हालांकि, यह पहले से ही नकल के एक उदार राशि तथ्य के लिए अनुमति देने के लिए कर रहा सकता है, कि उप संदेशों भी लंबाई-पहले से जुड़ा हुआ है, इसलिए करतब दिखाने की जरूरत हो सकती। संदेश में "समूहबद्ध" उप-प्रारूप आंतरिक रूप से का उपयोग करके इस जुगलिंग को बहुत कम किया जा सकता है, क्योंकि समूह ट्रैक-बैक के बिना केवल-निर्माण के निर्माण की अनुमति देते हैं।

तो क्या एक ऐसी विधि जोड़ना संभव है जो स्ट्रीम में क्रमबद्धता के बिना संदेश आकार का अनुमान लगाता हो?

एक अनुमान बेकार के बगल में है; चूंकि कोई टर्मिनेटर नहीं है, इसलिए इसे सटीक होना चाहिए। आखिरकार, आकार वास्तव में ऐसा करने के बिना भविष्यवाणी करने के लिए थोड़ा मुश्किल है। आकार पूर्वानुमान के लिए v1 में कुछ कोड था, लेकिन वर्तमान में सिंगल-पास कोड पसंद किया जाता है, और ज्यादातर मामलों में बफर ओवरहेड नाममात्र होता है (आंतरिक बफर का पुन: उपयोग करने के लिए कोड होता है ताकि वह सभी खर्च न करे छोटे संदेशों के लिए बफर आवंटित करने का समय)।

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

पुन: SerializeWithLengthPrefix विधि

और आवश्यकता संदेश लंबाई उपसर्ग Varint32 होने के लिए, हम उपयोग नहीं कर सकते

मैं काफी यकीन है कि मैं वहाँ संबंध देख नहीं कर रहा हूँ - यह प्रारूपों की शृंखला की अनुमति देता है यहां इस्तेमाल किया जाना चाहिए; शायद अगर आप अधिक विशिष्ट हो सकते हैं?

डेटा के चारों ओर प्रतिलिपि बनाना - एक विचार जिसे मैंने यहां खेला है, लंबाई उपसर्ग के लिए उप-सामान्य रूपों का उपयोग करना है। उदाहरण के लिए, यह हो सकता है कि ज्यादातर मामलों में 5 बाइट्स बहुत अधिक हैं, इसलिए juggle के बजाय, यह 5 बाइट्स छोड़ सकता है, और फिर को बिना कंडेनसिंग के बिना ओवरराइट करें (क्योंकि ऑक्टेट 10000000 अभी भी "शून्य और जारी रखें" का अर्थ है अगर यह अनावश्यक है)। इसे अभी भी buffered (बैकफिल की अनुमति देने के लिए) की आवश्यकता होगी, लेकिन डेटा की आवश्यकता और आंदोलन की आवश्यकता नहीं होगी।

एक अंतिम सरल विचार बस होगा: FileStream पर क्रमबद्ध करें; फिर फ़ाइल की लंबाई, और फ़ाइल डेटा लिखें। यह स्पष्ट रूप से आईओ के लिए स्मृति उपयोग व्यापार करता है।

+0

लंबाई उपसर्ग और SerializeWithLengthPrefix के बारे में: विधि बेस 128, फिक्स्ड 32 और फिक्स्ड 32 बिग इंडियन के रूप में एन्कोड किए गए उपसर्ग लिख सकती है, लेकिन यह Varint32 प्रकार का समर्थन नहीं करती है। हमारा प्रोटोकॉल निम्न संदेश संरचना को परिभाषित करता है: [टाइप: varint32] [लंबाई: varint32] [वास्तविक प्रोटोबफ संदेश]। –

+0

@ बोरीस जो फ़ील्ड-नंबर ** के साथ बिल्कुल आधार-128 है ** ** है। फ़ील्ड-नंबर === प्रकार होगा (शायद एक >> 3 शिफ्ट के साथ)। मुझे * सटीक बाइट्स * देखना होगा, लेकिन यह शायद प्रयोग योग्य है। यदि नहीं, तो मैन्युअल रूप से प्रकार जोड़ें, और आधार-128 और फ़ील्ड 0 का उपयोग करके एन्कोड करें (जो फ़ील्ड नंबर छोड़ देगा) –

+0

स्पष्टीकरण के लिए धन्यवाद, मुझे लगता है कि मुझे वह याद आया। और विस्तृत प्रतिक्रिया के लिए धन्यवाद। हम अभी भी संभावित दृष्टिकोण का मूल्यांकन कर रहे हैं और यथासंभव छोटे डेटा जॉगलिंग और बफर आवंटन के साथ इष्टतम तरीके खोजने की कोशिश कर रहे हैं। –

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