2016-04-29 2 views
2

Ich lese Vulkan Memory Allocation - Memory Host und scheint, dass VkAllocationCallbacks mit naiven malloc/realloc/free Funktionen implementiert werden kann.Vulkan's VkAllocationCallbacks implementiert mit malloc/free()

typedef struct VkAllocationCallbacks { 
    void*         pUserData; 
    PFN_vkAllocationFunction    pfnAllocation; 
    PFN_vkReallocationFunction    pfnReallocation; 
    PFN_vkFreeFunction      pfnFree; 
    PFN_vkInternalAllocationNotification pfnInternalAllocation; 
    PFN_vkInternalFreeNotification   pfnInternalFree; 
} VkAllocationCallbacks; 

Aber ich sehe nur zwei mögliche Gründe meine eigene vkAllocationCallback umzusetzen:

  • Log-und Speichernutzung von Vulkan-API zu verfolgen;
  • Implementieren Sie eine Art von Heap-Speicher-Management, es ist ein großer Teil des Arbeitsspeichers verwendet und wiederverwendet werden, immer und immer wieder. Offensichtlich kann es ein Overkill sein und die gleichen Probleme mit verwaltetem Speicher haben (wie in Java JVM).

Fehle ich hier etwas? Welche Art von Anwendungen wäre es wert, vkAllocationCallbacks zu implementieren?

+2

"* Fehlt mir hier etwas? *" Warum glauben Sie, dass diese Gründe nicht ausreichen, um die Funktion zu rechtfertigen? –

+0

Meinst du "Ja, das ist alles! Gehen Sie voran, implementieren Sie es genau so, wie Sie es geschrieben haben"? Oder "Hey, Dummkopf, lies die f * specs und halt den Mund!" –

+0

Mein Punkt ist mehr, dass Ihre Frage darauf hindeutet, dass diese Gründe nicht ausreichen, um Rückrufe zu erlauben. –

Antwort

5

Aus der Spezifikation: "Da die meisten Speicherzuordnungen nicht auf dem kritischen Pfad liegen, ist dies nicht als Leistungsmerkmal gedacht. Vielmehr kann es für bestimmte eingebettete Systeme nützlich sein, um Debugging-Aufgaben zu erledigen Hostzuweisungen) oder für die Speicherzuweisungsprotokollierung. "

Mit einem eingebetteten System haben Sie möglicherweise gleich zu Beginn den gesamten Speicher belegt, so dass der Treiber malloc nicht aufrufen soll, da möglicherweise nichts mehr im Tank ist. Guard-Pages und Speicher-Logging (nur für Debug-Builds) könnten für die Vorsichtigen/Neugierigen nützlich sein.

Ich lese irgendwo auf einer Folie (kann mich nicht erinnern, wo, tut mir leid), dass Sie definitiv keine Zuordnung Callbacks, die nur durch malloc/realloc/frei durchsetzen, weil Sie im Allgemeinen davon ausgehen können, dass die Treiber viel besser machen Job als das (zB kleine Zuweisungen in Pools konsolidieren).

Ich denke, wenn Sie nicht sicher sind, ob Sie Zuteilung Callbacks implementieren sollten, dann müssen Sie keine Zuordnung Callbacks implementieren und Sie müssen nicht befürchten, dass Sie vielleicht haben sollten.

Ich denke, sie sind dort für diese spezifischen Anwendungsfälle und für diejenigen, die wirklich wollen, um alles unter Kontrolle zu haben.

+0

"* Bei einem eingebetteten System haben Sie vielleicht gleich zu Beginn den gesamten Speicher belegt, so dass der Treiber malloc nicht aufrufen muss, da möglicherweise nichts mehr im Tank ist. *" Es sollte beachtet werden, dass die Implementierungen von Vulkan sind erlaubt immer noch Speicher zuzuweisen, unabhängig von Ihren Rückrufen. Dafür gibt es die "internen" Zuordnungsrückrufe; Sie werden aufgerufen, wenn die Implementierung Speicher direkt zuweist. Sie müssen es immer noch mitteilen, aber es ist nicht erforderlich, dass Sie sich einmischen. –

