webForumDet fria alternativet

Allokering av variabler

34 svar · 1 109 visningar · startad av Sang-drax

Sang-draxMedlem sedan juli 2002581 inlägg
#1

Denna tråd fortsätter off-topic diskussionen i
http://www.webforum.nu/showthread.php?s=&threadid=75870

Jag menar att man ska vänta med deklareringen om man kan, medan PeW menar att det inte har någon betydelse för hastigeten och bör undvikas.

Här kommer ett mycket enkelt exempel som visar på att olika maskinkod kommer att genereras. I detta fall används en enkel int, vilket gör att vinsten endast blir en enda mov instruktion. Men testar man detta med std::string eller andra mer komplexa datatyper, kommer skillnaden att bli mer markant.

Deklaration på olika ställen

    
    int a=2;
0040103A: C745F802000000  mov      dword ptr [ebp-0x8],0x2
    
    
    if (b)
00401041: 807DFF00        cmp      byte ptr [ebp-0x1],0x0
00401045: 7410            je       main+0x47 (0x401057)h
    {
        int c=2;
00401047: C745F402000000  mov      dword ptr [ebp-0xc],0x2
        
        //anrop av funktion
        f(c);
0040104E: FF75F4          push     dword ptr [ebp-0xc]
00401051: E8AAFFFFFF      call     f(int) (0x401000)5
00401056: 59              pop      ecx
    }
    
    //Slut på intressant kod

Deklaration på samma ställe

    //Start av intressant kod
    
    int a=2,c=2;
00401034: C745F802000000  mov      dword ptr [ebp-0x8],0x2
0040103B: C745F402000000  mov      dword ptr [ebp-0xc],0x2
    
    
    if (b)
00401042: 807DFF00        cmp      byte ptr [ebp-0x1],0x0
00401046: 7409            je       main+0x41 (0x401051)h
    {
        //anrop av funktion
        f(c);
00401048: FF75F4          push     dword ptr [ebp-0xc]
0040104B: E8B0FFFFFF      call     f(int) (0x401000)5
00401050: 59              pop      ecx
    }
    
    //Slut på intressant kod

Visserligen har PeW rätt när han menar att stacken allokeras när funktionen anropas, men som synes finns andra faktorer med i bilden, till exempel initering av variabler och komplexa datatyper såsom std::string.

Betänk följande kod:

// this function defines the variable "encrypted" too soon
string encryptPassword(const string& password)
{
  string encrypted;
  if (password.length() < MINIMUM_PASSWORD_LENGTH) {
     throw logic_error("Password is too short");
  }
  //do whatever is necessary to place an encrypted
  //version of password in encrypted;
  return encrypted;
}

Följande kod kommer att gå snabbare:

// this function postpones "encrypted"'s definition until
// it's truly necessary
string encryptPassword(const string& password)
{
  if (password.length() < MINIMUM_PASSWORD_LENGTH) {
    throw logic_error("Password is too short");
  }
  string encrypted;
  //do whatever is necessary to place an encrypted
  //version of password in encrypted;
  return encrypted;
}

Jag rekommenderar än en gång boken Effective C++ (från vilken exemplet är hämtat), för andra argument som talar för samma sak.

"By postponing variable definitions, you improve program efficiency, increase program clarity, and reduce the need to document variable meanings. It looks like it's time to kiss those block-opening variable definitions good-bye. "

PeWMedlem sedan juni 200010 432 inlägg
#2

Det är i bägge fallen exakt samma instruktioner och samma adresser. Det som sker är en allokering på samma ställe, dvs 4 bytes efter int a, som hittas på 0x8 från basen. Stacken är redan allokerad i bägge fallen och det sker som sagt i början av funktionen. Av detta kan vi anta att C++ kompilatorn redan har allokerat denna efter att först ha scannat igenom all deklaration och sen uppdaterat sin lista innan nästa steg ska ske. Min tes om att det blir samma sak är därmed i detta fall bevisat då avståndet mellan allokerat utrymme och användandet är lika i båda fallen ;)

