2009-06-07 15 views
5

में तेजी से मैं एक बफर को डी/इंटरलीव करने का सबसे तेज़ तरीका ढूंढ रहा हूं। अधिक विशिष्ट होने के लिए, मैं ऑडियो डेटा से निपट रहा हूं, इसलिए मैं चैनलों और एफएफटी बफर को विभाजित/संयोजन करने पर खर्च करने का समय अनुकूलित करने की कोशिश कर रहा हूं।डी/इंटरलीव सरणी सी #

वर्तमान में मैं प्रत्येक सरणी के लिए 2 इंडेक्स चर के साथ लूप के लिए उपयोग कर रहा हूं, इसलिए केवल प्लस ऑपरेशंस, लेकिन सभी प्रबंधित सरणी चेक एक सी सूचक विधि से तुलना नहीं करेंगे।

मुझे बफर.ब्लॉककॉपी और ऐरे.कॉपी विधियों को पसंद है, जो चैनलों को संसाधित करते समय बहुत समय निकालते हैं, लेकिन एक सरणी के लिए कस्टम इंडेक्सर होने का कोई तरीका नहीं है।

मैं एक सरणी मास्क बनाने का एक तरीका खोजने का प्रयास कर रहा था, जहां यह कस्टम इंडेक्सर के साथ एक नकली सरणी होगी, लेकिन यह मेरे एफएफटी ऑपरेशन में इसका उपयोग करते समय दो गुना धीमा साबित होता है। मुझे लगता है कि एक सरणी को सीधे एक्सेस करते समय संकलक बहुत सारी ऑप्टिमाइज़ेशन चाल खींच सकता है, लेकिन क्लास इंडेक्सर के माध्यम से एक्सेस करने के लिए अनुकूलित नहीं किया जा सकता है।

मुझे एक असुरक्षित समाधान नहीं चाहिए, हालांकि इसकी प्रकृति से, यह इस प्रकार के ऑपरेशन को अनुकूलित करने का एकमात्र तरीका हो सकता है।

धन्यवाद।

private float[][] DeInterleave(float[] buffer, int channels) 
{ 
    float[][] tempbuf = new float[channels][]; 
    int length = buffer.Length/channels; 
    for (int c = 0; c < channels; c++) 
    { 
     tempbuf[c] = new float[length]; 
     for (int i = 0, offset = c; i < tempbuf[c].Length; i++, offset += channels) 
      tempbuf[c][i] = buffer[offset]; 
    } 
    return tempbuf; 
} 
+0

क्या आप जो भी करने की कोशिश कर रहे हैं उसका एक कोड खंड प्रदान कर सकते हैं? आप जो हासिल करने की कोशिश कर रहे हैं उसके ठोस नमूने के साथ आपकी सहायता करने के लिए यह बहुत आसान होगा। – jerryjvl

उत्तर

5

मैं कुछ परीक्षण भाग गया और यहाँ कोड मैं परीक्षण किया है:

delegate(float[] inout) 
{ // My Original Code 
    float[][] tempbuf = new float[2][]; 
    int length = inout.Length/2; 
    for (int c = 0; c < 2; c++) 
    { 
     tempbuf[c] = new float[length]; 
     for (int i = 0, offset = c; i < tempbuf[c].Length; i++, offset += 2) 
      tempbuf[c][i] = inout[offset]; 
    } 
} 
delegate(float[] inout) 
{ // jerryjvl's recommendation: loop unrolling 
    float[][] tempbuf = new float[2][]; 
    int length = inout.Length/2; 
    for (int c = 0; c < 2; c++) 
     tempbuf[c] = new float[length]; 
    for (int ix = 0, i = 0; ix < length; ix++) 
    { 
     tempbuf[0][ix] = inout[i++]; 
     tempbuf[1][ix] = inout[i++]; 
    } 

} 
delegate(float[] inout) 
{ // Unsafe Code 
    unsafe 
    { 
     float[][] tempbuf = new float[2][]; 
     int length = inout.Length/2; 
     fixed (float* buffer = inout) 
      for (int c = 0; c < 2; c++) 
      { 
       tempbuf[c] = new float[length]; 
       float* offset = buffer + c; 
       fixed (float* buffer2 = tempbuf[c]) 
       { 
        float* p = buffer2; 
        for (int i = 0; i < length; i++, offset += 2) 
         *p++ = *offset; 
       } 
      } 
    } 
} 
delegate(float[] inout) 
{ // Modifying my original code to see if the compiler is not as smart as i think it is. 
    float[][] tempbuf = new float[2][]; 
    int length = inout.Length/2; 
    for (int c = 0; c < 2; c++) 
    { 
     float[] buf = tempbuf[c] = new float[length]; 
     for (int i = 0, offset = c; i < buf.Length; i++, offset += 2) 
      buf[i] = inout[offset]; 
    } 
} 

