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



نواصل سلسلة مقالاتنا حول أداة تجميع البيانات المهملة بلغة D. هذا هو الجزء الثاني من المقالة المخصص لتخصيص الذاكرة خارج GC. تحدث الجزء الأول عن تخصيص الذاكرة على المكدس. الآن سننظر في تخصيص الذاكرة من الكومة.



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



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



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



كلي الوجود شي



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



الحزمة core.stdc عبارة عن مجموعة من وحدات D المترجمة من رؤوس مكتبة C.



import core.stdc.stdio : puts;
void main() 
{
    puts("Hello C standard library.");
}


, D, , C extern(C), , D as a Better C [], -betterC. , . D C , extern(C) . puts core.stdc.stdio — , , .



malloc



D C, , malloc, calloc, realloc free. , core.stdc.stdlib. D GC .



import core.stdc.stdlib;
void main() 
{
    enum totalInts = 10;

    //    10   int.
    int* intPtr = cast(int*)malloc(int.sizeof * totalInts);

    // assert(0) ( assert(false))     ,  
    //   assert ,       
    //   malloc.
    if(!intPtr) assert(0, "Out of memory!");

    //      .     
    // ,     ,  
    //  .

    scope(exit) free(intPtr);

    //    ,     
    //  +.
    int[] intArray = intPtr[0 .. totalInts];
}


GC, D . T, GC, T.initint 0. D, . malloc calloc, . , float.initfloat.nan, 0.0f. .



, , malloc free . :



import core.stdc.stdlib;

//    ,      .
void[] allocate(size_t size)
{
    //  malloc(0)    (  null  - ),     ,    .
    assert(size != 0);

    void* ptr = malloc(size);
    if(!ptr) assert(0, "Out of memory!");

    //    ,      
    //  .
    return ptr[0 .. size];
}

T[] allocArray(T)(size_t count) 
{ 
    // ,      !
    return cast(T[])allocate(T.sizeof * count); 
}

//   deallocate  
void deallocate(void* ptr)
{   
    // free handles null pointers fine.
    free(ptr);
}

void deallocate(void[] mem) 
{ 
    deallocate(mem.ptr); 
}

void main() {
    import std.stdio : writeln;
    int[] ints = allocArray!int(10);
    scope(exit) deallocate(ints);

    foreach(i; 0 .. 10) {
        ints[i] = i;
    }

    foreach(i; ints[]) {
        writeln(i);
    }
}


allocate void[] void*, length. , , allocate , allocArray , , allocate , . , C , — , , . calloc realloc, , C.



, (, allocArray) -betterC, . D.



, -



, GC, , . , ~= ~, , GC. ( ). . , , GC.



import core.stdc.stdlib : malloc;
import std.stdio : writeln;

void main()
{
    int[] ints = (cast(int*)malloc(int.sizeof * 10))[0 .. 10];
    writeln("Capacity: ", ints.capacity);

    //      
    int* ptr = ints.ptr;
    ints ~= 22;
    writeln(ptr == ints.ptr);
}


:



Capacity: 0
false


0 , . , GC, , . , , . GC , , . , ints GC, , (. D slices ).



, , , - , malloc .



:



void leaker(ref int[] arr)
{
    ...
    arr ~= 10;
    ...
}

void cleaner(int[] arr)
{
    ...
    arr ~= 10;
    ...
}


, — , , . , (, length ptr) . — .



leaker , C, GC. : , free ptr ( , GC, C), . cleaner . , , . GC, ptr .



, . cleaner , . , , , @nogc. , , malloc, free, , .



Array std.container.array: GC, , .



API



C — . malloc, . , . API: , Win32 HeapAlloc ( core.sys.windows.windows). , D , GC.





, . . . .



, int.



struct Point { int x, y; }
Point* onePoint = cast(Point*)malloc(Point.sizeof);
Point* tenPoints = cast(Point*)malloc(Point.sizeof * 10);


, . malloc D. , Phobos , .



std.conv.emplace , void[], , . , emplace malloc, allocate :



struct Vertex4f 
{ 
    float x, y, z, w; 
    this(float x, float y, float z, float w = 1.0f)
    {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }
}

void main()
{
    import core.stdc.stdlib : malloc;
    import std.conv : emplace;
    import std.stdio : writeln;

    Vertex4f* temp1 = cast(Vertex4f*)malloc(Vertex4f.sizeof);
    Vertex4f* vert1 = emplace(temp1, 4.0f, 3.0f, 2.0f); 
    writeln(*vert1);

    void[] temp2 = allocate(Vertex4f.sizeof);
    Vertex4f* vert2 = emplace!Vertex4f(temp2, 10.0f, 9.0f, 8.0f);
    writeln(*vert2);
}


emplace . , D . , Vertex4f:



struct Vertex4f 
{
    // x, y  z   float.nan
    float x, y, z;

    // w   1.0f
    float w = 1.0f;
}

void main()
{
    import core.stdc.stdlib : malloc;
    import std.conv : emplace;
    import std.stdio : writeln;

    Vertex4f vert1, vert2 = Vertex4f(4.0f, 3.0f, 2.0f);
    writeln(vert1);
    writeln(vert2);    

    auto vert3 = emplace!Vertex4f(allocate(Vertex4f.sizeof));
    auto vert4 = emplace!Vertex4f(allocate(Vertex4f.sizeof), 4.0f, 3.0f, 2.0f);
    writeln(*vert3);
    writeln(*vert4);
}


:



Vertex4f(nan, nan, nan, 1)
Vertex4f(4, 3, 2, 1)
Vertex4f(nan, nan, nan, 1)
Vertex4f(4, 3, 2, 1)


, emplace , — . int float. , , . , emplace , .



std.experimental.allocator



. , - , std.experimental.allocator D. API, , , (Design by Introspection), , , . Mallocator GCAllocator , , - . — emsi-containers.



GC



GC , D, GC, , GC. , GC. , malloc , new.



GC GC.addRange.



import core.memory;
enum size = int.sizeof * 10;
void* p1 = malloc(size);
GC.addRange(p1, size);

void[] p2 = allocate!int(10);
GC.addRange(p2.ptr, p2.length);


, GC.removeRange, . . free , . , .



GC , , , . . , , . GC , . addRange . , GC, addRange .





addRange. C , .



struct Item { SomeClass foo; }
auto items = (cast(Item*)malloc(Item.sizeof * 10))[0 .. 10];
GC.addRange(items.ptr, items.length);


GC 10 . length . , — void[] ( , byte ubyte). :



GC.addRange(items.ptr, items.length * Item.sizeof);


API , , void[].



void addRange(void[] mem) 
{
    import core.memory;
    GC.addRange(mem.ptr, mem.length);
}


addRange(items) . void[] , mem.length , items.length * Item.sizeof.



GC



غطت هذه المقالة أساسيات كيفية استخدام الكومة دون اللجوء إلى GC. بالإضافة إلى الطبقات ، هناك فجوة أخرى كبيرة متبقية في قصتنا: ما يجب فعله مع المدمرات. سأحفظ هذا الموضوع للمقال التالي ، حيث سيكون مناسبًا جدًا. إليك ما هو مخطط لـ GC التالي في هذه السلسلة. ابق على تواصل!



بفضل Walter Bright و Guillaume Piolat و Adam D. Ruppe و Steven Schveighoffer لما قدموه من مساعدة لا تقدر بثمن في إعداد هذا المقال.



بدلا من الاستمرار
, . , , . API core.memory.GC inFinalizer , , .



All Articles