Att std biblioteken är uppbyggda av en otroligt effektiv blandning mellan c och asm-kod betvivlar jag dock inte.

Jag tycker denna diskussion är kalas och det vore bra med mera exempel och kommentarer :)

Sang-draxMedlem sedan juli 2002581 inlägg
#3

Ja, det är samma instruktion och adress. Det som skiljer är var den dyker upp. Allokeringen är redan gjord, men den första koden är mer effektiv då den andra mov-instruktionen inte alltid utförs, utan bara när den behöver utföras, till skillnad mot det andra exemplet då den utförs vare sig det behövs eller ej.

PeWMedlem sedan juni 200010 432 inlägg
#4

Men mov instruktionen är bara en tilldelning, ingen deklaration / allokering ;)

PeWMedlem sedan juni 200010 432 inlägg
#5

Vilket ger att med en modern processor förlorar man mellan 1 - 1/4 klockcykel mellan dina exempel om villkoret b inte finns. I andra fall av deklarationer kan det bli precis tvärtom så för den som programmerar gäller det att tänka "rätt". Ingen direkt fördel då det ställer krav om hög nivå på kunskaperna, eller hur? Samma exempel skulle då skrivas

int a;
int c;

a = 2;

if(b){
   c = 2;
  ...
}

osv

med den notation jag förespråkar. Ingen skillnad ;)

Sang-draxMedlem sedan juli 2002581 inlägg
#6

PeW skrev:

Men mov instruktionen är bara en tilldelning, ingen deklaration / allokering

Nu var ju det ett enkelt exempel med en simpel datatyp. 'Allokering' av en int innebär 0 intruktioner, men så lätt kommer du inte undan om vi använder lite mer avancerade datatyper än ints.

Dessutom så kan kompilatorn spara utrymme genom att lagra flera variabler på samma utrymme och därmed behöva allokera mindre minnne när funktionen anropas:

int main()
{
  
  if ()
  {
    int a;
    cout << &a;
  }

  if ()
  {
    double c;
    cout << &c;
  }
   
}

Här kan utrymme sparas genom att lägga variablerna på samma utrymme i minnet, eftersom de omöjligen kan användas utanför respektive if-sats.

EDIT:
Har just testat och upptäckt att ovan nämnda optimering utförs i Visual C++ 7.0. Samma adress skrivs ut i båda fallen. Detta sker givetvis inte om man deklarerar variablerna i början.
Det finns därmed utrymme att spara även med enkla datatyper.

PeWMedlem sedan juni 200010 432 inlägg
#7

Nu var ju det ett enkelt exempel med en simpel datatyp. 'Allokering' av en int innebär 0 intruktioner, men så lätt kommer du inte undan om vi använder lite mer avancerade datatyper än ints.

I assembler finns det inga "datatyper" mer än signed, unsigned och antal bytes. En "avancerad" datatyp är bara en fråga om hur många bytes som ska allokeras - inget annat. Kompilatorn summerar antalet bytes som behövs på den "lokala" stacken och allokerar plats för det. Om det så är en int eller 42 datatyper av modell 555 bytes "objekt" har ingen betydelse. Samma stack, samma adresser samma allokering (förutom storleken då ;)).

Dessutom så kan kompilatorn spara utrymme genom att lagra flera variabler på samma utrymme och därmed behöva allokera mindre minnne när funktionen anropas

Du pratar nu om "scope", summa sumarum blir det samma effekt som med exemplet ovan... samma stack, samma pekare, samma allokering. Dvs minnet allokeras när funktionen anropas och inte mitt i. Det som skiljer sig när ett scope finns i funktioner är att stackpekaren pekar på en ett eget utrymme (som ändock allokerades vid funktionens inträde).

