Skip to content

Commit 463e974

Browse files
author
Kevin Lewis
committed
Add another async solution to Nim
1 parent dc63102 commit 463e974

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import chronos
2+
import std/os
3+
import std/strutils
4+
5+
6+
7+
##
8+
## # Channel[T]
9+
##
10+
## I craeted a very simple async-awaitable channel type so I can match the
11+
## reference Go implementation this benchmark originated from.
12+
##
13+
14+
15+
type Channel[T] = object
16+
## An simple one-item, async-awaitable Channel.
17+
untilIsEmpty: Future[void]
18+
untilIsFull: Future[void]
19+
val: T
20+
21+
22+
proc newChannel[T](): ref Channel[T] =
23+
## Initializer. Allocate a new ref Channel object on the heap.
24+
result = new Channel[T]
25+
result[].untilIsEmpty = newFuture[void]()
26+
result[].untilIsFull = newFuture[void]()
27+
result[].untilIsEmpty.complete()
28+
29+
30+
proc send(chan: ref Channel[int], val: int) {.async.} =
31+
# Accept val if empty, otherwise, suspend until empty.
32+
await chan[].untilIsEmpty
33+
chan[].untilIsEmpty = newFuture[void]()
34+
chan[].val = val
35+
chan[].untilIsFull.complete()
36+
37+
38+
proc recv[T](chan: ref Channel[T]): Future[T] {.async.} =
39+
# Return held val if full, otherwise, suspend until full.
40+
await chan[].untilIsFull
41+
chan[].untilIsFull = newFuture[void]()
42+
result = chan[].val
43+
chan[].untilIsEmpty.complete()
44+
45+
46+
47+
##
48+
## # Benchmark
49+
##
50+
## Below, "Concurrent Prime Sieve" that matches Go reference implementation.
51+
##
52+
## [X] Uses coroutines.
53+
## [X] Uses a coroutine scheduler.
54+
## [X] Uses an async channel for communitating between coroutines.
55+
## [X] Same 3 functions, structured like the reference.
56+
##
57+
58+
59+
proc generate(chan: ref Channel[int]) {.async.} =
60+
## Send the sequence 2, 3, 4, ... to cannel `chan`.
61+
for i in 2 .. int.high:
62+
await chan.send(i)
63+
64+
65+
proc filter(inChan, outChan: ref Channel[int], prime: int) {.async.} =
66+
## Copy the values from channel `inChan` to channel `outChan`, removing those
67+
## divisible by `prime`.
68+
while true:
69+
let i = await inChan.recv() # revieve value from `inChan`
70+
if i mod prime != 0:
71+
await outChan.send(i) # send `i` to `outChan`
72+
73+
74+
proc main(n: int) {.async.} =
75+
## The prime sieve: Daisy-chain filter processes.
76+
var firstChan = newChannel[int]() # craete a new channel
77+
asyncCheck generate(firstChan) # launch generate coroutine
78+
for i in 0 ..< n:
79+
let prime = await firstChan.recv()
80+
echo prime
81+
var secondChan = newChannel[int]()
82+
asyncCheck filter(firstChan, secondChan, prime)
83+
firstChan = secondChan
84+
85+
86+
when isMainModule:
87+
88+
let n = if paramCount() > 0: parseInt(paramStr(1)) else: 100
89+
waitFor main(n)
90+
91+

bench/bench_nim.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ problems:
3333
source:
3434
- 1.nim
3535
- 3.nim
36+
- 4.nim
3637
- name: lru
3738
source:
3839
- 1.nim
@@ -60,7 +61,7 @@ environments:
6061
include: nim
6162
include_sub_dir:
6263
before_build:
63-
build: nimble build app -y --mm:orc -d:danger --panics:on -d:nimCoroutines --threads:on --tlsEmulation:off --verbose
64+
build: nimble build app -y --mm:orc -d:danger --panics:on -d:nimCoroutines --threads:on --tlsEmulation:off --passC="-march=native -mtune=native -flto -fwhole-program" --verbose
6465
after_build:
6566
- cp app out
6667
out_dir: out

bench/include/nim/app.nimble

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ bin = @["app"]
1010
# Dependencies
1111

1212
requires "nim >= 2.0.0"
13+
requires "chronos"

0 commit comments

Comments
 (0)