2008-12-09 16 views
34

हमारे पास हमारे ऐप में बहुत सारे लॉगिंग कॉल हैं। हमारा लॉगर सिस्टम लेता है। टाइप पैरामीटर ताकि यह दिखा सके कि कौन सा घटक कॉल बनाया गया है। कभी-कभी, जब हमें परेशान किया जा सकता है, हम कुछ ऐसा करते हैं:ऑब्जेक्ट का प्रदर्शन। गेट टाइप()

class Foo 
{ 
    private static readonly Type myType = typeof(Foo); 

    void SomeMethod() 
    { 
    Logger.Log(myType, "SomeMethod started..."); 
    } 
} 

क्योंकि इसे केवल एक बार टाइप ऑब्जेक्ट प्राप्त करने की आवश्यकता है। हालांकि हमारे पास इस पर कोई वास्तविक मीट्रिक नहीं है। किसी को भी यह पता चला कि यह कॉल करने पर कितना बचाता है। गेट टाइप() हर बार जब हम लॉग करते हैं?

(मुझे पता है मैं मैट्रिक्स अपने आप को कोई बड़ी समस्या के साथ कर सकता है, लेकिन हे, क्या StackOverflow है?)

उत्तर

68

मैं दृढ़ता से संदेह है कि GetType() किसी भी वास्तविक लॉगिंग तुलना में काफी कम समय लगेगा। बेशक, संभावना है कि Logger.Log पर आपका कॉल कोई वास्तविक आईओ नहीं करेगा ... मुझे अभी भी संदेह है कि अंतर अप्रासंगिक होगा हालांकि।

संपादित करें: बेंचमार्क कोड नीचे है। परिणाम:

typeof(Test): 2756ms 
TestType (field): 1175ms 
test.GetType(): 3734ms 

विधि 100 लाख बार कॉल कर रहा है यही कारण है कि - अनुकूलन लाभ सेकंड के या तो एक जोड़े। मुझे संदेह है कि असली लॉगिंग विधि के लिए बहुत अधिक काम होगा, और 100 मिलियन बार कॉल करने में कुल 4 सेकंड से अधिक समय लगेगा, भले ही यह कुछ भी लिख न सके। (मैं निश्चित रूप से गलत हो सकता था - आपको खुद को आजमाने की ज़रूरत होगी।)

दूसरे शब्दों में, सामान्य रूप से, मैं सूक्ष्म अनुकूलन के बजाय सबसे अधिक पढ़ने योग्य कोड के साथ जाऊंगा।

using System; 
using System.Diagnostics; 
using System.Runtime.CompilerServices; 

class Test 
{ 
    const int Iterations = 100000000; 

    private static readonly Type TestType = typeof(Test); 

    static void Main() 
    { 
     int total = 0; 
     // Make sure it's JIT-compiled 
     Log(typeof(Test)); 

     Stopwatch sw = Stopwatch.StartNew(); 
     for (int i = 0; i < Iterations; i++) 
     { 
      total += Log(typeof(Test)); 
     } 
     sw.Stop(); 
     Console.WriteLine("typeof(Test): {0}ms", sw.ElapsedMilliseconds); 

     sw = Stopwatch.StartNew(); 
     for (int i = 0; i < Iterations; i++) 
     { 
      total += Log(TestType); 
     } 
     sw.Stop(); 
     Console.WriteLine("TestType (field): {0}ms", sw.ElapsedMilliseconds); 

     Test test = new Test(); 
     sw = Stopwatch.StartNew(); 
     for (int i = 0; i < Iterations; i++) 
     { 
      total += Log(test.GetType()); 
     } 
     sw.Stop(); 
     Console.WriteLine("test.GetType(): {0}ms", sw.ElapsedMilliseconds); 
    } 

    // I suspect your real Log method won't be inlined, 
    // so let's mimic that here 
    [MethodImpl(MethodImplOptions.NoInlining)] 
    static int Log(Type type) 
    { 
     return 1; 
    } 
} 
+0

