Threading and paralelism

Hello, I'm new to Racket, but I also use Idris2, which compiles to this language.

Is there a more detailed explanation of Racket's parallelism beyond these resources?

These documents mention that futures can be bottlenecked by certain operations, and that native threads are unsafe for specific tasks. I’d like to understand why this happens. Is it an issue inherent to Racket's runtime system, or is it more about the standard libraries that the official Racket language comes with? Could other languages in the Racket ecosystem (like Rhombus) potentially leverage parallelism more effectively?

For example, the guide mentions:

"Most every arithmetic operation in this example produces an inexact number whose storage must be allocated, and that triggers frequent garbage collections as reflected by dense pink lines, effectively giving the whole graph a pink background. Garbage collection is not necessarily a problem, but since a garbage collection requires synchronization across parallel tasks, it can sometimes limit performance."

Does this still hold true? The guide also states:

"The CS implementation can perform synchronized operations without stopping a future."

Has this improved with recent updates, especially in how Racket CS handles garbage collection and synchronized operations?

Also, does each future in Racket have its own nursery or generation 0 garbage collector? In some newer Scheme implementations like Cyclone Scheme, each thread gets its own nursery. For example, Cyclone uses a generational garbage collector where objects are moved from the first generation (on the stack) to the second generation (on the heap). The application thread that created the object performs this move, and to prevent race conditions, Cyclone automatically relocates objects to the heap before they can be accessed by multiple threads. Does Racket CS implement anything similar for thread safety in concurrent environments?

Reference: Cyclone Scheme User Manual - Multithreaded Programming

1 Like

EDIT : I had forgotten to delete the prompt i used for an LLM to improve the redaction in the comment above. Sorry

1 Like

It's more the libraries, especially the I/O layer. That layer has locks to ensure safety with respect to Racket's threads, but not safe with respect to Chez Scheme / OS threads.

For example, the guide mentions [... garbage collection and synchronization ...] Does this still hold true?

Mostly, yes. It's less of an issue than when the sentence was written, mainly because the GC is itself internally parallelized, but a collection is still a synchronization barrier.

Also, does each future in Racket have its own nursery or generation 0 garbage collector?

Yes. A functional language needs allocation without synchronization to be practical at all. Allocated objects are marked as owned (at an allocation-page granularity) by the creating thread, and that ownership tracking is crucial to parallelism within the GC — so different collecting threads can mostly work on their own data.

I think it's possible for Racket-based languages with different libraries to offer better parallelism than #lang racket. But, as usual with performance when building something in layers, it's complicated. Almost certainly, some new and improved things would be needed at the Racket layer to better support such languages, and the trick is to figure out what those things are.

3 Likes