+0

@NicolBolas - Bei Spezifikationen können wir lesen "Wenn pfnAllocation den angeforderten Speicher nicht zuordnen kann, muss NULL zurückgeben. Wenn die Zuordnung erfolgreich war, muss ein gültiger Zeiger auf Speicherzuweisung mindestens Größe Bytes und mit dem Zeiger zurückgegeben Wert ist ein Vielfaches der Ausrichtung. " Ich schätze, die Funktionen von vkAllocationCallbacks funktionieren doppelt: (i) spiele malloc/realloc/free und (ii) spiele Callbacks ab. Ich habe es noch nicht versucht, aber ich nehme an, dass die Benutzerimplementierung den Standardzuordner des Treibers außer Kraft setzt, falls gesetzt. Ich werde versuchen und geben Sie Feedback –

+1

Sie missverstanden meinen Punkt. Die Implementierung darf "interne" Zuordnungen vornehmen. Es ist notwendig, Sie darüber zu informieren (was "pfnInternalAllocation" und "pfnInternalFree" sind). Aber wenn Sie diese Funktionssignaturen betrachten, gibt die allocate-Funktion keinen Zeiger zurück. Und die freie Funktion nimmt keinen Zeiger. Sie existieren nur für die Implementierung, um Ihnen mitzuteilen, dass sie Speicher zugewiesen/freigegeben hat, aber Sie können den Speicher für die Implementierung nicht freigeben/freigeben. –

0

Ich implementiert meine eigenen VkAllocatorCallback mit Plain C malloc()/realloc()/free(). Es ist eine naive Implementierung und ignoriert den Alignment-Parameter vollständig. Unter Berücksichtigung, dass malloc in 64-Bit-Betriebssystem immer Zeiger mit 16 (!) Bytes Ausrichtung, die ziemlich große Ausrichtung ist, das wäre kein Problem in meinen Tests. Siehe Reference.

Aus Gründen der Vollständigkeit der Informationen ist eine 16-Byte-Ausrichtung auch 8/4/2 Byte ausgerichtet.

Mein Code ist folgende:

/** 
    * PFN_vkAllocationFunction implementation 
    */ 
    void* allocationFunction(void* pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope){ 

    printf("pAllocator's allocationFunction: <%s>, size: %u, alignment: %u, allocationScope: %d", 
     (USER_TYPE)pUserData, size, alignment, allocationScope); 
    // the allocation itself - ignore alignment, for while 
    void* ptr = malloc(size);//_aligned_malloc(size, alignment); 
    memset(ptr, 0, size); 
    printf(", return ptr* : 0x%p \n", ptr); 
    return ptr; 
} 

/** 
* The PFN_vkFreeFunction implementation 
*/ 
void freeFunction(void* pUserData, void* pMemory){ 
    printf("pAllocator's freeFunction: <%s> ptr: 0x%p\n", 
    (USER_TYPE)pUserData, pMemory); 
    // now, the free operation !  
    free(pMemory); 
} 

/** 
* The PFN_vkReallocationFunction implementation 
*/ 
void* reallocationFunction(void* pUserData, void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope){ 
    printf("pAllocator's REallocationFunction: <%s>, size %u, alignment %u, allocationScope %d \n", 
    (USER_TYPE)pUserData, size, alignment, allocationScope);  
    return realloc(pOriginal, size); 
} 

/** 
* PFN_vkInternalAllocationNotification implementation 
*/ 
void internalAllocationNotification(void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope      allocationScope){ 
    printf("pAllocator's internalAllocationNotification: <%s>, size %uz, alignment %uz, allocationType %uz, allocationScope %s \n", 
    (USER_TYPE)pUserData, 
    size, 
    allocationType, 
    allocationScope); 

} 

