Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions bench/algorithm/coro-prime-sieve/4.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import chronos
import std/os
import std/strutils



##
## # Channel[T]
##
## I craeted a very simple async-awaitable channel type so I can match the
## reference Go implementation this benchmark originated from.
##


type Channel[T] = object
## An simple one-item, async-awaitable Channel.
untilIsEmpty: Future[void]
untilIsFull: Future[void]
val: T


proc newChannel[T](): ref Channel[T] =
## Initializer. Allocate a new ref Channel object on the heap.
result = new Channel[T]
result[].untilIsEmpty = newFuture[void]()
result[].untilIsFull = newFuture[void]()
result[].untilIsEmpty.complete()


proc send(chan: ref Channel[int], val: int) {.async.} =
# Accept val if empty, otherwise, suspend until empty.
await chan[].untilIsEmpty
chan[].untilIsEmpty = newFuture[void]()
chan[].val = val
chan[].untilIsFull.complete()


proc recv[T](chan: ref Channel[T]): Future[T] {.async.} =
# Return held val if full, otherwise, suspend until full.
await chan[].untilIsFull
chan[].untilIsFull = newFuture[void]()
result = chan[].val
chan[].untilIsEmpty.complete()



##
## # Benchmark
##
## Below, "Concurrent Prime Sieve" that matches Go reference implementation.
##
## [X] Uses coroutines.
## [X] Uses a coroutine scheduler.
## [X] Uses an async channel for communitating between coroutines.
## [X] Same 3 functions, structured like the reference.
##


proc generate(chan: ref Channel[int]) {.async.} =
## Send the sequence 2, 3, 4, ... to cannel `chan`.
for i in 2 .. int.high:
await chan.send(i)


proc filter(inChan, outChan: ref Channel[int], prime: int) {.async.} =
## Copy the values from channel `inChan` to channel `outChan`, removing those
## divisible by `prime`.
while true:
let i = await inChan.recv() # revieve value from `inChan`
if i mod prime != 0:
await outChan.send(i) # send `i` to `outChan`


proc main(n: int) {.async.} =
## The prime sieve: Daisy-chain filter processes.
var firstChan = newChannel[int]() # craete a new channel
asyncCheck generate(firstChan) # launch generate coroutine
for i in 0 ..< n:
let prime = await firstChan.recv()
echo prime
var secondChan = newChannel[int]()
asyncCheck filter(firstChan, secondChan, prime)
firstChan = secondChan


when isMainModule:

let n = if paramCount() > 0: parseInt(paramStr(1)) else: 100
waitFor main(n)


43 changes: 43 additions & 0 deletions bench/algorithm/coro-prime-sieve/5.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import chronos
import std/os
import std/strutils







proc generate(chan: AsyncQueue[int]) {.async.} =
## Send the sequence 2, 3, 4, ... to cannel `chan`.
for i in 2 .. int.high:
await chan.addFirst(i)


proc filter(inChan, outChan: AsyncQueue[int], prime: int) {.async.} =
## Copy the values from channel `inChan` to channel `outChan`, removing those
## divisible by `prime`.
while true:
let i = await inChan.popLast() # revieve value from `inChan`
if i mod prime != 0:
await outChan.addFirst(i) # send `i` to `outChan`


proc main(n: int) {.async.} =
## The prime sieve: Daisy-chain filter processes.
var firstChan = newAsyncQueue[int](1) # craete a new channel
asyncCheck generate(firstChan) # launch generate coroutine
for i in 0 ..< n:
let prime = await firstChan.popLast()
echo prime
var secondChan = newAsyncQueue[int](1)
asyncCheck filter(firstChan, secondChan, prime)
firstChan = secondChan


when isMainModule:

let n = if paramCount() > 0: parseInt(paramStr(1)) else: 100
waitFor main(n)


83 changes: 83 additions & 0 deletions bench/algorithm/coro-prime-sieve/6.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import yasync
import chronos
import std/os
import std/strutils


