Life in the fast lane



In the first of a series on GC, I introduced the D garbage collector and the language features that use it. Two key points that I tried to convey:



  1. GC only runs when you request a memory allocation . Contrary to popular misconception, the D language GC cannot simply pause and pause your Minecraft clone in the middle of a game loop. It only runs when you request memory through it and only when needed.



  2. Simple C and C ++ style memory allocation strategies can reduce the load on the GC . Don't allocate memory inside loops - instead, pre-allocate as many resources as possible or use the stack. Minimize the total number of memory allocations through the GC. These strategies work because of # 1. The developer can dictate when to run garbage collection by intelligently using GC-managed heap allocation.





The strategies from point # 2 are fine for code that the programmer writes himself, but they are not particularly helpful when it comes to third-party libraries. In these cases, using the mechanisms of the D language and its runtime, you can ensure that no memory allocation occurs at critical points in the code. There are also command line options to help make sure GC doesn't get in your way.



Let's imagine that you are writing a D program and for one reason or another decided to eliminate garbage collection entirely. You have two obvious solutions.



Pill for greed



The first solution is to call GC.disablewhen the program starts. Allocating memory via GC will still work, but garbage collection will stop. All garbage collection, including what may have happened on other threads.



void main() {
    import core.memory;
    import std.stdio;
    GC.disable;
    writeln("Goodbye, GC!");
}


Output:



Goodbye, GC!


, , GC, , . , , , . , - . :



, , .

, . , - , . GC.enable GC.collect. , C C++.





, @nogc. main, .



@nogc
void main() { ... }


GC. @nogc, main, , . ยซ ยป.



, GC.disable. .



@nogc
void main() {
    import std.stdio;
    writeln("GC be gone!");
}


:



Error: @nogc function 'D main' cannot call non-@nogc function 'std.stdio.writeln!string.writeln'
(: @nogc- 'D main'    -@nogcโ€“ 'std.stdio.writeln!string.writeln')


@nogc , . . @nogc, , , @nogc. , writeln .



:



@nogc 
void main() {
    auto ints = new int[](100);
}


:



Error: cannot use 'new' in @nogc function 'D main'
(:   'new'  @nogc- 'D main')


@nogc- , GC ( ). . , , GC . , , @nogc, .



, @nogc , . , , - ( ). โ€” . :



throw new Exception("Blah");


- , new, @nogc- . , , , - , โ€ฆ , . D , , throw new Exception GC, .



, @nogc- . (. .)

@nogc main โ€” , .



, : @nogc main GC . D . main, โ€” . @nogc, , , GC @nogc-. , @nogc, main, , main , , GC.





. , D, GC . , GC โ€” . , , D: GC . , , .



, , , GC. @nogc / API core.memory.GC . @nogc main, , GC. GC.disable . , GC.enable. , GC (, ), GC.collect.



, , , . API core.memory.GC GC . D.



( !) D --DRT-gcopt=profile:1, . GC, , .



: gcstat.d .



void main() {
    import std.stdio;
    int[] ints;
    foreach(i; 0 .. 20) {
        ints ~= i;
    }
    writeln(ints);
}


GC:



dmd gcstat.d
gcstat --DRT-gcopt=profile:1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
        Number of collections:  1
        Total GC prep time:  0 milliseconds
        Total mark time:  0 milliseconds
        Total sweep time:  0 milliseconds
        Total page recovery time:  0 milliseconds
        Max Pause Time:  0 milliseconds
        Grand total GC time:  0 milliseconds
GC summary:    1 MB,    1 GC    0 ms, Pauses    0 ms <    0 ms


, , , . D GC , ( ) . , , D , GC - ( ).



DMD -vgc, GC โ€” , , ~=.



: inner.d.



void printInts(int[] delegate() dg)
{
    import std.stdio;
    foreach(i; dg()) writeln(i);
} 

void main() {
    int[] ints;
    auto makeInts() {
        foreach(i; 0 .. 20) {
            ints ~= i;
        }
        return ints;
    }

    printInts(&makeInts);
}


makeInts โ€” . , , / ( static, delegate function). .



-vgc:



dmd -vgc inner.d
inner.d(11): vgc: operator ~= may cause GC allocation
inner.d(7): vgc: using closure causes GC allocation

(inner.d(11): vgc:  ~=      GC)
(inner.d(7): vgc:        GC)


, , ints, ( โ€” - delegate). ints makeInts . , - . printInts :



void printInts(scope int[] delegate() dg)


scope , . , dg . , . . , , .





, GC D , Java C#, . , D , Java, . , D. , Java, GC , .



, , , , . D โ„– 2 , @nogc core.memory.GC , . , , , .



, D. , Phobos โ€” D โ€” @nogc. , , .



In future articles, we will look at how to allocate memory without resorting to the GC, and use it in parallel with the memory from the GC, than replace the @nogclanguage features that are not available in the code, and much more.



Thanks to Vladimir Panteleev, Guillaume Piolat and Steven Schveighoffer for their valuable feedback on the draft of this article.




All Articles