@mratsim Nice!
Would this be beneficial for regular peasants like me how are just writing web apps with Nim?
Would swapping the 'regular' Nim runtime with Weave have any advantage?
Would swapping the default Nim runtime with Weave have any advantage?
I think this is currently not really possible as Weave is rather low level and doesn't support ref yet. I hope once https://github.com/nim-lang/RFCs/issues/244 matures Weave can support ref.
I love weave but I think the CPU usage is still way higher then threadpool native for my needs. The lowest CPU usage I could get in this contrived example below is 7.3% for weave and 0.09% for threadpool.
proc fib(n: int): int =
# int64 on x86-64
if n < 2:
return n
let x = spawn fib(n-1)
let y = fib(n-2)
result = sync(x) + y
proc main()=
var
sel = newSelector[int]()
sel.registerTimer(1000 * 5,false,0)
init(Weave)
while true:
var ev = sel.select(100)
for e in ev:
if Event.Timer in e.events:
discard spawn fib(rand(10))
getMem()
loadBalance(Weave)
if stop: break
echo "done"
exit(Weave)
main()
teroz@wolverine:~$ nim --version
Nim Compiler Version 1.2.6 [Linux: amd64]
Compiled at 2020-07-29
Copyright (c) 2006-2020 by Andreas Rumpf
active boot switches: -d:release
teroz@wolverine: $ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.1 LTS
Release: 20.04
Codename: focal
I recently converted my threadpool + async (asynctools) tool to use just threadpool or weave cause asynctools no longer builds for me.
I notice that weave is certainly slower than threadpool - at least 20-30% slower.
Build:
git clone https://github.com/genotrance/autodup
cd autodup
nimble install -d
nimble install weave@#head
nim c -d:danger autodup
# OR
nim c -d:danger -d:weave autodup
Simple test to find duplicate files:
autodup -D path/to/dir
I'm also seeing an out of memory error with weave for larger searches which doesn't occur with threadpool.
True a busier CPU is better. Though the timer in there pretty much assures that the application is not CPU limited and the extra CPU cycles points to needless spinning threads. I have edited the examples to have the application complete a fixed piece of work. The thread-pool example is marginally slower
import selectors,posix,random,os,weave,times
var
stop:bool
start:times.Time
onSignal(SIGINT):
echo "Exiting: ", sig
stop = true
proc getMem()=
var ru:Rusage
getrusage(RusageSelf, ru.addr)
var
utime = ru.ru_utime.tv_sec.float + (1.0/1000000) * ru.ru_utime.tv_usec.float
stime = ru.ru_stime.tv_sec.float + (1.0/1000000) * ru.ru_stime.tv_usec.float
cpu = ((utime + stime)/(getTime() - start).seconds.float) * 100
echo "utime: ",utime
echo "stime: ",stime
echo "cpu: ",cpu
proc fib(n: int): int =
# int64 on x86-64
if n < 2:
return n
let x = spawn fib(n-1)
let y = fib(n-2)
result = sync(x) + y
proc main()=
var
sel = newSelector[int]()
x = 0
pool:seq[Flowvar[int]]
sel.registerTimer(500,false,0)
init(Weave)
start = getTime()
while true:
var ev = sel.select(-1)
for e in ev:
if Event.Timer in e.events:
pool.add(spawn fib(x))
getMem()
inc x
loadBalance(Weave)
if x == 10:
syncRoot(Weave)
break
if stop:break
echo "done in ",getTime() - start
exit(Weave)
main()
It's mentioned in the README: https://github.com/mratsim/weave#weave-using-all-cpus
Weave using all CPUs
Weave multithreading is cooperative, idle threads send steal requests instead of actively stealing in other workers queue. This is called "work-requesting" in the literature as opposed to "work-stealing".
This means that a thread sleeping or stuck in a long computation may starve other threads and they will spin burning CPU cycles.
Don't sleep or block a thread as this blocks Weave scheduler. This is a similar to async/await libraries.
If you really need to sleep or block the root thread, make sure to empty all the tasks beforehand with syncRoot(Weave) in the root thread. The child threads will be put to sleep until new tasks are spawned.
The loadBalance(Weave) call can be used in the middle of heavy computations to force the worker to answer steal requests. This is automatically done in parallelFor loops. loadBalance(Weave) is a very fast call that makes a worker thread checks its queue and dispatch its pending tasks to others. It does not block.
We call the root thread the thread that called init(Weave)
select will block the root Weave threads which then cannot put the child threads to sleep. You should never block the root Weave thread.