2011-12-13 12 views
12

मैं यह निर्धारित करने की कोशिश कर रहा हूं कि प्रबंधित असुरक्षित structs के लिए निश्चित कथन वाले सी # के भीतर निश्चित कथन का उपयोग करने की वास्तविक लागत क्या है। कृपया ध्यान दें कि मैं अप्रबंधित structs का जिक्र नहीं कर रहा हूं।फिक्स्ड एरे युक्त एक प्रबंधित असुरक्षित संरचना पर सी # फिक्स्ड कथन का ओवरहेड क्या है?

विशेष रूप से, नीचे 'एकाधिक फिक्स्ड' वर्ग द्वारा दिखाए गए पैटर्न से बचने का कोई कारण है? क्या शून्य शून्य के करीब डेटा शून्य को ठीक करने की लागत है (== लागत & सेट करने के समान है जो निश्चित स्कोप में प्रवेश/बाहर निकलने पर एक झंडा साफ़ कर रहा है), या जब संभव हो तो इससे बचने के लिए पर्याप्त महत्वपूर्ण है?

स्पष्ट रूप से इन वर्गों को प्रश्न की व्याख्या करने में मदद करने के लिए योगदान दिया जाता है। यह एक एक्सएनए गेम में उच्च उपयोग डेटा संरचना के लिए है जहां इस डेटा को पढ़ने/लिखने का प्रदर्शन महत्वपूर्ण है, इसलिए यदि मुझे सरणी को ठीक करने और इसे हर जगह पास करने की आवश्यकता है तो मैं ऐसा करूँगा लेकिन अगर कोई अंतर नहीं है तो मैं ऐसा करूंगा। फ़ंक्शन हस्ताक्षर को प्लेटफॉर्म पर थोड़ा अधिक पोर्टेबल रखने में मदद के लिए निश्चित() स्थानीय को तरीकों से स्थानीय रखना पसंद नहीं करते हैं जो असुरक्षित कोड का समर्थन नहीं करते हैं। (हाँ, इसके लिए कुछ अतिरिक्त घुरघुराना कोड लेकिन जो कुछ भी यह लेता है ..)

 

    unsafe struct ByteArray 
    { 
     public fixed byte Data[1024]; 
    } 

    class MultipleFixed 
    { 
     unsafe void SetValue(ref ByteArray bytes, int index, byte value) 
     { 
      fixed(byte* data = bytes.Data) 
      { 
       data[index] = value; 
      } 
     } 

     unsafe bool Validate(ref ByteArray bytes, int index, byte expectedValue) 
     { 
      fixed(byte* data = bytes.Data) 
      { 
       return data[index] == expectedValue; 
      } 
     } 

     void Test(ref ByteArray bytes) 
     { 
      SetValue(ref bytes, 0, 1); 
      Validate(ref bytes, 0, 1); 
     } 
    } 

    class SingleFixed 
    { 
     unsafe void SetValue(byte* data, int index, byte value) 
     { 
      data[index] = value; 
     } 

     unsafe bool Validate(byte* data, int index, byte expectedValue) 
     { 
      return data[index] == expectedValue; 
     } 

     unsafe void Test(ref ByteArray bytes) 
     { 
      fixed(byte* data = bytes.Data) 
      { 
       SetValue(data, 0, 1); 
       Validate(data, 0, 1); 
      } 
     } 
    } 

इसके अलावा, मैं इसी तरह के सवाल के लिए देखा और करीबी मैंने पाया this था, लेकिन में है कि यह केवल शुद्ध के साथ संबंध है इस सवाल अलग है प्रबंधित कोड और उस संदर्भ में निश्चित उपयोग करने की विशिष्ट लागत।

किसी भी जानकारी के लिए धन्यवाद!

उत्तर

8