दिलचस्प है कि सैम मेल्ड्रम का बेंचमार्क नीचे भी कम महत्वपूर्ण अंतर देता है ... – Gaz

+0

यह देखते हुए कि मेरे ईई पीसी (शायद ही एक पावरहाउस) पर उसका परीक्षण केवल ~ 2500 एमएस में चलता है, मेरा अनुमान है कि वह या तो डीबग संस्करण चला रहा है या चल रहा है डीबगर के तहत। हालांकि वे वैसे भी थोड़ा अलग काम कर रहे हैं। नोट इनलाइनिंग आदि –

+0

और वास्तव में, उसका परीक्षण वैसे भी फर्जी है - बस एक बग देखा। एक टिप्पणी जोड़ देंगे। –

15

GetType() समारोह विशेष विशेषता [MethodImpl(MethodImplOptions.InternalCall)] के साथ चिह्नित है। इसका मतलब है कि इसके विधि निकाय में आईएल नहीं है लेकिन इसके बजाय .NET CLR के आंतरिक में एक हुक है। इस मामले में, यह ऑब्जेक्ट के मेटाडेटा की बाइनरी संरचना को देखता है और इसके आसपास System.Type ऑब्जेक्ट बनाता है।

संपादित करें: मुझे लगता है कि मैं कुछ के बारे में गलत था ...

मैंने कहा कि: "क्योंकि GetType() एक नई वस्तु की आवश्यकता का निर्माण होने के लिए" लेकिन ऐसा लगता है यह सही नहीं है। किसी भी तरह, सीएलआर Type कैश करता है और हमेशा एक ही ऑब्जेक्ट देता है इसलिए इसे एक नया प्रकार ऑब्जेक्ट बनाने की आवश्यकता नहीं होती है।

मैं निम्नलिखित परीक्षण के आधार पर कर रहा हूँ:

Object o1 = new Object(); 
Type t1 = o1.GetType(); 
Type t2 = o1.GetType(); 
if (object.ReferenceEquals(t1,t2)) 
    Console.WriteLine("same reference"); 

तो, मैं अपने कार्यान्वयन में ज्यादा लाभ की उम्मीद नहीं है।

+3

आपको क्या लगता है कि यह हर बार एक नई वस्तु बना रहा है? वास्तव में यह दिखाने के लिए तुच्छ है कि मामला नहीं मिलता है। ऑब्जेक्ट प्रिंट करें। संदर्भ Equals (x.GetType(), x.GetType())। –

+2

:) मैंने यह टिप्पणी लिखने से पहले भी किया और मेरा जवाब सही किया। धन्यवाद। –

7

मुझे संदेह है कि आपको इस विषय पर SO से एक संतोषजनक उत्तर मिल जाएगा। उस प्रदर्शन का कारण, विशेष रूप से इस प्रकार के परिदृश्य, अत्यधिक आवेदन विशिष्ट हैं।

कोई भी त्वरित स्टॉपवॉच उदाहरण के साथ वापस पोस्ट कर सकता है जिसमें कच्चे मिलीसेकंड के मामले में तेज़ी से तेजी आएगी। लेकिन स्पष्ट रूप से इसका मतलब आपके आवेदन के लिए कुछ भी नहीं है। क्यूं कर? यह उस विशेष परिदृश्य के आसपास उपयोग पैटर्न पर निर्भर करता है। उदाहरण के लिए ...

  1. आपके पास कितने प्रकार हैं?
  2. आप कितने बड़े तरीके हैं?
  3. क्या आप इसे हर विधि, या केवल बड़े लोगों के लिए करते हैं?

ये केवल कुछ प्रश्न हैं जो सीधे समय बेंचमार्क की प्रासंगिकता को बदल देंगे।

+1