और परिणाम: (बफर आकार = 2^17, नंबर पुनरावृत्तियों परीक्षण प्रति समय समाप्त हो गया = 200)

Average for test #1:  0.001286 seconds +/- 0.000026 
Average for test #2:  0.001193 seconds +/- 0.000025 
Average for test #3:  0.000686 seconds +/- 0.000009 
Average for test #4:  0.000847 seconds +/- 0.000008 

Average for test #1:  0.001210 seconds +/- 0.000012 
Average for test #2:  0.001048 seconds +/- 0.000012 
Average for test #3:  0.000690 seconds +/- 0.000009 
Average for test #4:  0.000883 seconds +/- 0.000011 

Average for test #1:  0.001209 seconds +/- 0.000015 
Average for test #2:  0.001060 seconds +/- 0.000013 
Average for test #3:  0.000695 seconds +/- 0.000010 
Average for test #4:  0.000861 seconds +/- 0.000009 

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

संपादित करें: मैंने असुरक्षित कोड के साथ अनलॉक करने की कोशिश की और इसका कोई प्रभाव नहीं पड़ा।

delegate(float[] inout) 
{ 
    float[][] tempbuf = new float[2][]; 
    int length = inout.Length/2; 
    float[] tempbuf0 = tempbuf[0] = new float[length]; 
    float[] tempbuf1 = tempbuf[1] = new float[length]; 

    for (int ix = 0, i = 0; ix < length; ix++) 
    { 
     tempbuf0[ix] = inout[i++]; 
     tempbuf1[ix] = inout[i++]; 
    } 
} 

परिणाम भी कर रहे हैं 1% अंतर के साथ एक हिट याद आती है की तुलना में परीक्षण # 4: मैं भी पाश unrolling विधि के अनुकूलन की कोशिश की। टेस्ट # 4 अब तक जाने का मेरा सबसे अच्छा तरीका है।

जैसा कि मैंने एक दूसरे जाँच जोड़ने के बाद से jerryjvl, समस्या नहीं सूचकांक इनपुट बफर जाँच करने के लिए सीएलएस हो रही है, बताया (& & ऑफसेट < inout.Length) यह धीमी हो जाएगी ...

संपादित करें 2 : मैं परीक्षण आईडीई में पहले, इसलिए यहाँ भाग गया परिणाम में नहीं हैं:

2^17 items, repeated 200 times 
****************************************** 
Average for test #1:  0.000533 seconds +/- 0.000017 
Average for test #2:  0.000527 seconds +/- 0.000016 
Average for test #3:  0.000407 seconds +/- 0.000008 
Average for test #4:  0.000374 seconds +/- 0.000008 
Average for test #5:  0.000424 seconds +/- 0.000009 

2^17 items, repeated 200 times 
****************************************** 
Average for test #1:  0.000547 seconds +/- 0.000016 
Average for test #2:  0.000732 seconds +/- 0.000020 
Average for test #3:  0.000423 seconds +/- 0.000009 
Average for test #4:  0.000360 seconds +/- 0.000008 
Average for test #5:  0.000406 seconds +/- 0.000008 


2^18 items, repeated 200 times 
****************************************** 
Average for test #1:  0.001295 seconds +/- 0.000036 
Average for test #2:  0.001283 seconds +/- 0.000020 
Average for test #3:  0.001085 seconds +/- 0.000027 
Average for test #4:  0.001035 seconds +/- 0.000025 
Average for test #5:  0.001130 seconds +/- 0.000025 

