Wat sneller is: stapeltoewijzing of heaptoewijzing

Deze vraag klinkt misschien vrij eenvoudig, maar dit is een discussie die ik had met een andere ontwikkelaar waarmee ik werk.

Ik zorgde ervoor om dingen te stapelen waar ik kon, in plaats van ze op een hoop te gooien. Hij praatte tegen me en keek over mijn schouder mee en merkte op dat het niet nodig was omdat ze qua prestaties hetzelfde zijn.

Ik had altijd de indruk dat het vergroten van de stapel een constante tijd was, en de prestaties van heaptoewijzing waren afhankelijk van de huidige complexiteit van de heap voor zowel toewijzing (een gat van de juiste grootte vinden) als de-toewijzing (instortende gaten om fragmentatie, aangezien veel implementaties van standaardbibliotheken tijd nodig hebben om dit te doen tijdens het verwijderen als ik me niet vergis).

Dit lijkt me iets dat waarschijnlijk erg compiler-afhankelijk zou zijn. Voor dit project in het bijzonder gebruik ik een Metrowerkscompiler voor de PPC-architectuur. Inzicht in deze combinatie zou zeer nuttig zijn, maar wat is in het algemeen het geval voor GCC en MSVC++? Presteert heaptoewijzing niet zo goed als stapeltoewijzing? Is er geen verschil? Of zijn de verschillen zo miniem dat micro-optimalisatie zinloos wordt.


Antwoord 1, autoriteit 100%

Stacktoewijzing is veel sneller, omdat het alleen de stackaanwijzer verplaatst.
Door geheugenpools te gebruiken, kunt u vergelijkbare prestaties krijgen uit heaptoewijzing, maar dat gaat gepaard met een lichte extra complexiteit en zijn eigen hoofdpijn.

Bovendien is stack versus heap niet alleen een prestatieoverweging; het vertelt je ook veel over de verwachte levensduur van objecten.


Antwoord 2, autoriteit 32%

Stapel is veel sneller. Het gebruikt letterlijk slechts een enkele instructie voor de meeste architecturen, in de meeste gevallen, b.v. op x86:

sub esp, 0x10

(Hiermee wordt de stackaanwijzer 0x10 bytes naar beneden verplaatst en worden die bytes daardoor “toegewezen” voor gebruik door een variabele.)

Natuurlijk is de grootte van de stapel zeer, zeer eindig, aangezien u snel zult ontdekken of u de stapeltoewijzing te veel gebruikt of recursie probeert uit te voeren 🙂

Bovendien is er weinig reden om de prestaties te optimaliseren van code die dit niet aantoonbaar nodig heeft, zoals blijkt uit profilering. “Voortijdige optimalisatie” veroorzaakt vaak meer problemen dan het waard is.

Mijn vuistregel: als ik weet dat ik tijdens het compilerenwat gegevens nodig heb, en het is minder dan een paar honderd bytes groot, dan verdeel ik het op een stapel. Anders heap ik het toe.


Antwoord 3, autoriteit 23%

Eerlijk gezegd is het triviaal om een ​​programma te schrijven om de prestaties te vergelijken:

#include <ctime>
#include <iostream>
namespace {
    class empty { }; // even empty classes take up 1 byte of space, minimum
}
int main()
{
    std::clock_t start = std::clock();
    for (int i = 0; i < 100000; ++i)
        empty e;
    std::clock_t duration = std::clock() - start;
    std::cout << "stack allocation took " << duration << " clock ticks\n";
    start = std::clock();
    for (int i = 0; i < 100000; ++i) {
        empty* e = new empty;
        delete e;
    };
    duration = std::clock() - start;
    std::cout << "heap allocation took " << duration << " clock ticks\n";
}

Er wordt gezegd dat een dwaze consistentie is de kobold van kleine geesten. Blijkbaar zijn het optimaliseren van compilers de hobgoblins van de geest van veel programmeurs. Deze discussie stond vroeger onderaan het antwoord, maar blijkbaar hebben mensen niet de moeite om zo ver te lezen, dus ik verplaats het hierheen om te voorkomen dat ik vragen krijg die ik al heb beantwoord.