Jag hävdar fortfarande att detta med lokala deklarationer är i första hand till för kodarens "idealbild" och inte för programkörningens effektivitet. Sen anser jag fortfarande att denna "idealbild" för programmeraren endast är fåfänga och inte till nån större nytta mer än att man kan bygga mer synbart finessrik kod - om man kan sina saker. Kan man inte det blir det lätt en soppa som är svår att överblicka :)

PeWMedlem sedan juni 200010 432 inlägg
#8

men så lätt kommer du inte undan

Har inte för avsikt att "komma undan", utan väntar med spänning på att få se hur C++ kompilatorn kan producera effektivare kod med lokala deklarationer än C's strikta variant (som jag f.ö föredrar tillsvidare) :)

PeWMedlem sedan juni 200010 432 inlägg
#9

EDIT:
Har just testat och upptäckt att ovan nämnda optimering utförs i Visual C++ 7.0. Samma adress skrivs ut i båda fallen. Detta sker givetvis inte om man deklarerar variablerna i början.

Frågan är hur stort minne som allokerades? Troligen allokerades det minne för både en int och en double, men eftersom det ena kom före det andra så utföll adresslotteriet till den enas fördel och den andra fick resten om den så ville. Eller menar du att båda variablerna skulle haft plats för 4 bytes enbart? Hur stor var stacken?

PeWMedlem sedan juni 200010 432 inlägg
#10

Ok.. för att utröna påverkan av det allokerade utrymmet på stacken så testade jag följande funktioner i vc6:

1

void f(){

cout << "tjo";
int v = 2;

if(v==1){
	double t = 2;
}
if(v==2){
	int p = 3;
	}
}

2

void f(){
	double t;
           int p;
	int v = 2;
cout << "tjo";

if(v==1){
	 t = 2;
}
if(v==2){
	 p = 3;
	}
}

Vilket med ditt resonemang (för att spara utrymme på stacken) borde ge att en 4 bytes mindre stack allokerades. Men:

1

5:    void f(){
00401150   push        ebp
00401151   mov         ebp,esp
00401153   sub         esp,50h
00401156   push        ebx
00401157   push        esi
00401158   push        edi
00401159   lea         edi,[ebp-50h]
0040115C   mov         ecx,14h
00401161   mov         eax,0CCCCCCCCh
00401166   rep stos    dword ptr [edi]
6:    cout << "tjo";
00401168   push        offset string "tjo" (0043501c)
0040116D   push        offset std::cout (0043c8b8)
00401172   call        @ILT+145(std::operator<<) (00401096)
00401177   add         esp,8
7:    int v = 2;
0040117A   mov         dword ptr [ebp-4],2
8:
9:    if(v==1){
00401181   cmp         dword ptr [ebp-4],1
00401185   jne         f+45h (00401195)
10:       double t = 2;
00401187   mov         dword ptr [t],0
0040118E   mov         dword ptr [ebp-8],40000000h
11:   }
12:   if(v==2){
00401195   cmp         dword ptr [ebp-4],2
00401199   jne         f+52h (004011a2)
13:       int p = 3;
0040119B   mov         dword ptr [p],3
14:       }
15:   }
004011A2   pop         edi
004011A3   pop         esi
004011A4   pop         ebx
004011A5   add         esp,50h
004011A8   cmp         ebp,esp
004011AA   call        __chkesp (00408eb0)
004011AF   mov         esp,ebp
004011B1   pop         ebp
004011B2   ret

2

