Gehen Sie Ihren eigenen Weg. Zweiter Teil. Haufen



Wir setzen unsere Artikelserie ĂĽber den D-Garbage-Collector fort. Dies ist der zweite Teil des Artikels ĂĽber die Speicherzuweisung auĂźerhalb des GC. Im ersten Teil ging es um die Zuweisung von Speicher auf dem Stapel. Nun werden wir uns die Speicherzuordnung vom Heap ansehen.



Während dies nur der vierte Beitrag in dieser Reihe ist, ist dies der dritte, in dem ich über Möglichkeiten spreche, die Verwendung von GC zu vermeiden . Machen Sie keinen Fehler: Ich versuche nicht, Programmierer vom D-Garbage-Collector abzuschrecken. Im Gegenteil. Um effektiv zu arbeiten, ist es wichtig zu verstehen, wann und wie man auf GC verzichtet.



Ich sage es noch einmal: Für eine effiziente Speicherbereinigung müssen Sie die Belastung des GC reduzieren. Wie im ersten und den folgenden Artikeln der Reihe erwähnt, bedeutet dies nicht, dass es vollständig aufgegeben werden sollte. Dies bedeutet, dass Sie überlegen müssen, wie viel und wie oft Speicher über den GC zugewiesen werden soll. Je weniger Speicherzuordnungen vorhanden sind, desto weniger Stellen sind noch verfügbar, an denen die Speicherbereinigung beginnen kann. Je weniger Speicher sich auf dem Heap des Garbage Collectors befindet, desto weniger Speicher muss gescannt werden.



Es ist unmöglich, genau und umfassend zu bestimmen, in welchen Anwendungen die Auswirkungen der GC spürbar sind und in welchen nicht - dies hängt sehr stark vom jeweiligen Programm ab. Wir können jedoch mit Sicherheit sagen, dass in den meisten Anwendungen die GC nicht vorübergehend oder vollständig deaktiviert werden muss. Wenn sie jedoch weiterhin benötigt wird, ist es wichtig zu wissen, wie Sie darauf verzichten können. Die naheliegende Lösung besteht darin, Speicher auf dem Stapel zuzuweisen, aber D ermöglicht auch die Zuweisung von Speicher auf dem regulären Heap unter Umgehung des GC.



Allgegenwärtiges Xi



, C . , , - API C. , C ABI, - , . D — . , D C.



core.stdc — D, C. 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.init — int 0. D, . malloc calloc, . , float.init — float.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



Dieser Artikel behandelt die Grundlagen der Verwendung des Heaps ohne Rückgriff auf GC. Neben dem Unterricht gibt es noch eine weitere Lücke in unserer Geschichte: Was tun mit Destruktoren? Ich werde dieses Thema für den nächsten Artikel speichern, wo es sehr angemessen sein wird. Hier ist, was für den nächsten GC in dieser Serie geplant ist. In Kontakt bleiben!



Vielen Dank an Walter Bright, Guillaume Piolat, Adam D. Ruppe und Steven Schveighoffer für ihre unschätzbare Hilfe bei der Erstellung dieses Artikels.



Anstatt fortzufahren
, . , , . API core.memory.GC inFinalizer , , .



All Articles