Een optimaliserende compiler merkt misschien dat deze code niets doet, en kan het allemaal weg optimaliseren. Het is de taak van de optimizer om dat soort dingen te doen, en vechten tegen de optimizer is een dwaze opdracht.

Ik raad aan om deze code te compileren met optimalisatie uitgeschakeld, omdat er geen goede manier is om elke optimizer voor de gek te houden die momenteel in gebruik is of in de toekomst zal worden gebruikt.

Iedereen die de optimizer aanzet en vervolgens klaagt over het bestrijden ervan, zou publiekelijk belachelijk moeten worden gemaakt.

Als ik om nanoseconde precisie zou geven, zou ik std::clock()niet gebruiken. Als ik de resultaten als een proefschrift zou willen publiceren, zou ik er meer over doen, en ik zou waarschijnlijk GCC, Tendra/Ten15, LLVM, Watcom, Borland, Visual C++, Digital Mars, ICC en andere compilers vergelijken. Zoals het is, duurt heaptoewijzing honderden keren langer dan stacktoewijzing, en ik zie niets nuttigs om de vraag verder te onderzoeken.

De optimizer heeft een missie om de code die ik aan het testen ben te verwijderen. Ik zie geen reden om de optimizer te vertellen om te draaien en dan te proberen de optimizer voor de gek te houden om niet echt te optimaliseren. Maar als ik daar waarde in zag, zou ik een of meer van de volgende dingen doen:

  1. Voeg een gegevenslid toe aan emptyen krijg toegang tot dat gegevenslid in de lus; maar als ik alleen maar uit het gegevenslid lees, kan de optimizer constant vouwen en de lus verwijderen; als ik alleen maar naar het gegevenslid schrijf, kan de optimizer alles overslaan, behalve de allerlaatste iteratie van de lus. Bovendien was de vraag niet “stacktoewijzing en gegevenstoegang versus heaptoewijzing en gegevenstoegang.”

  2. Verklaar evolatile, maar volatileis vaak onjuist gecompileerd(PDF).

  3. Neem het adres van ein de lus (en wijs het misschien toe aan een variabele die als externis gedeclareerd en in een ander bestand is gedefinieerd). Maar zelfs in dit geval kan de compiler opmerken dat — op de stapel tenminste — ealtijd op hetzelfde geheugenadres zal worden toegewezen, en dan constant vouwen zoals in (1) hierboven. Ik krijg alle herhalingen van de lus, maar het object wordt nooit echt toegewezen.

Buiten het voor de hand liggende, is deze test gebrekkig omdat hij zowel toewijzing als deallocatie meet, en de oorspronkelijke vraag ging niet over deallocatie. Natuurlijk worden variabelen die op de stapel zijn toegewezen automatisch ongedaan gemaakt aan het einde van hun bereik, dus het niet aanroepen van deletezou (1) de cijfers scheeftrekken (deallocatie van stapels is opgenomen in de cijfers over stapeltoewijzing, dus het is alleen redelijk te meten heap deallocation) en (2) een behoorlijk slecht geheugenlek veroorzaken, tenzij we een verwijzing naar de nieuwe aanwijzer behouden en deleteaanroepen nadat we onze tijdmeting hebben gedaan.

Op mijn computer, met g++ 3.4.4 op Windows, krijg ik “0 kloktikken” voor zowel stapel- als heaptoewijzing voor alles minder dan 100000 toewijzingen, en zelfs dan krijg ik “0 kloktikken” voor stapeltoewijzing en ” 15 kloktikken” voor heaptoewijzing. Als ik 10.000.000 toewijzingen meet, duurt de stapeltoewijzing 31 kloktikken en duurt de heaptoewijzing 1562 kloktikken.


Ja, een optimaliserende compiler kan het maken van lege objecten vermijden. Als ik het goed begrijp, kan het zelfs de hele eerste lus weglaten. Toen ik de iteraties opvoerde tot 10.000.000 stapeltoewijzingen, kostte het 31 kloktikken en de heaptoewijzing 1562 kloktikken. Ik denk dat het veilig is om te zeggen dat zonder g++ te vertellen om het uitvoerbare bestand te optimaliseren, g++ de constructors niet heeft verwijderd.


