2010-11-02 18 views
14

मुझे एहसास है कि यह माइक्रो-ऑप्टिमाइज़ेशन क्षेत्र में बहुत दूर है, लेकिन मुझे समझने में उत्सुकता है कि डेटटाइम पर कॉल क्यों करें.अब और डेटटाइम। यूटीसीएनओ इतनी "महंगी" । मेरे पास एक नमूना कार्यक्रम है जो कुछ "काम" करने (कुछ काउंटर में जोड़ने) करने के कुछ परिदृश्य चलाता है और इसे 1 सेकंड के लिए करने का प्रयास करता है। मैंने सीमित समय के लिए काम करने के कई दृष्टिकोण किए हैं। उदाहरणों से पता चलता है कि DateTime.Now और DateTime.UtcNow Environment.TickCount की तुलना में काफी कम होती है लेकिन यहां तक ​​कि सिर्फ 1 सेकंड के लिए एक अलग थ्रेड नींद दे और फिर कर्मी धागा बंद करने के लिए इंगित करने के लिए एक मूल्य की स्थापना की तुलना में धीमी है।डेटटाइम क्यों हैं.अब डेटटाइम.यूटीसीएनओ इतनी धीमी/महंगी

  • मुझे पता है कि UtcNow तेजी से है क्योंकि यह समय क्षेत्र जानकारी नहीं है, कारण है कि यह अभी भी बहुत बहुत धीमी है TickCount से:

    तो मेरे सवालों इन कर रहे हैं?

  • एक इंटीरियर से तेजी से बूलियन क्यों पढ़ रहा है?
  • परिदृश्यों जहां आप समय की एक सीमित मात्रा में करने के लिए चलाने के लिए कुछ की अनुमति देनी होगी के इन प्रकार से निपटने का आदर्श तरीका क्या है, लेकिन आप और अधिक समय वास्तव में काम कर रही की तुलना में समय की जाँच बर्बाद नहीं करना चाहते हैं?

उदाहरण के शब्दाडंबर क्षमा करें:

class Program 
{ 
    private static volatile bool done = false; 
    private static volatile int doneInt = 0; 
    private static UInt64 doneLong = 0; 

    private static ManualResetEvent readyEvent = new ManualResetEvent(false); 

    static void Main(string[] args) 
    { 
     MethodA_PrecalcEndTime(); 
     MethodB_CalcEndTimeEachTime(); 
     MethodC_PrecalcEndTimeUsingUtcNow(); 

     MethodD_EnvironmentTickCount(); 

     MethodX_SeperateThreadBool(); 
     MethodY_SeperateThreadInt(); 
     MethodZ_SeperateThreadLong(); 

     Console.WriteLine("Done..."); 
     Console.ReadLine(); 
    } 

    private static void MethodA_PrecalcEndTime() 
    { 
     int cnt = 0; 
     var doneTime = DateTime.Now.AddSeconds(1); 
     var startDT = DateTime.Now; 
     while (DateTime.Now <= doneTime) 
     { 
      cnt++; 
     } 
     var endDT = DateTime.Now; 
     Console.WriteLine("Time Taken: {0,30} Total Counted: {1,20}", endDT.Subtract(startDT), cnt); 
    } 

    private static void MethodB_CalcEndTimeEachTime() 
    { 
     int cnt = 0; 
     var startDT = DateTime.Now; 
     while (DateTime.Now <= startDT.AddSeconds(1)) 
     { 
      cnt++; 
     } 
     var endDT = DateTime.Now; 
     Console.WriteLine("Time Taken: {0,30} Total Counted: {1,20}", endDT.Subtract(startDT), cnt); 
    } 

    private static void MethodC_PrecalcEndTimeUsingUtcNow() 
    { 
     int cnt = 0; 
     var doneTime = DateTime.UtcNow.AddSeconds(1); 
     var startDT = DateTime.Now; 
     while (DateTime.UtcNow <= doneTime) 
     { 
      cnt++; 
     } 
     var endDT = DateTime.Now; 
     Console.WriteLine("Time Taken: {0,30} Total Counted: {1,20}", endDT.Subtract(startDT), cnt); 
    } 


    private static void MethodD_EnvironmentTickCount() 
    { 
     int cnt = 0; 
     int doneTick = Environment.TickCount + 1000; // <-- should be sane near where the counter clocks... 
     var startDT = DateTime.Now; 
     while (Environment.TickCount <= doneTick) 
     { 
      cnt++; 
     } 
     var endDT = DateTime.Now; 
     Console.WriteLine("Time Taken: {0,30} Total Counted: {1,20}", endDT.Subtract(startDT), cnt); 
    } 

