2014-05-23 3 views
5

मेरे पास बाइनरी शाब्दिक 0x0 पर डेटाबेस तालिका से varbinary (8000) कॉलम के एकाधिक रनों को विभाजित करने का कार्य है। हालांकि, यह बदल सकता है इसलिए मैं चाहता हूं कि मैं इस चर को रखना चाहूंगा। मैं स्ट्रीमिंग तालिका मूल्य फ़ंक्शन के रूप में इसे जल्दी से करने के लिए SQLCLR का उपयोग करना चाहता हूं। मुझे पता है कि मेरे तार हमेशा कम से कम कुछ हज़ार बाइट होने जा रहे हैं।मैं इस लूप वेक्टरेशन पर सरणीबद्ध जांच को कैसे समाप्त कर सकता हूं?

संपादित करें: मैंने अपना एल्गोरिदम अपडेट किया है। आंतरिक लूप अनोलिंग की नीचता से बचने के लिए। लेकिन रजिस्टर आवंटन के बारे में सही विकल्प बनाने के लिए सीएलआर को मनाने के लिए यह बेहद मुश्किल है। यह शानदार होगा अगर सीएलआर को मनाने के लिए एक आसान तरीका था कि मैं और मैं वास्तव में वही बात कर रहा हूं। लेकिन इसके बजाय यह वास्तव में गूंगा चीजें करता है। पहले पथ पाश को अनुकूलित करना अच्छा लगेगा। लेकिन आप एक पाश में एक गोटो का उपयोग नहीं कर सकते हैं।

मैंने सी फ़ंक्शन memchr के 64-बिट कार्यान्वयन को अनुकूलित करने का निर्णय लिया। मूल रूप से एक समय में एक बाइट स्कैन करने और तुलना करने के बजाय, मैं थोड़ी सी झुकाव का उपयोग करके एक समय में 8 बाइट स्कैन करता हूं। संदर्भ के लिए, Array.IndexOf<Byte> एक उत्तर के लिए 4 बाइट स्कैन के समान कुछ करता है, मैं बस इसे करना चाहता हूं। वहाँ नोट करने के लिए चीजों की एक जोड़ी हैं:

  1. मेमोरी दबाव SQLCLR कार्यों में एक बहुत ही वास्तविक समस्या है। String.Split बाहर है क्योंकि सामने यह बहुत सारी स्मृति आवंटित करता है जिसे मैं वास्तव में टालना चाहता हूं। यह यूसीएस -2 स्ट्रिंग्स पर भी काम करता है, जिसके लिए मुझे अपने एसीआई स्ट्रिंग्स को यूनिकोड तारों में परिवर्तित करने की आवश्यकता होगी, और इस प्रकार मेरे डेटा को रिटर्न पर लॉब डेटाटाइप के रूप में व्यवहार करें। (SqlChars/SqlString लॉब प्रकार में बदलने से पहले केवल 4000 बाइट्स लौटा सकता है)।

  2. मैं स्ट्रीम करना चाहता हूं। String.Split से बचने का एक और कारण यह एक बार में सभी काम करता है, जिससे बहुत सारे मेमोरी दबाव पैदा होते हैं। बहुत सारे डिलीमीटर शुद्ध टी-एसक्यूएल विधियों के साथ कोड पर इसे मारना शुरू हो जाएगा।

  3. मैं इसे "सुरक्षित" रखना चाहता हूं। तो सभी प्रबंधित। सुरक्षा जांच में बहुत बड़ा जुर्माना लगता है।

Buffer.BlockCopy वास्तव में तेजी से है, और यह है कि लागत का भुगतान करने के लिए सामने एक बार से लगातार BitConverter के लिए लागत का भुगतान बेहतर हो रहा है। यह अभी भी सस्ता है, मेरे इनपुट को स्ट्रिंग में परिवर्तित करने और उस संदर्भ को पकड़ने से पहले।

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

यहाँ मेरी कोड है,

class SplitBytesEnumeratorA : IEnumerator 
{ 
    // Fields 
    private readonly byte[] _bytes; 
    private readonly ulong[] _longs; 
    private readonly ulong _comparer; 
    private readonly Record _record = new Record(); 
    private int _start; 
    private readonly int _length; 

    // Methods 
    internal SplitBytesEnumeratorA(byte[] bytes, byte delimiter) 
    { 
     this._bytes = bytes; 
     this._length = bytes.Length; 
     // we do this so that we can avoid a spillover scan near the end. 
     // in unsafe implementation this would be dangerous as we potentially 
     // will be reading more bytes than we should. 

     this._longs = new ulong[(_length + 7)/8]; 
     Buffer.BlockCopy(bytes, 0, _longs, 0, _length); 
     var c = (((ulong)delimiter << 8) + (ulong)delimiter); 
     c = (c << 16) + c; 
     // comparer is now 8 copies of the original delimiter. 
     c |= (c << 32); 
     this._comparer = c; 
    } 

