I have a system that does some simulations and outputs a lot of log information with timestamps.
I was surprised to see that DateTime.ToString() was quite expensive in the loop (it's really called a lot), so I tried to make a much faster version that outputs the date, time, and milliseconds.
Can this be done faster (the output needs to have milliseconds)? I didn't try to use a pointer because I assumed (maybe wrongly) that for such a small piece of code, the overhead of pinning the object would be higher.
module DateTimeFormatter =
let inline private valueToDigit (value: int) : char =
char (value + int '0')
let inline private write2Characters (c: char[]) offset value =
c.[offset + 0] <- valueToDigit (value / 10)
c.[offset + 1] <- valueToDigit (value % 10)
let inline private write3Characters (c: char[]) offset value =
c.[offset + 0] <- valueToDigit (value / 100)
c.[offset + 1] <- valueToDigit ((value % 100) / 10)
c.[offset + 2] <- valueToDigit (value % 10)
let format (dateTime: DateTime) =
let c = Array.zeroCreate<char> 23
write2Characters c 0 (dateTime.Year / 100)
write2Characters c 2 (dateTime.Year % 100)
c.[4] <- '-'
write2Characters c 5 dateTime.Month
c.[7] <- '-'
write2Characters c 8 dateTime.Day
c.[10] <- ' '
write2Characters c 11 dateTime.Hour
c.[13] <- ':'
write2Characters c 14 dateTime.Minute
c.[16] <- ':'
write2Characters c 17 dateTime.Second
c.[19] <- '.'
write3Characters c 20 dateTime.Millisecond
new string(c)
Here is the test code:
let a = DateTime.UtcNow
let iterations = 10_000_000
let sw = Stopwatch()
sw.Start()
for i = 0 to iterations do
a.ToString() |> ignore
sw.Stop()
printfn $"original no ms display {sw.ElapsedMilliseconds} ms"
sw.Reset()
sw.Start()
for i = 0 to iterations do
a.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture) |> ignore
sw.Stop()
printfn $"original with ms display {sw.ElapsedMilliseconds} ms"
sw.Reset()
sw.Start()
for i = 0 to iterations do
a |> DateTimeFormatter.format |> ignore
sw.Stop()
printfn $"new with ms display {sw.ElapsedMilliseconds} ms"
and the test results (MBP i7 2019):
original no ms display 2892 ms
original with ms display 4042 ms
new with ms display 1435 ms