/** 
* PFN_vkInternalFreeNotification implementation 
**/ 
void internalFreeNotification(void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope      allocationScope){ 
    printf("pAllocator's internalFreeNotification: <%s>, size %uz, alignment %uz, allocationType %d, allocationScope %s \n", 
      (USER_TYPE)pUserData, size, allocationType, allocationScope); 
} 



/** 
    * Create Pallocator 
    * @param info - String for tracking Allocator usage 
    */ 
static VkAllocationCallbacks* createPAllocator(const char* info){ 
    VkAllocationCallbacks* m_allocator =  (VkAllocationCallbacks*)malloc(sizeof(VkAllocationCallbacks)); 
    memset(m_allocator, 0, sizeof(VkAllocationCallbacks)); 
    m_allocator->pUserData = (void*)info; 
    m_allocator->pfnAllocation = (PFN_vkAllocationFunction)(&allocationFunction); 
    m_allocator->pfnReallocation = (PFN_vkReallocationFunction)(&reallocationFunction); 
    m_allocator->pfnFree = (PFN_vkFreeFunction)&freeFunction; 
    m_allocator->pfnInternalAllocation = (PFN_vkInternalAllocationNotification)&internalAllocationNotification; 
    m_allocator->pfnInternalFree = (PFN_vkInternalFreeNotification)&internalFreeNotification; 
    // storePAllocator(m_allocator); 
    return m_allocator; 
    } 