5:    void f(){
00401150   push        ebp
00401151   mov         ebp,esp
00401153   sub         esp,50h
00401156   push        ebx
00401157   push        esi
00401158   push        edi
00401159   lea         edi,[ebp-50h]
0040115C   mov         ecx,14h
00401161   mov         eax,0CCCCCCCCh
00401166   rep stos    dword ptr [edi]
6:        double t;
7:        int p;
8:        int v = 2;
00401168   mov         dword ptr [ebp-10h],2
9:    cout << "tjo";
0040116F   push        offset string "tjo" (0043501c)
00401174   push        offset std::cout (0043c8b8)
00401179   call        @ILT+145(std::operator<<) (00401096)
0040117E   add         esp,8
10:
11:   if(v==1){
00401181   cmp         dword ptr [ebp-10h],1
00401185   jne         f+45h (00401195)
12:        t = 2;
00401187   mov         dword ptr [ebp-8],0
0040118E   mov         dword ptr [ebp-4],40000000h
13:   }
14:   if(v==2){
00401195   cmp         dword ptr [ebp-10h],2
00401199   jne         f+52h (004011a2)
15:        p = 3;
0040119B   mov         dword ptr [ebp-0Ch],3
16:       }
17:   }
004011A2   pop         edi
004011A3   pop         esi
004011A4   pop         ebx
004011A5   add         esp,50h
004011A8   cmp         ebp,esp
004011AA   call        __chkesp (00408eb0)
004011AF   mov         esp,ebp
004011B1   pop         ebp
004011B2   ret

Om man betraktar raderna med "sub esp,xxx" som betyder allokerande av stacken i funktionen i båda fallen, ser man att den är lika stor i båda fallen (0x50h). Vidare är instruktionerna för 'double' variabeln lika.. vilka i sig är "avancerade datatyper" då flyttalsprocessorn får lite rutiner och det utförs mera nyttjande av klockcyklar. Dessa torde vara lika i båda fallen och det faller utanför allokerande & den intressanta tidsrymden. När det gäller int'arna finns en skillnad. Den som är deklarerad lokalt i scope har en label som adress mot den som är pekad med en fix adress mot stacken. Anledningen som jag ser det är precis som jag tidigare skrev - variabeln får den adressen som är ledig - på stacken. (som i bägge fallen var lika stor).

Därvid anser jag att det är mer eller mindre fastställt att "sparandet" av minne är ett mer intelligent beteende än vad kompilatorn klarar och således en inte riktigt sanningsenligt. Påståendet om att det sparas tid kan jag hålla med om, men då på utvecklingsstadiet och inte under programkörning.

Att egna datatyper skulle förändra beteendet på maskinkodsnivån såpass att den grundläggande stackhanteringen åsidosätts är föga troligt - men vem vet? Du kanske har illustrerande svar på det, Sang-drax?

red
stavfel

tillägg 25/5:
En int och en double belastar inte stacken särskilt mycket så jag gjorde om testet med att låta bägge variablerna vara arrays av storlek 1024 istället. Resultatet blev dock fortfarande detsamma i båda fallen men storleken ökades (naturligtvis) från 50h till 3044h.

*kommentar:
Det som jag finner udda är storleken på stacken. 1024 * 4 * 2 ger 0x2000 och inte 0x3044... varför allokeras 0x1040 extra (-4 för int v)? Resultatet är iofs lika i båda fallen, men ändock slösas(?) det exponentiellt med utrymme. Är detta fenomen kompilatorspecifikt eller finns det en naturlig förklaring som jag ivf inte ser?

Sang-draxMedlem sedan juli 2002581 inlägg
#11

Klart intressant debatt, hehe.

Du har uppenbarligen inte kompilerat med optimeringar, ty då skulle hela if-statserna ha försvunnit, då v har ett känt värde.

Det framgår inte heller av din kod att variablerna skulle ha lagrats på samma ställe. Pröva att skriva ut adresserna.

VC6 optimerar möjligen inte på det sättet.

Egna datatyper påverkar så tillvida att de ofta har konstruktorer som anropas. Alltså tar följande kod längre tid att exekvera:

string s;
if ()
{
  s = "hej";
}

än endast

..
if()
{
  string s = "hej";
}

Precis som jag påpekade i mina föregående poster.

Sang-draxMedlem sedan juli 2002581 inlägg
#12

Här kommer ett lite mer komplext exempel som visar hur VC7 optimerar när variablerna deklareras on the fly.

f() är en rekursiv funktion som anropas från main()
a och b är bool som läses in från tangentbordet till true i main()
count är en global variablel som sätts till 0 innan f() anropas

