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 sample program that runs a couple of scenarios of doing some "work" (adding to a counter) and attempts to do this for 1 second. I have several approached of making it do the work for a limited quantity of time. The examples show that DateTime.Now and DateTime.UtcNow are significantly slower than Environment.TickCount, but even that is slow compared to just letting a separate thread sleep for 1 second and then setting a value to indicate the worker thread to stop.
So my questions are these:
Please pardon the verbosity of the example:
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; } }
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!
UtcNow tells you the date and time as it would be in Coordinated Universal Time, which is also called the Greenwich Mean Time time zone - basically like it would be if you were in London England, but not during the summer. DateTime. Now gives the date and time as it would appear to someone in your current locale.
Now has an approximate resolution of 10 milliseconds on all NT operating systems. The actual precision is hardware dependent.
The DateTime. Now property returns the current date and time, for example 2011-07-01 10:09.45310 . The DateTime. Today property returns the current date with the time compnents set to zero, for example 2011-07-01 00:00.00000 .
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With