+1। तथ्य यह है कि माइक्रो बेंचमार्क, कैश मिस बनाने के बिना कोड ब्लोट (अतिरिक्त चर) के साथ दूर हो सकते हैं, जबकि वास्तविक अनुप्रयोग कैशिंग में वास्तविक अनुप्रयोग कैशिंग, जीसी दबाव, कैश मिस, पेजिंग ... माइक्रो बेंचमार्क अप्रासंगिक हैं। प्रोफाइलिंग जाने का रास्ता है। –

+0

मैं मानता हूं कि माइक्रोबेंचमार्क का उपयोग देखभाल के साथ किया जाना चाहिए - लेकिन मुझे लगता है कि इस मामले में वे दिखाते हैं कि GetType() को यह बहुत सस्ता है, इसलिए माइक्रो-ऑप्टिमाइज़ेशन मदद करने की बहुत संभावना नहीं है।ध्यान दें कि यह एक स्थिर क्षेत्र था - हालांकि बहुत सारे अतिरिक्त जीसी दबाव नहीं ... –

+0

आगे ध्यान दें: प्रोफाइलिंग ऐप के प्रदर्शन को * * * * GetType() को कॉल करने के बीच अंतर से अधिक बदल जाएगी। यह एक स्वाभाविक रूप से आक्रामक ऑपरेशन है। आवेदन-विशिष्ट बेंचमार्किंग जाने का सबसे अच्छा तरीका है। यदि आप यह नहीं दिखा सकते कि एक अनुकूलन ऐप में * महत्वपूर्ण * मदद करता है ... –

2

अंतर प्रदर्शन संभवतः जहां तक ​​प्रदर्शन प्रदर्शन का संबंध है, नगण्य है। लेकिन पहला दृष्टिकोण जहां आप प्रकार को कैश करते हैं, तेज़ी से होना चाहिए। चलो चलें और परीक्षण करें।

इस कोड को आप अंतर दिखाई देगा:

using System; 

namespace ConsoleApplicationTest { 
    class Program { 
     static void Main(string[] args) { 

      int loopCount = 100000000; 

      System.Diagnostics.Stopwatch timer1 = new System.Diagnostics.Stopwatch(); 
      timer1.Start(); 
      Foo foo = new Foo(); 
      for (int i = 0; i < loopCount; i++) { 
       bar.SomeMethod(); 
      } 
      timer1.Stop(); 
      Console.WriteLine(timer1.ElapsedMilliseconds); 

      System.Diagnostics.Stopwatch timer2 = new System.Diagnostics.Stopwatch(); 
      timer2.Start(); 
      Bar bar = new Bar(); 
      for (int i = 0; i < loopCount; i++) { 
       foo.SomeMethod(); 
      } 
      timer2.Stop(); 
      Console.WriteLine(timer2.ElapsedMilliseconds); 

      Console.ReadLine(); 
     } 
    } 

    public class Bar { 
     public void SomeMethod() { 
      Logger.Log(this.GetType(), "SomeMethod started..."); 
     } 
    } 

    public class Foo { 
     private static readonly Type myType = typeof(Foo); 
     public void SomeMethod() { 
      Logger.Log(myType, "SomeMethod started..."); 
     } 
    } 

    public class Logger { 
     public static void Log(Type type, string text) { 
     } 
    } 
} 

मेरी मशीन पर, यह दिया लगभग का परिणाम है। पहले दृष्टिकोण और लगभग 1500 मिलीसेकंड्स। दूसरे के लिए 2200 मिलीसेकंड।

(कोड और सही समय - DOH)

+1

दूसरी foo.SomeMethod() को बार में बदलने का प्रयास करें। कुछ विधि()। आप वर्तमान में एक ही चीज़ को दो बार बेंचमार्क कर रहे हैं :) –

+0

धन्यवाद जॉन - बहुत जल्दबाजी - दोह! –

0

क्षेत्र का उपयोग सबसे अच्छा तरीका है और यह typeof() और GetType() द्वारा पैदा कर रहा अनन्य संदर्भ रखने के लिए आंतरिक शब्दकोश ताला से बचें।

0

क्या आपने nameof ऑपरेटर का उपयोग करने पर विचार किया है?

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