Poängen är att se hur länge f kan köras innan stacken tar slut.

void f()
{
    //Utan denna variabel krashar aldrig funktionen
    //när w och q är deklarerade inom if-statserna
    char data[10000] = {0};

    //double w[10000] = {5}; 
    //int q[10000] = {5}; 

    if (b)
    {
        int q[10000] = {5};   
        std::cout << &q;
    }
    
    if (a)
    {
        double w[10000] = {5}; 
        std::cout << &w;
    }

    count++;

    std::cout << "Anrop nummer " << count <<  data << std::endl;

    //Rekursiv funktion
    f();

}

Hur många gånger gick då denna funktion att köra?

Jo, om jag deklarerade variablerna i början av funktionen (de som är bortkommenterade här) erhölls en stackoverflow i 8:e anropet.

Om jag däremot anropade funktionen med variablerna deklarerade inom if-statserna krashade funktionen vid anrop nummer 12. Följdaktligen hade mindre minne allokerats.

Kompilatorn var faktiskt så smart att om inte variablen 'data' hade införts, så hade funktioen hållt på mycket, mykcet länge (>40 000 anrop)

Du kan ju själv testa detta exempel, men jag vet ej om det funkar i VC6.

PeWMedlem sedan juni 200010 432 inlägg
#13

Du har uppenbarligen inte kompilerat med optimeringar, ty då skulle hela if-statserna ha försvunnit, då v har ett känt värde.

Nej jag har inte kompilerat med optimeringar, men det ska inte heller behövas då vi talade om ren standard c++ kompilering och med en optimering blir det kompiltatorspecifikt och vi faller utanför resonemangets hörnsten. Det spelar ingen roll för exemplet var bara illustrativt och man testar inte ett känt värde på samma vis IRL. Du kan "tänka dig" ett okänt värde för att få fram det jag menar, ty det ger samma sak som i exemplet utan optimeringar.

Det framgår inte heller av din kod att variablerna skulle ha lagrats på samma ställe. Pröva att skriva ut adresserna.

Utskriften om adresserna finns visst med.

15:        p = 3;
0040119B   mov         dword ptr [ebp-0Ch],3

Vilket ger att int p har en adress på 0Ch från basen. Det är dennes adress. Men i diskussionen om utnyttjande av minne är själva adressen ointressant då minnet som utnyttjas är ekvalient med storleken på stacken. Ett anrop av en egen datatyp på det sättet du nu visade har inte heller med minnesresonemanget i stacken (det är inte funktionens stack som allokeras utan en reserverad del av minnet) att göra men som du skrev så anropas konstruktorn i två olika fall och en optimering med avseende på tid finns. Att jämföra med funktionsanrop, vilka alltid ska göras när de behövs och inte annars. Så 1 fall finns, där det är en fördel pga tid, samma sak uppnås dock med pekare enligt den notationen jag föredrar ;)

red
sär skriv ning

PeWMedlem sedan juni 200010 432 inlägg
#14

Du kan ju själv testa detta exempel, men jag vet ej om det funkar i VC6.

Sure thing!
Tar det lite senare då annat kom före... I'll be back :)

Men om det inte funkar utan optimeringar i VC6 har vi hamnat utanför diskussionens område då jag redan från början talade om att det beror på hur 'smart' den specifika kompilatorn är och att det inte är säkert att det fungerar generellt ;)

PeWMedlem sedan juni 200010 432 inlägg
#15

Ang ditt exempel, Sang-drax

Samma exempel gav overflow vid 8:e anropet i båda fallen för mig med vc6. Resulatet är analogt med resultatet från det exempel jag tidigare skrev, dvs samma stack, samma antal bytes m.m.. för subrutinen.

Jag misstänker att din körning med allokerande lokalt (som enligt dig gav 12 ggr) inte alls gav ett 'smartare' minnesutnyttjande utan att det helt enkelt allokerades en större stack och då ger snarare ett "sämre" utnyttjande av minnet. Kan du visa asmkoden från din kompilator i just dessa fall?