In de jaren sinds ik dit schreef, ging de voorkeur op Stack Overflow uit naar het posten van prestaties van geoptimaliseerde builds. In het algemeen denk ik dat dit correct is. Ik vind het echter nog steeds dwaas om de compiler te vragen om code te optimaliseren, terwijl je die code eigenlijk niet wilt optimaliseren. Het lijkt me erg vergelijkbaar met extra betalen voor valet parking, maar weigeren de sleutels te overhandigen. In dit specifieke geval wil ik niet dat de optimizer wordt uitgevoerd.

Een licht gewijzigde versie van de benchmark gebruiken (om het geldige punt aan te pakken dat het originele programma niet elke keer via de lus iets op de stapel heeft toegewezen) en compileren zonder optimalisaties maar door te linken naar releasebibliotheken (om het geldige punt aan te pakken) dat we geen vertraging willen opnemen die wordt veroorzaakt door koppelingen naar foutopsporingsbibliotheken):

#include <cstdio>
#include <chrono>
namespace {
    void on_stack()
    {
        int i;
    }
    void on_heap()
    {
        int* i = new int;
        delete i;
    }
}
int main()
{
    auto begin = std::chrono::system_clock::now();
    for (int i = 0; i < 1000000000; ++i)
        on_stack();
    auto end = std::chrono::system_clock::now();
    std::printf("on_stack took %f seconds\n", std::chrono::duration<double>(end - begin).count());
    begin = std::chrono::system_clock::now();
    for (int i = 0; i < 1000000000; ++i)
        on_heap();
    end = std::chrono::system_clock::now();
    std::printf("on_heap took %f seconds\n", std::chrono::duration<double>(end - begin).count());
    return 0;
}

toont:

on_stack took 2.070003 seconds
on_heap took 57.980081 seconds

op mijn systeem wanneer gecompileerd met de opdrachtregel cl foo.cc /Od /MT /EHsc.

Misschien ben je het niet eens met mijn aanpak om een ​​niet-geoptimaliseerde build te krijgen. Dat is prima: voel je vrij om de benchmark zo vaak aan te passen als je wilt. Als ik optimalisatie inschakel, krijg ik:

on_stack took 0.000000 seconds
on_heap took 51.608723 seconds

Niet omdat de toewijzing van stacks in feite onmiddellijk is, maar omdat elke fatsoenlijke compiler kan opmerken dat on_stackniets nuttigs doet en kan worden geoptimaliseerd. GCC op mijn Linux-laptop merkt ook dat on_heapniets nuttigs doet, en optimaliseert het ook weg:

on_stack took 0.000003 seconds
on_heap took 0.000002 seconds

Antwoord 4, autoriteit 6%

Een interessant ding dat ik heb geleerd over Stack vs. Heap Allocation op de Xbox 360 Xenon-processor, wat ook van toepassing kan zijn op andere multicore-systemen, is dat het toewijzen op de Heap ervoor zorgt dat een kritieke sectie wordt ingevoerd om alle andere cores te stoppen, zodat de toewijzing is niet in strijd. Dus, in een strakke lus, was Stack Allocation de beste keuze voor arrays met een vaste grootte, omdat het stallen voorkwam.

Dit kan een andere versnelling zijn om te overwegen als je codeert voor multicore/multiproc, in die zin dat je stacktoewijzing alleen kan worden bekeken door de core die je scoped-functie uitvoert, en dat heeft geen invloed op andere cores/CPU’s.

p>


Antwoord 5, autoriteit 4%

Je kunt een speciale heap-allocator schrijven voor objecten met specifieke afmetingen die zeer goed presteren. De algemeneheapallocator is echter niet bijzonder efficiënt.

Ook ben ik het eens met Torbjörn Gyllebring over de verwachte levensduur van objecten. Goed punt!


Antwoord 6, autoriteit 2%

