Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么DateTime.Now DateTime.utc这么慢/贵_C#_Performance_Micro Optimization - Fatal编程技术网

C# 为什么DateTime.Now DateTime.utc这么慢/贵

C# 为什么DateTime.Now DateTime.utc这么慢/贵,c#,performance,micro-optimization,C#,Performance,Micro Optimization,我意识到这对于微优化领域来说太远了,但我很好奇为什么调用DateTime.Now和DateTime.UtcNow会如此“昂贵”。我有一个示例程序,它运行两个场景来做一些“工作”(添加到计数器),并尝试做1秒。我有好几种方法让它在有限的时间内完成这项工作。示例显示DateTime.Now和DateTime.UtcNow明显比Environment.TickCount慢,但即使这样,也比让单独的线程睡眠1秒,然后设置一个值来指示工作线程停止要慢 所以我的问题是: 我知道UtcNow速度更快,因为它

我意识到这对于微优化领域来说太远了,但我很好奇为什么调用DateTime.Now和DateTime.UtcNow会如此“昂贵”。我有一个示例程序,它运行两个场景来做一些“工作”(添加到计数器),并尝试做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;
    }

}
类程序
{
私有静态易失性bool done=false;
私有静态volatile int doneInt=0;
专用静态UInt64 doneLong=0;
专用静态手动复位事件readyEvent=新手动复位事件(假);
静态void Main(字符串[]参数)
{
方法a_precalcenttime();
方法b_CalcEndTimeEachTime();
方法c_PrecalcEndTimeUsingUtcNow();
MethodD_环境TickCount();
MethodX_分离ThreadBool();
MethodY_separateThreadInt();
MethodZ_separateThreadLong();
控制台。WriteLine(“完成…”);
Console.ReadLine();
}
私有静态无效方法a_PrecalcEndTime()
{
int-cnt=0;
var doneTime=DateTime.Now.AddSeconds(1);
var startDT=DateTime.Now;

while(DateTime.Now
TickCount
只是读取一个不断增加的计数器。这几乎是你能做的最简单的事情

DateTime.UtcNow
需要查询系统时间-不要忘记,虽然
TickCount
对用户更改时钟或NTP之类的事情一无所知,但
UtcNow
必须考虑到这一点

现在,您已经表达了一个性能问题-但是在您给出的示例中,您所做的只是增加一个计数器。我希望在实际代码中,您所做的工作会比这多。如果您正在做大量工作,这可能会缩短
UtcNow
所花费的时间。在做任何其他事情之前,您需要应该衡量一下,看看你是否真的在试图解决一个不存在的问题

如果您确实需要改进,那么:

  • 你可以使用计时器,而不是显式地创建一个新线程。框架中有各种各样的计时器,在不知道具体情况的情况下,我无法建议使用哪种计时器最明智——但这感觉比启动线程更好
  • 您可以测量任务的几个迭代,然后猜测实际需要多少次。然后您可能希望执行一半的迭代,评估所需的时间,然后相应地调整剩余周期的数量。当然,如果每次迭代所需的时间变化很大,这是行不通的

FWIW这里是NLog用来获取每个日志消息的时间戳的一些代码。在这种情况下,“工作”是当前时间的实际检索(当然,它发生在可能更昂贵的“工作”的上下文中,即消息的日志记录)。NLog通过只获取“真实”来最小化获取当前时间的成本时间(通过
DateTime.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;
内部类CurrentTimeGetter
{        
私有静态int lastTicks=-1;
私有静态DateTime lastDateTime=DateTime.MinValue;
///         
///以优化方式获取当前时间。
///         
///当前时间。
现在是公共静态日期时间
{            
得到
{                
int tickCount=Environment.tickCount;
如果(滴答声=最后滴答声)
{                    
返回lastDateTime;
}                
DateTime dt=DateTime.Now;
lastTicks=滴答声计数;
lastDateTime=dt;
返回dt;
}        
}    
}
//它的用法如下:
DateTime timeToLog=CurrentTimeGetter.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);                        }                             
}
[__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));
    }
}
private static void MethodA_precalcenttime()
{
int-cnt=0;
var doneTime=DateTime.Now.AddSeconds(1);
var startDT=CurrentTimeGetter.Now;

而(CurrentTimeGetter.Now据我所知,
DateTime.UtcNow
(不要与
DateTime.Now混为一谈,后者要慢得多)是获得时间的最快方式。
事实上,以@wageoghe建议的方式缓存它会显著降低性能(在我的测试中,这是3.5倍)

在ILSpy中,UtcNow如下所示:

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

我认为,这表明该函数是由编译器内联的,以实现最大速度。可能有更快的方法来获取时间,但到目前为止,我还没有看到一个来更新
DateTime.UtcNow
/
DateTimeOffset.UtcNow
,请参见此处,其中
BenchmarkDotNet
用于奥菲尔

不幸的是,出现了性能倒退