C data types, tar knäcken på mig
Re: C data types, tar knäcken på mig
Allt har två sidor. På de flesta arkitekturer är det snabbare att köra med den
storlek som passar arkitekturen bäst och låta kompilatorn välja en optimal
storlek på "int". På större maskiner kan det vara snabbare att hantera 32
eller 64 bitars heltal än 8 eller 16.
storlek som passar arkitekturen bäst och låta kompilatorn välja en optimal
storlek på "int". På större maskiner kan det vara snabbare att hantera 32
eller 64 bitars heltal än 8 eller 16.
Re: C data types, tar knäcken på mig
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).
Re: C data types, tar knäcken på mig
Jorå, det kan det visst göra. Man kan få "alignment faults" och liknande fenomen.
Processorn kanske måste dela ett 32 eller 64 bitars ord i mondre delar o.s.v.
Om du t.ex gör en struct med 2 16-bit tal så kan de hamna intill varandra
(om man inte ber kompilatorn att utföra alignment och lägga varje
struct del på en jämn adress.
> Kompilatorn kan ju inte veta vad som är optimal storlek för en "int" i mitt program.
Nej, men den vet normalt vad som är optimalt på den aktuella arkitekturen.
Processorn kanske måste dela ett 32 eller 64 bitars ord i mondre delar o.s.v.
Om du t.ex gör en struct med 2 16-bit tal så kan de hamna intill varandra
(om man inte ber kompilatorn att utföra alignment och lägga varje
struct del på en jämn adress.
> Kompilatorn kan ju inte veta vad som är optimal storlek för en "int" i mitt program.
Nej, men den vet normalt vad som är optimalt på den aktuella arkitekturen.
Re: C data types, tar knäcken på mig
Kompilatorn är ju naturligtvis optimerad för den arkitektur och ordlängd för processorn den är avsedd för, konstigt vore väl annars.
Jobbar du med en 8-bitars processor, så är kompilatorn optimerad för just detta, samma gäller 16, 32 och 64-bitars prollar.
Jobbar du med en 8-bitars processor, så är kompilatorn optimerad för just detta, samma gäller 16, 32 och 64-bitars prollar.
Re: C data types, tar knäcken på mig
Jo, exakt. Därför kan jag ju bättre själv välja vilken längd jag vill ha på en int. T.ex 8 bitar eller 64 bitar. Sedan optimerar kompilatorn detta till arkitekturen. Men det finns ju inget smart med att den bara trunkerar bort halva mitt 64-bitars tal bara för att 32 bitar passar bättre i arkitekturen, dessutom utan att jag vet om det, om jag inte särskilt inför varje kompilering tar reda på hur många bitar jag råkar få lov att använda just då.
Re: C data types, tar knäcken på mig
Nja, om du använder det som är optimalt för arkitekturen i fråga, slipper kompilatorn att generera en massa extra kod.
Re: C data types, tar knäcken på mig
Så du kör vanligtvis med 32 bitars variabler överallt , även om det skulle duga med en 8-bitars?
t.ex.
hellre än
Du tror inte att kompilatorn ändå snurrar ett 32-bitars register, och därmed blir det ingen skillnad?
t.ex.
Kod: Markera allt
for (uint32_t i = 0; i < 10; i++) { ... kod ... }
Kod: Markera allt
for (uint8_t i = 0; i < 10; i++) { ... kod ... }
Re: C data types, tar knäcken på mig
Naturligtvis inte, eftersom den kan packa variabler och gör det.
Men kunskap om hur olika saker optimeras är rätt bra, speciellt om man får ont om plats.
Men kunskap om hur olika saker optimeras är rätt bra, speciellt om man får ont om plats.
Re: C data types, tar knäcken på mig
Problemet har med alignment att göra. Processorn kan få lägga extra
tid på att fixa till mindre variabler som har hamnat i samma "native word"
eller en variable som är delad över två "words".
Nu så kan "member alignment" ju vara default för kompilatorn, och
då kommer den att lägga in dummy utrymme i structen så att datat
ligger på naturliga adresser för den aktuella data typen. T.ex en struct
med en char med en long direkt efter så kommer det att läggas till 3 st
dummy bytes mellan char'en och long'en, så att longen blir "aligned".
Visst, det tar lite mer plats än vad man kanske trodde, men det
blir snabbare.
En long som inte ligger på ett jämt "longword" är väldigt ineffektivt. Det
blir dubbla minnesaccesser och ett pusslande med två halvor.
tid på att fixa till mindre variabler som har hamnat i samma "native word"
eller en variable som är delad över två "words".
Nu så kan "member alignment" ju vara default för kompilatorn, och
då kommer den att lägga in dummy utrymme i structen så att datat
ligger på naturliga adresser för den aktuella data typen. T.ex en struct
med en char med en long direkt efter så kommer det att läggas till 3 st
dummy bytes mellan char'en och long'en, så att longen blir "aligned".
Visst, det tar lite mer plats än vad man kanske trodde, men det
blir snabbare.
En long som inte ligger på ett jämt "longword" är väldigt ineffektivt. Det
blir dubbla minnesaccesser och ett pusslande med två halvor.
Re: C data types, tar knäcken på mig
thebolt skrev:ISO/IEC 9899:TC2gkar skrev: Är det så?! Visa mig texten i standarden som specificerar long till minst 32bitar.
"5.2.4.2.1 Sizes of integer types <limits.h>
— minimum value for an object of type long int
LONG_MIN -2147483647 // −(231 − 1)
— maximum value for an object of type long int
LONG_MAX +2147483647 // 231 − 1
— maximum value for an object of type unsigned long int
ULONG_MAX 4294967295 // 232 − 1
"
Dvs en long måste vara minst 32 bitar.
Tackar!
Re: C data types, tar knäcken på mig
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.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).
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!
Re: C data types, tar knäcken på mig
Nu har ju C99 korrigerat detta genom att införa de storleksfasta versionerna (u)int8_t, (u)int16_t osv samt diverse andra versioner så som (u)int_least16_t (typ med minst 16 bitar) och (u)int_fast16_t ("type being usually fastest on architecture with at least 16 bits") samt motsvarande defines i limit.h med deras område.jesse skrev:Det är väl det största misstaget i C-språkets historia att de inte definierade heltalstyperna fast. Det hade inte varit svårt att uppkalla dem efter antal bitar, eller varför inte antal bytes? Är det lika tokigt i C++ och C# att de har ärvt denna förvirring?
Re: C data types, tar knäcken på mig
Fast det här är ju inte specifikt för char, utan felet är att unsigned long start_sector; är en enkel variabel medan unsigned char start_sector[4]; är en array om fyra variabler och arrayer hanteras syntaxmässigt som pekare till variabler istället för som en vanlig variabel.ekman skrev:Nä för att testa. Jag såg inte varför det inte skulle funka, så jag testade. Så nu har jag lärt mig lite mer hur Char fungerar
Du skulle få samma fel om du bytte ut unsigned long start_sector; mot unsigned long start_sector[1];
Det här är väl ett av de vanligaste nybörjarfelen när man håller på med C, och dessutom är det nog ett av skälen till att folk helt enkelt inte orkade lära sig C när de använde PC-burkar på den tiden de flesta körde operativsystem utan vettigt minnesskydd (DOS och eventuellt windows 3.x) eftersom fel ofta ledde till att hela burken tvärhängde.
Man kan för övrigt tycka att de flesta läroböcker är rätt usla på att förklara detta - man undrar om de som skrivit böckerna verkligen till 100% förstår vad som händer?
Det blir ju inte lättare för nybörjare att lära sig när *foo = 42 och foo[0] = 42 gör samma sak.

Re: C data types, tar knäcken på mig
Väldig förvirrande är ju att a == b[a].
Det brukar sätta griller i huvudet på många att man byta plats på fältets namn och indexet.
Hur kan indexet plötslig bli ett fält, och hur kan fältet, som just fattat är en pekare(minnesadress) plötsligt bli ett index?
Men a beräknas som *(a+b), d.v.s. "adressen a + offset b, och hämta värdet på den nya beräknade adressen"
Efter som det är en addition spelar det ju ingen roll om vi kastar om a och b, *(b+a) blir samma sak.
Men det rekommendera inte att göra så vid "normal" programmering.
Det brukar sätta griller i huvudet på många att man byta plats på fältets namn och indexet.
Hur kan indexet plötslig bli ett fält, och hur kan fältet, som just fattat är en pekare(minnesadress) plötsligt bli ett index?
Men a beräknas som *(a+b), d.v.s. "adressen a + offset b, och hämta värdet på den nya beräknade adressen"
Efter som det är en addition spelar det ju ingen roll om vi kastar om a och b, *(b+a) blir samma sak.
Men det rekommendera inte att göra så vid "normal" programmering.

Re: C data types, tar knäcken på mig
Om du undviker int, long osv och i stället definierar upp INT8, INT16 osv så kan du även byta/uppgradera kompilator och endast modifiera dina typ definitioner.