Ik denk niet dat stapeltoewijzing en heaptoewijzing over het algemeen uitwisselbaar zijn. Ik hoop ook dat de prestaties van beide voldoende zijn voor algemeen gebruik.

Ik zou het ten zeerste aanbevelen voor kleine items, welke het meest geschikt is voor de reikwijdte van de toewijzing. Voor grote items is de hoop waarschijnlijk nodig.

Op 32-bits besturingssystemen die meerdere threads hebben, is de stapel vaak vrij beperkt (hoewel meestal tot ten minste een paar mb), omdat de adresruimte moet worden opgedeeld en vroeg of laat zal de ene thread-stack in de andere lopen . Op single-threaded systemen (in ieder geval Linux glibc single-threaded) is de beperking veel minder omdat de stapel gewoon kan groeien en groeien.

Op 64-bits besturingssystemen is er voldoende adresruimte om threadstacks behoorlijk groot te maken.


Antwoord 7

Gewoonlijk bestaat stapeltoewijzing alleen uit het aftrekken van het stapelaanwijzerregister. Dit is ton sneller dan een hoop zoeken.

Soms vereist stapeltoewijzing het toevoegen van een pagina(‘s) virtueel geheugen. Voor het toevoegen van een nieuwe pagina met geheugen op nul is het niet nodig om een ​​pagina van de schijf te lezen, dus meestal gaat dit nog steeds tonnen sneller dan het doorzoeken van een heap (vooral als een deel van de heap ook was uitgewisseld). In een zeldzame situatie, en je zou zo’n voorbeeld kunnen maken, is er toevallig genoeg ruimte beschikbaar in een deel van de heap die al in het RAM zit, maar het toewijzen van een nieuwe pagina voor de stapel moet wachten tot een andere pagina wordt weggeschreven naar schijf. In die zeldzame situatie is de hoop sneller.


Antwoord 8

Afgezien van het enorme prestatievoordeel ten opzichte van heaptoewijzing, heeft stacktoewijzing de voorkeur voor langlopende servertoepassingen. Zelfs de best beheerde hopen raken uiteindelijk zo gefragmenteerd dat de prestaties van applicaties afnemen.


Antwoord 9

Waarschijnlijk het grootste probleem van heap-toewijzing versus stapel-toewijzing, is dat heap-toewijzing in het algemeen een onbeperkte bewerking is, en dus kunt u het niet gebruiken waar timing een probleem is.

Voor andere toepassingen waar timing geen probleem is, maakt het misschien niet zoveel uit, maar als u veel toewijst, heeft dit invloed op de uitvoeringssnelheid. Probeer altijd de stapel te gebruiken voor kortstondig en vaak toegewezen geheugen (bijvoorbeeld in lussen), en zo lang mogelijk – doe heaptoewijzing tijdens het opstarten van de toepassing.


Antwoord 10

Een stapel heeft een beperkte capaciteit, een heap niet. De typische stapel voor een proces of thread is ongeveer 8K. Je kunt de maat niet meer wijzigen als deze eenmaal is toegewezen.

Een stapelvariabele volgt de scopingregels, terwijl een heapvariabele dat niet doet. Als je instructieaanwijzer verder gaat dan een functie, verdwijnen alle nieuwe variabelen die bij de functie horen.

Het belangrijkste van alles is dat u de algemene functieaanroepketen niet van tevoren kunt voorspellen. Dus een toewijzing van slechts 200 bytes van uw kant kan een stapeloverloop veroorzaken. Dit is vooral belangrijk als je een bibliotheek schrijft, geen applicatie.


Antwoord 11

Het is niet alleen de stapeltoewijzing die sneller is. Je wint ook veel bij het gebruik van stapelvariabelen. Ze hebben een betere referentielocatie. En tot slot is deallocatie ook een stuk goedkoper.


Antwoord 12

Ik denk dat de levensduur cruciaal is, en of het ding dat wordt toegewezen op een complexe manier moet worden gebouwd. Bij transactiegestuurde modellering moet u bijvoorbeeld meestal een transactiestructuur met een heleboel velden invullen en doorgeven aan bewerkingsfuncties. Kijk voor een voorbeeld naar de OSCI SystemC TLM-2.0-standaard.

