diff --git a/README.md b/README.md index 8d6d955aa..92a26ea37 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,41 @@ # MPL -MaPLe (MPL) is an extension of the [MLton](http://mlton.org) -compiler for Standard ML which implements support for -nested (fork-join) parallelism. MPL generates executables with -excellent multicore performance, utilizing a novel approach to -memory management based on the theory of disentanglement +MPL-EM (MPL\*) is a compiler for standard ML that implements +support for nested (fork-join) parallelism. MPL-EM generates executables with +excellent multicore performance because it utilizes the theory of disentanglement +to manage memory of parallel programs. [[1](#rmab16),[2](#gwraf18),[3](#wyfa20),[4](#awa21),[5](#waa22)]. +It builds on the [MPL](https://github.com/MPLLang/mpl) compiler. -MPL is research software and is being actively developed. +Roughly speaking, the disentanglement property states that an object allocated +by a thread may not be shared with a concurrently executing thread. +MPL-EM exploits disentanglement at the granularity of memory objects. Specifically, +it distinguishes between disentangled and entangled objects and handles disentangled objects very efficiently, +while incurring modest overhead for entangled objects, which are rare. -If you are you interested in using MPL, consider checking -out the [tutorial](https://github.com/MPLLang/mpl-tutorial). -You might also be interested in exploring -[`mpllib`](https://github.com/MPLLang/mpllib) -(a library for MPL) and the -[Parallel ML benchmark suite](https://github.com/MPLLang/parallel-ml-bench). -## Docker - -Try out MPL with Docker: -``` -$ docker pull shwestrick/mpl -$ docker run -it shwestrick/mpl /bin/bash -...# examples/bin/primes @mpl procs 4 -- -``` - -If you want to try out MPL by writing and compiling your own code, we recommend -mounting a local directory inside the container. For example, here's how you -can use MPL to compile and run your own `main.mlb` in the current directory. -(To mount some other directory, replace `$(pwd -P)` with a different path.) -``` -$ ls -main.mlb -$ docker run -it -v $(pwd -P):/root/mycode shwestrick/mpl /bin/bash -...# cd /root/mycode -...# mpl main.mlb -...# ./main @mpl procs 4 -- -``` +We note, for the artifact reviewers, that MPL does not support entanglement but MPL-EM (the language presented in the paper) does. We are in the process of merging MPL-EM and MPL. In the meantime, we have two separate branches within the same [repo](https://github.com/MPLLang/mpl). MPL is at the master branch and MPL-EM is at pldi23-artifact. ## Build and Install (from source) +MPL has only been tested on Linux with x86-64. We list the compilation instructions +and the software requirements below. We also provide a Dockerfile that +you can use to build a docker container, +or just to get commands and references to the software requirements. + ### Requirements -MPL has only been tested on Linux with x86-64. The following software is +The following software is required. * [GCC](http://gcc.gnu.org) * [GMP](http://gmplib.org) (GNU Multiple Precision arithmetic library) * [GNU Make](http://savannah.gnu.org/projects/make), [GNU Bash](http://www.gnu.org/software/bash/) - * binutils (`ar`, `ranlib`, `strip`, ...) - * miscellaneous Unix utilities (`diff`, `find`, `grep`, `gzip`, `patch`, `sed`, `tar`, `xargs`, ...) + * binutils (`ar`, `ranlib`, `strip`) * Standard ML compiler and tools: - Recommended: [MLton](http://mlton.org) (`mlton`, `mllex`, and `mlyacc`). Pre-built binary packages for MLton can be installed via an OS package manager or (for select platforms) obtained from http://mlton.org. - Supported but not recommended: [SML/NJ](http://www.smlnj.org) (`sml`, `ml-lex`, `ml-yacc`). + * RECOMMENDED: miscellaneous utilities (`git`, `vim`, `time`, `numactl`, `curl`, `jq`, `zip`) ### Instructions @@ -73,10 +56,9 @@ $ make PREFIX=/opt/mpl install ## Parallel and Concurrent Extensions MPL extends SML with a number of primitives for parallelism and concurrency. -Take a look at `examples/` to see these primitives in action. +To directly run some examples, +please read `examples/README.md` which provides instructions to run programs. -**Note**: Before writing any of your own code, make sure to read the section -"Disentanglement" below. ### The `ForkJoin` Structure ``` @@ -163,8 +145,6 @@ by default. * `-debug true -debug-runtime true -keep g` For debugging, keeps the generated C files and uses the debug version of the runtime (with assertions enabled). The resulting executable is somewhat peruse-able with tools like `gdb`. -* `-detect-entanglement true` enables the dynamic entanglement detector. -See below for more information. For example: ``` @@ -198,52 +178,7 @@ argument `bar` using 4 pinned processors. $ foo @mpl procs 4 set-affinity -- bar ``` -## Disentanglement - -Currently, MPL only supports programs that are **disentangled**, which -(roughly speaking) is the property that concurrent threads remain oblivious -to each other's allocations [[3](#wyfa20)]. - -Here are a number of different ways to guarantee that your code is -disentangled. -- (Option 1) Use only purely functional data (no `ref`s or `array`s). This is -the simplest but most restrictive approach. -- (Option 2) If using mutable data, use only non-pointer data. MPL guarantees -that simple types (`int`, `word`, `char`, `real`, etc.) are never -indirected through a -pointer, so for example it is safe to use `int array`. Other types such as -`int list array` and `int array array` should be avoided. This approach -is very easy to check and is surprisingly general. Data races are fine! -- (Option 3) Make sure that your program is race-free. This can be -tricky to check but allows you to use any type of data. Many of our example -programs are race-free. - -## Entanglement Detection - -Whenever a thread acquires a reference -to an object allocated concurrently by some other thread, then we say that -the two threads are **entangled**. This is a violation of disentanglement, -which MPL currently does not allow. - -MPL has a built-in dynamic entanglement detector which is enabled by default. -The entanglement detector monitors individual reads and writes during execution; -if entanglement is found, the program will terminate with an error message. - -The entanglement detector is both "sound" and "complete": there are neither -false negatives nor false positives. In other words, the detector always raises -an alarm when entanglement occurs, and never raises an alarm otherwise. Note -however that entanglement (and therefore also entanglement detection) can -be execution-dependent: if your program is non-deterministic (e.g. racy), -then entanglement may or may not occur depending on the outcome of a race -condition. Similarly, entanglement could be input-dependent. - -Entanglement detection is highly optimized, and typically has negligible -overhead (see [[5](#waa22)]). It can be disabled at compile-time by passing -`-detect-entanglement false`; however, we recommend against doing so. MPL -relies on entanglement detection to ensure memory safety. We recommend leaving -entanglement detection enabled at all times. - -## Bugs and Known Issues +## Known Issues ### Basis Library In general, the basis library has not yet been thoroughly scrubbed, and many @@ -253,13 +188,6 @@ Some known issues: * `Int.toString` is racy when called in parallel. * `Real.fromString` may throw an error when called in parallel. -### Garbage Collection -* ([#115](https://github.com/MPLLang/mpl/issues/115)) The GC is currently -disabled at the "top level" (outside any calls to `ForkJoin.par`). -For highly parallel programs, this has generally not been a problem so far, -but it can cause a memory explosion for programs that are mostly (or entirely) -sequential. - ## Unsupported MLton Features Many [MLton-specific features](http://mlton.org/MLtonStructure) are unsupported, including (but not limited to): diff --git a/examples/README.md b/examples/README.md index 636c4d66d..1777bc1df 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,7 +5,7 @@ Each example program has a corresponding subdirectory in `src/`. The `lib/` directory contains common functions used across all examples. To build everything, run `make` or `make -j`. Compiled programs are -put into a `bin/`. +put into a `bin/`. You can also build programs one at a time as shown below. ## Fibonacci diff --git a/release-notes.md b/release-notes.md new file mode 100644 index 000000000..a854a4aa8 --- /dev/null +++ b/release-notes.md @@ -0,0 +1,19 @@ +With this version, +we lift the disentanglement restriction from MPL +and extend it to support all fork-join programs, no holds barred. + +MPL supports entangled programs by treating entanglement as an object-level property: +it distinguishes entangled objects from disentangled objects and manages them in-place, +without moving them. + +MPL dynamically distinguishes entangled objects and disentangled objects. +It does so with the help of the read barrier (see `runtime/gc/assign.c`), which +intercepts mutable reads and checks if they create entanglement. +If the read is entangled, the barrier pins the `entanglement region` of the entangled object, +instructing the collector not to move any object of the region +(performed by the function `manage_entangled`, see `runtime/gc/decheck.c`). +The write barrier accounts for inter-heap pointers created as a result of mutable updates by adding +them to the remembered set of the target heaps (see `Assignable_writeBarrier` in `runtime/gc/assign.c`). +The collection algorithm is a hybrid algorithm, which keeps the pinned entangled objects in place +and relocates disentangled objects to compact them (see function `markAndAdd` and `HM_HHC_collectLocal` in `hierarchical-heap-collection.c`). + diff --git a/runtime/gc/assign.c b/runtime/gc/assign.c index 1d5560a59..3c26682b5 100644 --- a/runtime/gc/assign.c +++ b/runtime/gc/assign.c @@ -8,6 +8,8 @@ */ #ifdef DETECT_ENTANGLEMENT +// read barrier for mutable objects +// the argument dst points to object src objptr Assignable_decheckObjptr(objptr dst, objptr src) { GC_state s = pthread_getspecific(gcstate_key); @@ -16,16 +18,25 @@ objptr Assignable_decheckObjptr(objptr dst, objptr src) pointer dstp = objptrToPointer(dst, NULL); HM_HierarchicalHeap dstHH = HM_getLevelHead(HM_getChunkOf(dstp)); + + /* if src is an unboxed value, or if it is scheduler data (depth 0), + * or dst is not in the frontier, + * then no entanglement checking required.*/ if (!isObjptr(src) || HM_HH_getDepth(dstHH) == 0 || !ES_contains(NULL, dst)) { return src; } // HM_EBR_leaveQuiescentState(s); + if (!decheck(s, src)) { assert (isMutable(s, dstp)); s->cumulativeStatistics->numEntanglements++; + /* the src object is entangled and its entanglement region may be pinned* + * the function manage_entangled (decheck.c: 435) is called pin_region in the PLDI paper + * Efficient parallel programming with effects.*/ + // the returned object new_src will differ from src if src has been relocated by garbage collection new_src = manage_entangled(s, src, getThreadCurrent(s)->decheckState); assert (isPinned(new_src)); } @@ -34,6 +45,8 @@ objptr Assignable_decheckObjptr(objptr dst, objptr src) return new_src; } + +// read barrier for mutable arrays objptr Assignable_readBarrier( GC_state s, objptr obj, @@ -95,6 +108,7 @@ static inline bool decheck_opt_fast (GC_state s, pointer p) { } +/*write barrier for updates*/ void Assignable_writeBarrier( GC_state s, objptr dst, diff --git a/runtime/gc/decheck.c b/runtime/gc/decheck.c index 14556e204..06c6cc360 100644 --- a/runtime/gc/decheck.c +++ b/runtime/gc/decheck.c @@ -320,6 +320,8 @@ bool decheckIsOrdered(GC_thread thread, decheck_tid_t t1) { #ifdef DETECT_ENTANGLEMENT #if ASSERT +// traverse the entanglement region and check all pinning and suspect invariants +// this function is super slow, don't call unless debugging. void traverseAndCheck( GC_state s, __attribute__((unused)) objptr *opp, @@ -465,6 +467,7 @@ objptr manage_entangled( current_pt != PIN_ANY || current_ud > unpinDepth; + /* skip entanglement management on scheduler objects (depth = 0)*/ if (current_pt != PIN_NONE && current_ud == 0) { return ptr; @@ -478,9 +481,14 @@ objptr manage_entangled( .unpinDepth = newUnpinDepth, .firstCall = !(current_pt == PIN_DOWN && current_ud == 1) }; + // this procedure loops and pins the entanglement region of ptr make_entangled(s, &ptr, ptr, (void*) &mea); } else { + /* the object is already pinned and marked entangled. + * It may not be marked as an entangled suspect because another thread maybe slow. + * Just ensure that if mutable, the object is a suspect (named entanglement candidates in the paper). + * see entanglement-supects.c */ if (isMutableH(s, header) && !ES_contains(NULL, ptr)) { HM_HierarchicalHeap lcaHeap = HM_HH_getHeapAtDepth(s, getThreadCurrent(s), unpinDepth); ES_add(s, HM_HH_getSuspects(lcaHeap), ptr); @@ -489,7 +497,6 @@ objptr manage_entangled( traverseAndCheck(s, &ptr, ptr, NULL); } - traverseAndCheck(s, &ptr, ptr, NULL); return ptr; // GC_header header = getRacyHeader(objptrToPointer(ptr, NULL));