##
## Channel implementation copied from the `yasync` repo, testcase `test7.nim`
##

type
Channel[T] = object
waitingCont: ptr Cont[T]
sendingCont: ptr Cont[void]
val: T

proc send[T](c: var Channel[T], v: T, env: ptr Cont[void]) {.asyncRaw.} =
doAssert(c.sendingCont == nil, "Too many senders")
if c.waitingCont == nil:
c.val = v
c.sendingCont = env
else:
let cont = c.waitingCont
c.waitingCont = nil
cont.complete(v)
env.complete()

proc recv[T](c: var Channel[T], env: ptr Cont[T]) {.asyncRaw.} =
doAssert(c.waitingCont == nil, "Too many receivers")
if c.sendingCont == nil:
c.waitingCont = env
else:
let cont = c.sendingCont
c.sendingCont = nil
env.complete(c.val)
cont.complete()

proc newChannel[T](): ref Channel[T] =
new(result)


##
## # Benchmark
##
## Below, "Concurrent Prime Sieve" that matches Go reference implementation.
##
## [X] Uses coroutines.
## [X] Uses a coroutine scheduler.
## [X] Uses an async channel for communitating between coroutines.
## [X] Same 3 functions, structured like the reference.
##

proc generate(chan: ref Channel[int]) {.yasync.async.} =
## Send the sequence 2, 3, 4, ... to cannel `chan`.
for i in 2 .. int.high:
await chan[].send(i)

proc filter(inChan, outChan: ref Channel[int], prime: int) {.yasync.async.} =
## Copy the values from channel `inChan` to channel `outChan`, removing those
## divisible by `prime`.
while true:
let i = await inChan[].recv() # revieve value from `inChan`
if i mod prime != 0:
await outChan[].send(i) # send `i` to `outChan`

proc main(n: int) {.yasync.async.} =
## The prime sieve: Daisy-chain filter processes.
var firstChan = newChannel[int]() # craete a new channel
discard generate(firstChan) # launch generate coroutine
for i in 0 ..< n:
let prime = await firstChan[].recv()
echo prime
var secondChan = newChannel[int]()
discard filter(firstChan, secondChan, prime)
firstChan = secondChan

when isMainModule:

let n = if paramCount() > 0: parseInt(paramStr(1)) else: 100
var env: asyncCallEnvType(main(n))
asyncLaunchWithEnv(env, main(n))


10 changes: 8 additions & 2 deletions bench/bench_nim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ problems:
source:
- 1.nim
- 3.nim
- 5.nim
- 6.nim
- name: lru
source:
- 1.nim
Expand Down Expand Up @@ -60,7 +62,9 @@ environments:
include: nim
include_sub_dir:
before_build:
build: nimble build app -y --mm:orc -d:danger --panics:on -d:nimCoroutines --threads:on --tlsEmulation:off --verbose
- nimble refresh
- nimble install --depsOnly
build: nimble cpp app -y --mm:orc -d:danger --panics:on -d:nimCoroutines --threads:on --tlsEmulation:off --passC=-march=native --passC=-mtune=native --passC=-flto --passC=-fwhole-program --passC=-ffp-contract=off --verbose
after_build:
- cp app out
out_dir: out
Expand All @@ -72,7 +76,9 @@ environments:
include: nim
include_sub_dir:
before_build:
build: nimble build app -y --mm:orc --cc:clang -d:danger --panics:on -d:nimCoroutines --threads:on --tlsEmulation:off --verbose
- nimble refresh
- nimble install --depsOnly
build: nimble build app -y --mm:orc --cc:clang -d:danger --panics:on -d:nimCoroutines --threads:on --tlsEmulation:off --verbose
after_build:
- cp app out
out_dir: out
Expand Down
2 changes: 2 additions & 0 deletions bench/include/nim/app.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ bin = @["app"]
# Dependencies

requires "nim >= 2.0.0"
requires "chronos"
requires "yasync"
Loading