अनुभवजन्य रूप से, ओवरहेड सबसे अच्छा मामला है, 32 बिट जेआईटी पर ~ 270% और 64 बिट पर ~ 200% (और ओवरहेड अधिक बार खराब हो जाता है जिसे आप fixed कहते हैं)। इसलिए यदि प्रदर्शन वास्तव में महत्वपूर्ण है तो मैं आपके fixed ब्लॉक को कम करने का प्रयास करूंगा।

क्षमा करें, मैं जानना चाहता है कि उस मामले


विवरण

मैं भी कुछ TestMore तरीकों जो अपने दो परीक्षण तरीकों 10 फोन जोड़ा है निश्चित/असुरक्षित कोड के साथ काफी परिचित नहीं हूँ आपके fixed संरचना पर कई विधियों के एक और वास्तविक विश्व परिदृश्य को देने के लिए 2 के बजाय बार।

कोड मैं प्रयोग किया है:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var someData = new ByteArray(); 
     int iterations = 1000000000; 
     var multiple = new MultipleFixed(); 
     var single = new SingleFixed(); 

     // Warmup. 
     for (int i = 0; i < 100; i++) 
     { 
      multiple.Test(ref someData); 
      single.Test(ref someData); 
      multiple.TestMore(ref someData); 
      single.TestMore(ref someData); 
     } 

     // Environment. 
     if (Debugger.IsAttached) 
      Console.WriteLine("Debugger is attached!!!!!!!!!! This run is invalid!"); 
     Console.WriteLine("CLR Version: " + Environment.Version); 
     Console.WriteLine("Pointer size: {0} bytes", IntPtr.Size); 
     Console.WriteLine("Iterations: " + iterations); 

     Console.Write("Starting run for Single... "); 
     var sw = Stopwatch.StartNew(); 
     for (int i = 0; i < iterations; i++) 
     { 
      single.Test(ref someData); 
     } 
     sw.Stop(); 
     Console.WriteLine("Completed in {0:N3}ms - {1:N2}/sec", sw.Elapsed.TotalMilliseconds, iterations/sw.Elapsed.TotalSeconds); 

     Console.Write("Starting run for More Single... "); 
     sw = Stopwatch.StartNew(); 
     for (int i = 0; i < iterations; i++) 
     { 
      single.Test(ref someData); 
     } 
     sw.Stop(); 
     Console.WriteLine("Completed in {0:N3}ms - {1:N2}/sec", sw.Elapsed.TotalMilliseconds, iterations/sw.Elapsed.TotalSeconds); 


     Console.Write("Starting run for Multiple... "); 
     sw = Stopwatch.StartNew(); 
     for (int i = 0; i < iterations; i++) 
     { 
      multiple.Test(ref someData); 
     } 
     sw.Stop(); 
     Console.WriteLine("Completed in {0:N3}ms - {1:N2}/sec", sw.Elapsed.TotalMilliseconds, iterations/sw.Elapsed.TotalSeconds); 

     Console.Write("Starting run for More Multiple... "); 
     sw = Stopwatch.StartNew(); 
     for (int i = 0; i < iterations; i++) 
     { 
      multiple.TestMore(ref someData); 
     } 
     sw.Stop(); 
     Console.WriteLine("Completed in {0:N3}ms - {1:N2}/sec", sw.Elapsed.TotalMilliseconds, iterations/sw.Elapsed.TotalSeconds); 


     Console.ReadLine(); 
    } 
} 

unsafe struct ByteArray 
{ 
    public fixed byte Data[1024]; 
} 

class MultipleFixed 
{ 
    unsafe void SetValue(ref ByteArray bytes, int index, byte value) 
    { 
     fixed (byte* data = bytes.Data) 
     { 
      data[index] = value; 
     } 
    } 

    unsafe bool Validate(ref ByteArray bytes, int index, byte expectedValue) 
    { 
     fixed (byte* data = bytes.Data) 
     { 
      return data[index] == expectedValue; 
     } 
    } 

