loci
Locus (pl. loci): the place where something is situated or occurs; - Merriam-Webster Dictionary
Loci enables the development of parallel programs that can take advantage of machines with multiple processors, cores or hardware threads... but this time for real!
Currently, the loci library only works on Linux and MacOS due to the use of unix channels for communication between loci.
This library attempts to follow the places API very closely. When this is not the case, feel free to report a bug. As opposed to places from the standard library, loci use OS processes instead of OS threads to allow the parallelization of multiple tasks. Up until 16 simultaneous places there’s very little different between loci and places but once you go into 32, 64, 144, 256, etc. core machines then places will break down and stop working properly due to memory allocation inter-thread locking.
1 API Documentation
(require loci) | package: loci |
procedure
procedure
(dynamic-locus module-path start-name) → locus?
module-path : (or/c module-path? path?) start-name : symbol?
The module indicated by module-path must export a function with the name start-name. The function must accept a single argument, which is a locus channel that corresponds to the other end of communication for the locus descriptor returned by locus.
When the locus is created, the initial exit handler terminates the locus, using the argument to the exit handler as the locus’ completion value. Use (exit v) to immediatelly terminate a locus with the completion value v. Since a completion value is limited to an exact integer between 0 and 255, any other value for v is converted to 0.
If the function indicated by module-path and start-name returns, the the locus terminates with the completion value 0.
In the created locus, the current-input-port parameter is set to an empty input port, while the values of the current-output-port and current-error-port parameters are connected to the ports in the creating locus.
syntax
(locus id body ...+)
The generated submodule has the name locus-body-n for an integer n, and the submodule exports a main function that takes a locus channel for the new locus. The submodule is not intended for use, however, except by the expansion of the locus form.
The locus binding is protected in the same way as dynamic-locus. }
syntax
(locus/context id body ...+)
procedure
(locus-wait l) → exact-integer?
l : locus?
If any pumping threads were created to connect a non-file-stream port to the ports in the locus for l (see dynamic-locus), locus-wait returns only when the pumping threads have completed.
procedure
(locus-dead-evt l) → evt?
l : locus?
If any pumping threads were created to connect a non-file-stream port to the ports in the locus for l (see dynamic-locus), the event returned by locus-dead-evt may become ready even if a pumping thread is still running.
procedure
(locus-kill l) → void?
l : locus?
2 Motivation
Given a problem that requires parallelism, where you use all the available cores in your machine, the Racket answer as been places. I happily used places for a long time until I started stressing tests by getting larger and larger machines (up to 144 cores) and starting 144 places simultaneously. I noticed that once I exceeded 12 cores something started to go wrong, in particular the cores were idle most of the time and there was a lot of kernel locking taking place. This triggered me sending a message to the Racket mailing list for help.