2^18 items, repeated 200 times 
****************************************** 
Average for test #1:  0.0seconds +/- 0.000026 
Average for test #2:  0.001319 seconds +/- 0.000023 
Average for test #3:  0.001309 seconds +/- 0.000025 
Average for test #4:  0.001191 seconds +/- 0.000026 
Average for test #5:  0.001196 seconds +/- 0.000022 

Test#1 = My Original Code 
Test#2 = Optimized safe loop unrolling 
Test#3 = Unsafe code - loop unrolling 
Test#4 = Unsafe code 
Test#5 = My Optimized Code 

ऐसा लगता है कि पाश unrolling अनुकूल नहीं है। मेरा अनुकूलित कोड अभी भी असुरक्षित कोड की तुलना में जाने के लिए और केवल 10% अंतर के साथ मेरा सबसे अच्छा तरीका है। अगर मैं केवल संकलक को बता सकता हूं कि (i < buf.Length) का तात्पर्य है कि (ऑफसेट < इनआउट। लम्बाई), यह चेक (इनआउट [ऑफसेट]) को छोड़ देगा और मुझे मूल रूप से असुरक्षित प्रदर्शन मिलेगा।

+0

मुझे लगता है कि इस चरण में सवाल 'तेजी से पर्याप्त' पर वापस चला जाता है;) ... यदि परफ अब आपकी ज़रूरतों को पूरा करता है तो मैं कार्यान्वयन का पालन करने के लिए सबसे साफ, आसानतम चुनता हूं, और शायद सबसे इष्टतम संस्करण छोड़ सकता हूं यह एक टिप्पणी में ... या दूसरी तरफ। – jerryjvl

+0

वैसे मेरा मूल कोड पर्याप्त तेज़ था।मुझे कोई प्रदर्शन हिट नहीं मिली; विभिन्न नमूना दर, resampling, मिश्रण, winmm और openal को भेजने के साथ 3 एमपी 3 फ़ाइलों (फ्लाई पर) decoding। हालांकि, क्योंकि मैंने आधार दो गणित के बजाय बिटवाई को धक्का देना शुरू कर दिया और बफर के साथ सबकुछ बदलना शुरू कर दिया। ब्लॉककॉपी, मैंने सोचा कि इस समस्या से निपटने का सबसे अच्छा तरीका कम शक्तिशाली मशीनों (पूर्व विंडोज़ मोबाइल डिवाइस) पर अच्छा प्रदर्शन सुनिश्चित करेगा। – MaXKilleR

+0

+1, अपने स्वयं के प्रश्न का उत्तर देने के लिए नहीं, बल्कि दुनिया के साथ साझा करने के लिए इस उपयोगी अभ्यास के परिणाम। – ja72

1

वहाँ के रूप में कोई समारोह में बनाया गया है कि ऐसा करने के लिए, सरणी अनुक्रमणिका का उपयोग कर सबसे तेजी से आपरेशन के बारे में सोच सकता है:

यहाँ बात के प्रकार मैं अभी कर रहा हूँ है। इंडेक्सर्स और समाधान जैसे कि विधि कॉल शुरू करके और जेआईटी ऑप्टिमाइज़र को बाध्य चेक अनुकूलित करने में सक्षम होने के लिए केवल चीजों को और खराब बनाते हैं।

वैसे भी, मुझे लगता है कि आपकी वर्तमान विधि सबसे तेज़ गैर-unsafe समाधान है जिसका आप उपयोग कर सकते हैं। यदि प्रदर्शन वास्तव में आपके लिए महत्वपूर्ण है (जो आमतौर पर सिग्नल प्रोसेसिंग अनुप्रयोगों में करता है), तो आप पूरी चीज unsafe सी # (जो पर्याप्त तेज़ है, शायद सी के साथ तुलनीय है) में कर सकते हैं और इसे उस विधि में लपेटें जिसे आप अपनी सुरक्षित से कॉल करेंगे तरीकों।

0

मुझे लगता है कि बहुत से पाठक सवाल करेंगे कि आप ऑडियो प्रोसेसिंग जैसी किसी चीज़ के लिए असुरक्षित समाधान क्यों नहीं चाहते हैं। यह ऐसी चीज है जो गर्म खून वाले अनुकूलन के लिए begs और मैं आकस्मिक रूप से यह जानकर दुखी होगा कि इसे एक वीएम के माध्यम से मजबूर किया जा रहा है।

