Gehen Sie Ihren eigenen Weg. Teil eins. Stapel



Dies ist der dritte Teil einer Reihe über GC. Im ersten Artikel habe ich den D-Garbage-Collector und die dafür erforderlichen Sprachfunktionen vorgestellt und auch einfache Techniken zur effektiven Verwendung angesprochen. Im zweiten Artikel habe ich gezeigt, welche Tools in der Sprache und in den Bibliotheken vorhanden sind, die die GC an bestimmten Stellen im Code einschränken, und wie der Compiler bei der Identifizierung von Stellen helfen kann, an denen es sich lohnt, und auch empfohlen, wenn Sie Programme in D schreiben, zuerst GC verwenden, während Minimieren Sie die Auswirkungen auf die Leistung mit einfachen Strategien und optimieren Sie den Code, um GC zu vermeiden, oder optimieren Sie seine Verwendung nur dann weiter, wenn der Profiler dies garantiert.



Wenn der Garbage Collector durch ein GC.disableFunktionsattribut deaktiviert oder deaktiviert wird @nogc, muss der Speicher noch irgendwo zugewiesen werden. Und selbst wenn Sie GC in vollem Umfang nutzen, ist es dennoch ratsam, die Anzahl und Anzahl der Speicherzuweisungen durch den GC zu minimieren. Dies bedeutet, dass Speicher entweder auf dem Stapel oder auf dem regulären Heap zugewiesen wird. Dieser Artikel konzentriert sich auf Ersteres. Die Speicherzuweisung auf dem Heap wird Gegenstand des nächsten Artikels sein.



Zuweisen von Speicher auf dem Stapel



Die einfachste Speicherzuweisungsstrategie in D ist dieselbe wie in C: Vermeiden Sie die Verwendung des Heaps und verwenden Sie den Stapel so oft wie möglich. Wenn ein Array benötigt wird und seine Größe zur Kompilierungszeit bekannt ist, verwenden Sie ein statisches Array anstelle eines dynamischen. Strukturen haben eine Wertsemantik und werden standardmäßig auf dem Stapel erstellt, während Klassen eine Referenzsemantik haben und normalerweise auf dem Heap erstellt werden. Strukturen sollten nach Möglichkeit bevorzugt werden. Mit den D-Funktionen zur Kompilierungszeit können Sie viele Dinge erreichen, die sonst nicht möglich wären.



Statische Arrays



Für statische Arrays in D muss die Größe zur Kompilierungszeit bekannt sein.



// OK
int[10] nums;

// :  x     
int x = 10;
int[x] err;


Im Gegensatz zu dynamischen Arrays wird beim Initialisieren statischer Arrays über ein Literal kein Speicher über den GC zugewiesen. Die Längen des Arrays und des Literal müssen übereinstimmen, sonst generiert der Compiler einen Fehler.



@nogc void main() {
    int[3] nums = [1, 2, 3];
}


, , , .



void printNums(int[] nums) {
    import std.stdio : writeln;
    writeln(nums);
}

void main() {
    int[]  dnums = [0, 1, 2];
    int[3] snums = [0, 1, 2];
    printNums(dnums);
    printNums(snums);
}


-vgc , , — . :



int[] foo() {
    auto nums = [0, 1, 2];

    //  -  nums...

    return nums;
}


nums . , , . , .



, - , , . , . .dup:



int[] foo() {
    int[3] nums = [0, 1, 2];

    //  x —  -   nums
    bool condition = x;

    if(condition) return nums.dup;
    else return [];
}


GC .dup, . , [] null — , ( length) 0, ptr null.





D , . , , : .



struct Foo {
    int x;
    ~this() {
        import std.stdio;
        writefln("#%s says bye!", x);
    }
}
void main() {
    Foo f1 = Foo(1);
    Foo f2 = Foo(2);
    Foo f3 = Foo(3);
}


, :



#3 says bye!
#2 says bye!
#1 says bye!


, , . GC new, GC . , . [std.typecons.scoped](https://dlang.org/phobos/std_typecons.html#.scoped) .



class Foo {
    int x;

    this(int x) { 
        this.x = x; 
    }

    ~this() {
        import std.stdio;
        writefln("#%s says bye!", x);
    }
}
void main() {
    import std.typecons : scoped;
    auto f1 = scoped!Foo(1);
    auto f2 = scoped!Foo(2);
    auto f3 = scoped!Foo(3);
}


, , . core.object.destroy, .



, scoped, destroy @nogc-. , , GC, , @nogc-. , nogc, .



, . (Plain Old Data, POD) , - GUI, , . , , . , , , .



alloca



C D « », alloca. , GC, . , :



import core.stdc.stdlib : alloca;

void main() {
    size_t size = 10;
    void* mem = alloca(size);

    // Slice the memory block
    int[] arr = cast(int[])mem[0 .. size];
}


C, alloca : . , arr . arr.dup.





, Queue, «». D , . Java , , . D , (Design by Introspection). , , , , UFCS, ( ).



DbI . (. .)

Queue . , , . , : - . , , , .



//  `Size`   0     
//    ;  
//    
struct Queue(T, size_t Size = 0) 
{
    //       .
    //   `public`  DbI-   
    // ,   Queue   .
    enum isFixedSize = Size > 0;

    void enqueue(T item) 
    {
        static if(isFixedSize) {
            assert(_itemCount < _items.length);
        }
        else {
            ensureCapacity();
        }
        push(item);
    }

    T dequeue() {
        assert(_itemCount != 0);
        static if(isFixedSize) {
            return pop();
        }
        else {
            auto ret = pop();
            ensurePacked();
            return ret;
        }
    }

    //       
    static if(!isFixedSize) {
        void reserve(size_t capacity) { 
            /*      */ 
        }
    }

private:   
    static if(isFixedSize) {
        T[Size] _items;     
    }
    else T[] _items;
    size_t _head, _tail;
    size_t _itemCount;

    void push(T item) { 
        /*  item,  _head  _tail */
        static if(isFixedSize) { ... }
        else { ... }
    }

    T pop() { 
        /*  item,  _head  _tail */ 
        static if(isFixedSize) { ... }
        else { ... }
    }

    //       
    static if(!isFixedSize) {
        void ensureCapacity() { /*  ,   */ }
        void ensurePacked() { /*  ,   */}
    }
}


:



Queue!Foo qUnbounded;
Queue!(Foo, 128) qBounded;


qBounded . qUnbounded, . , . isFixedSize:



void doSomethingWithQueueInterface(T)(T queue)
{
    static if(T.isFixedSize) { ... }
    else { ... }
}


: __traits(hasMember, T, "reserve"), — : hasMember!T("reserve"). __traits std.traits — DbI; .





GC. , , — GC .



Im nächsten Artikel der Reihe werden wir uns mit Möglichkeiten befassen, Speicher auf einem regulären Heap zuzuweisen, ohne den GC zu durchlaufen.




All Articles