    public void Test(ref ByteArray bytes) 
    { 
     SetValue(ref bytes, 0, 1); 
     Validate(ref bytes, 0, 1); 
    } 
    public void TestMore(ref ByteArray bytes) 
    { 
     SetValue(ref bytes, 0, 1); 
     Validate(ref bytes, 0, 1); 
     SetValue(ref bytes, 0, 2); 
     Validate(ref bytes, 0, 2); 
     SetValue(ref bytes, 0, 3); 
     Validate(ref bytes, 0, 3); 
     SetValue(ref bytes, 0, 4); 
     Validate(ref bytes, 0, 4); 
     SetValue(ref bytes, 0, 5); 
     Validate(ref bytes, 0, 5); 
    } 
} 

class SingleFixed 
{ 
    unsafe void SetValue(byte* data, int index, byte value) 
    { 
     data[index] = value; 
    } 

    unsafe bool Validate(byte* data, int index, byte expectedValue) 
    { 
     return data[index] == expectedValue; 
    } 

    public unsafe void Test(ref ByteArray bytes) 
    { 
     fixed (byte* data = bytes.Data) 
     { 
      SetValue(data, 0, 1); 
      Validate(data, 0, 1); 
     } 
    } 
    public unsafe void TestMore(ref ByteArray bytes) 
    { 
     fixed (byte* data = bytes.Data) 
     { 
      SetValue(data, 0, 1); 
      Validate(data, 0, 1); 
      SetValue(data, 0, 2); 
      Validate(data, 0, 2); 
      SetValue(data, 0, 3); 
      Validate(data, 0, 3); 
      SetValue(data, 0, 4); 
      Validate(data, 0, 4); 
      SetValue(data, 0, 5); 
      Validate(data, 0, 5); 
     } 
    } 
} 

और .NET 4.0 में परिणाम, 32 बिट JIT:

CLR Version: 4.0.30319.239 
Pointer size: 4 bytes 
Iterations: 1000000000 
Starting run for Single... Completed in 2,092.350ms - 477,931,580.94/sec 
Starting run for More Single... Completed in 2,236.767ms - 447,073,934.63/sec 
Starting run for Multiple... Completed in 5,775.922ms - 173,132,528.92/sec 
Starting run for More Multiple... Completed in 26,637.862ms - 37,540,550.36/sec 

और .NET 4.0 में, 64 बिट JIT:

CLR Version: 4.0.30319.239 
Pointer size: 8 bytes 
Iterations: 1000000000 
Starting run for Single... Completed in 2,907.946ms - 343,885,316.72/sec 
Starting run for More Single... Completed in 2,904.903ms - 344,245,585.63/sec 
Starting run for Multiple... Completed in 5,754.893ms - 173,765,185.93/sec 
Starting run for More Multiple... Completed in 18,679.593ms - 53,534,358.13/sec 
+0

धन्यवाद - अच्छा जानकारी! मुझे अभी भी आश्चर्य है कि ओवरहेड के लिए अंतर्निहित कारण क्या है, लेकिन अच्छा प्रदर्शन प्राप्त करना मुख्य लक्ष्य है। –

+0