    public bool MoveNext() 
    { 
     if (this._start >= this._length) return false; 
     int i = this._start; 
     var longs = this._longs; 
     var comparer = this._comparer; 
     var record = this._record; 
     record.id++; 
     // handle the case where start is not divisible by eight. 
     for (; (i & 7) != 0; i++) 
     { 
      if (i == _length || _bytes[i] == (comparer & 0xFF)) 
      { 
       record.item = new byte[(i - _start)]; 
       Buffer.BlockCopy(_bytes, _start, record.item, 0, i - _start); 
       _start = i + 1; 
       return true; 
      } 
     } 

     // main loop. We crawl the array 8 bytes at a time. 

     for (int j=i/8; j < longs.Length; j++) 
     { 
      ulong t1 = longs[j]; 
      unchecked 
      { 
       t1 ^= comparer; 
       ulong t2 = (t1 - 0x0101010101010101) & ~t1; 
       if ((t2 & 0x8080808080808080) != 0) 
       { 
        i =j*8; 
        // make every case 3 comparison instead of n. Potentially better. 
        // This is an unrolled binary search. 
        if ((t2 & 0x80808080) == 0) 
        { 
         i += 4; 
         t2 >>= 32; 
        } 

        if ((t2 & 0x8080) == 0) 
        { 
         i += 2; 
         t2 >>= 16; 
        } 

        if ((t2 & 0x80) == 0) 
       { 
       i++; 
       } 
       record.item = new byte[(i - _start)]; 
       // improve cache locality by not switching collections. 
       Buffer.BlockCopy(longs, _start, record.item, 0, i - _start);    _start = i + 1; 
       return true; 
      } 
     } 
     // no matches found increment by 8 
    } 
    // no matches left. Let's return the remaining buffer. 
    record.item = new byte[(_length - _start)]; 
    Buffer.BlockCopy(longs, _start, record.item, 0, (_length - _start)); 
    _start = _bytes.Length; 
    return true; 
    } 

    void IEnumerator.Reset() 
    { 
     throw new NotImplementedException(); 
    } 

    public object Current 
    { 
     get 
     { 
      return this._record; 
     } 
    } 
} 

// We use a class to avoid boxing . 
class Record 
{ 
    internal int id; 
    internal byte[] item; 
} 
+0

संभावित डुप्लिकेट [.net 4 और ऊपर में ऐरे सीमाओं की जांच दक्षता] (http://stackoverflow.com/questions/16713076/array-bounds-check- दक्षता-in-net-4-and-above) – ClickRick

+0

मुझे नहीं पता कि यह बिल्कुल वही है। महत्वपूर्ण लूप क्रॉल के लिए रिलीज़ x64 में चलाए जाने पर कोड बाइट स्कैन पर 8 बाउंड चेक करता है ('अगर (बाइट्स [i] == _delim) का मजेदार बिट'। वास्तव में यह काफी कोड उत्पन्न करता है तुलना करें जो कुछ हद तक खतरनाक है। मुझे आश्चर्य है कि बाइट्स क्रॉल पर थोड़ा सा झुकाव के साथ फिर से समीक्षा करना उचित है। –

+0

.NET JIT एक अच्छा अनुकूलन कंपाइलर नहीं है और यह केवल 0 के आदिम मामले में सीमाओं को समाप्त करता है -to-लंबाई सरणी ट्रैवर्सल (और बहुत ही सरल विविधताएं)। आप भाग्य से बाहर हैं। (या, असुरक्षित कोड का उपयोग करें। लेकिन मैंने देखा है कि अन्य अनुकूलन को रोकता है और चीजों को धीमा कर देता है) – usr

उत्तर

0

बॉक्स के बाहर सोच, आप विभाजन करने के लिए XQuery का उपयोग कर एक्सएमएल करने के लिए अपने तार बदलने और माना जाता है?

उदाहरण के लिए, आप सीमांकक और (हवा कोड) पास कर सकता है:

DECLARE @xml as xml 
DECLARE @str as varchar(max) 
SET @str = (SELECT CAST(t.YourBinaryColumn AS varchar(max) FROM [tableName] t) 
SET @xml = cast(('<X>'+replace(@str,@delimiter,'</X><X>')+'</X>') as xml) 

यह एक स्ट्रिंग के लिए बाइनरी में कनवर्ट करता है और एक्सएमएल टैग के साथ सीमांकक बदल देता है। फिर:

SELECT N.value('.', 'varchar(10)') as value FROM @xml.nodes('X') as T(N) 

व्यक्तिगत "तत्व", प्रत्येक डेलीमीटर घटना के बीच डेटा प्राप्त होगा।

शायद यह विचार उपयोगी हो सकता है या उत्प्रेरक के रूप में आप जिस पर निर्माण कर सकते हैं।

+0

इस दृष्टिकोण का प्रदर्शन सीएलआर स्प्लिटर या यहां तक ​​कि एक टेबल टेबल स्प्लिटर से भी बदतर है। इस पोस्ट को देखें :: http://sqlblog.com/blogs/paul_white/archive/2012/09/05/compute-scalars-expressions-and-execution-plan-performance.aspx प्लस एक समय में केवल एक पंक्ति है । मैं एक समय में पंक्तियों की एक श्रृंखला को संसाधित करने में सक्षम होना चाहता हूं। –

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