Het toewijzen van deze op de stapel dicht bij de aanroep van de operatie heeft de neiging om enorme overhead te veroorzaken, omdat de constructie duur is. De goede manier is om de transactieobjecten toe te wijzen aan de heap en ze opnieuw te gebruiken, hetzij door pooling of door een eenvoudig beleid zoals “deze module heeft maar één transactieobject ooit nodig”.

Dit is vele malen sneller dan het toewijzen van het object bij elke bewerkingsaanroep.

De reden is simpelweg dat het object een dure constructie heeft en een vrij lange levensduur heeft.

Ik zou zeggen: probeer beide en kijk wat het beste werkt in jouw geval, want het kan echt afhangen van het gedrag van je code.


Antwoord 13

Stacktoewijzing is een paar instructies, terwijl de snelste rtos-heapallocator die ik ken (TLSF) gemiddeld 150 instructies gebruikt. Ook voor stapeltoewijzingen is geen vergrendeling vereist omdat ze lokale threadopslag gebruiken, wat weer een enorme prestatiewinst is. Dus stapeltoewijzingen kunnen 2-3 orden van grootte sneller zijn, afhankelijk van hoe zwaar multithreaded uw omgeving is.

Over het algemeen is heaptoewijzing uw laatste redmiddel als u om prestaties geeft. Een haalbare tussenoptie kan een vaste pool-allocator zijn, die ook slechts een paar instructies is en zeer weinig overhead per toewijzing heeft, dus het is geweldig voor kleine objecten met een vaste grootte. Het nadeel is dat het alleen werkt met objecten met een vaste grootte, niet inherent threadveilig is en problemen met blokfragmentatie heeft.


Antwoord 14

Zoals anderen al hebben gezegd, is de stapeltoewijzing over het algemeen veel sneller.

Als uw objecten echter duur zijn om te kopiëren, kan het toewijzen op de stapel later leiden tot een enorme prestatiehit wanneer u de objecten gebruikt als u niet voorzichtig bent.

Als u bijvoorbeeld iets op de stapel toewijst en het vervolgens in een container plaatst, zou het beter zijn geweest om het op de heap toe te wijzen en de aanwijzer in de container op te slaan (bijvoorbeeld met een std::shared_ptr<> ). Hetzelfde geldt als u objecten op waarde doorgeeft of retourneert, en andere vergelijkbare scenario’s.

Het punt is dat hoewel stapeltoewijzing in veel gevallen meestal beter is dan heaptoewijzing, soms als u uw best doet om stapeltoewijzing te doen wanneer dit niet het beste past in het berekeningsmodel, dit soms meer problemen kan veroorzaken dan het lost op.


Antwoord 15

Er moet een algemeen punt worden gemaakt over dergelijke optimalisaties.

De optimalisatie die u krijgt, is evenredig met de tijd dat de programmateller zich daadwerkelijk in die code bevindt.

Als je de programmateller proeft, zul je ontdekken waar het zijn tijd doorbrengt, en dat is meestal in een klein deel van de code, en vaak in bibliotheekroutines waar je geen controle over hebt.

Alleen als je merkt dat het veel tijd besteedt aan de heap-toewijzing van je objecten, zal het merkbaar sneller zijn om ze te stapelen.


Antwoord 16

Stacktoewijzing zal bijna altijd even snel of sneller zijn dan heaptoewijzing, hoewel het zeker mogelijk is voor een heapallocator om eenvoudigweg een op stack gebaseerde toewijzingstechniek te gebruiken.

