2012-07-03 14 views
8

के साथ मेमोरी उपयोग क्रमबद्ध छिद्रित बाइट सरणी हमारे आवेदन में हमारे पास कुछ डेटा संरचनाएं हैं जिनमें अन्य चीजों के अलावा बाइट्स की एक खंडित सूची होती है (वर्तमान में List<byte[]> के रूप में उजागर)। हम बाइट्स को तोड़ते हैं क्योंकि अगर हम बाइट एरे को बड़े ऑब्जेक्ट ढेर पर डाल देते हैं तो समय के साथ हम स्मृति विखंडन से पीड़ित होते हैं।प्रोटोबफ-नेट

हमने अपने स्वयं के जेनरेट किए गए क्रमिकरण डीएलएल का उपयोग करके इन संरचनाओं को क्रमबद्ध करने के लिए प्रोटोबफ-नेट का उपयोग करना भी शुरू कर दिया है।

हालांकि हमने देखा है कि प्रोटोबफ-नेट धारावाहिक करते समय बहुत बड़े मेमोरी बफर बना रहा है। स्रोत कोड के माध्यम से देखकर ऐसा प्रतीत होता है कि शायद यह List<byte[]> संरचना को लिखा गया है, जब तक यह बफर के सामने कुल लंबाई लिखने की आवश्यकता नहीं है, तब तक यह अपने आंतरिक बफर को फ्लश नहीं कर सकता है।

दुर्भाग्यवश दुर्भाग्यवश बाइट्स को पहले स्थान पर जोड़ने के साथ हमारे काम को कम कर देता है, और अंततः हमें स्मृति विखंडन के कारण आउटऑफमेमरी अपवाद देता है (उस समय अपवाद होता है जहां प्रोटोबफ-नेट बफर को 84k से अधिक करने का प्रयास कर रहा है, जो स्पष्ट रूप से इसे LOH पर रखता है, और हमारी समग्र प्रक्रिया मेमोरी उपयोग काफी कम है)।

यदि प्रोटोबफ-नेट कैसे काम कर रहा है, तो मेरा विश्लेषण सही है, क्या इस मुद्दे के आसपास कोई तरीका है?


अद्यतन

मार्क के जवाब के आधार पर, यहाँ मैं क्या कोशिश की है है:

[ProtoContract] 
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)] 
public class ABase 
{ 
} 

[ProtoContract] 
public class A : ABase 
{ 
    [ProtoMember(1, DataFormat = DataFormat.Group)] 
    public B B 
    { 
     get; 
     set; 
    } 
} 

[ProtoContract] 
public class B 
{ 
    [ProtoMember(1, DataFormat = DataFormat.Group)] 
    public List<byte[]> Data 
    { 
     get; 
     set; 
    } 
} 

तो यह क्रमानुसार करने:

var a = new A(); 
var b = new B(); 
a.B = b; 
b.Data = new List<byte[]> 
{ 
    Enumerable.Range(0, 1999).Select(v => (byte)v).ToArray(), 
    Enumerable.Range(2000, 3999).Select(v => (byte)v).ToArray(), 
}; 

var stream = new MemoryStream(); 
Serializer.Serialize(stream, a); 

लेकिन अगर मैं छड़ी ProtoWriter.WriteBytes() में ब्रेकपॉइंट जहां यहपर कॉल करता है विधि के निचले भाग की ओरऔर चरण DemandSpace() में, मैं देख सकता हूं कि बफर फ़्लश नहीं किया जा रहा है क्योंकि writer.flushLock1 के बराबर है।

अगर मैं इस तरह अपमानित करना के लिए एक और आधार वर्ग बनाने के लिए:

[ProtoContract] 
[ProtoInclude(1, typeof(ABase), DataFormat = DataFormat.Group)] 
public class ABaseBase 
{ 
} 

[ProtoContract] 
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)] 
public class ABase : ABaseBase 
{ 
} 

फिर writer.flushLockDemandSpace() में 2 बराबर होती है।

मुझे लगता है कि व्युत्पन्न प्रकारों के साथ यहां एक स्पष्ट कदम है जिसे मैंने याद किया है?

उत्तर

5

मैं कुछ पंक्तियाँ यहाँ के बीच पढ़ने के लिए ... क्योंकि List<T> (Protobuf भाषा में repeated के रूप में मैप किया गया) एक समग्र लंबाई-उपसर्ग नहीं है जा रहा हूँ, और byte[] (bytes के रूप में मैप किया गया) एक छोटी सी लंबाई-उपसर्ग जो अतिरिक्त बफरिंग का कारण नहीं बनना चाहिए। इसलिए मैं आप वास्तव में है और अधिक की तरह क्या है अनुमान लगा रहा हूँ:

[ProtoContract] 
public class A { 
    [ProtoMember(1)] 
    public B Foo {get;set;} 
} 
[ProtoContract] 
public class B { 
    [ProtoMember(1)] 
    public List<byte[]> Bar {get;set;} 
} 

यहाँ, लंबाई-उपसर्ग के लिए बफ़र की जरूरत वास्तव में है जब A.Fooके लिए लिख, मूल रूप से घोषित "निम्नलिखित जटिल डेटा है A.Foo "के लिए मूल्य")।

[ProtoMember(1, DataFormat=DataFormat.Group)] 
public B Foo {get;set;} 

यह Protobuf में 2 पैकिंग तकनीकों के बीच बदलता है:

  • डिफ़ॉल्ट (गूगल ने कहा वरीयता) लंबाई-पहले से जुड़ा हुआ है, तो आप एक मार्कर की लंबाई का संकेत मिलता है, जिसका अर्थ है सौभाग्य से उन्हें आसानी से ठीक है संदेश का पालन करने के लिए, तो उप संदेश पेलोड
  • लेकिन वहां भी शुरू मार्कर, उप संदेश पेलोड, और एक अंत मार्कर का उपयोग करने के एक विकल्प है

दूसरी तकनीक का उपयोग करते समय इसे बफर करने की आवश्यकता नहीं है, इसलिए: ऐसा नहीं है। इसका मतलब यह है कि यह एक ही डेटा के लिए थोड़ा अलग बाइट लिख रहा होगा, लेकिन प्रोटोबफ-नेट बहुत क्षमा कर रहा है, और या तो प्रारूप से डेटा को खुशी से deserialize करेगा। मतलब: यदि आप यह परिवर्तन करते हैं, तो भी आप अपना मौजूदा डेटा पढ़ सकते हैं, लेकिन नया डेटा स्टार्ट/एंड-मार्कर तकनीक का उपयोग करेगा।

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

+0

त्वरित उत्तर के लिए धन्यवाद। हमारी डेटा संरचना के बारे में आपका अनुमान सही था। क्या मैं यह कहने में सही कहूंगा कि हमें डेटाफॉर्मेट को किसी भी संदर्भ के लिए समूह में बदलने की आवश्यकता है जिसमें ए के संदर्भ भी शामिल हैं, और इसलिए ऑब्जेक्ट ग्राफ़ की जड़ तक? और इस परिवर्तन को भी प्रासंगिक ProtoInclude विशेषताओं पर भी होना चाहिए? –

+0

@ जेम्स अनिवार्य रूप से, हां। हमम ... शायद मुझे इसके लिए एक मॉडल-स्तर डिफ़ॉल्ट जोड़ना चाहिए! –

+0

मैंने समस्या को हल करने के लिए DataFormat.Group का उपयोग करने के अपने प्रयास के साथ अपना प्रश्न अपडेट कर दिया है, लेकिन मुझे अभी भी बफर को फ्लश करने में समस्याएं आ रही हैं। क्षमा करें अगर मैं मूर्ख हूँ .. –

2

अतिरिक्त संपादन आपके संपादन; [ProtoInclude(..., DataFormat=...)] ऐसा लगता है कि इसे संसाधित नहीं किया जा रहा था। मैं अपने वर्तमान स्थानीय निर्माण में इस के लिए एक परीक्षण को शामिल किया है, और अब यह गुजरता है:

[Test] 
public void Execute() 
{ 

    var a = new A(); 
    var b = new B(); 
    a.B = b; 

    b.Data = new List<byte[]> 
    { 
     Enumerable.Range(0, 1999).Select(v => (byte)v).ToArray(), 
     Enumerable.Range(2000, 3999).Select(v => (byte)v).ToArray(), 
    }; 

    var stream = new MemoryStream(); 
    var model = TypeModel.Create(); 
    model.AutoCompile = false; 
#if DEBUG // this is only available in debug builds; if set, an exception is 
    // thrown if the stream tries to buffer 
    model.ForwardsOnly = true; 
#endif 
    CheckClone(model, a); 
    model.CompileInPlace(); 
    CheckClone(model, a); 
    CheckClone(model.Compile(), a); 
} 
void CheckClone(TypeModel model, A original) 
{ 
    int sum = original.B.Data.Sum(x => x.Sum(b => (int)b)); 
    var clone = (A)model.DeepClone(original); 
    Assert.IsInstanceOfType(typeof(A), clone); 
    Assert.IsInstanceOfType(typeof(B), clone.B); 
    Assert.AreEqual(sum, clone.B.Data.Sum(x => x.Sum(b => (int)b))); 
} 

यह प्रतिबद्ध कुछ अन्य, असंबंधित refactorings (WinRT/iKVM के लिए कुछ पुनर्निमाण) में बंधा हुआ है, लेकिन जल्द से जल्द प्रतिबद्ध होना चाहिए।