Pergilah dengan Cara Anda Sendiri. Bagian satu. Tumpukan

Seri GC


Ini adalah seri ketiga dari GC. Pada artikel pertama, saya memperkenalkan pengumpul sampah D dan fitur bahasa yang membutuhkannya, dan juga menyentuh teknik sederhana untuk menggunakannya secara efektif. Pada artikel kedua, saya menunjukkan alat apa dalam bahasa dan perpustakaan yang membatasi GC di tempat-tempat tertentu dalam kode, dan bagaimana kompiler dapat membantu mengidentifikasi tempat-tempat yang layak dilakukan, dan juga merekomendasikan saat menulis program dalam D untuk pertama menggunakan GC dengan aman, sementara meminimalkan dampak kinerjanya dengan strategi sederhana, dan kemudian mengubah kode untuk menghindari GC atau bahkan lebih mengoptimalkan penggunaannya hanya jika profiler menjaminnya.



Ketika pengumpul sampah dinonaktifkan GC.disableatau dinonaktifkan oleh atribut fungsi @nogc, memori masih perlu dialokasikan di suatu tempat. Dan bahkan jika Anda menggunakan GC secara maksimal, masih disarankan untuk meminimalkan jumlah dan jumlah alokasi memori melalui GC. Ini berarti mengalokasikan memori pada stack atau pada heap biasa. Artikel ini akan fokus pada yang pertama. Alokasi memori pada heap akan menjadi subjek dari artikel selanjutnya.



Mengalokasikan memori pada tumpukan



Strategi alokasi memori yang paling sederhana di D adalah sama dengan di C: hindari menggunakan tumpukan dan gunakan tumpukan sebanyak mungkin. Jika sebuah array diperlukan dan ukurannya diketahui pada waktu kompilasi, gunakan array statis alih-alih yang dinamis. Struktur memiliki semantik nilai dan dibuat di stack secara default, sedangkan kelas memiliki semantik referensi dan biasanya dibuat di heap; struktur harus lebih disukai bila memungkinkan. Kemampuan kompilasi waktu D membantu Anda mencapai banyak hal yang tidak mungkin terjadi sebaliknya.



Array statis



Array statis dalam D membutuhkan ukuran untuk diketahui pada waktu kompilasi.



// OK
int[10] nums;

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


Tidak seperti array dinamis, menginisialisasi array statis melalui literal tidak mengalokasikan memori melalui GC. Panjang array dan literal harus cocok, jika tidak kompiler akan menghasilkan kesalahan.



@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 .



Pada artikel selanjutnya dalam seri ini, kita akan melihat cara untuk mengalokasikan memori pada tumpukan reguler tanpa melalui GC.




All Articles