From fa6378751e85e7a60f1d2e55dddaed79a0534ff2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 24 Nov 2021 15:49:32 +0100 Subject: [PATCH] renamed 'gc' switch to 'mm'; [backport:1.6] (#19187) * renamed 'gc' switch to 'mm'; [backport:1.6] * better docs --- changelog.md | 5 +- compiler/commands.nim | 4 +- doc/advopt.txt | 7 +-- doc/backends.rst | 38 +------------- doc/destructors.rst | 12 ++--- doc/docs.rst | 4 +- doc/mm.rst | 95 ++++++++++++++++++++++++++++++++++ doc/nimc.rst | 22 ++++---- doc/{gc.rst => refc.rst} | 109 +++++++++++---------------------------- koch.nim | 2 +- tools/kochdocs.nim | 2 +- 11 files changed, 157 insertions(+), 143 deletions(-) create mode 100644 doc/mm.rst rename doc/{gc.rst => refc.rst} (62%) diff --git a/changelog.md b/changelog.md index 63c3eff406255..258cf73e46d21 100644 --- a/changelog.md +++ b/changelog.md @@ -53,7 +53,7 @@ Baz = object ``` - [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental, - meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. + meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. ## Compiler changes @@ -63,5 +63,6 @@ ## Tool changes - +- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the + reality better. (Nim moved away from all techniques based on "tracing".) diff --git a/compiler/commands.nim b/compiler/commands.nim index a8caad91637d9..d5c5f24e47f5b 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -248,7 +248,7 @@ template deprecatedAlias(oldName, newName: string) = proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool = case switch.normalize - of "gc": + of "gc", "mm": case arg.normalize of "boehm": result = conf.selectedGC == gcBoehm of "refc": result = conf.selectedGC == gcRefc @@ -596,7 +596,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info) of "project": processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info) - of "gc": + of "gc", "mm": if conf.backend == backendJs: return # for: bug #16033 expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: diff --git a/doc/advopt.txt b/doc/advopt.txt index 063d018d1bf4a..79e784fde3e53 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -122,8 +122,9 @@ Advanced options: --skipUserCfg:on|off do not read the user's configuration file --skipParentCfg:on|off do not read the parent dirs' configuration files --skipProjCfg:on|off do not read the project's configuration file - --gc:refc|arc|orc|markAndSweep|boehm|go|none|regions - select the GC to use; default is 'refc' + --mm:orc|arc|refc|markAndSweep|boehm|go|none|regions + select which memory management to use; default is 'refc' + recommended is 'orc' --exceptions:setjmp|cpp|goto|quirky select the exception handling implementation --index:on|off turn index file generation on|off @@ -163,4 +164,4 @@ Advanced options: --profileVM:on|off turn compile time VM profiler on|off --sinkInference:on|off turn sink parameter inference on|off (default: on) --panics:on|off turn panics into process terminations (default: off) - --deepcopy:on|off enable 'system.deepCopy' for ``--gc:arc|orc`` + --deepcopy:on|off enable 'system.deepCopy' for ``--mm:arc|orc`` diff --git a/doc/backends.rst b/doc/backends.rst index 377e899b03ceb..d55503d10aaa0 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -105,7 +105,7 @@ file. However, you can also run the code with `nodejs`:idx: If you experience errors saying that `globalThis` is not defined, be sure to run a recent version of Node.js (at least 12.0). - + Interfacing =========== @@ -387,14 +387,8 @@ A similar thing happens with C code invoking Nim code which returns a proc gimme(): cstring {.exportc.} = result = "Hey there C code! " & $rand(100) -Since Nim's garbage collector is not aware of the C code, once the +Since Nim's reference counting mechanism is not aware of the C code, once the `gimme` proc has finished it can reclaim the memory of the `cstring`. -However, from a practical standpoint, the C code invoking the `gimme` -function directly will be able to use it since Nim's garbage collector has -not had a chance to run *yet*. This gives you enough time to make a copy for -the C side of the program, as calling any further Nim procs *might* trigger -garbage collection making the previously returned string garbage. Or maybe you -are `yourself triggering the collection `_. Custom data types @@ -414,31 +408,3 @@ you can clean it up. And of course, once cleaned you should avoid accessing it from Nim (or C for that matter). Typically C data structures have their own `malloc_structure`:c: and `free_structure`:c: specific functions, so wrapping these for the Nim side should be enough. - - -Thread coordination -------------------- - -When the `NimMain()` function is called Nim initializes the garbage -collector to the current thread, which is usually the main thread of your -application. If your C code later spawns a different thread and calls Nim -code, the garbage collector will fail to work properly and you will crash. - -As long as you don't use the threadvar emulation Nim uses native thread -variables, of which you get a fresh version whenever you create a thread. You -can then attach a GC to this thread via - -.. code-block:: nim - - system.setupForeignThreadGc() - -It is **not** safe to disable the garbage collector and enable it after the -call from your background thread even if the code you are calling is short -lived. - -Before the thread exits, you should tear down the thread's GC to prevent memory -leaks by calling - -.. code-block:: nim - - system.tearDownForeignThreadGc() diff --git a/doc/destructors.rst b/doc/destructors.rst index 83a50230b915a..c98e94536a41e 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -43,7 +43,7 @@ written as: dealloc(x.data) proc `=trace`[T](x: var myseq[T]; env: pointer) = - # `=trace` allows the cycle collector `--gc:orc` + # `=trace` allows the cycle collector `--mm:orc` # to understand how to trace the object graph. if x.data != nil: for i in 0..`_ | Description of some tools that come with the standard distribution. -- | `GC `_ - | Additional documentation about Nim's multi-paradigm memory management strategies +- | `Memory management `_ + | Additional documentation about Nim's memory management strategies | and how to operate them in a realtime setting. - | `Source code filters `_ diff --git a/doc/mm.rst b/doc/mm.rst new file mode 100644 index 0000000000000..b6941a901cdec --- /dev/null +++ b/doc/mm.rst @@ -0,0 +1,95 @@ +======================= +Nim's Memory Management +======================= + +.. default-role:: code +.. include:: rstcommon.rst + +:Author: Andreas Rumpf +:Version: |nimversion| + +.. + + + "The road to hell is paved with good intentions." + + +Multi-paradigm Memory Management Strategies +=========================================== + +.. default-role:: option + +Nim offers multiple different memory management strategies. +To choose the memory management strategy use the `--mm:` switch. + +**The recommended switch for newly written Nim code is `--mm:orc`.** + + +ARC/ORC +------- + +`--mm:orc` is a memory management mode primarily based on reference counting. Cycles +in the object graph are handled by a "cycle collector" which is based on "trial deletion". +Since algorithms based on "tracing" are not used, the runtime behavior is oblivious to +the involved heap sizes. + +The reference counting operations (= "RC ops") do not use atomic instructions and do not have to -- +instead entire subgraphs are *moved* between threads. The Nim compiler also aggressively +optimizes away RC ops and exploits `move semantics `_. + +Nim performs a fair share of optimizations for ARC/ORC; you can inspect what it did +to your time critical function via `--expandArc:functionName`. + +`--mm:arc` uses the same mechanism as `--mm:orc`, but it leaves out the cycle collector. +Both ARC and ORC offer deterministic performance for `hard realtime`:idx: systems, but +ARC can be easier to reason about for people coming from Ada/C++/C -- roughly speaking +the memory for a variable is freed when it goes "out of scope". + +We generally advise you to use the `acyclic` annotation in order to optimize away the +cycle collector's overhead +but `--mm:orc` also produces more machine code than `--mm:arc`, so if you're on a target +where code size matters and you know that your code does not produce cycles, you can +use `--mm:arc`. Notice that the default `async`:idx: implementation produces cycles +and leaks memory with `--mm:arc`, in other words, for `async` you need to use `--mm:orc`. + + + +Other MM modes +-------------- + +.. note:: The default `refc` GC is incremental, thread-local and not "stop-the-world". + +--mm:refc This is the default memory management strategy. It's a + deferred reference counting based garbage collector + with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local. + `This document `_ contains further information. +--mm:markAndSweep Simple Mark-And-Sweep based garbage collector. + Heaps are thread-local. +--mm:boehm Boehm based garbage collector, it offers a shared heap. +--mm:go Go's garbage collector, useful for interoperability with Go. + Offers a shared heap. + +--mm:none No memory management strategy nor a garbage collector. Allocated memory is + simply never freed. You should use `--mm:arc` instead. + +Here is a comparison of the different memory management modes: + +================== ======== ================= ============== =================== +Memory Management Heap Reference Cycles Stop-The-World Command line switch +================== ======== ================= ============== =================== +ORC Shared Cycle Collector No `--mm:orc` +ARC Shared Leak No `--mm:arc` +RefC Local Cycle Collector No `--mm:refc` +Mark & Sweep Local Cycle Collector No `--mm:markAndSweep` +Boehm Shared Cycle Collector Yes `--mm:boehm` +Go Shared Cycle Collector Yes `--mm:go` +None Manual Manual Manual `--mm:none` +================== ======== ================= ============== =================== + +.. default-role:: code +.. include:: rstcommon.rst + +JavaScript's garbage collector is used for the `JavaScript and NodeJS +`_ compilation targets. +The `NimScript `_ target uses the memory management strategy built into +the Nim compiler. diff --git a/doc/nimc.rst b/doc/nimc.rst index 6be4c204ebf74..eb066e748d106 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -408,13 +408,13 @@ to your usual `nim c`:cmd: or `nim cpp`:cmd: command and set the `passC`:option: and `passL`:option: command line switches to something like: .. code-block:: cmd - nim c ... --d:nimAllocPagesViaMalloc --gc:orc --passC="-I$DEVKITPRO/libnx/include" ... + nim c ... --d:nimAllocPagesViaMalloc --mm:orc --passC="-I$DEVKITPRO/libnx/include" ... --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" or setup a ``nim.cfg`` file like so:: #nim.cfg - --gc:orc + --mm:orc --d:nimAllocPagesViaMalloc --passC="-I$DEVKITPRO/libnx/include" --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" @@ -485,10 +485,10 @@ Define Effect `useMalloc` Makes Nim use C's `malloc`:idx: instead of Nim's own memory manager, albeit prefixing each allocation with its size to support clearing memory on reallocation. - This only works with `--gc:none`:option:, - `--gc:arc`:option: and `--gc:orc`:option:. + This only works with `--mm:none`:option:, + `--mm:arc`:option: and `--mm:orc`:option:. `useRealtimeGC` Enables support of Nim's GC for *soft* realtime - systems. See the documentation of the `gc `_ + systems. See the documentation of the `mm `_ for further information. `logGC` Enable GC logging to stdout. `nodejs` The JS target is actually ``node.js``. @@ -614,9 +614,9 @@ A good start is to use the `any` operating target together with the .. code:: cmd - nim c --os:any --gc:arc -d:useMalloc [...] x.nim + nim c --os:any --mm:arc -d:useMalloc [...] x.nim -- `--gc:arc`:option: will enable the reference counting memory management instead +- `--mm:arc`:option: will enable the reference counting memory management instead of the default garbage collector. This enables Nim to use heap memory which is required for strings and seqs, for example. @@ -654,7 +654,7 @@ devices. This allocator gets blocks/pages of memory via a currently undocumented `osalloc` API which usually uses POSIX's `mmap` call. On many environments `mmap` is not available but C's `malloc` is. You can use the `nimAllocPagesViaMalloc` define to use `malloc` instead of `mmap`. `nimAllocPagesViaMalloc` is currently -only supported with `--gc:arc` or `--gc:orc`. (Since version 1.6) +only supported with `--mm:arc` or `--mm:orc`. (Since version 1.6) nimPage256 / nimPage512 / nimPage1k =================================== @@ -681,19 +681,19 @@ nimMemAlignTiny Sets `MemAlign` to `4` bytes which reduces the memory alignment to better match some embedded devices. -Thread stack size +Thread stack size ================= Nim's thread API provides a simple wrapper around more advanced RTOS task features. Customizing the stack size and stack guard size can be done by setting `-d:nimThreadStackSize=16384` or `-d:nimThreadStackGuard=32`. -Currently only Zephyr and FreeRTOS support these configurations. +Currently only Zephyr and FreeRTOS support these configurations. Nim for realtime systems ======================== -See the documentation of Nim's soft realtime `GC `_ for further +See the `--mm:arc` or `--mm:orc` memory management settings in `MM `_ for further information. diff --git a/doc/gc.rst b/doc/refc.rst similarity index 62% rename from doc/gc.rst rename to doc/refc.rst index 5de02f73d700f..766097f23b8bd 100644 --- a/doc/gc.rst +++ b/doc/refc.rst @@ -1,81 +1,3 @@ -======================= -Nim's Memory Management -======================= - -.. default-role:: code -.. include:: rstcommon.rst - -:Author: Andreas Rumpf -:Version: |nimversion| - -.. - - - "The road to hell is paved with good intentions." - - -Introduction -============ - -A memory-management algorithm optimal for every use-case cannot exist. -Nim provides multiple paradigms for needs ranging from large multi-threaded -applications, to games, hard-realtime systems and small microcontrollers. - -This document describes how the management strategies work; -How to tune the garbage collectors for your needs, like (soft) `realtime systems`:idx:, -and how the memory management strategies other than garbage collectors work. - -.. note:: the default GC is incremental, thread-local and not "stop-the-world" - -Multi-paradigm Memory Management Strategies -=========================================== - -.. default-role:: option - -To choose the memory management strategy use the `--gc:` switch. - ---gc:refc This is the default GC. It's a - deferred reference counting based garbage collector - with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local. ---gc:markAndSweep Simple Mark-And-Sweep based garbage collector. - Heaps are thread-local. ---gc:boehm Boehm based garbage collector, it offers a shared heap. ---gc:go Go's garbage collector, useful for interoperability with Go. - Offers a shared heap. ---gc:arc Plain reference counting with - `move semantic optimizations `_, offers a shared heap. - It offers deterministic performance for `hard realtime`:idx: systems. Reference cycles - cause memory leaks, beware. - ---gc:orc Same as `--gc:arc` but adds a cycle collector based on "trial deletion". - Unfortunately, that makes its performance profile hard to reason about so it is less - useful for hard real-time systems. - ---gc:none No memory management strategy nor a garbage collector. Allocated memory is - simply never freed. You should use `--gc:arc` instead. - - -================== ======== ================= ============== =================== -Memory Management Heap Reference Cycles Stop-The-World Command line switch -================== ======== ================= ============== =================== -RefC Local Cycle Collector No `--gc:refc` -Mark & Sweep Local Cycle Collector No `--gc:markAndSweep` -ARC Shared Leak No `--gc:arc` -ORC Shared Cycle Collector No `--gc:orc` -Boehm Shared Cycle Collector Yes `--gc:boehm` -Go Shared Cycle Collector Yes `--gc:go` -None Manual Manual Manual `--gc:none` -================== ======== ================= ============== =================== - -.. default-role:: code -.. include:: rstcommon.rst - -JavaScript's garbage collector is used for the `JavaScript and NodeJS -`_ compilation targets. -The `NimScript `_ target uses the memory management strategy built into -the Nim compiler. - - Tweaking the refc GC ==================== @@ -164,6 +86,35 @@ that up to 100 objects are traversed and freed before it checks again. Thus highly specialized environments or for older hardware. +Thread coordination +------------------- + +When the `NimMain()` function is called Nim initializes the garbage +collector to the current thread, which is usually the main thread of your +application. If your C code later spawns a different thread and calls Nim +code, the garbage collector will fail to work properly and you will crash. + +As long as you don't use the threadvar emulation Nim uses native thread +variables, of which you get a fresh version whenever you create a thread. You +can then attach a GC to this thread via + +.. code-block:: nim + + system.setupForeignThreadGc() + +It is **not** safe to disable the garbage collector and enable it after the +call from your background thread even if the code you are calling is short +lived. + +Before the thread exits, you should tear down the thread's GC to prevent memory +leaks by calling + +.. code-block:: nim + + system.tearDownForeignThreadGc() + + + Keeping track of memory ======================= @@ -178,7 +129,7 @@ Other useful procs from `system `_ you can use to keep track of mem * `GC_getStatistics()` Garbage collector statistics as a human-readable string. These numbers are usually only for the running thread, not for the whole heap, -with the exception of `--gc:boehm`:option: and `--gc:go`:option:. +with the exception of `--mm:boehm`:option: and `--mm:go`:option:. In addition to `GC_ref` and `GC_unref` you can avoid the garbage collector by manually allocating memory with procs like `alloc`, `alloc0`, `allocShared`, `allocShared0` or `allocCStringArray`. diff --git a/koch.nim b/koch.nim index f87828cdc3be5..295b1584b8b60 100644 --- a/koch.nim +++ b/koch.nim @@ -604,7 +604,7 @@ proc runCI(cmd: string) = when not defined(bsd): # the BSDs are overwhelmed already, so only run this test on the other machines: - kochExecFold("Boot Nim ORC", "boot -d:release --gc:orc --lib:lib") + kochExecFold("Boot Nim ORC", "boot -d:release --mm:orc --lib:lib") proc testUnixInstall(cmdLineRest: string) = csource("-d:danger" & cmdLineRest) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 70ee5ad079080..4fbea8c77e4fb 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -129,7 +129,7 @@ tut2.rst tut3.rst nimc.rst niminst.rst -gc.rst +mm.rst """.splitWhitespace().mapIt("doc" / it) doc0 = """