    private static void MethodX_SeperateThreadBool() 
    { 
     readyEvent.Reset(); 
     Thread counter = new Thread(CountBool); 
     Thread waiter = new Thread(WaitBool); 
     counter.Start(); 
     waiter.Start(); 
     waiter.Join(); 
     counter.Join(); 
    } 

    private static void CountBool() 
    { 
     int cnt = 0; 
     readyEvent.WaitOne(); 
     var startDT = DateTime.Now; 
     while (!done) 
     { 
      cnt++; 
     } 
     var endDT = DateTime.Now; 
     Console.WriteLine("Time Taken: {0,30} Total Counted: {1,20}", endDT.Subtract(startDT), cnt); 
    } 

    private static void WaitBool() 
    { 
     readyEvent.Set(); 
     Thread.Sleep(TimeSpan.FromSeconds(1)); 
     done = true; 
    } 

    private static void MethodY_SeperateThreadInt() 
    { 
     readyEvent.Reset(); 
     Thread counter = new Thread(CountInt); 
     Thread waiter = new Thread(WaitInt); 
     counter.Start(); 
     waiter.Start(); 
     waiter.Join(); 
     counter.Join(); 
    } 

    private static void CountInt() 
    { 
     int cnt = 0; 
     readyEvent.WaitOne(); 
     var startDT = DateTime.Now; 
     while (doneInt<1) 
     { 
      cnt++; 
     } 
     var endDT = DateTime.Now; 
     Console.WriteLine("Time Taken: {0,30} Total Counted: {1,20}", endDT.Subtract(startDT), cnt); 
    } 

    private static void WaitInt() 
    { 
     readyEvent.Set(); 
     Thread.Sleep(TimeSpan.FromSeconds(1)); 
     doneInt = 1; 
    } 

    private static void MethodZ_SeperateThreadLong() 
    { 
     readyEvent.Reset(); 
     Thread counter = new Thread(CountLong); 
     Thread waiter = new Thread(WaitLong); 
     counter.Start(); 
     waiter.Start(); 
     waiter.Join(); 
     counter.Join(); 
    } 

    private static void CountLong() 
    { 
     int cnt = 0; 
     readyEvent.WaitOne(); 
     var startDT = DateTime.Now; 
     while (doneLong < 1) 
     { 
      cnt++; 
     } 
     var endDT = DateTime.Now; 
     Console.WriteLine("Time Taken: {0,30} Total Counted: {1,20}", endDT.Subtract(startDT), cnt); 
    } 

    private static void WaitLong() 
    { 
     readyEvent.Set(); 
     Thread.Sleep(TimeSpan.FromSeconds(1)); 
     doneLong = 1; 
    } 

} 

उत्तर

17

TickCount सिर्फ एक लगातार बढ़ती काउंटर पढ़ता है। यह केवल सबसे सरल चीज है जो आप कर सकते हैं।

DateTime.UtcNow प्रणाली समय क्वेरी करने के लिए की जरूरत है - और भूल नहीं है कि जब तक TickCount घड़ी, या एनटीपी बदलते उपयोगकर्ता जैसी चीजों का आनंदपूर्वक अज्ञानी है, UtcNow इस को ध्यान में रखना है।

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

आप चीजों को बेहतर बनाने के लिए जरूरत है, तो कर हैं:

  • आप बल्कि स्पष्ट रूप से एक नया धागा बनाने की तुलना में एक टाइमर का उपयोग कर सकते हैं। ढांचे में विभिन्न प्रकार के टाइमर हैं, और आपकी सटीक स्थिति को जानने के बिना, मैं सलाह नहीं दे सकता कि किस पर उपयोग करने के लिए सबसे समझदार होगा - लेकिन यह धागा शुरू करने से बेहतर समाधान की तरह लगता है।
  • आप अपने काम के कुछ ही पुनरावृत्तियों को माप सकते हैं तो लगता है कि कितने वास्तव में आवश्यक हो जाएगा। हो सकता है कि आप आधे से अधिक पुनरावृत्तियों को निष्पादित करना चाहें, कितनी देर तक ले लीजिए, फिर तदनुसार शेष चक्रों की संख्या समायोजित करें। बेशक, यह काम नहीं करता है अगर प्रति पुनरावृत्ति लेने का समय जंगली रूप से भिन्न हो सकता है।
+0

धन्यवाद जॉन नहीं देखा है। मैं टाइमर देखता हूँ। मुझे लगता है कि मेरे उदाहरण में "काम" गैर-यथार्थवादी है। हालांकि मैं उत्सुक था कि बड़ा प्रभाव कहां से आ रहा था। –

+1

@ मेरा अन्य: मूल रूप से, काउंटर को बढ़ाने के काम की तुलना में, लगभग * किसी भी * काम को एक बड़े प्रभाव के रूप में गिनने जा रहा है :) –

