Siga seu próprio caminho. Parte dois. Heap



Continuamos nossa série de artigos sobre o coletor de lixo na linguagem D. Esta é a segunda parte do artigo dedicada à alocação de memória fora do GC. A primeira parte falou sobre a alocação de memória na pilha. Agora veremos a alocação de memória do heap.



Embora este seja apenas o quarto post desta série, este é o terceiro em que falo sobre maneiras de evitar o uso de GC. Não se engane: não estou tentando afastar os programadores do coletor de lixo D. Muito pelo contrário. Entender quando e como ficar sem o GC é essencial para usá-lo com eficácia.



Mais uma vez, direi que, para uma coleta de lixo eficiente, você precisa reduzir a carga no GC. Conforme mencionado no primeiro artigo e nos subseqüentes da série, isso não significa que deva ser totalmente abandonado. Isso significa que você precisa ser criterioso sobre quanto e com que frequência alocar memória por meio do GC. Quanto menos alocações de memória, menos lugares restantes onde a coleta de lixo pode começar. Quanto menos memória estiver no heap do coletor de lixo, menos memória ele precisará ser varrido.



É impossível determinar de forma precisa e abrangente em quais aplicações o impacto do GC será perceptível e em quais não - depende muito do programa específico. Mas podemos dizer com segurança que na maioria dos aplicativos não há necessidade de desabilitar o GC temporariamente ou completamente, mas quando ainda for necessário, é importante saber como fazer sem ele. A solução óbvia é alocar memória na pilha, mas D também permite que a memória seja alocada no heap regular, ignorando o GC.



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



Este artigo cobriu os fundamentos básicos de como usar o heap sem recorrer ao GC. Além das classes, há mais uma lacuna em nossa história: o que fazer com os destruidores. Vou guardar este tópico para o próximo artigo, onde será muito apropriado. Aqui está o que está planejado para o próximo GC desta série. Manter contato!



Agradecimentos a Walter Bright, Guillaume Piolat, Adam D. Ruppe e Steven Schveighoffer por sua ajuda inestimável na preparação deste artigo.



Em vez de continuar
, . , , . API core.memory.GC inFinalizer , , .



All Articles