+0

सुरक्षित कोड में कोई वीएम शामिल नहीं है। –

+0

यह जेआईटी संकलित है। समस्या व्याख्या नहीं है लेकिन सरणी बाध्य चेक है। –

+0

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

1

यह आपको एक प्रमुख प्रदर्शन बढ़ावा नहीं देगा (मुझे लगभग मेरी मशीन पर लगभग 20% मापा जाता है), लेकिन आप सामान्य मामलों के लिए कुछ लूप अनोलिंग पर विचार कर सकते हैं। समय की सबसे आप चैनल की एक अपेक्षाकृत सीमित संख्या में हैं:

static private float[][] Alternative(float[] buffer, int channels) 
{ 
    float[][] result = new float[channels][]; 
    int length = buffer.Length/channels; 
    for (int c = 0; c < channels; c++) 
     result[c] = new float[length]; 

    int i = 0; 
    if (channels == 8) 
    { 
     for (int ix = 0; ix < length; ix++) 
     { 
      result[0][ix] = buffer[i++]; 
      result[1][ix] = buffer[i++]; 
      result[2][ix] = buffer[i++]; 
      result[3][ix] = buffer[i++]; 
      result[4][ix] = buffer[i++]; 
      result[5][ix] = buffer[i++]; 
      result[6][ix] = buffer[i++]; 
      result[7][ix] = buffer[i++]; 
     } 
    } 
    else 
     for (int ix = 0; ix < length; ix++) 
      for (int ch = 0; ch < channels; ch++) 
       result[ch][ix] = buffer[i++]; 


    return result; 
} 

जब तक आप वहाँ सामान्य वापस आने संस्करण छोड़ के रूप में यह चैनल के किसी भी संख्या को संभाल लेंगे, लेकिन आप एक गति को बढ़ावा देने अगर यह मिल जाएगा अनियंत्रित रूपों में से एक है।

+0

इन पंक्तियों के साथ आप मक्खी पर अनियंत्रित संस्करण को गतिशील रूप से उत्पन्न करने में सक्षम हो सकते हैं .... – Dolphin

+0

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

+0

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

1

हो सकता है कि अपनी खुद की सर्वश्रेष्ठ उत्तर में कुछ unrolling:

delegate(float[] inout) 
{ 
    unsafe 
    { 
     float[][] tempbuf = new float[2][]; 
     int length = inout.Length/2; 

     fixed (float* buffer = inout) 
     { 
      float* pbuffer = buffer; 

      tempbuf[0] = new float[length]; 
      tempbuf[1] = new float[length]; 

      fixed (float* buffer0 = tempbuf[0]) 
      fixed (float* buffer1 = tempbuf[1]) 
      { 
       float* pbuffer0 = buffer0; 
       float* pbuffer1 = buffer1; 

       for (int i = 0; i < length; i++) 
       { 
        *pbuffer0++ = *pbuffer++; 
        *pbuffer1++ = *pbuffer++; 
       } 
      } 
     } 
    } 
} 

यह अभी भी एक छोटे से अधिक प्रदर्शन मिल सकता है।

+0

मैंने आपके कोड का परीक्षण किया और यह एक हिट-मिस है। एक रन जितना तेज़ होगा उतना धीमा होगा, और केवल 1% तक। मेरा सबसे अच्छा जवाब अब तक परीक्षण # 4 है। मुझे एक सुरक्षित समाधान की ज़रूरत है। सुरक्षित और असुरक्षित के बीच 20% अंतर बुरा नहीं है लेकिन मुझे अभी भी लगता है कि उस अंतर को कम करना संभव है। समस्या सीएलएस को इनपुट बफर की जांच करने के लिए मजबूर नहीं कर रही है। एक और चेक (&& ऑफसेट MaXKilleR

+0

यदि आपको वास्तव में शीर्ष प्रदर्शन की आवश्यकता है, तो आपको अब तक के सबसे अच्छे उत्तर से उत्पादित आईएल की जांच करनी पड़ सकती है और देखें कि क्या कुछ भी अनावश्यक है जिसे छंटनी की जा सकती है। – jerryjvl

+0

पीएस: मुझे लगता है कि आप आईडीई के बाहर सभी माप कर रहे हैं, है ना? – jerryjvl

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