Skip to content

Add async solution to Nim #482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 13, 2025
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
93 changes: 93 additions & 0 deletions bench/algorithm/coro-prime-sieve/3.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import std/asyncdispatch
import std/os
import std/strutils



##
## # Channel[T]
##
## As far as I know Nim's asyncdispatch has many features but lacks an awaitable
## channel type. It uses Futures as the work horse of async communication instead.
##
## 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[T](chan: ref Channel[T], val: T) {.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)


1 change: 1 addition & 0 deletions bench/bench_nim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ problems:
- name: coro-prime-sieve
source:
- 1.nim
- 3.nim
- name: lru
source:
- 1.nim
Expand Down
Loading