我意识到这在微优化领域已经太过分了,但我很想知道为什么调用DateTime.Now和DateTime.UtcNow是如此“昂贵”。我有一个示例程序运行几个做“一些”工作的场景(添加到一个计数器)并试图这样做1秒钟。我有几个接近让它在有限的时间内完成工作。这些示例显示DateTime.Now和DateTime.UtcNow明显慢于Environment.TickCount,但与仅让一个单独的线程休眠1秒然后设置一个值以指示工作线程停止相比,即使这样也很慢。 p>
所以我的问题是这些:
请原谅示例的详细程度:
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;
}
}
答案 0 :(得分:19)
TickCount
只是读取一个不断增加的计数器。这是你能做的最简单的事情。
DateTime.UtcNow
需要查询系统时间 - 并且不要忘记,虽然TickCount
对用户更改时钟或NTP等事情一无所知,UtcNow
必须采取考虑到这一点。
现在你已经表达了一个性能问题 - 但是在你给出的例子中,你所做的只是增加一个计数器。我希望在你的真正的代码中,你将会做更多的工作。如果您正在进行重要的工作,那可能会使UtcNow
所花费的时间相形见绌。在做任何其他事情之前,你应该测量一下,看看你是否真的想要解决一个不存在的问题。
如果你做需要改进,那么:
答案 1 :(得分:17)
答案 2 :(得分:5)
有关DateTime.UtcNow
/ DateTimeOffset.UtcNow
的配置文件速度的最新信息,请参阅此dotnet thread,其中BenchmarkDotNet
用于配置文件。
不幸的是,与2.2相比,跳转到.NET(Core)3时存在性能下降,但是即使报告了回归值,DateTime.UtcNow
也在71 ns
摇摆不定的情况下出现(以前是25 ns
),即每秒七十亿分之一。
从角度来看,即使以71ns
的较慢速度,也意味着:
您只需花费1毫秒的时间即可拨打DateTime.UtcNow
〜14,000次!
以25 ns
以前更快的时间(希望他们能恢复这种性能),您可以致电DateTime.UtcNow
约40,000次,费用为1毫秒。
我不是在这里查看旧的.NET Framework时间,但至少可以使用较新的知识,我认为可以肯定地说,DateTime.UtcNow
至少“不再慢” / expensive”(但我很高兴提出了这个问题!)。
答案 3 :(得分:4)
据我所知,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));
}
}
我认为,这表明该函数由编译器内联以实现最大速度。可能有更快的方式来获得时间,但到目前为止,我还没有看到一个