हाँ, मैं "क्यों" के लिए स्कीट, लिपर्ट या बजवेल को रोक दूंगा। लेकिन अगर आप अपनी संरचना के आकार के साथ खेलते हैं, तो यह आपको बता सकता है कि रनटाइम प्रत्येक 'निश्चित' संरचना की एक प्रति बना रहा है या नहीं। मेरा अनुमान है कि पिनिंग ऑपरेशन पूरी संरचना की प्रतिलिपि बनाता है। (यह भी देखें: http://www.dotnetperls.com/fixed-buffer) – ligos

+9

यह परीक्षण सटीक नहीं है। आप पूरी तरह से अनुचित तरीके से तय कर रहे हैं। उचित उपयोग एक बार ठीक करना होगा, कई बार लिखें, unfix। – JakeSays

9

यह वास्तव में दिलचस्प सवाल था कि मेरे पास था।

जिन परिणामों को मैं प्राप्त करने में कामयाब रहा हूं, वे 'निश्चित' कथन से प्रदर्शन हानि के लिए थोड़ा अलग कारण सुझाते हैं।

आप परीक्षण मैं चलाने के लिए और नीचे परिणाम देख सकते हैं, लेकिन निम्नलिखित टिप्पणियों मैं उन लोगों से आकर्षित कर रहे हैं:

  • का उपयोग कर शुद्ध संकेत के साथ 'स्थिर' के प्रदर्शन (एक्स *), IntPtr के बिना, है प्रबंधित कोड के रूप में अच्छा; रिलीज मोड में, यह भी बेहतर है यदि निश्चित रूप से अक्सर उपयोग नहीं किया जाता है - यह 'निश्चित' (लूप के अंदर) का उपयोग करके कई सरणी मानों को
  • तक पहुंचने का सबसे छिद्रपूर्ण तरीका है, एक लूप के अंदर) का नकारात्मक नकारात्मक प्रभाव पड़ता है लेकिन रिलीज मोड में, यह सामान्य सरणी पहुंच (विधि फिक्स्डएप) के रूप में लगभग उतना ही अच्छा काम करता है;
  • एक संदर्भ प्रकार पैरामीटर मान पर 'रेफरी' का उपयोग कर (फ्लोट []) था लगातार अधिक या समान रूप से performant (दोनों मोड)
  • डीबग मोड जब के लिए IntPtr अंकगणित (IntPtrAccess), लेकिन का उपयोग कर महत्वपूर्ण प्रदर्शन ड्रॉप बनाम रिलीज मोड है दोनों मोड सामान्य सरणी एक्सेस
  • से ऑफसेट का उपयोग करते हुए ऑफसेट का उपयोग करके सरणी के मानों के ऑफसेट पर गठबंधन नहीं करते हैं, तो प्रदर्शन मोड भयानक है (यह वास्तव में दोनों मोड के लिए समान समय लेता है)। यह 'फ्लोट' के लिए सच है लेकिन 'int'

कई बार परीक्षण चलाना, थोड़ा अलग लेकिन व्यापक रूप से लगातार परिणाम देता है। शायद मैं परीक्षण के कई श्रृंखला भागा जाना चाहिए था और औसत बार ले - लेकिन यह है कि :)

परीक्षण वर्ग पहले के लिए समय नहीं था:

class Test { 
    public static void NormalAccess (float[] array, int index) { 
     array[index] = array[index] + 2; 
    } 

    public static void NormalRefAccess (ref float[] array, int index) { 
     array[index] = array[index] + 2; 
    } 

    public static void IntPtrAccess (IntPtr arrayPtr, int index) { 
     unsafe { 
      var array = (float*) IntPtr.Add (arrayPtr, index << 2); 
      (*array) = (*array) + 2; 
     } 
    } 

    public static void IntPtrMisalignedAccess (IntPtr arrayPtr, int index) { 
     unsafe { 
      var array = (float*) IntPtr.Add (arrayPtr, index); // getting bits of a float 
      (*array) = (*array) + 2; 
     } 
    } 

    public static void FixedAccess (float[] array, int index) { 
     unsafe { 
      fixed (float* ptr = &array[index]) 
       (*ptr) = (*ptr) + 2; 
     } 
    } 

    public unsafe static void PtrAccess (float* ptr) { 
     (*ptr) = (*ptr) + 2; 
    } 

} 

और परीक्षण के लिए खुद को:

static int runs = 1000*1000*100; 
    public static void Print (string name, Stopwatch sw) { 
     Console.WriteLine ("{0}, items/sec = {1:N} \t {2}", sw.Elapsed, (runs/sw.ElapsedMilliseconds) * 1000, name); 
    } 

    static void Main (string[] args) { 
     var buffer = new float[1024*1024*100]; 
     var len = buffer.Length; 

     var sw = new Stopwatch(); 
     for (int i = 0; i < 1000; i++) { 
      Test.FixedAccess (buffer, 55); 
      Test.NormalAccess (buffer, 66); 
     } 

     Console.WriteLine ("Starting {0:N0} items", runs); 


     sw.Restart(); 
     for (int i = 0; i < runs; i++) 
      Test.NormalAccess (buffer, i % len); 
     sw.Stop(); 

     Print ("Normal access", sw); 

     sw.Restart(); 
     for (int i = 0; i < runs; i++) 
      Test.NormalRefAccess (ref buffer, i % len); 
     sw.Stop(); 

     Print ("Normal Ref access", sw); 

     sw.Restart(); 
     unsafe { 
      fixed (float* ptr = &buffer[0]) 
       for (int i = 0; i < runs; i++) { 
        Test.IntPtrAccess ((IntPtr) ptr, i % len); 
       } 
     } 
     sw.Stop(); 

     Print ("IntPtr access (fixed outside loop)", sw); 

     sw.Restart(); 
     unsafe { 
      fixed (float* ptr = &buffer[0]) 
       for (int i = 0; i < runs; i++) { 
        Test.IntPtrMisalignedAccess ((IntPtr) ptr, i % len); 
       } 
     } 
     sw.Stop(); 

     Print ("IntPtr Misaligned access (fixed outside loop)", sw); 

     sw.Restart(); 
     for (int i = 0; i < runs; i++) 
      Test.FixedAccess (buffer, i % len); 
     sw.Stop(); 

     Print ("Fixed access (fixed inside loop)", sw); 

     sw.Restart(); 
     unsafe { 
      fixed (float* ptr = &buffer[0]) { 
       for (int i = 0; i < runs; i++) { 
        Test.PtrAccess (ptr + (i % len)); 
       } 
      } 
     } 
     sw.Stop(); 

     Print ("float* access (fixed outside loop)", sw); 

     sw.Restart(); 
     unsafe { 
      for (int i = 0; i < runs; i++) { 
       fixed (float* ptr = &buffer[i % len]) { 
        Test.PtrAccess (ptr); 
       } 
      } 
     } 
     sw.Stop(); 

     Print ("float* access (fixed in loop)", sw); 

और अंत में परिणाम:

डीबग मोड

Starting 100,000,000 items 
00:00:01.0373583, items/sec = 96,432,000.00  Normal access 
00:00:00.8582307, items/sec = 116,550,000.00  Normal Ref access 
00:00:01.8822085, items/sec = 53,134,000.00  IntPtr access (fixed outside loop) 
00:00:10.5356369, items/sec = 9,492,000.00  IntPtr Misaligned access (fixed outside loop) 
00:00:01.6860701, items/sec = 59,311,000.00  Fixed access (fixed inside loop) 
00:00:00.7577868, items/sec = 132,100,000.00  float* access (fixed outside loop) 
00:00:01.0387792, items/sec = 96,339,000.00  float* access (fixed in loop) 

रिलीज मोड

Starting 100,000,000 items 
00:00:00.7454832, items/sec = 134,228,000.00  Normal access 
00:00:00.6619090, items/sec = 151,285,000.00  Normal Ref access 
00:00:00.9859089, items/sec = 101,522,000.00  IntPtr access (fixed outside loop) 
00:00:10.1289018, items/sec = 9,873,000.00  IntPtr Misaligned access (fixed outside loop) 
00:00:00.7899355, items/sec = 126,742,000.00  Fixed access (fixed inside loop) 
00:00:00.5718507, items/sec = 175,131,000.00  float* access (fixed outside loop) 
00:00:00.6842333, items/sec = 146,198,000.00  float* access (fixed in loop) 
संबंधित मुद्दे