Er zijn echter grotere problemen bij het omgaan met de algehele prestaties van stapel versus heap gebaseerde toewijzing (of in iets betere termen, lokale versus externe toewijzing). Meestal is heap (externe) toewijzing traag omdat het te maken heeft met veel verschillende soorten toewijzingen en toewijzingspatronen. Het verkleinen van de reikwijdte van de toewijzing die u gebruikt (het maken van lokaal naar het algoritme / code) zal de neiging hebben de prestaties te verhogen zonder grote wijzigingen. Het toevoegen van een betere structuur aan uw toewijzingspatronen, bijvoorbeeld, het forceren van een LIFO-ordening op toewijzing en deallocatieparen kan ook de prestaties van uw toewijzing verbeteren door de allocator op een eenvoudiger en meer gestructureerde manier te gebruiken. Of, u kunt een toewijzing of schrijven of schrijven voor uw specifieke toewijzingspatroon; De meeste programma’s weken regelmatig een paar afzonderlijke afmetingen toe, dus een hoop die gebaseerd is op een lookaanbuffer van enkele vaste (bij voorkeur bekende) maten zal buitengewoon goed presteren. Windows gebruikt om deze reden de lage fragmentatie-heap.

Aan de andere kant is stapelgebaseerde toewijzing op een 32-bits geheugenbereik ook vol met gevaar als u te veel draden hebt. Stapels hebben een aangrenzend geheugenbereik nodig, dus hoe meer threads die je hebt, hoe meer virtuele adresruimte je nodig hebt om te rennen zonder een stapel overloop. Dit zal geen probleem (voorlopig) met 64-bit, maar het kan zeker ravoc in lange lopende programma’s met veel draden aanrichten. Het oplossen van virtuele adresruimte als gevolg van fragmentatie is altijd een pijn om mee om te gaan.


17

Opmerking dat de overwegingen typisch niet over snelheid en prestaties zijn bij het kiezen van stapel versus heap-toewijzing. De stapel fungeert als een stapel, wat betekent dat het goed geschikt is voor het duwen van blokken en ze weer te duwen, de laatste in, eerst uit. Uitvoering van procedures is ook stapelachtig, de ingevoerde procedure is eerst wordt afgesloten. In de meeste programmeertalen zijn alle variabelen die nodig zijn in een procedure alleen zichtbaar zijn tijdens de uitvoering van de procedure, waardoor ze worden ingedrukt bij het invoeren van een procedure en verschijnen de stapel bij afsluiten of terugkeren.

Nu voor een voorbeeld waar de stapel niet kan worden gebruikt:

Proc P
{
  pointer x;
  Proc S
  {
    pointer y;
    y = allocate_some_data();
    x = y;
  }
}

Als u een geheugen in de procedure slokt en op de stapel plaatst en vervolgens afsluit, worden de toegewezen gegevens uitgeput van de stapel. Maar de variabele X in P wees ook op die gegevens, dus X wijst nu naar een plaats onder de stapelpointer (aannemende stapel groeit naar beneden) met een onbekende inhoud. De inhoud kan er nog steeds zijn als de stapelaanwijzer net is verplaatst zonder de gegevens eronder te wissen, maar als u begint met het toewijzen van nieuwe gegevens op de stapel, kan de aanwijzer X in plaats daarvan daadwerkelijk aanwijzen aan die nieuwe gegevens.


18

class Foo {
public:
    Foo(int a) {
    }
}
int func() {
    int a1, a2;
    std::cin >> a1;
    std::cin >> a2;
    Foo f1(a1);
    __asm push a1;
    __asm lea ecx, [this];
    __asm call Foo::Foo(int);
    Foo* f2 = new Foo(a2);
    __asm push sizeof(Foo);
    __asm call operator new;//there's a lot instruction here(depends on system)
    __asm push a2;
    __asm call Foo::Foo(int);
    delete f2;
}

Het zou zo zijn in ASM. Wanneer u in funchebt, is de f1en POWER f2toegewezen aan stapel (geautomatiseerde opslag). En trouwens, FOO f1(a1)heeft geen instructie-effecten op stapelpointer (esp), het is toegewezen, indien funcWANNOOD Download het lid f1, IT’s Instructie is zoiets: lea ecx [ebp+f1], call Foo::SomeFunc(). Een ander ding dat de stapel toewijst, kan iemand denken dat het geheugen zoiets is als FIFO, de FIFOis net gebeurd als u in een functie gaat, als u iets in de functie bent en iets in de functie bent zoals int i = 0, er gebeurde er geen druk op.