15

FWIW यहां कुछ कोड है जो एनएलओजी प्रत्येक लॉग संदेश के लिए टाइमस्टैम्प प्राप्त करने के लिए उपयोग करता है। इस मामले में, "कार्य" वर्तमान समय की वास्तविक पुनर्प्राप्ति है (माना जाता है, यह शायद "काम" का एक अधिक महंगा हिस्सा, संदेश की लॉगिंग के संदर्भ में होता है)।एनएलओजी वर्तमान वास्तविक गणना को "असली" समय (DateTime.Now के माध्यम से) प्राप्त करके वर्तमान समय प्राप्त करने की लागत को कम करता है यदि वर्तमान टिक गणना पिछली टिक गणना से अलग है। यह वास्तव में आपके प्रश्न पर सीधे लागू नहीं होता है, लेकिन यह वर्तमान समय पुनर्प्राप्ति "गति" करने का एक दिलचस्प तरीका है।

private static void MethodA_PrecalcEndTime() 
{ 
    int cnt = 0; 
    var doneTime = DateTime.Now.AddSeconds(1); 
    var startDT = CurrentTimeGetter.Now; 
    while (CurrentTimeGetter.Now <= doneTime)        
    {   
    cnt++; 
    } 
    var endDT = DateTime.Now; 
    Console.WriteLine("Time Taken: {0,30} Total Counted: {1,20}", endDT.Subtract(startDT), cnt);      }        
} 

तो CurrentTimeGetter.Now कि लौटे समय होगा तो अक्सर कहा जाता है:

internal class CurrentTimeGetter  
{   
    private static int lastTicks = -1;   
    private static DateTime lastDateTime = DateTime.MinValue;   

    /// <summary>   
    /// Gets the current time in an optimized fashion.   
    /// </summary>   
    /// <value>Current time.</value>   

    public static DateTime Now   
    {    
    get    
    {     
     int tickCount = Environment.TickCount;     
     if (tickCount == lastTicks)     
     {      
     return lastDateTime;     
     }     
     DateTime dt = DateTime.Now;     
     lastTicks = tickCount;     
     lastDateTime = dt;     
     return dt;    
    }   
    }  
} 

// It would be used like this: 
DateTime timeToLog = CurrentTimeGetter.Now; 

अपने प्रश्न के संदर्भ में, आप शायद इस तरह अपना समय पाशन कोड के प्रदर्शन "सुधार" कर सकता है एक पंक्ति में कई बार, केवल Environment.TickCount की लागत का भुगतान किया जाना चाहिए। मैं यह नहीं कह सकता कि यह वास्तव में एनएलओजी लॉगिंग के प्रदर्शन में मदद करता है जैसे कि आप नोटिस करेंगे या नहीं।

मुझे नहीं पता कि यह वास्तव में आपके प्रश्न पर मदद करता है, या यदि आपको अब भी किसी भी मदद की ज़रूरत है, लेकिन मैंने सोचा कि यह एक तेज ऑपरेशन (Environment.Ticks) को संभावित रूप से तेज करने के लिए एक दिलचस्प उदाहरण के रूप में कार्य करेगा कुछ परिस्थितियों में अपेक्षाकृत धीमी ऑपरेशन (DateTime.Now)।

+0

मेरे परीक्षणों के मुताबिक, प्रदान की गई फ़ंक्शन डेटटाइम से पूछताछ के रूप में 3.5 गुना धीमी है। UtcNow –

2

जहाँ तक मैं कह सकता हूं, DateTime.UtcNow (DateTime.Now के साथ भ्रमित नहीं होना चाहिए, जो बहुत धीमा है) सबसे तेज़ तरीका है जिसे आप समय प्राप्त कर सकते हैं। वास्तव में, जिस तरह से @ wageoghe प्रस्तावों को कैशिंग कर रहा है, वह प्रदर्शन को कम करता है (मेरे परीक्षणों में, जो 3.5 गुना था)।

ILSpy में, UtcNow इस तरह दिखता है:

[__DynamicallyInvokable] 
public static DateTime UtcNow 
{ 
    [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), SecuritySafeCritical] 
    get 
    { 
     long systemTimeAsFileTime = DateTime.GetSystemTimeAsFileTime(); 
     return new DateTime((ulong)(systemTimeAsFileTime + 504911232000000000L | 4611686018427387904L)); 
    } 
} 

मुझे लगता है, यह पता चलता है कि समारोह संकलक द्वारा inlined है अधिकतम गति को प्राप्त करने के लिए। समय प्राप्त करने के तेज़ तरीके हो सकते हैं, लेकिन अब तक, मैंने एक

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