Sang-draxMedlem sedan juli 2002581 inlägg
#16

Jag kan då meddela att jag får exakt samma resultat i VC7 när jag slår av alla optimeringar (varför jag nu skulle vara så urbota dum att göra det).

När du pratar om att saker ska "fungera generellt" oberoende av kompilator måste du ta hänsyn till att ingenting i C++ standarden säger hur maskinkoden ska genereras, eller ens att maskinkod ska genereras.

Att det är bra att C++ tillåter detta och att det är användbart verkar vara en allmänt verdertagen uppfattning:
Scott Meyers i boken
http://www.amazon.com/exec/obidos/tg/detail/-/0201924889/qid=1053864435/sr=8-1/ref=sr_8_1/103-8074782-4784643?v=glance&s=books&n=507846
Bjarne Stroustrup i boken
http://www.amazon.com/exec/obidos/ASIN/0201700735/qid=1053864982/sr=2-1/ref=sr_2_1/103-8074782-4784643
Meyers har jag redan citerat och jag kan inte hitta Stroustrups citat.

PeW skrev:

Utskriften om adresserna finns visst med.

Jag hittade ej adressen för p i det första exemplet

Sang-draxMedlem sedan juli 2002581 inlägg
#17

Vi får dessvärre fortsätta denna synnerligen intressanta debatt på onsdag, eftersom jag måste återgå till det militära livet ett tag.

PeWMedlem sedan juni 200010 432 inlägg
#18

Sang-drax skrev:

Jag hittade ej adressen för p i det första exemplet

'p' är en label. Men det har ju inget med minnesallokerandet att göra då stacken bevisligen är lika stor i båda fallen och det är mot stacken 'p' arbetar, men inte till en fix adress. Om (!) det skulle vara så att 'p' pekar på ett annat utrymme så måste detta vara allokerat i förväg. Isf är minnesutnyttjandet sämre i det fallet (när int'en deklarerades lokalt).

När du pratar om att saker ska "fungera generellt" oberoende av kompilator måste du ta hänsyn till att ingenting i C++ standarden säger hur maskinkoden ska genereras, eller ens att maskinkod ska genereras.

Nej det är helt sant. Samma sak gäller i C, pascal e.t.c .. maskinkoden är olika beroende på plattform. Men det är de facto att maskinkoden är det som genereras för en körbar fil. Körs programmet i en runtime är det denna som genererar maskinkoden, så i botten är det samma sak som sker.

Ditt exempel fungerade ju inte "generellt" då den gav olika resultat på olika kompilatorer. Mao så kom inte de lokala deklarationerna till nån fördel i det ena fallet. Därför är det intressant att se vad som VC7 spottar ur sig. Jag gissar skarpt på att stacken är större i den koden än vad den blev i VC6... men det är än så länge endast en gissning baserad på det faktum att en stack är statisk. Med "generellt" menade jag från början att det ska fungera i alla kompilatorer för det språket.

Att det är bra att C++ tillåter detta och att det är användbart verkar vara en allmänt verdertagen uppfattning

Det har jag inte förnekat :)
Frågan är väl vart gränsen går ur lämplighetssynpunkt dvs när är det motiverat och när är det inte motiverat? ;)
Bara för att en sak står i en bästsäljare, är det inte säkert att det är applicerbart som alltid rådande.

Vi får dessvärre fortsätta denna synnerligen intressanta debatt på onsdag, eftersom jag måste återgå till det militära livet ett tag.

Vi gör så här:
Jag kollar adressen till labeln "p" och du tar fram stackallokeringen på funktionen f() i ditt exempel (VC7), så tar vi upp detta igen när du är ledig :)

PeWMedlem sedan juni 200010 432 inlägg
#19