19

Er is al eerder vermeld dat stapeltoewijzing eenvoudigweg de stapelaanwijzer verplaatst, dat wil zeggen een enkele instructie op de meeste architecturen. Vergelijk dat met wat in het algemeengebeurt in het geval van heaptoewijzing.

Het besturingssysteem houdt delen van het vrije geheugen bij als een gekoppelde lijst met de payload-gegevens bestaande uit de aanwijzer naar het startadres van het vrije deel en de grootte van het vrije deel. Om X bytes geheugen toe te wijzen, wordt de lijst met links doorlopen en wordt elke noot in volgorde bezocht, waarbij wordt gecontroleerd of de grootte ten minste X is. Wanneer een gedeelte met de grootte P >= X wordt gevonden, wordt P in twee delen gesplitst met maten X en PX. De gekoppelde lijst wordt bijgewerkt en de aanwijzer naar het eerste deel wordt teruggestuurd.

Zoals je kunt zien, hangt de heaptoewijzing af van factoren zoals hoeveel geheugen je opvraagt, hoe gefragmenteerd het geheugen is, enzovoort.


Antwoord 20

Over het algemeen is stapeltoewijzing sneller dan heaptoewijzing, zoals door bijna elk antwoord hierboven wordt vermeld. Een stack push of pop is O(1), terwijl het toewijzen of vrijmaken van een heap een wandeling van eerdere toewijzingen kan vereisen. Normaal gesproken zou je echter niet moeten toewijzen in strakke, prestatie-intensieve loops, dus de keuze zal meestal neerkomen op andere factoren.

Het is misschien goed om dit onderscheid te maken: je kunt een “stack allocator” op de heap gebruiken. Strikt genomen bedoel ik met stapeltoewijzing de feitelijke toewijzingsmethode in plaats van de locatie van de toewijzing. Als je veel dingen op de eigenlijke programma-stack toewijst, kan dat om verschillende redenen slecht zijn. Aan de andere kant is het gebruik van een stapelmethode om op de heap toe te wijzen wanneer mogelijk de beste keuze die je kunt maken voor een allocatiemethode.

Omdat je Metrowerks en PPC noemde, neem ik aan dat je Wii bedoelt. In dit geval is geheugen kostbaar en het gebruik van een stapeltoewijzingsmethode waar mogelijk garandeert dat u geen geheugen verspilt aan fragmenten. Dit vereist natuurlijk veel meer zorg dan “normale” heap-toewijzingsmethoden. Het is verstandig om de afwegingen voor elke situatie te evalueren.


Antwoord 21

Doe nooit voorbarige veronderstellingen, aangezien andere applicatiecode en gebruik uw functie kunnen beïnvloeden. Dus kijken naar de functie is dat isolatie geen zin heeft.

Als je serieus bent met applicaties, VTune het dan of gebruik een vergelijkbare profileringstool en kijk naar hotspots.

Ketan


Antwoord 22

Ik zou willen zeggen dat code daadwerkelijk wordt gegenereerd door GCC (ik herinner me ook VS) geen overhead heeft om stacktoewijzing uit te voeren.

Zeg voor de volgende functie:

 int f(int i)
  {
      if (i > 0)
      {   
          int array[1000];
      }   
  }

Hier volgt de code die wordt gegenereerd:

 __Z1fi:
  Leh_func_begin1:
      pushq   %rbp
  Ltmp0:
      movq    %rsp, %rbp
  Ltmp1:
      subq    $**3880**, %rsp <--- here we have the array allocated, even the if doesn't excited.
  Ltmp2:
      movl    %edi, -4(%rbp)
      movl    -8(%rbp), %eax
      addq    $3880, %rsp
      popq    %rbp
      ret 
  Leh_func_end1:

Dus hoeveel lokale variabele je ook hebt (zelfs binnen als of switch), alleen de 3880 zal veranderen in een andere waarde. Tenzij u geen lokale variabele had, hoeft u deze instructie alleen maar uit te voeren. Dus het toewijzen van een lokale variabele heeft geen overhead.

Other episodes