Speed up readLine by using getc_unlocked

- Only on POSIX and Windows (_fgetc_nolock here) systems
- File is locked to our thread before and unlocked after readLine
- About 3 times faster in some simple tests
This commit is contained in:
def 2015-03-12 22:46:18 +01:00
commit ef9a3b4d3b

View file

@ -31,6 +31,24 @@ proc fprintf(f: File, frmt: cstring) {.importc: "fprintf",
proc strlen(c: cstring): int {. proc strlen(c: cstring): int {.
importc: "strlen", header: "<string.h>", tags: [].} importc: "strlen", header: "<string.h>", tags: [].}
when defined(posix):
proc getc_unlocked(stream: File): cint {.importc: "getc_unlocked",
header: "<stdio.h>", tags: [ReadIOEffect].}
proc flockfile(stream: File) {.importc: "flockfile", header: "<stdio.h>",
tags: [ReadIOEffect].}
proc funlockfile(stream: File) {.importc: "funlockfile", header: "<stdio.h>",
tags: [ReadIOEffect].}
elif defined(windows):
proc getc_unlocked(stream: File): cint {.importc: "_fgetc_nolock",
header: "<stdio.h>", tags: [ReadIOEffect].}
proc flockfile(stream: File) {.importc: "_lock_file", header: "<stdio.h>",
tags: [ReadIOEffect].}
proc funlockfile(stream: File) {.importc: "_unlock_file", header: "<stdio.h>",
tags: [ReadIOEffect].}
# C routine that is used here: # C routine that is used here:
proc fread(buf: pointer, size, n: int, f: File): int {. proc fread(buf: pointer, size, n: int, f: File): int {.
@ -67,22 +85,40 @@ const
proc raiseEIO(msg: string) {.noinline, noreturn.} = proc raiseEIO(msg: string) {.noinline, noreturn.} =
sysFatal(IOError, msg) sysFatal(IOError, msg)
proc readLine(f: File, line: var TaintedString): bool = when defined(posix) or defined(windows):
# of course this could be optimized a bit; but IO is slow anyway... proc readLine(f: File, line: var TaintedString): bool =
# and it was difficult to get this CORRECT with Ansi C's methods setLen(line.string, 0) # reuse the buffer!
setLen(line.string, 0) # reuse the buffer! flockfile(f)
while true: while true:
var c = fgetc(f) var c = getc_unlocked(f)
if c < 0'i32: if c < 0'i32:
if line.len > 0: break if line.len > 0: break
else: return false else: return false
if c == 10'i32: break # LF if c == 10'i32: break # LF
if c == 13'i32: # CR if c == 13'i32: # CR
c = fgetc(f) # is the next char LF? c = getc_unlocked(f) # is the next char LF?
if c != 10'i32: ungetc(c, f) # no, put the character back if c != 10'i32: ungetc(c, f) # no, put the character back
break break
add line.string, chr(int(c)) add line.string, chr(int(c))
result = true result = true
funlockfile(f)
else:
proc readLine(f: File, line: var TaintedString): bool =
# of course this could be optimized a bit; but IO is slow anyway...
# and it was difficult to get this CORRECT with Ansi C's methods
setLen(line.string, 0) # reuse the buffer!
while true:
var c = fgetc(f)
if c < 0'i32:
if line.len > 0: break
else: return false
if c == 10'i32: break # LF
if c == 13'i32: # CR
c = fgetc(f) # is the next char LF?
if c != 10'i32: ungetc(c, f) # no, put the character back
break
add line.string, chr(int(c))
result = true
proc readLine(f: File): TaintedString = proc readLine(f: File): TaintedString =
result = TaintedString(newStringOfCap(80)) result = TaintedString(newStringOfCap(80))