我意识到这对微优化 Realm 来说太遥不可及,但是我很好奇理解为什么对DateTime.Now和DateTime.UtcNow的调用是如此“昂贵”。我有一个示例程序,该程序运行一些完成某些“工作”(添加到计数器)的场景,并尝试执行1秒钟。我有几种方法可以使其在有限的时间内完成工作。这些示例显示DateTime.Now和DateTime.UtcNow明显比Environment.TickCount慢,但与仅让一个单独的线程睡眠1秒然后设置一个值以指示工作线程停止相比,这仍然很慢。

所以我的问题是:

  • 我知道UtcNow更快,因为它没有时区信息,为什么它仍然比TickCount慢得多?
  • 为什么读取 bool 值比读取int更快?
  • 处理这些类型的场景的理想方法是什么,您需要允许某些东西在有限的时间内运行,但又不想浪费比实际工作更多的时间?

  • 请原谅这个例子的冗长:
    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;
        }
    
    }
    

    最佳答案

    TickCount只是读取一个不断增加的计数器。这只是您可以做的最简单的事情。
    DateTime.UtcNow需要查询系统时间-别忘了TickCount非常乐于忽略用户更改时钟或NTP之类的事情,而UtcNow必须考虑到这一点。

    现在您已经表达了对性能的关注-但是在给出的示例中,您要做的只是增加一个计数器。我希望在您的真实代码中,您将做的工作更多。如果您要进行大量工作,则可能会使UtcNow花费的时间相形见.。在执行其他任何操作之前,您应该先进行测量,以查明您是否实际上正在尝试解决不存在的问题。

    如果确实需要改进,则:

  • 您可以使用计时器,而不是显式创建新线程。框架中有各种各样的计时器,在不知道您的确切情况的情况下,我不建议使用哪种方法最明智-但这比启动线程感觉更好。
  • 您可以衡量任务的几次迭代,然后猜测实际需要多少次迭代。然后,您可能需要执行一半的迭代,盘点所花的时间,然后相应地调整剩余循环数。当然,如果每次迭代所花费的时间差异很大,这是行不通的。
  • 10-08 19:20