إمض في طريقك. الجزء الأول. كومة



هذا هو الثالث في سلسلة على GC. في المقالة الأولى ، قدمت جامع القمامة D وميزات اللغة التي تتطلبه ، ولمس أيضًا تقنيات بسيطة لاستخدامه بشكل فعال. في المقالة الثانية ، عرضت ما هي الأدوات الموجودة في اللغة والمكتبات التي تحد من GC في أماكن معينة في التعليمات البرمجية ، وكيف يمكن للمترجم المساعدة في تحديد الأماكن التي يستحق القيام بها ، وأوصت أيضًا عند كتابة البرامج في D لاستخدام GC لأول مرة بأمان ، بينما التقليل من تأثير أدائها باستراتيجيات بسيطة ، ثم تعديل الشفرة لتجنب GC أو حتى تحسين استخدامها فقط عندما يضمن المحلل ذلك.



عندما يتم تعطيل جامع البيانات المهملة GC.disableأو تعطيله بواسطة سمة دالة @nogc، فلا يزال يتعين تخصيص الذاكرة في مكان ما. وحتى إذا كنت تستخدم GC إلى أقصى حد ، فلا يزال من المستحسن تقليل مقدار وعدد عمليات تخصيص الذاكرة من خلال GC. وهذا يعني تخصيص ذاكرة إما على المكدس أو على الكومة العادية. ستركز هذه المقالة على السابق. سيكون تخصيص الذاكرة على كومة الذاكرة المؤقتة موضوع المقالة التالية.



تخصيص الذاكرة على المكدس



أبسط استراتيجية لتخصيص الذاكرة في D هي نفسها الموجودة في C: تجنب استخدام الكومة واستخدم المكدس قدر الإمكان. إذا كانت هناك حاجة إلى صفيف وكان حجمه معروفًا في وقت الترجمة ، استخدم صفيفًا ثابتًا بدلاً من صفيف ديناميكي. تحتوي الهياكل على دلالات قيمة ويتم إنشاؤها على المكدس افتراضيًا ، بينما تحتوي الفئات على دلالات مرجعية وعادة ما يتم إنشاؤها في كومة الذاكرة المؤقتة ؛ ينبغي تفضيل الهياكل كلما أمكن ذلك. تساعدك إمكانات D في الترجمة على تحقيق العديد من الأشياء التي لن تكون ممكنة خلاف ذلك.



صفائف ثابتة



تتطلب المصفوفات الثابتة في D أن يكون الحجم معروفًا في وقت الترجمة.



// OK
int[10] nums;

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


على عكس المصفوفات الديناميكية ، لا تؤدي تهيئة المصفوفات الثابتة عن طريق حرفية إلى تخصيص ذاكرة عبر GC. يجب أن تتطابق أطوال المصفوفة والقيمة الحرفية ، وإلا سينتج المترجم خطأ.



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



في المقالة التالية في السلسلة ، سنلقي نظرة على طرق لتخصيص ذاكرة في كومة منتظمة دون المرور عبر GC.




All Articles