Don't import/export chronos by default (#20)

Chronos support is optional and should not have to be imported in order
to use faststreams for non-async use cases, as doing so pollutes the
global namespace and slows down compilation.

`async` support must now explicitly be enabled with
-d:async_backend=chronos|asyncdispatch
This commit is contained in:
Jacek Sieka 2021-08-20 13:10:26 +02:00 • committed by GitHub
commit 3a0ab42573
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 841 additions and 705 deletions

View file

@ -1,128 +1,136 @@
{.used.}
import
# Std lib:
std/[strutils, random, base64, terminal],
# Other packages:
testutils/unittests,
# FastStreams modules:
../faststreams/[pipelines, multisync],
# Testing modules:
./base64 as fsBase64
../faststreams/[pipelines, multisync]
include system/timers
when fsAsyncSupport:
import
# Std lib:
std/[strutils, random, base64, terminal],
# FastStreams modules:
../faststreams/[pipelines, multisync],
# Testing modules:
./base64 as fsBase64
type
TestTimes = object
fsPipeline: Nanos
fsAsyncPipeline: Nanos
stdFunctionCalls: Nanos
include system/timers
proc upcaseAllCharacters(i: InputStream, o: OutputStream) {.fsMultiSync.} =
let inputLen = i.len
if inputLen.isSome:
o.ensureRunway inputLen.get
type
TestTimes = object
fsPipeline: Nanos
fsAsyncPipeline: Nanos
stdFunctionCalls: Nanos
while i.readable:
o.write toUpperAscii(i.read.char)
proc upcaseAllCharacters(i: InputStream, o: OutputStream) {.fsMultiSync.} =
let inputLen = i.len
if inputLen.isSome:
o.ensureRunway inputLen.get
close o
while i.readable:
o.write toUpperAscii(i.read.char)
proc printTimes(t: TestTimes) =
styledEcho " cpu time [FS Sync ]: ", styleBright, $t.fsPipeline, "ms"
styledEcho " cpu time [FS Async ]: ", styleBright, $t.fsAsyncPipeline, "ms"
styledEcho " cpu time [Std Lib ]: ", styleBright, $t.stdFunctionCalls, "ms"
close o
template timeit(timerVar: var Nanos, code: untyped) =
let t0 = getTicks()
code
timerVar = int(getTicks() - t0) div 1000000
proc printTimes(t: TestTimes) =
styledEcho " cpu time [FS Sync ]: ", styleBright, $t.fsPipeline, "ms"
styledEcho " cpu time [FS Async ]: ", styleBright, $t.fsAsyncPipeline, "ms"
styledEcho " cpu time [Std Lib ]: ", styleBright, $t.stdFunctionCalls, "ms"
proc getOutput(sp: AsyncInputStream, T: type string): Future[string] {.async.} =
# this proc is a quick hack to let the test pass
# do not use it in production code
let size = sp.totalUnconsumedBytes()
if size > 0:
var data = newSeq[byte](size)
discard sp.readinto(data)
result = cast[string](data)
template timeit(timerVar: var Nanos, code: untyped) =
let t0 = getTicks()
code
timerVar = int(getTicks() - t0) div 1000000
procSuite "pipelines":
let loremIpsum = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
proc getOutput(sp: AsyncInputStream, T: type string): Future[string] {.async.} =
# this proc is a quick hack to let the test pass
# do not use it in production code
let size = sp.totalUnconsumedBytes()
if size > 0:
var data = newSeq[byte](size)
discard sp.readinto(data)
result = cast[string](data)
"""
suite "pipelines":
const loremIpsum = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
test "upper-case/base64 pipeline benchmark":
var
times: TestTimes
stdRes: string
fsRes: string
fsAsyncRes: string
"""
let inputText = loremIpsum.repeat(5000)
test "upper-case/base64 pipeline benchmark":
var
times: TestTimes
stdRes: string
fsRes: string
fsAsyncRes: string
timeIt times.stdFunctionCalls:
stdRes = base64.decode(base64.encode(toUpperAscii(inputText)))
let inputText = loremIpsum.repeat(5000)
timeIt times.fsPipeline:
fsRes = executePipeline(unsafeMemoryInput(inputText),
timeIt times.stdFunctionCalls:
stdRes = base64.decode(base64.encode(toUpperAscii(inputText)))
timeIt times.fsPipeline:
fsRes = executePipeline(unsafeMemoryInput(inputText),
upcaseAllCharacters,
base64encode,
base64decode,
getOutput string)
timeIt times.fsAsyncPipeline:
fsAsyncRes = waitFor executePipeline(Async unsafeMemoryInput(inputText),
upcaseAllCharacters,
base64encode,
base64decode,
getOutput string)
check fsAsyncRes == stdRes
check fsRes == stdRes
printTimes times
asyncTest "upper-case/base64 async pipeline":
let pipe = asyncPipe()
let inputText = repeat(loremIpsum, 100)
proc pipeFeeder(s: AsyncOutputStream) {.gcsafe, async.} =
randomize 1234
var pos = 0
while pos != inputText.len:
let bytesToWrite = rand(15)
if bytesToWrite == 0:
s.write inputText[pos]
inc pos
else:
let endPos = min(pos + bytesToWrite, inputText.len)
s.writeAndWait inputText[pos ..< endPos]
pos = endPos
let sleep = rand(50) - 45
if sleep > 0:
await sleepAsync(sleep.milliseconds)
close s
asyncCheck pipeFeeder(pipe.initWriter)
let f = executePipeline(pipe.initReader,
upcaseAllCharacters,
base64encode,
base64decode,
getOutput string)
timeIt times.fsAsyncPipeline:
fsAsyncRes = waitFor executePipeline(Async unsafeMemoryInput(inputText),
upcaseAllCharacters,
base64encode,
base64decode,
getOutput string)
let fsAsyncres = await f
check fsAsyncRes == stdRes
check fsRes == stdRes
printTimes times
asyncTest "upper-case/base64 async pipeline":
let pipe = asyncPipe()
let inputText = repeat(loremIpsum, 100)
proc pipeFeeder(s: AsyncOutputStream) {.gcsafe, async.} =
randomize 1234
var pos = 0
while pos != inputText.len:
let bytesToWrite = rand(15)
if bytesToWrite == 0:
s.write inputText[pos]
inc pos
else:
let endPos = min(pos + bytesToWrite, inputText.len)
s.writeAndWait inputText[pos ..< endPos]
pos = endPos
let sleep = rand(50) - 45
if sleep > 0:
await sleepAsync(sleep.milliseconds)
close s
asyncCheck pipeFeeder(pipe.initWriter)
let f = executePipeline(pipe.initReader,
upcaseAllCharacters,
base64encode,
base64decode,
getOutput string)
let fsAsyncres = await f
check fsAsyncRes == toUpperAscii(inputText)
check fsAsyncRes == toUpperAscii(inputText)
else:
test "pipelines":
skip