Ok. Nu är det onsdag och förhoppningsvis har Sang-drax undvikit att (bokstavligt) skjuta sig i foten :p ;)

Jag har kollat mot mitt exempel på adresser och då speciellt på [p].
Resultet nedan:

#fallet med den initiala deklarationen:
ebp         = 12FF2C  //basen
variabeln v = 12FF28  //ger ett avstånd med 4 bytes från basen
variabeln p = 12FF24  //ger ett avstånd med 8 bytes från basen
ebp-50      = 12FEDC  //slut på stacken.

#fallet med den lokala deklarationen:
ebp         = 12FF2C  //basen för stacken
variabeln v = 12FF28  //ger ett avstånd med 4 bytes från basen
[p]         = 12FF1C  //ger ett avstånd med 10 bytes från basen
ebp-50      = 12FEDC  //slut på stacken.

Som vi ser ligger variablerna i båda fallen i funktionsstacken. Samma resultat om man istället för att tilldela v ett värde och sen selekterar samt ger v ett värde från en inparameter. Dvs ett för funktionen okänt värde. Stacken är lika stor i båda fallen.

Noteringar om ovan:
I den initala deklarationen läggs v & p på rad direkt i början av stacken. Inget går till spillo. I den lokala deklarationen läggs först v på stacken sen läggs p på en address + 2 bytes bortanför v. På dessa 2 bytes finns...? Inget i koden säger något om vad som finns där, men troligen är det bara skräp.
Varför går då variabeln p en omväg via en label [p] om den ändå ska hamna på stacken? Det är inte säkert att den hamnar på stacken, det beror antagligen på hur stort kodblocket är (kompilatorns optimering för cacheminnet). Vilket i sig är ett plus, men inte alltid.

Slutsats:
Inget minne sparades med lokala deklaration, resultatet blev egentligen tvärtom då 2 bytes förloras på stacken. Tidsmässigt var det i detta fall ingen skillnad, men vid stora kodblock som inte får plats i cacheblock kan det vara en fördel. Men det beror isf helt på tekniken för att skicka med adresser för variablerna till varje inläst block. En sådan teknik skulle tid och om man vill spara tid behövs det mer minne för att duplicera dessa adresser till varje block.
Så mer minne gick åt för den lokala deklarationen, men tidsmässigt är det ingen skillnad.

Sang-drax rekursion:
Vid tester på mina burkar med VC-6 var resultatet som med mitt exempel. Dvs ingen skillnad i tid, men mer minne användes i stacken vid lokal deklaration. Enligt Sang-drax är det skillnad vid körning i VC-7. Detta är inte alls en omöjlighet men det måste vara en felaktighet nånstans om hans kommentar i koden stämmer:

//Utan denna variabel krashar aldrig funktionen
    //när w och q är deklarerade inom if-statserna

Vaddå fel? Jo.. för att en funktion ska kunna vara rekursiv krävs det att adressrymden inte skrivs över och således allokeras ständigt ett nytt utrymme (även de anropande instruktionerna tar nytt utrymme), det tar mao alltid stopp förr eller senare. I hans exempel allokeras utrymme nånstans och vart det än är så är det hela tiden nytt utrymme och det kommer mao ta stopp, vart man än allokerar detta utrymme. Variablerna inom funktionen ska ju inte skrivas över... och enda sättet att skapa utrymme under runtime är på heapen. Nånstans måste ju minne allokeras.

Jag gissar att hans exempel med den lokala deklarationen gör att VC-7 helt enkelt skapar en större stack och därmed ett större user-space. Men det ska bli intressant att se om han kan dementera/verifiera detta med storleken på hans stack :)

Sang-draxMedlem sedan juli 2002581 inlägg
#20

Jag menade inte att den aldrig krashade... sorry.
Jag menade att jag aldrig såg den krasha (jag avbröt vid runt 4000 om jag kommer ihåg rätt)

Ska se om jag kan få fram stack-storleken.

148 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
145 ms — hämta tråd, inlägg och bilagor (db)