Why are DateTime.Now DateTime.UtcNow so slow/expensive
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.
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.