September 17, 2019
https://issues.dlang.org/show_bug.cgi?id=20219

          Issue ID: 20219
           Summary: Idle D programs keep consuming CPU in
                    Gcx.scanBackground
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: regression
          Priority: P2
         Component: druntime
          Assignee: nobody@puremagic.com
          Reporter: dlang-bugzilla@thecybershadow.net
                CC: r.sagitario@gmx.de

This program will keep consuming CPU cycles in the background, when it should be completely idle:

/////////////// test.d ///////////////
import core.memory;
import core.stdc.stdio;
import core.sys.posix.unistd;

void main()
{
    printf("Creating garbage...\n");
    foreach (n; 0 .. 1_000)
        new uint[1_000_000];
    printf("Collecting garbage...\n");
    GC.collect();
    printf("Sleeping...\n");
    sleep(uint.max);
}
//////////////////////////////////////

Examining the program's state with a debugger reveals that the CPU consumption comes from a loop in Gcx.scanBackground. This function will wake up every 10 milliseconds to check if it has any work to do, then go back to sleep, until the garbage collector is deinitialized.

Even though the CPU consumption is small (about 0.5% per core on my machine), it can still add up when many otherwise-idle D programs are running, which would otherwise have no load impact on the system other than RAM usage.

More importantly, the timer is causing the CPU to wake up and thus exit a low power state frequently. This can significantly affect power consumption of portable / low-power devices. For this reason, I consider this to be a significant regression. If possible, the implementation should be changed so that the garbage collector notifies background threads that they have work to do using synchronization primitives like semaphores, and timers should not be used at all anywhere.

I should also note that it is atypical for system programming language runtimes to create runtime threads and leave them around, even if they are idle when the program is. These still consume some resources like PIDs, which are finite. Unless an uncompromising solution can be found, perhaps it would be appropriate to make parallel marking opt-in rather than opt-out.

To illustrate and extrapolate on the above: on a Linux machine, open htop, make sure that threads are visible (toggled with H) and kernel threads are hidden (toggled with K). Note that even though some processes (lines in white or black text) have threads attached to them (lines in green text), most do not. If all of the software on the system were to be hypothetically rewritten in D, every process would have a dozen threads attached to it, which to me doesn't seem like a great outcome.

--