jesse skrev:Kompilatorn kan ju inte veta vad som är optimal storlek för en "int" i mitt program. Om jag behöver 32 bitar så anger jag det, annars anger jag 16 bitar. Det tar inte mera kraft för en 32-bitars processor att hantera 16-bitars int (eftersom det då automatiskt blir 32 bitars i alla fall).
Jo, det går långsammare på många RISCar, även x86 för den delen, men av en annan anledning, och nej det sker inte automatiskt i alla fall.
Får se om jag kan förklara detta enkelt?
Det ena fallet:
I en 32 bitars RISC finns bara 32 bitars ALU, inget konstigt med det, det är en RISC.
Dvs allt kommer att vara 32 bitars när man räknar med det, men måste bete sig exakt som 16 bitars variabler om man specificerat det i koden.
Lite fulkod...
void foo(int32_t b, int32_t c, inte32_t d){
short a;
volatile apa;
for (a = b; a != 0; a+=d){
apa = 0;
}
}
Hur många varv skall vi lopa egentligen?
Låt oss kompilera detta för ARM likadant som ARMs SDT kompilator skulle ha gjort.
mov r4, r0 ; a = 0
label:
...
...
add r0, r0, r1 ;a+=d
mov r0, r0 lsl #16 ; ARM löser detta med 2st skiftningar eftersom and r0,r0,0xffff inte finns direkt tillgängligt.
mov r0, r0, lsr #16
cmp r0,r2
Dessa 16 bitars skiftningar(maskning) måste utföras eftersom vi bara skall göra jämförelse med de undre 16 bitarna och det bla kan finnas ettor i d som är 32 bitar.
Detta eftersom variabeln är registeroptimerad.
Det andra fallet är tex X86, instruktionsmässigt behövs inte maskningen, eller skiftningarna. Processorn har 16 bitars stöd i instruktionsuppsättningen.
Dock är fortfarande ALU numera 32bitar+, 16 bitars aritmetik emuleras och orsakar låsningar i pipelinen. Intel säger inte så mycket men gissningsvis orsakar det en read-modify-write historia i registerfilen.
Detta varierar mellan X86 tillverkare och generation, men långsamma 16bitare har vi haft nu sedan millenieskiftet.
Slutligen:
Om vi har 16 bitarsvariabler på en risc (ARM) och vi råkar ha högt registertryck(så att en vanlig diskret 16_t hamnar på stacken) eller vi har datat i en array[] eller vi har specificerat att variabeln skall vara volatile, eller vi har tagit adressen på den, kommer den att hamna i minnet.
På moderna ARMar finns stöd för LDRH/STRH (Ladda och skriv 16 bitars,) på de äldre saknas detta helt.
Då behövs inte maskningen, eftersom vi skriver tillbaka värdet helatiden till minnet med 16 bitars accesser. Detta blir dock långsamt eftersom vi skriver mot minnet ofta, men 32 bitars data hade också varit långsamt.
Med 16 bitars data kan det ta en extra late cycle(risk för pipelinebubbla) beroende på ARM eftersom den behöver göra sign eller zero extend vid inläsningen.
Om det kommer att ta en extra cykel i praktiken beror på om vi har någon annan instruktion utan databeroende att exekvera under tiden.
Så vad skall man göra om man vill skriva effektiv kod.
Ryms datat i 16 bitar och man inte är beroende på MSB overflow (add, lsl etc) använd int.
Ryms det inte, använd long.
Måste du har en viss storlek, använd den(int16_t, int32_t etc), men det kanske inte kommer att att exekveras optimalt på alla plattformar.
Sedan är ju läsbarhet och kodstandard etc en annan historia...
Mer glögg!