### Install Weave Development Version Source: https://github.com/mratsim/weave/blob/master/docs/src/2_installation.md Install the latest development version of Weave from the master branch using nimble. ```bash nimble install weave@#master ``` -------------------------------- ### Install Weave Stable Version Source: https://github.com/mratsim/weave/blob/master/docs/src/2_installation.md Use this command to install the latest stable release of Weave via nimble. ```bash nimble install weave ``` -------------------------------- ### Clone Repository and Install Dependencies Source: https://github.com/mratsim/weave/blob/master/demos/raytracing/README.md Clone the Weave repository and install its dependencies using nimble. Ensure you have Nim version 1.2.0 or newer. ```bash git clone https://github.com/mratsim/weave cd weave nimble install -y # install Weave dependencies, here synthesis, overwriting if asked. nim -v # Ensure you have nim 1.2.0 or more recent ``` -------------------------------- ### Parallel Loop with Prologue/Epilogue: `parallelForStaged` Source: https://context7.com/mratsim/weave/llms.txt Enables thread-local setup (`prologue`) and teardown (`epilogue`) around loop iterations, ideal for reductions. Requires `weave` initialization and `sync` on the loop's `Flowvar`. ```nim import weave, std/atomics proc parallelSum(n: int): int64 = var total: Atomic[int64] total.store(0, moRelaxed) let totalPtr = total.addr parallelForStaged i in 0 ..< n: captures: {totalPtr} awaitable: loopFv prologue: var localSum: int64 = 0 loop: localSum += int64(i) epilogue: discard totalPtr[].fetchAdd(localSum, moRelaxed) discard sync(loopFv) result = total.load(moRelaxed) proc main() = init(Weave) let s = parallelSum(1_000_000) exit(Weave) echo "Sum 0..999999 = ", s # Sum 0..999999 = 499999500000 doAssert s == 499_999_500_000 main() ``` -------------------------------- ### Runtime Lifecycle: init and exit Source: https://context7.com/mratsim/weave/llms.txt Starts and stops the Weave runtime. `init` spawns worker threads, and `exit` drains tasks and joins threads. Both can accept an optional auxiliary procedure. ```APIDOC ## Runtime Lifecycle: `init` and `exit` `init(Weave)` starts the runtime, spawning one worker thread per logical CPU core (configurable via `WEAVE_NUM_THREADS`). The calling thread becomes the root thread. `exit(Weave)` drains all pending tasks with an implicit `syncRoot`, signals worker termination, and joins all threads. Both accept an optional auxiliary procedure called once per worker thread on init/exit. ```nim import weave proc heavyCompute(x: int): int = var acc = 0 for i in 0 ..< x * 1000: acc += i acc proc main() = init(Weave) echo "Workers: ", getNumThreads(Weave) # e.g. 8 let fv = spawn heavyCompute(100) let result = sync(fv) echo "Result: ", result # 4999950000 exit(Weave) main() # Compile: nim c --threads:on -d:release main.nim ``` ``` -------------------------------- ### Awaitable Loop Example in Weave Source: https://github.com/mratsim/weave/blob/master/README.md Demonstrates the usage of an awaitable loop in Weave. The loop is awaited, and the thread that spawned it will continue to steal and run other tasks while blocked. `sync` returns true for the last thread to exit the loop. ```Nim import weave init(Weave) # expandMacros: parallelFor i in 0 ..< 10: awaitable: iLoop echo "iteration: ", i let wasLastThread = sync(iLoop) echo wasLastThread exit(Weave) ``` -------------------------------- ### Unacceptable Threaded Output Example Source: https://github.com/mratsim/weave/blob/master/weave/instrumentation/README.md Illustrates an unacceptable interleaving of output from multiple threads, leading to corrupted messages. Ensure thread-safe and atomic output operations to prevent this. ```text TThhrreeaadd #1121:: HHello loW Woorlld! d! ``` -------------------------------- ### Dataflow with Loop Iterations using `newFlowEvent(start, stop, stride)` and `dependsOn` Source: https://context7.com/mratsim/weave/llms.txt Create iteration-indexed `FlowEvent`s to enable pipelined data-parallel processing. Downstream `parallelFor` iterations are triggered independently by matching upstream events. ```nim import weave, os proc main() = init(Weave) let evA = newFlowEvent(0, 10, 1) # iteration-indexed event for 0..9 let evB = newFlowEvent(0, 10, 1) parallelFor i in 0 ..< 10: captures: {evA} sleep(i * 5) evA.trigger(i) echo "A[", i, "] done" parallelFor i in 0 ..< 10: dependsOn: (evA, i) captures: {evB} evB.trigger(i) echo "B[", i, "] done" parallelFor i in 0 ..< 10: dependsOn: (evB, i) echo "C[", i, "] done" syncRoot(Weave) exit(Weave) main() ``` -------------------------------- ### Strided Parallel For Loops Source: https://github.com/mratsim/weave/blob/master/docs/src/3.2_data_parallelism.md Utilize `parallelForStrided` for loops with non-unit strides. This example demonstrates iterating with custom strides and logging matrix indices along with thread IDs. Remember to initialize and exit Weave. ```Nim import weave init(Weave) # expandMacros: parallelForStrided i in 0 ..< 100, stride = 30: parallelForStrided j in 0 ..< 200, stride = 60: captures: {i} log("Matrix[%d, %d] (thread %d) ", i, j, myID()) exit(Weave) ``` -------------------------------- ### Run Raytracer Benchmark Source: https://github.com/mratsim/weave/blob/master/demos/raytracing/README.md Execute the compiled raytracer binaries with 300 samples to generate benchmark results. This command should be run for each compiled executable. ```bash build/ray_threaded 300 # ... build/ray_clang_omp 300 ``` -------------------------------- ### Initialize and Exit Weave Runtime Source: https://context7.com/mratsim/weave/llms.txt Demonstrates the basic lifecycle of the Weave runtime. Ensure compilation with `--threads:on`. ```nim import weave proc heavyCompute(x: int): int = var acc = 0 for i in 0 ..< x * 1000: acc += i acc proc main() = init(Weave) echo "Workers: ", getNumThreads(Weave) # e.g. 8 let fv = spawn heavyCompute(100) let result = sync(fv) echo "Result: ", result # 4999950000 exit(Weave) main() # Compile: nim c --threads:on -d:release main.nim ``` -------------------------------- ### Enable High-Resolution Timers and Profiling Source: https://github.com/mratsim/weave/blob/master/README.md Combine flags for detailed profiling, including high-resolution timers and CPU frequency. Ensure the specified CPU frequency matches your system. ```bash -d:WV_metrics -d:WV_profile -d:CpuFreqMhz=3000 ``` -------------------------------- ### Run Weave in Background Thread Source: https://github.com/mratsim/weave/blob/master/README.md Starts the Weave runtime on a new background thread. This thread will process jobs, balance load, and spin down when idle. The thread exits after the event loop finishes. ```Nim proc runInBackground*( _: typedesc[Weave], signalShutdown: ptr Atomic[bool] ): Thread[ptr Atomic[bool]] = ## Start the Weave runtime on a background thread. ## It wakes-up on job submissions, handles multithreaded load balancing, ## help process tasks ## and spin down when there is no work anymore. proc eventLoop(shutdown: ptr Atomic[bool]) {.thread.} = init(Weave) Weave.runUntil(shutdown) exit(Weave) result.createThread(eventLoop, signalShutdown) ``` -------------------------------- ### Basic Dataflow with `FlowEvent` Source: https://context7.com/mratsim/weave/llms.txt Utilize `FlowEvent`, `newFlowEvent`, `trigger`, and `spawnOnEvent` to create precise producer-consumer pipelines. Tasks are delayed until an event is triggered, avoiding polling or locks. ```nim import weave, os proc stageA(ev: FlowEvent) = echo "A: computing..." sleep(100) ev.trigger() # unblock tasks waiting on ev proc stageB(ev: FlowEvent) = echo "B: triggered after A" sleep(50) ev.trigger() proc stageC() = echo "C: triggered after B" proc main() = init(Weave) let evA = newFlowEvent() let evB = newFlowEvent() spawnOnEvent evB, stageC() # C waits for B spawnOnEvent evA, stageB(evB) # B waits for A spawn stageA(evA) # A runs immediately syncRoot(Weave) exit(Weave) # Output (in order): A: computing... → B: triggered after A → C: triggered after B main() ``` -------------------------------- ### Compile and Run Fibonacci Benchmark with Clang Source: https://github.com/mratsim/weave/blob/master/benchmarks/fibonacci/README.md Compiles the OpenMP Fibonacci benchmark using Clang with optimization and runs the executable, timing its execution. This is used to observe the performance characteristics of Clang's work-stealing scheduler. ```bash clang -O3 -fopenmp benchmarks/fibonacci/omp_fib.c time a.out ``` -------------------------------- ### Weave Runtime Initialization and Exit Source: https://github.com/mratsim/weave/blob/master/README.md Initialize and stop the Weave runtime using init(Weave) and exit(Weave). Forgetting these calls can lead to nil pointer exceptions when using spawn. ```Nim init(Weave) # ... your weave code ... exit(Weave) ``` -------------------------------- ### Get Weave Thread ID and Count Source: https://context7.com/mratsim/weave/llms.txt Retrieves the Weave-assigned ID for the current thread and the total number of worker threads. These functions must be called from within a Weave worker thread. The root thread has an ID of 0. ```nim import weave proc printThreadInfo(dummy: int) = echo "Hello from Weave thread ", getThreadId(Weave), " of ", getNumThreads(Weave) proc main() = init(Weave) # Root thread info echo "Root thread ID: ", getThreadId(Weave) # 0 echo "Total threads: ", getNumThreads(Weave) # e.g. 8 # Spawn on all threads for i in 0 ..< getNumThreads(Weave): spawn printThreadInfo(i) syncRoot(Weave) exit(Weave) main() ``` -------------------------------- ### Experimental Task Parallelism API: submit Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md The `submit` macro is an experimental alternative to `spawn` that guarantees execution on a different thread, suitable for scheduling tasks without blocking the submitter thread. ```APIDOC ## submit (Experimental) ### Description Schedules a procedure call for execution, guaranteeing it runs on a different thread. This is intended as an alternative to `spawn` for scenarios where blocking the submitter thread is undesirable. ### Method Signatures ```Nim macro submit(ex: MyExecutor, procCall: typed{nkCall | nkCommand}): Pending or void ## Executor-local submit macro submit(procCall: typed{nkCall | nkCommand}): Pending or void ## Global submit ``` ### Notes - `submit` was added to Weave to improve interoperability with async event loops. - It guarantees execution on a different thread from the async thread, allowing a thread to submit `Job` to an executor without blocking. ``` -------------------------------- ### Run Weave Event Loop Until Signal Source: https://github.com/mratsim/weave/blob/master/README.md Starts a Weave event loop on the current thread that processes jobs and parks when idle. It continues until the provided shutdown signal becomes true. Ensure the signal pointer is not nil before calling. ```Nim proc runUntil*(_: typedesc[Weave], signal: ptr Atomic[bool]) = ## Start a Weave event loop until signal is true on the current thread. ## It wakes-up on job submission, handles multithreaded load balancing, ## help process tasks ## and spin down when there is no work anymore. preCondition: not signal.isNil while not signal[].load(moRelaxed): processAllandTryPark(Weave) syncRoot(Weave) ``` -------------------------------- ### Run Weave as Background Service Source: https://context7.com/mratsim/weave/llms.txt Starts Weave on a background thread for asynchronous job execution. Client threads can then set up a submitter, dispatch jobs using `submit`, and retrieve results with `waitFor`. Ensure to call `teardownSubmitterThread` and join the client thread before shutting down the Weave background thread. ```nim import weave, std/[atomics, os] proc fib(n: int): int {.gcsafe.} = if n < 2: return n let x = spawn fib(n - 1) let y = fib(n - 2) sync(x) + y proc main() = var shutdown: Atomic[bool] shutdown.store(false, moRelaxed) var weaveThr: Thread[ptr Atomic[bool]] weaveThr.runInBackground(Weave, shutdown.addr) # start Weave on background thread proc clientThread(done: ptr Atomic[bool]) = setupSubmitterThread(Weave) waitUntilReady(Weave) let job1 = submit fib(20) let job2 = submit fib(25) echo "fib(20) = ", waitFor(job1) # 6765 echo "fib(25) = ", waitFor(job2) # 75025 teardownSubmitterThread(Weave) done[].store(true, moRelaxed) var done: Atomic[bool] done.store(false, moRelaxed) var t: Thread[ptr Atomic[bool]] t.createThread(clientThread, done.addr) joinThread(t) shutdown.store(true, moRelaxed) joinThread(weaveThr) main() ``` -------------------------------- ### Naive Bounded Queue Implementation (Nim) Source: https://github.com/mratsim/weave/blob/master/weave/cross_thread_com/channels_spsc.md A single-threaded implementation of a bounded queue using a ring buffer. It includes an extra element to distinguish between full and empty states, which can lead to off-by-one errors and overhead. For multithreading, atomics or locks and padding would be required. ```Nim type BoundedQueue*[N: static int, T] = object head, tail: int buffer: ptr array[N+1, T] # One extra to distinguish between full and empty queues proc bounded_queue_alloc*( T: typedesc, capacity: static int ): BoundedQueue[capacity, T] {.inline.}= result.buffer = cast[ptr array[capacity+1, T]]( # One extra entry to distinguish full and empty queues malloc(T, capacity + 1) ) if result.buffer.isNil: raise newException(OutOfMemError, "bounded_queue_alloc failed") func bounded_queue_free*[N, T](queue: sink BoundedQueue[N, T]) {.inline.}= free(queue.buffer) func bounded_queue_empty*(queue: BoundedQueue): bool {.inline.} = queue.head == queue.tail func bounded_queue_full(queue: BoundedQueue): bool {.inline.} = (queue.tail + 1) mod (queue.N+1) == queue.head func bounded_queue_enqueue*[N,T](queue: var BoundedQueue[N,T], elem: sink T){.inline.} = assert not queue.bounded_queue_full() queue.buffer[queue.tail] = elem queue.tail = (queue.tail + 1) mod (N+1) func bounded_queue_dequeue*[N,T](queue: var BoundedQueue[N,T]): owned T {.inline.} = assert not queue.bounded_queue_empty() result = queue.buffer[queue.head] queue.head = (queue.head + 1) mod (N+1) func bounded_queue_head*[N,T](queue: BoundedQueue[N,T]): lent T {.inline.} = assert not queue.bounded_queue_empty() queue.buffer[queue.head] ``` -------------------------------- ### Global executor spawn in Nim Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md Demonstrates creating a new task on a global executor using a macro. Scheduling is eager and not delayed. ```Nim macro spawn(procCall: typed{nkCall | nkCommand}): FlowVar or void ## Global spawn ``` ```Nim let fut = spawn myFunc(a, b, c) ``` -------------------------------- ### Task Parallelism with Spawn and Sync Source: https://context7.com/mratsim/weave/llms.txt Illustrates recursive task parallelism using `spawn` to dispatch tasks and `sync` to retrieve results. Functions returning `void` can be spawned without a `Flowvar`. ```nim import weave proc fib(n: int): int = if n < 2: return n let x = spawn fib(n - 1) # dispatched to another thread let y = fib(n - 2) # computed on current thread result = sync(x) + y # blocks until x is ready proc main() = init(Weave) let answer = fib(30) exit(Weave) echo "fib(30) = ", answer # fib(30) = 832040 main() ``` -------------------------------- ### Fork-join model in Nim for merge sort Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md Demonstrates the fork-join model using `spawn` and `sync` in Nim for a recursive merge sort. The `spawn` keyword forks a child task, and `sync` joins the result. ```Nim proc mergeSort[T](a, b: var openarray[T]; left, right: int) = if right - left <= 1: return let middle = (left + right) div 2 let fut = spawn mergeSort(a, b, left, middle) # <-- Fork child task # ------------------------------------------------- Everything below is the continuation mergeSort(a, b, middle, right) sync(fut) # <-- Join merge(a, b, left, middle, right) ``` -------------------------------- ### Local executor spawn in Nim Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md Shows how to create a new task on a specific executor using a macro. This is for executor-local spawning. ```Nim macro spawn(ex: MyExecutor, procCall: typed{nkCall | nkCommand}): FlowVar or void ## Executor-local spawn ``` ```Nim # var pool: MyExecutor # ... let fut = pool.spawn myFunc(a, b, c) ``` -------------------------------- ### Dataflow with Multiple Dependencies using `spawnOnEvents` Source: https://context7.com/mratsim/weave/llms.txt Use `spawnOnEvents` to delay a task until all specified events are triggered, enabling fork-join patterns based on data readiness. ```nim import weave, os proc branchLeft(ev: FlowEvent) = echo "Left branch done" sleep(80) ev.trigger() proc branchRight(ev: FlowEvent) = echo "Right branch done" sleep(120) ev.trigger() proc merge() = echo "Merge: both branches finished" proc root(evA: FlowEvent) = echo "Root: splitting" sleep(10) evA.trigger() proc main() = init(Weave) let evRoot = newFlowEvent() let evLeft = newFlowEvent() let evRight = newFlowEvent() spawnOnEvents evLeft, evRight, merge() # merge waits for both spawnOnEvent evRoot, branchRight(evRight) spawnOnEvent evRoot, branchLeft(evLeft) spawn root(evRoot) syncRoot(Weave) exit(Weave) main() ``` -------------------------------- ### Compile and Run Fibonacci Benchmark with GCC Source: https://github.com/mratsim/weave/blob/master/benchmarks/fibonacci/README.md Compiles the OpenMP Fibonacci benchmark using GCC with optimization and runs the executable, timing its execution. This is used to observe the performance characteristics of GCC's single-queue task scheduler. ```bash gcc -O3 -fopenmp benchmarks/fibonacci/omp_fib.c time a.out ``` -------------------------------- ### Data Parallelism with parallelFor Source: https://context7.com/mratsim/weave/llms.txt Shows how to use `parallelFor` for data parallelism. The `captures` clause is required for variables from the enclosing scope. Load balancing is automatic. ```nim import weave proc main() = let N = 1000 var data = newSeq[float64](N) let buf = cast[ptr UncheckedArray[float64]](data[0].addr) init(Weave) parallelFor i in 0 ..< N: captures: {buf} buf[i] = float64(i) * float64(i) # buf[i] = i² syncRoot(Weave) exit(Weave) echo "data[10] = ", data[10] # 100.0 echo "data[31] = ", data[31] # 961.0 main() ``` -------------------------------- ### Global executor spawn with dummy type parameter in Nim Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md Shows how global executors can use a dummy type parameter to refer to their global executor, allowing for a more explicit spawn call. ```Nim macro spawn(_: type MyExecutor, procCall: typed{nkCall | nkCommand}): FlowVar or void ## Global spawn ``` ```Nim let fut = MyExecutor.spawn myFunc(a, b, c) ``` -------------------------------- ### Task Parallelism: spawn and sync Source: https://context7.com/mratsim/weave/llms.txt Asynchronously dispatches function calls using `spawn` and retrieves results using `sync`. `spawn` returns a `Flowvar` handle, and `sync` blocks until the result is available. ```APIDOC ## Task Parallelism: `spawn` and `sync` `spawn fnCall(args)` asynchronously dispatches a function call to any worker thread and returns a `Flowvar[T]` handle. `sync(Flowvar[T])` blocks the calling thread until the result is ready, while the calling thread continues processing other tasks. Functions that return `void` can be spawned without capturing a `Flowvar`. ```nim import weave proc fib(n: int): int = if n < 2: return n let x = spawn fib(n - 1) # dispatched to another thread let y = fib(n - 2) # computed on current thread result = sync(x) + y # blocks until x is ready proc main() = init(Weave) let answer = fib(30) exit(Weave) echo "fib(30) = ", answer # fib(30) = 832040 main() ``` ``` -------------------------------- ### Compile Weave with Profiling Source: https://context7.com/mratsim/weave/llms.txt Enables profiling with CPU frequency information for detailed time breakdown analysis by compiling with `-d:WV_metrics` and `-d:WV_profile`, and specifying the CPU frequency with `-d:CpuFreqMhz`. ```bash # Enable profiling with CPU frequency (shows time breakdown) nim c --threads:on -d:WV_metrics -d:WV_profile -d:CpuFreqMhz=3000 -d:release myprogram.nim ``` -------------------------------- ### Global executor spawn with module prefix in Nim Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md Illustrates disambiguating between global executors by prefixing the module name. This ensures the correct global executor is used. ```Nim let fut = threadpool.spawn myFunc(a, b, c) ``` -------------------------------- ### Batched Non-blocking Send for Channels Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md Sends a linked list of items to the back of the channel. Returns true if successful, indicating the channel had enough free slots. The items should be linked by their 'next' field. ```Nim proc trySendBatch[T](chan: var Chan, bFirst, bLast: sink Isolated[T], count: SomeInteger): bool = ## Send a linked list of items to the back of the channel ## They should be linked together by their next field. ## `count` refer to the number of items in the list. ## Returns true if successful (channel had enough free slots) ``` -------------------------------- ### Compile Weave with Runtime Metrics Source: https://context7.com/mratsim/weave/llms.txt Enables the collection of runtime metrics, such as executed tasks and steal requests, by compiling with the `-d:WV_metrics` flag. ```bash # Enable runtime metrics (tasks executed, steal requests, etc.) nim c --threads:on -d:WV_metrics -d:release myprogram.nim ``` -------------------------------- ### Compile Raytracer Binaries Source: https://github.com/mratsim/weave/blob/master/demos/raytracing/README.md Compile the raytracer executable using Nim with and without threads enabled. Also compiles C++ versions using GCC and Clang with different optimization and OpenMP flags. ```bash # Threads on (by default in this repo) nim c -d:danger -o:build/ray_threaded demos/raytracing/smallpt.nim # Threads off nim c -d:danger --threads:off -o:build/ray_single demos/raytracing/smallpt.nim g++ -O3 -o build/ray_gcc_single demos/raytracing/smallpt.cpp g++ -O3 -fopenmp -o build/ray_gcc_omp demos/raytracing/smallpt.cpp clang++ -O3 -o build/ray_clang_single demos/raytracing/smallpt.cpp clang++ -O3 -fopenmp -o build/ray_clang_omp demos/raytracing/smallpt.cpp ``` -------------------------------- ### Global Barrier Synchronization: `syncRoot` Source: https://context7.com/mratsim/weave/llms.txt Blocks the root thread until all tasks and parallel loops complete. `exit(Weave)` implicitly calls `syncRoot`. Ensure `weave` is initialized. ```nim import weave var results: array[4, int] proc computeSlot(idx: int) = results[idx] = idx * idx * 100 proc main() = init(Weave) for i in 0 ..< 4: spawn computeSlot(i) syncRoot(Weave) # wait for all spawned tasks for i, v in results: echo "results[", i, "] = ", v # 0, 100, 400, 900 exit(Weave) main() ``` -------------------------------- ### Recursive Fibonacci with Task Parallelism Source: https://github.com/mratsim/weave/blob/master/docs/src/3.1_task_parallelism.md Implement a recursive Fibonacci calculation using Weave's `spawn` and `sync` for task parallelism. Ensure Weave is initialized before use and exited afterward. ```Nim import weave 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 n = 20 init(Weave) let f = fib(n) exit(Weave) echo f main() ``` -------------------------------- ### Batched Non-blocking Receive from Channels Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md Attempts to receive all items buffered in the channel, returning them as a linked list. Returns the number of items received. If no items are returned, bFirst and bLast are undefined. ```Nim proc tryRecvBatch[T](chan: var Chan, bFirst, bLast: var Isolated[T]): int = ## Try receiving all items buffered in the channel ## Returns true if at least some items are dequeued. ## There might be competition with producers for the last item ## ## Items are returned as a linked list ## Returns the number of items received ## ## If no items are returned bFirst and bLast are undefined ## and should not be used. ## ## The `next` field in `bLast` is undefined. ## nil or overwrite it for further use in linked lists ``` -------------------------------- ### Assembly for Modulo Operation Source: https://github.com/mratsim/weave/blob/master/weave/cross_thread_com/channels_spsc.md Assembly code demonstrating a modulo operation with a precondition. It uses conditional moves and bitwise operations to achieve the result efficiently. ```Assembly ; precondition: 0 <= x <= N ; (N is constant) mov y, 0 ; set up mask cmp x, N-1 ; set carry flag if x >= N sbb y, 0 ; subtract 1 from y if carry flag set and x, y ; set x to zero if x == N ``` -------------------------------- ### Modulo Arithmetic for Index Wrapping Source: https://github.com/mratsim/weave/blob/master/weave/cross_thread_com/channels_spsc.md Demonstrates Nim code for wrapping tail index within capacity, suitable for hardware prediction. Avoids direct subtraction for potential performance gains. ```Nim chan.tail += if chan.tail + 1 >= Capacity: 1 - Capacity else: 1 ``` -------------------------------- ### Enable Runtime Statistics in Weave Source: https://github.com/mratsim/weave/blob/master/README.md Use the -d:WV_metrics flag to access low-level runtime statistics like task execution counts and steal requests. ```bash nim c -d:WV_metrics main.nim ``` -------------------------------- ### spawn: scheduling left to the executor Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md The `spawn` macro is used to eagerly add a task to an executor for execution. It can be used with a local executor or a global executor. ```APIDOC ## `spawn` macro ### Description Eagerly adds a task to the executor for execution as soon as an execution resource is available. Scheduling is not delayed. ### Signature (Local Executor) ```nim macro spawn(ex: MyExecutor, procCall: typed{nkCall | nkCommand}): FlowVar or void ``` ### Example (Local Executor) ```nim let fut = pool.spawn myFunc(a, b, c) ``` ### Signature (Global Executor) ```nim macro spawn(procCall: typed{nkCall | nkCommand}): FlowVar or void ``` ### Example (Global Executor) ```nim let fut = spawn myFunc(a, b, c) ``` ### Example (Global Executor with Module Prefix) ```nim let fut = threadpool.spawn myFunc(a, b, c) ``` ### Signature (Global Executor with Dummy Type Parameter) ```nim macro spawn(_: type MyExecutor, procCall: typed{nkCall | nkCommand}): FlowVar or void ``` ### Example (Global Executor with Dummy Type Parameter) ```nim let fut = MyExecutor.spawn myFunc(a, b, c) ``` ``` -------------------------------- ### Parallel Reduction with Implicit Synchronization in Weave Source: https://github.com/mratsim/weave/blob/master/README.md Demonstrates Weave's parallel reduction construct, which simplifies synchronization by using `sync(Flowvar)` internally. It allows for thread-local computations that are merged into a final result without explicit atomic operations. ```Nim proc sumReduce(n: int): int = var waitableSum: Flowvar[int] # expandMacros: parallelReduceImpl i in 0 .. n, stride = 1: reduce(waitableSum): prologue: var localSum = 0 fold: localSum += i merge(remoteSum): localSum += sync(remoteSum) return localSum result = sync(waitableSum) init(Weave) let sum1M = sumReduce(1000000) echo "Sum reduce(0..1000000): ", sum1M doAssert sum1M == 500_000_500_000 exit(Weave) ``` -------------------------------- ### Experimental Task Parallelism API (Nim) Source: https://github.com/mratsim/weave/blob/master/rfcs/multithreading_apis.md Defines an experimental API for task parallelism where scheduling must not block the submitter thread. It provides `submit` for executor-local or global task submission, returning a `Pending` object or void. ```Nim macro submit(ex: MyExecutor, procCall: typed{nkCall | nkCommand}): Pending or void ## Executor-local submit macro submit(procCall: typed{nkCall | nkCommand}): Pending or void ## Global submit ```