Why are DateTime.Now DateTime.UtcNow so slow/expensive

后端 未结 4 1036
迷失自我
迷失自我 2021-01-01 12:14

I realize this is way too far into the micro-optimization area, but I am curious to understand why Calls to DateTime.Now and DateTime.UtcNow are so \"expensive\". I have a s

相关标签:
4条回答
  • 2021-01-01 12:33

    TickCount just reads a constantly increasing counter. It's just about the simplest thing you can do.

    DateTime.UtcNow needs to query the system time - and don't forget that while TickCount is blissfully ignorant of things like the user changing the clock, or NTP, UtcNow has to take this into account.

    Now you've expressed a performance concern - but in the examples you've given, all you're doing is incrementing a counter. I would expect that in your real code, you'll be doing rather more work than that. If you're doing a significant amount of work, that's likely to dwarf the time taken by UtcNow. Before doing anything else, you should measure that to find out whether you're actually trying to solve a problem which doesn't exist.

    If you do need to improve things, then:

    • You can use a timer rather than creating a new thread explicitly. There are various kinds of timers in the framework, and without knowing your exact situation, I can't advise on which would be most sensible to use - but it feels like a better solution than starting a thread.
    • You can measure a few iterations of your task, then guess how many will actually be required. You might want to then execute half that many iterations, take stock of how long that's taken, then adjust the number of remaining cycles accordingly. Of course, this doesn't work if the time take per iteration can vary wildly.
    0 讨论(0)
  • 2021-01-01 12:37

    FWIW here is some code that NLog uses to get the timestamp for each log message. In this case, the "work" is the actual retrieval of the current time (granted, it happens in the context of probably a much more expensive bit of "work", the logging of a message). NLog minimizes the cost of getting the current time by only getting the "real" time (via DateTime.Now) if the current tick count is different than the previous tick count. This does not really apply directly to your question, but it is an interesting way to "speed up" current time retrieval.

    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;
    

    In the context of your question, you could probably "improve" the performance of your time looping code like this:

    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);                        }                             
    }
    

    If CurrentTimeGetter.Now is called so frequently that the returned time would be the same many times in a row, only the cost of Environment.TickCount must be paid. I can't say if it really helps with performance of NLog logging such that you would notice or not.

    I don't know that it really helps on your question, or if you even need any help anymore, but I thought that it would serve as an interesting example of leveraging a faster operation (Environment.Ticks) to potentially speed up a relatively slow operation (DateTime.Now) in some circumstances.

    0 讨论(0)
  • 2021-01-01 12:46

    As far as I can tell, DateTime.UtcNow (not to be confused with DateTime.Now, which is much slower) is the fastest way you can get time. In fact, caching it the way @wageoghe proposes decreases performance signifficantly (in my tests, that were 3.5 times).

    In ILSpy, UtcNow looks like this:

    [__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));
        }
    }
    

    I think, this suggests that the function is inlined by the compiler to achieve maximum speed. There might be faster ways to get time, but so far, I haven't seen one

    0 讨论(0)
  • 2021-01-01 12:48

    For an up-to-date look on the profiled speed of DateTime.UtcNow / DateTimeOffset.UtcNow, see this dotnet thread, where BenchmarkDotNet was used to profile.

    There was unfortunately a perf regression with the jump to .NET (Core) 3 compared to 2.2, but even reporting with the regressed value, DateTime.UtcNow is coming in at a pretty rocking time of 71 ns (it had been 25 ns), i.e. 71 billionths of a second.

    To put that in perspective, even at the slower speed of 71ns, it means:

    You can call DateTime.UtcNow ~ 14,000 times for a cost of only 1 millisecond!

    At the previously faster time of 25 ns (hopefully they will get this performance back), you can call DateTime.UtcNow ~ 40,000 times for a cost of 1 millisecond.

    I'm not looking at old .NET Framework times here, but at least with the newer bits, I think it can safely be stated that it is at least no longer accurate to say that DateTime.UtcNow is "slow/expensive" (I appreciate that the question was asked however!).

    0 讨论(0)
提交回复
热议问题