`

Ich benutzte das Cube.c Beispiel aus VulkanSDK, meinen Code und Annahmen zu testen.Modifizierte Versionen finden Sie hier GitHub

Eine Probe des Ausgangs:

pAllocator's allocationFunction: <Device>, size: 800, alignment: 8, allocationScope: 1, return ptr* : 0x00000000061ECE40 
pAllocator's allocationFunction: <RenderPass>, size: 128, alignment: 8, allocationScope: 1, return ptr* : 0x000000000623FAB0 
pAllocator's allocationFunction: <ShaderModule>, size: 96, alignment: 8, allocationScope: 1, return ptr* : 0x00000000061F2C30 
pAllocator's allocationFunction: <ShaderModule>, size: 96, alignment: 8, allocationScope: 1, return ptr* : 0x00000000061F8790 
pAllocator's allocationFunction: <PipelineCache>, size: 152, alignment: 8, allocationScope: 1, return ptr* : 0x00000000061F2590 
pAllocator's allocationFunction: <Device>, size: 424, alignment: 8, allocationScope: 1, return ptr* : 0x00000000061F8EB0 
pAllocator's freeFunction: <ShaderModule> ptr: 0x00000000061F8790 
pAllocator's freeFunction: <ShaderModule> ptr: 0x00000000061F2C30 
pAllocator's allocationFunction: <Device>, size: 3448, alignment: 8, allocationScope: 1, return ptr* : 0x000000000624D260 
pAllocator's allocationFunction: <Device>, size: 3448, alignment: 8, allocationScope: 1, return ptr* : 0x0000000006249A80 

Schlussfolgerungen:

  • der Benutzer implementiert PFN_vkAllocationFunction, PFN_vkReallocationFunction, PFN_vkFreeFunction wirklich tut malloc/realoc/freien Betrieb in Namen von Vulkan. Nicht sicher, ob sie ALLE Zuordnungen durchführen, da Vulkan wählen kann, um einige Teile selbst zuzuteilen/freizugeben.

  • Die von meinen Implementierungen zur Verfügung gestellte Ausgabe zeigt, dass die typische Ausrichtung angefordert 8 Bytes ist, in meinem Win 7-64/NVidia. Dies zeigt, dass es Platz für Optimierungen gibt, wie zum Beispiel ein gut gemanagter Speicher, in dem Sie einen großen Teil des Speichers belegen und für Ihre Vulkan-App (einen Speicherpool) unterteilen. Es Mai * reduziert Speicherverbrauch (denke 8 Bytes vor und bis zu 8 Bytes nach jedem zugewiesenen Block). Es kann auch schneller sein, wie malloc() Anruf länger dauern kann als ein direkter Zeiger auf Ihren eigenen Speicherpool bereits allokiert.

  • Zumindest mit meinen aktuellen Vulkan-Treiber, die PFN_vkInternalAllocationNotification und PFN_vkInternalFreeNotification nicht ausgeführt. Vielleicht ein Fehler in meinen NVidia-Treibern. Ich werde später meine AMD einchecken.

  • Der * pUserData wird verwendet, um Informationen und/oder Management zu debuggen. Tatsächlich können Sie damit ein C++ - Objekt übergeben und dort alle erforderlichen Performance-Jobs abspielen. Es ist eine Art offensichtliche Information, aber Sie können sie für jeden Anruf oder jedes VkCreateXXX-Objekt ändern.

  • Sie können einen einzelnen und generischen VkAllocatorCallBack-Allokator für alle Anwendungen verwenden, aber ich denke, dass die Verwendung eines benutzerdefinierten Allokators zu besseren Ergebnissen führen kann. I mein Test, VkSemaphore Erstellung zeigt ein typisches Muster auf intensive Allok/frei von kleinen Chunks (72 Bytes), die mit der Wiederverwendung eines zuvor Chunk im Speicher adressiert werden können, in einem angepassten Allokator. malloc()/free() verwendet bereits Speicher, wenn es möglich ist, ist aber verlockend, versuchen Sie, unseren eigenen Speichermanager zu verwenden, zumindest für kurzlebige kleine Speicherblöcke.

  • Die Speicherausrichtung ist möglicherweise ein Problem bei der Implementierung von VkAllocationCallback (es ist keine _aligned_realoc-Funktion verfügbar, sondern nur _aligned_malloc und _aligned_free). Aber nur wenn Vulkan alignments größer als mallocs Standard anfordert (8 Bytes für x86, 16 für AMD64, etc. müssen ARM defaults überprüfen). Aber bis jetzt fragt seens Vulkan tatsächlich Speicher mit niedriger Ausrichtung als malloc() Standard, mindestens auf 64bit OS.

letzter Gedanke:

Sie können glücklich leben, bis das Ende der Zeit nur alle VkAllocatorCallback * pAllocator Einstellung, die Sie als NULL finden;) Möglicherweise Vulkan-Standard allocator bereits funktioniert das alles besser als sich selbst.

ABER ...

Ein Höhepunkt Vulkan Vorteile waren der Entwickler allem unter Kontrolle gebracht werden würde, einschließlich Speicher-Management.Khronos presentation, slide 6

+0

Meine Ergebnisse bestätigen nur, was @Columbo in der akzeptierten Antwort traurig ist. Vielen Dank ! –

+0

"* Sie sind keine Dummy-Funktionen. *" Niemand hat gesagt oder angedeutet, dass es sich um Dummy-Funktionen handelt. "* Vielleicht ein Fehler in Treibern. *" Oder weil die Implementierung intern keinen Speicher zuweist und Sie daher nicht darüber informieren muss. Oder weil Sie nie etwas getan haben, das eine interne Speicherzuweisung erfordern würde. Außerdem ist es eine gute Idee, keine Schlussfolgerungen aus dem Verhalten einer einzelnen Implementierung zu ziehen. –

+0

"* Die * pUserData kann sowohl für Informationen und Verwaltung verwendet werden.Tatsächlich können Sie es verwenden, um ein C++ - Objekt zu übergeben und alle erforderlichen Performance-Jobs abzuspielen. * "Ich bin neugierig: War das irgendwie aus der API und der Spezifikation nicht offensichtlich? Ein Zeiger auf Callback-Funktionszeiger zu übergeben geehrt, dass solche Callbacks beispielsweise C++ - Objekte oder andere Zustandsdaten verwenden können –

Verwandte Themen