Skip to content

Commit

Permalink
Cache time representation. (#149)
Browse files Browse the repository at this point in the history
* Cache time representation.

* Fix compilation issue.

* Add timezone caching and use faster time primitives.

* Fix names and Posix issues.

* Fix compilation warnings.

* Attempt to optimize a bit.

* New optimization scheme.

* Fix MacOS issue.

* Make cache thread-safe.

* Address review comments.

* Fix compilation issues.
  • Loading branch information
cheatfate authored Aug 22, 2024
1 parent 33761a5 commit a28bb97
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 6 deletions.
58 changes: 52 additions & 6 deletions chronicles/log_output.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import
strutils, times, macros, options, os,
dynamic_scope_types
strutils, times, macros, options, os, timestamp,
dynamic_scope_types, stew/[objects, byteutils]

when defined(js):
import
Expand Down Expand Up @@ -422,15 +422,61 @@ macro append*(o: var AnyOutput,
result.add newCall("append", o, arg2)
for arg in restArgs: result.add newCall("append", o, arg)

proc rfcTimestamp: string =
now().format("yyyy-MM-dd HH:mm:ss'.'fffzzz")

proc epochTimestamp: string =
formatFloat(epochTime(), ffDecimal, 6)

var
cachedMinutes {.threadvar.}: int64
cachedTimeArray {.threadvar.}: array[17, byte] # "yyyy-MM-dd HH:mm:"
cachedZoneArray {.threadvar.}: array[6, byte] # "zzz"

proc getSecondsPart(timestamp: Time): string =
var res = "00.000"
let
sec = timestamp.toUnix() mod 60
msec = timestamp.nanosecond() div 1_000_000

if sec > 0 and sec <= 9:
res[1] = chr(ord('0') + sec)
elif sec >= 10:
res[0] = chr(ord('0') + (sec div 10))
res[1] = chr(ord('0') + (sec mod 10))

if msec > 0 and msec <= 9:
res[5] = chr(ord('0') + msec)
elif msec >= 10 and msec <= 99:
res[4] = chr(ord('0') + (msec div 10))
res[5] = chr(ord('0') + (msec mod 10))
elif msec >= 100:
let tmp = msec mod 100
res[3] = chr(ord('0') + (msec div 100))
res[4] = chr(ord('0') + (tmp div 10))
res[5] = chr(ord('0') + (tmp mod 10))
res

proc getFastDateTimeString(): string =
let
timestamp = getFastTime()
minutes = timestamp.toUnix() div 60

if minutes != cachedMinutes:
cachedMinutes = minutes
let datetime = timestamp.local()
block:
# Cache string representation of first part (without seconds)
let tmp = datetime.format("yyyy-MM-dd HH:mm:")
cachedTimeArray = toArray(17, tmp.toOpenArrayByte(0, 16))
block:
# Cache string representation of zone part
let tmp = datetime.format("zzz")
cachedZoneArray = toArray(6, tmp.toOpenArrayByte(0, 5))

string.fromBytes(cachedTimeArray) & timestamp.getSecondsPart() &
string.fromBytes(cachedZoneArray)

template timestamp(record): string =
when record.timestamps == RfcTime:
rfcTimestamp()
getFastDateTimeString()
else:
epochTimestamp()

Expand Down
42 changes: 42 additions & 0 deletions chronicles/timestamp.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

{.push raises: [].}

import std/times
export times

when defined(macos) or defined(macosx) or defined(osx):
from posix import Timeval
proc gettimeofday(tp: var Timeval, tzp: pointer = nil) {.
importc: "gettimeofday", header: "<sys/time.h>", sideEffect.}
elif defined(windows):
type
FILETIME {.final, pure, completeStruct.} = object
dwLowDateTime: uint32
dwHighDateTime: uint32
proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall,
sideEffect.}
else:
const CLOCK_REALTIME_COARSE = 5
from std/posix import Timespec, Time, clock_gettime

proc getFastTime*(): times.Time =
when defined(js):
let
millis = newDate().getTime()
seconds = millis div 1_000
nanos = (millis mod 1_000) * 1_000_000
initTime(seconds, nanos)
elif defined(macosx):
var a {.noinit.}: Timeval
gettimeofday(a)
initTime(a.tv_sec.int64, int(a.tv_usec) * 1_000)
elif defined(windows):
var f {.noinit.}: FILETIME
getSystemTimeAsFileTime(f)
let tmp = uint64(f.dwLowDateTime) or (uint64(f.dwHighDateTime) shl 32)
fromWinTime(cast[int64](tmp))
else:
var ts {.noinit.}: Timespec
discard clock_gettime(CLOCK_REALTIME_COARSE, ts)
initTime(int64(ts.tv_sec), int(ts.tv_nsec))

0 comments on commit a28bb97

Please sign in to comment.