HW-buggar eller konstigt beteende DU känner till

Permalänk
Datavetare

HW-buggar eller konstigt beteende DU känner till

Denna tråd skapas p.g.a av diskussion som gick lite väl off-topic.

Men diskussionen i sig är faktiskt riktigt intressant och tänkte testa med en tråd där alla kan visa exempel på buggar eller konstigt beteende som man upplevt som programmerare.

Kan köra två exempel på hur det kan se ut (det andra kommer i ett andra inlägg). Alla känner nog till historien om att den ursprungliga Pentium CPUn räknade fel i vissa lägen, men den CPUn hade även en annan lite mindre känd bug som går under namnet F00-buggen, egentligen ska det vara F00F då det är den op-kod (maskinkod) som orsakar felet.

Buggen triggas av att man kör

lock cmpxchg8b edi

lock betyder att nästföljande instruktion ska köras med RAM-bussen låst vilket i sin tur gör instruktionen atomär sett från andra CPU-kärnor. Instruktionen med det komplicerade namnet cmpxchg8b betyder jämför det som ligger i registren eax:edx (64 bitar, 8 bytes därav suffixet 8b) men en minnesadress som ska pekas ut av argumentet, om det är samma data så byter man ut innehållet i RAM mot det som ligger i registren ebx:ecx. Problemet är sista delen där det står edi inte är en pekare, utan ett register som bara kan hålla 4 bytes (32-bitar).

Den korrekta formen av denna instruktion skulle vara något likt

lock cmpxchg8b (edi)

som betyder jämför med det som ligger på den adress som edi innehåller, medan varianten ovan säger: jämför med värdet i registret.

Korrekt beteende här är att programmet ska orsaka ett undantag och hoppa in i OS-kärnan som typiskt ska städa upp programmet, men i stället så hängde sig hela datorn vilket var rätt illa då man får köra den instruktionen som "vanlig" användare (behöver ej vara UNIX "root" eller Windows "Administrator"). Buggen triggades tydligen av att man "glömt" ta bort effekten av lock prefixet så OS-et blev hängande på första bästa RAM access.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Datavetare

Det andra exemplet är ingen bug utan en intressant observation kring hur man ibland designar CPU-finesser på ett sätt som gör att program i teorin kan hänga sig i ett så kallat live-lock. Men i praktiken så är det lite som kärnkraftverk, sannolikheten att det ska ske ett haveri är ju så liten att man faktiskt kan ignorera problemet...

Alla RISC implementationer jag jobbat med, vilket inkluderar PowerPC, MIPS och ARM, implementerar s.k. atomära instruktioner på ungefär samma sätt. Stegen man gör är

  1. läs upp innehållet från en minnesadress i ett speciellt läge som kallas load linked, detta leder till att systemet håller reda på skrivningar från ALLA CPU-kärnor på just den adressen

  2. man utför det man ska utföra, addera 1 i mitt exempel nedan

  3. man försöker sedan skrivna ner resultatet på samma adress man läste upp startvärdet från med en s.k store conditional instruktion. Denna operation kommer misslyckas om det skett ett interrupt som modifierat adressen eller om någon annan CPU-kärna modifierat adressen sedan steg 1. Misslyckas detta anrop går man till punkt 1 och gör om.

Den uppmärksamme inser snabbat att det finns faktiskt ingen garanti för att denna typ av operation någonsin lyckas då man inte har någon övre gräns för hur många misslyckade försök som tillåts. Men tittar man i assembler för t.ex. ARM på Linux så ser man att atomic_inc(atomic_t *v) är implementerat på följande sätt:

Notera semikolon är början på kommentar i assembler, så det som står efter ; på varje rad är alltså bara en kommentar

; värdet på pekaren v ligger i register r0 från start movs r3, #1 ; lagra 1 i register r3 1: ldrex r2, [r0] ; läs upp värdet som 'v' pekar på i "länkat" läge och lägg det i r2 add r2, r3 ; lägg på 1 (som är det värde r3 har) till r2 strex r1, r2, [r0] ; försök spara värdet i r2 i adressen som r0 innehåller, r1 kommer innehåller om det gick bra eller inte teq r1, #0 ; ett värde på 0 i r1 betyder att det gick bra bne 1b ; om r1 inte var 0, hoppa tillbaka till 1:

(Jag förenklade detta jämfört med diskussionen detta kommer i från där operationen var atomic_inc_and_return())

Ganska komplicerat för att lägga ett till en cell i RAM, eller hur?

Jämför detta med den, så ofta bespottade, x86 arkitekturen där samma sak ser ut så här

lock xadd 0x1, (rdi) ; värdet på pekaren v ligger i register rdi (som alltid innehåller första argumentet till en C-funktion)

Denna instruktion kan inte misslyckas med mindre än att rdi är en ogiltig adress och då blir det ett undantag. Är ju inte heller någon större nackdel för CPUn att det är en enda instruktion som utför read-modify-update i en smäll, något som man då kan optimera för.

Dessa typer av atomära instruktioner är kritiska för att implementera saker som lås (mutex, semaphores) så det är lite intressant att se att x86 som anses "dålig" och "komplicerad" är så väldigt enklare än RISC som ska vara "enkel" och "vacker".

Har någon annan exempel på konstigheter, buggar eller något som visar väldigt för/nackdelar mellan RISC/CICS?

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd

Läste straxt efter att du hade skrivit och skulle kommentera men ville kolla upp lite saker innanoch sedan glömde jag bort, sorry. Klockan är nu 04:30 och jag har varit varken i stort sätt 3 dygn så jag ska vara kort i min kommentar och ta det mer ingående senare.

I de väntetider jag har haft senaste dagarna så har jag kollat upp lite noggrannare hur lock fungerar på X86, programmerar ju som sagt normalt ARM. Efter att ha pillat lite och räknat på saken så tar lock xadd (på en sandy bridge) upp minst 20 cyklar och när jag frekventa läsningar för att kolla när lock släpptes så tog det i vissa fall över 200 cyklar.

Det är just detta som jag inte tycker om med X86, det är svårt att få full koll på vad som händer ibland. Ja det kan vara lite omständligare att skriva på ARM ibland, dock skulle jag aldrig skriva det som det exemplet du tog upp, men man vet till 100% när proceduren klar.

Permalänk
Datavetare
Skrivet av Morkul:

Det är just detta som jag inte tycker om med X86, det är svårt att få full koll på vad som händer ibland. Ja det kan vara lite omständligare att skriva på ARM ibland, dock skulle jag aldrig skriva det som det exemplet du tog upp, men man vet till 100% när proceduren klar.

Att "lock" prefix kan ta upp till 200 cykler är extremt enkelt att förklara: vad är latensen mot RAM? Ungefär 150-200 cykler. I vissa lägen så måste saker ända ut till RAM och "lock", precis som "dmb", säkerställer att nästföljande instruktion inte körs innan man vet att alla CPU-kärnor nu ser de skrivningar som den lokala CPUn gjort upp till punkten där "lock" / "dmb" körs. Så x86, ARM, PowerPC eller MIPS fungerar på exakt samma sätt här.

Och för att kommentera mitt exempel ovan: hur skulle du annars lösa detta på ARM? Det jag skrev ovan är exakt den kod som Linux och Windows kommer använda för att implementera atomic add. Det är också den kod som gcc genererar om du använder __sync_fetch_and_add (gcc atomic extensions).

RISC CPU:er har endast ett sätt att göra något atomärt (de är ju "reduced" instruction set) och det är via "load-linked" / "store conditional".

Har en till intressant exempel på x86 kontra ARM, PowerPC och MIPS

Om du initialt har

volatile int x = 0; volatile int y = 1;

och sedan kör detta på en tråd

x = 10; y = 11;

och ungefär samtidigt kör detta på en annan tråd

while (x != 10) /* do nothing*/; int a = y; printf("%d\n", a);

Vad kommer skrivas ut?

På x86 kommer det ALLTID bli 11, på ARM, PowerPC eller MIPS blir antagligen 11, men det kan även bli 1...

Notera att detta är inte en bug i HW utan en bug i mitt program som saknar en explicit minnesbarriär mellan "x=10;" och "y=11;", men det visar att finns sätt att skjuta sig i foten på ARM som inte existerar på x86. Att köra en minnesbarriär är som du redan noterat med "lock" (som är en implicit minnesbarriär) allt annat än snabbt.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd
Skrivet av Yoshman:

Att "lock" prefix kan ta upp till 200 cykler är extremt enkelt att förklara: vad är latensen mot RAM? Ungefär 150-200 cykler. I vissa lägen så måste saker ända ut till RAM och "lock", precis som "dmb", säkerställer att nästföljande instruktion inte körs innan man vet att alla CPU-kärnor nu ser de skrivningar som den lokala CPUn gjort upp till punkten där "lock" / "dmb" körs. Så x86, ARM, PowerPC eller MIPS fungerar på exakt samma sätt här.

Ja för utom att i ditt ARM exempel så använde du ett register, vilket du kommenterade i den andra tråden. Detta gör att latensen inte blir ett problem.

Citat:

Om du initialt har

volatile int x = 0; volatile int y = 1;

och sedan kör detta på en tråd

x = 10; y = 11;

och ungefär samtidigt kör detta på en annan tråd

while (x != 10) /* do nothing*/; int a = y; printf("%d\n", a);

Vad kommer skrivas ut?

På x86 kommer det ALLTID bli 11, på ARM, PowerPC eller MIPS blir antagligen 11, men det kan även bli 1...

Notera att detta är inte en bug i HW utan en bug i mitt program som saknar en explicit minnesbarriär mellan "x=10;" och "y=11;", men det visar att finns sätt att skjuta sig i foten på ARM som inte existerar på x86. Att köra en minnesbarriär är som du redan noterat med "lock" (som är en implicit minnesbarriär) allt annat än snabbt.

Poängen är? Att en programmerare som inte har koll kan stöta på problem? Då kan man ju skissa på ett oändligt antal scenarion som är olika bra beroende på CPU.

Jag ber om ursäkt för att jag fortfarande inte har kommenterar ARM koden för lock exemplet, sömnbrist här och jag vill verkligen förklara noggrant så jag tar det i helgen.

För att ta exemplet lite mer seriöst: Enligt mitt sätt och se på det hela så gör ARM rätt och X86 fel beroende på hur koden som compilern har gjort ser ut.. Jag är tämligen säker på att olika compilers kommer tolka din kod i detta fallet lite olika och dör med få ut olika resultat.

Permalänk
Datavetare
Skrivet av Morkul:

Ja för utom att i ditt ARM exempel så använde du ett register, vilket du kommenterade i den andra tråden. Detta gör att latensen inte blir ett problem.

Spelar absolut ingen roll att jag använder ett register på ARM, sedan är jag TVUNGEN att använda register på ARM. Kombinationen av ldrex, some-op, strex är det som motsvaras av lock some-op på x86. ldrex och strex gör, per definition en läsning respektive en skrivning mot delad cache / RAM och de kommer kosta MINST lika mycket som en instruktion som körs med lock-prefix. x86 har ju fördelen att den vet hela förloppet som ska vara atomärt då instruktioner som t.ex. xadd är av typen read-modify-write, detta går då naturligtvis att optimera. RISC:ar vet inte vad som kommer mellan load-linked (ldrex på ARM) och store-conditional (strex på ARM) så det är svårare att optimera det hela i HW.

I teorin är load-linked/store-conditional mer generell, men i praktiken finns det nog inget man kan göra på RISC som inte har en direkt motsvarighet på x86. Och med TSX i Haswell så kommer x86 få en variant av load-linked/store-conditional som är LÅNGT mer generell än något som implementerats i någon RISC CPU.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Datavetare
Skrivet av Morkul:

Poängen är? Att en programmerare som inte har koll kan stöta på problem? Då kan man ju skissa på ett oändligt antal scenarion som är olika bra beroende på CPU.

Ah, du har en poäng att jag missade poängen. Poängen är att du kan skriva ett KORREKT program på x86 som inte behöver skrivbarriärer då ordningen för synlighet är för andra CPU:er är garanterad att vara korrekt. Detta betyder att du kan implementera saker som "work-stealing scheduler" (fork/join i Java, OpenMP för C/C++, TPL för .Net) mer effektivt på x86 än på RISC, då RISC (eller i alla fall ARM, PowerPC och MIPS) kommer kräva explicita minnesbarriärer som, per definition, är synkroniserade instruktioner och att köra sådana är DYRT på en out-of-order CPU (som ARM är sedan Cortex A9).

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd
Skrivet av Yoshman:

; värdet på pekaren v ligger i register r0 från start movs r3, #1 ; lagra 1 i register r3 1: ldrex r2, [r0] ; läs upp värdet som 'v' pekar på i "länkat" läge och lägg det i r2 add r2, r3 ; lägg på 1 (som är det värde r3 har) till r2 strex r1, r2, [r0] ; försök spara värdet i r2 i adressen som r0 innehåller, r1 kommer innehåller om det gick bra eller inte teq r1, #0 ; ett värde på 0 i r1 betyder att det gick bra bne 1b ; om r1 inte var 0, hoppa tillbaka till 1:

Då så har jag äntligen tid att kommentera detta lite bättre.

Jag har ingen aning om varför de har gjort på detta sättet, på en ARM så är detta resursslöseri i alla fall.

Jag tar det rad för rad:
movs r3, #1 ; lagra 1 i register r3

Helt onödig och kan tas bort helt mer om det seanre. Men även om jag vill använda 3 register så skulle så har jag svårt att se varför de använder tiägget s till mov, kanske gör något senare i koden som inte finns med?

try
Helt enkelt en flagga som man senare kan hoppa tillbaka till för att skapa en loop.

ldrex r2, [r0] ; läs upp värdet som 'v' pekar på i "länkat" läge och lägg det i r2
Man låser helt enkelt värdet från r0 i r2. Precis efter skolboken.

add r2, r3 ; lägg på 1 (som är det värde r3 har) till r2
Nu vill jag inte alls vara med lägre. Att använda add är helt korrekt men varför med 3 register?
Add ser ut som följer:
add {cond} {Rd}, Rn, #imm12
Add instruktionen lägger till värdet från rn ELLER #imn12 alltså skulle det vara bättre att använde fäljande:
add r2,2r,#1

strex r1, r2, [r0] ; försök spara värdet i r2 i adressen som r0 innehåller, r1 kommer innehåller om det gick bra eller inte
Personligen använder jag oftast strexeq men saken är den att nu när jag har tittat på saken närmare så finns det egentligen ingen anledning till det utan nog bara varit så på grund av gammal vana.

teq r1, #0 ; ett värde på 0 i r1 betyder att det gick bra
Själv använder cmpeq alternativt xor istället, inte helt säkert vilket som är snabbast men det är något som jag faktiskt ska ta mig en titt på.

bne try
inte mycket att säga om det.
Slutsats:

; värdet på pekaren v ligger i register r0 från start try: ldrex r2, [r0] add r2,r2,#1 strex r1, r2, [r0] cmpeq r1, #0 bne try

Jämför man detta emot X86 lock xadd så är det snabbare dessutom så bör man inte använda lock xadd vid intensiva loopar då det kan bli extremt segt när trådarna måste sitta och vänta på varandra alt för långa tider. Den absoluta minimum tiden för lock xadd är 20 cyklar och med lite otur som sagt 20 gånger med än så vilket inte fungerar när man ska optimera en loop utan då blir man även utlämnad till att börja använda regiser.

Permalänk
Medlem

Nu kan jag ingenting alls om något sånt här.
Men läste för ett tag sedan att FX-8120, tror jag det var, hade någon hårdvaru-bugg. Tänkte att det kanske kunde vara av intresse.

Edit: Om jag kommer ihåg rätt så var det att vid några specifika kommandon på rad så låste sig instruktionspekaren (?)

Visa signatur

i5 750 @ 3.9 GHz | NH-D14 | P7P55D |GTX 960 | Vulcan 8GB | Seagate 600 480 GB | Newton 650w | P280 | Dell U2515H

Permalänk
Avstängd
Skrivet av Yoshman:

Ah, du har en poäng att jag missade poängen. Poängen är att du kan skriva ett KORREKT program på x86 som inte behöver skrivbarriärer då ordningen för synlighet är för andra CPU:er är garanterad att vara korrekt. Detta betyder att du kan implementera saker som "work-stealing scheduler" (fork/join i Java, OpenMP för C/C++, TPL för .Net) mer effektivt på x86 än på RISC, då RISC (eller i alla fall ARM, PowerPC och MIPS) kommer kräva explicita minnesbarriärer som, per definition, är synkroniserade instruktioner och att köra sådana är DYRT på en out-of-order CPU (som ARM är sedan Cortex A9).

Förstår inte riktigt hur du argumenterar. Hur diverse språk så som java/C/C++, etc etc löser saker och ting är helt betydelselöst när man diskuterar hur bra en CPU fungerar. Allt som har betydelse är instruktionssettet och hur du med hjälp av det kan läsa olika problem. Med EXAKT samma C kod kan du får ett antal olika outputs beroende på kompilator men även samma kompilator kan få olika resultat beroende på kampilatordirektiv och detta är någon som gäller alla plattformar. Så att en kompilator är dåligt konfigurerad och där med får dåligt optimerad kod beror aldrig på CPUn.

Du för gärna förklara hur rent instruktionsmässigt varför du anser att det är ineffektivt för annars måste vi börja gissa de olika kompilatorernas output vilket inte ger speciellt mycket.

Lite kul fakta som egentligen inte har med detta att göra:
Alltid lika roligt att höra när någon har använd någon C++ compiler och säger att de skapat ett optimera program. När man sedan dissasemblerar programmet och oftast igenom några snabba förändringar kan göra ganska stora optimeringar så blir man lite trött. Bara det att många kompilatorer fortfarande använder mov istället för xor när det ska nollställa variabler är ju helt löjligt.

Permalänk
Avstängd
Skrivet av Rydisen:

Nu kan jag ingenting alls om något sånt här.
Men läste för ett tag sedan att FX-8120, tror jag det var, hade någon hårdvaru-bugg. Tänkte att det kanske kunde vara av intresse.

Edit: Om jag kommer ihåg rätt så var det att vid några specifika kommandon på rad så låste sig instruktionspekaren (?)

Ja vissta ja. Resettades inte stacken under vissa förutsättningar så att instruktionspekaren inte pekade på någonting.

Permalänk
Medlem
Skrivet av Morkul:

Ja vissta ja. Resettades inte stacken under vissa förutsättningar så att instruktionspekaren inte pekade på någonting.

Något sånt var det nog ja!

Visa signatur

i5 750 @ 3.9 GHz | NH-D14 | P7P55D |GTX 960 | Vulcan 8GB | Seagate 600 480 GB | Newton 650w | P280 | Dell U2515H

Permalänk
Datavetare
Skrivet av Morkul:

Då så har jag äntligen tid att kommentera detta lite bättre.

Jag har ingen aning om varför de har gjort på detta sättet, på en ARM så är detta resursslöseri i alla fall.

Jag tar det rad för rad:
movs r3, #1 ; lagra 1 i register r3

Helt onödig och kan tas bort helt mer om det seanre. Men även om jag vill använda 3 register så skulle så har jag svårt att se varför de använder tiägget s till mov, kanske gör något senare i koden som inte finns med?

Inte alls onödigt, det är snabbare att addera register än att addera register + konstant. Visst blir detta en extra instruktion, men om det blir fler än ett varv i loopen så spar man in denna a instruktion. Du konstaterade redan nedan att man adderad mot register r3, som just innehåller en 1:a.

Skrivet av Morkul:

try
Helt enkelt en flagga som man senare kan hoppa tillbaka till för att skapa en loop.

ldrex r2, [r0] ; läs upp värdet som 'v' pekar på i "länkat" läge och lägg det i r2
Man låser helt enkelt värdet från r0 i r2. Precis efter skolboken.

Man läser upp värdet r0 pekar på i r2 och man låser inget, gjorde man det skulle aldrig strex misslyckas. Man sätter någon intern flagga i CPUn så att den håller reda på om någon skriver till den cache-line som värdet r0 pekar på. Notera att denna instruktion är säker 50-100 gånger dyrare än den "movs" du optimerade bort, så den optimeringen är meningslös.

Skrivet av Morkul:

add r2, r3 ; lägg på 1 (som är det värde r3 har) till r2
Nu vill jag inte alls vara med lägre. Att använda add är helt korrekt men varför med 3 register?

Add ser ut som följer:
add {cond} {Rd}, Rn, #imm12
Add instruktionen lägger till värdet från rn ELLER #imn12 alltså skulle det vara bättre att använde fäljande:
add r2,2r,#1

Därför att r3 innehåller värdet 1. Om man måste köra denna loop flera varv så är detta snabbare, sedan kommer min implementation från en mer generell funktion än att addera exakt 1. Du kan bara addera ett tal mellan 0 och 4095 med på ditt sätt med att addera ett konstant värde, med "mov" till ett register utanför loopen hanterar man tal mellan 0-65535 + att man enkelt kan lägga till en extra mov för att hantera 0-0xffffffff.

Skrivet av Morkul:

strex r1, r2, [r0] ; försök spara värdet i r2 i adressen som r0 innehåller, r1 kommer innehåller om det gick bra eller inte
Personligen använder jag oftast strexeq men saken är den att nu när jag har tittat på saken närmare så finns det egentligen ingen anledning till det utan nog bara varit så på grund av gammal vana.

Och här har du nästa instruktion som är betydligt mycket dyrare än t.ex. en "movs" som du optimerat bort.

Skrivet av Morkul:

teq r1, #0 ; ett värde på 0 i r1 betyder att det gick bra
Själv använder cmpeq alternativt xor istället, inte helt säkert vilket som är snabbast men det är något som jag faktiskt ska ta mig en titt på.

bne try
inte mycket att säga om det.
Slutsats:

; värdet på pekaren v ligger i register r0 från start try: ldrex r2, [r0] add r2,r2,#1 strex r1, r2, [r0] cmpeq r1, #0 bne try

Jämför man detta emot X86 lock xadd så är det snabbare dessutom så bör man inte använda lock xadd vid intensiva loopar då det kan bli extremt segt när trådarna måste sitta och vänta på varandra alt för långa tider. Den absoluta minimum tiden för lock xadd är 20 cyklar och med lite otur som sagt 20 gånger med än så vilket inte fungerar när man ska optimera en loop utan då blir man även utlämnad till att börja använda regiser.

Vad tror du tiden för ldrex och strex är? Kanske lägre i antal cykler, men det är i så fall för att din ARM CPU inte går på 3GHz som din x86 CPU gjorde. Den erfarenhet jag har av att jobba med x86, PowerPC och MIPS är just hanteringen av atomära instruktioner definitivt är mer effektivt på x86. På lås som det är "contention" om så ökar bara skillnaden.

Har svårt att tro den kod jag listade initialt har några som helst prestandaproblem, för det var inget jag skrev (är som sagt ingen ARM expert) utan är vad gcc producerar när man använder __sync_add_and_fetch (vilket också resulterar i "lock xadd" på x86) samt kolla precis upp Linux kärnan på ARM och de använder också exakt denna implementation för atomic_add och personen som skrivit den koden jobbar (eller i alla fall jobbade) på ARM.

Edit: och en poäng jag försökte göra är att x86, tvärt om vad du skrev, är MER deterministisk än RISC. Visst, det KAN ta latensen motsvarande en RAM access att köra en instruktion med lock-prefix, men det är i alla fall en specificerat worst-case.

På en RISC är den maximala tid det tar att köra motsvarande sekvens obestämd!!

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Datavetare
Skrivet av Morkul:

Förstår inte riktigt hur du argumenterar. Hur diverse språk så som java/C/C++, etc etc löser saker och ting är helt betydelselöst när man diskuterar hur bra en CPU fungerar. Allt som har betydelse är instruktionssettet och hur du med hjälp av det kan läsa olika problem. Med EXAKT samma C kod kan du får ett antal olika outputs beroende på kompilator men även samma kompilator kan få olika resultat beroende på kampilatordirektiv och detta är någon som gäller alla plattformar. Så att en kompilator är dåligt konfigurerad och där med får dåligt optimerad kod beror aldrig på CPUn.

Du för gärna förklara hur rent instruktionsmässigt varför du anser att det är ineffektivt för annars måste vi börja gissa de olika kompilatorernas output vilket inte ger speciellt mycket.

Lite kul fakta som egentligen inte har med detta att göra:
Alltid lika roligt att höra när någon har använd någon C++ compiler och säger att de skapat ett optimera program. När man sedan dissasemblerar programmet och oftast igenom några snabba förändringar kan göra ganska stora optimeringar så blir man lite trött. Bara det att många kompilatorer fortfarande använder mov istället för xor när det ska nollställa variabler är ju helt löjligt.

gcc använder definitivt xor för att nollställa variabler, på Sandy Bridge och senare känns just sekvensen xor regX, regX igen och kan utföras utan att det ens tar upp en exekveringsport!

Men det är väl extremt relevant hur effektivt en CPU kan hantera konstruktioner som moderna programmeringsspråk utnyttja. Är ju rätt få som sitter och pillar i assembler idag. Anledningen att fork/join är att man kan skriva sin "work-stealing" logik så att de andra CPU:erna inte kommer göra något innan de "ser" den sista förändringen man gjorde i en serie. På RISC så vet man inte om tidigare skrivningar är synliga med mindre än att lägga in explicita barriärer, men x86 garanterar inte NÄR man ser förändringen, men den garanterar att när man väl ser den SISTA förändringen så är även alla andra förändringar synliga.

Så för en out-of-order CPU så är ju x86 att föredra då man i vissa lägen kan optimera bort (dyra) skrivbarriärer.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd
Skrivet av Yoshman:

Inte alls onödigt, det är snabbare att addera register än att addera register + konstant. Visst blir detta en extra instruktion, men om det blir fler än ett varv i loopen så spar man in denna a instruktion. Du konstaterade redan nedan att man adderad mot register r3, som just innehåller en 1:a.

Då loopen normalt sätt lyckas på första försöket vilket gör min version snabbare, om vi inte pratar om många kärnor som jobbar extremt intensivt och i sådana fall är ingen av våra versioner av detta att föredra.

Citat:

Man läser upp värdet r0 pekar på i r2 och man låser inget, gjorde man det skulle aldrig strex misslyckas. Man sätter någon intern flagga i CPUn så att den håller reda på om någon skriver till den cache-line som värdet r0 pekar på. Notera att denna instruktion är säker 50-100 gånger dyrare än den "movs" du optimerade bort, så den optimeringen är meningslös.

Var faktiskt tvungen att kolla om jag hade rätt i ARM referens:
If the physical address has the Shared TLB attribute, LDREX tags the physical address as exclusive access for the current processor, and clears any exclusive access tag for this processor for any other physical address.

Citat:

Därför att r3 innehåller värdet 1. Om man måste köra denna loop flera varv så är detta snabbare, sedan kommer min implementation från en mer generell funktion än att addera exakt 1. Du kan bara addera ett tal mellan 0 och 4095 med på ditt sätt med att addera ett konstant värde, med "mov" till ett register utanför loopen hanterar man tal mellan 0-65535 + att man enkelt kan lägga till en extra mov för att hantera 0-0xffffffff.

Och här har du nästa instruktion som är betydligt mycket dyrare än t.ex. en "movs" som du optimerat bort.

Dessutom tänkte jag fel när jag skrev förra posten. ADDS Rd, Rn, #imm hade varit bättre att använda då sparar jag lite tid och är det någonting annat man skulle vilja göra i samband med detta så är det en enkelt instruktion att para med. Kan dock bara addera 0-7 då. Jag antog att addera 1 skulle räcka du du hade en fast tilldelning av r3 med #1

add r2, r3 eller add r2,r2,#1 gör 1 cykels skillnad

Citat:

Vad tror du tiden för ldrex och strex är? Kanske lägre i antal cykler, men det är i så fall för att din ARM CPU inte går på 3GHz som din x86 CPU gjorde. Den erfarenhet jag har av att jobba med x86, PowerPC och MIPS är just hanteringen av atomära instruktioner definitivt är mer effektivt på x86. På lås som det är "contention" om så ökar bara skillnaden.

ldrex = 2 cyklar (1 cpu och 1 minnes)
strex = 2 cyklar 2 cyklar (1 cpu och 1 minnes)

Vad frekvensen har med saken att göra förstår jag inte riktigt, snarare IPC där x86 i många fall är lite bättre en ARM dock inte i detta exemplet.

Citat:

Har svårt att tro den kod jag listade initialt har några som helst prestandaproblem, för det var inget jag skrev (är som sagt ingen ARM expert) utan är vad gcc producerar när man använder __sync_add_and_fetch (vilket också resulterar i "lock xadd" på x86) samt kolla precis upp Linux kärnan på ARM och de använder också exakt denna implementation för atomic_add och personen som skrivit den koden jobbar (eller i alla fall jobbade) på ARM.

Jag har en standard rutin som jag använder i sådana har fall. Den kör ett slumplatsgenerator som slumpar fram 10 000 tal som är mellan 1 och 100 för simulera en CPU last, ibland varierar jag antalet tal som ska genereras beroende på hur snabbt jag vill att det ska loopa. Slumptalsgeneratorn är statistisk korrekt men inte helt optimerad, jag har gjort den så att det lastar CPU till 100%, inte inte ens en 1/4 cykel över.

Jag testar på en Samsung Exynos4412 Cortex-A9 Quad Core 1.4Ghz. Jag kör systemet och grundinstansen på en core och låser 3 cores för att endast köra 3 trådar med programvaran jag testar. I detta fallet skulle ena tråden generera 2000 tal, nästa 7000 tal och den tredje 10 000 tal för att sedan öka talet enligt våra två olika sätt att göra det på. Jag genererade olika antal tal för att det skulle kunna ske krockar, kör man samma så skulle man kunna ha otur att trådarna aldrig försöker göra lrdx samtidigt. Summan blev på detta att jag hade 2-4% bättre prestanda, inte problem kanske men jag är kinkig som assembler programmerare.

Jag har faktiskt mailat en del och frågat varför de har gjort som det exempel du visade, kollar man ARMs egna referensmaterial så gör de inte så, upptäckte att de inte gör som jag heller de använder qadd. Vilket gör denna diskussion rolig för mig eftersom jag aldrig tänkt tanken att använda qadd vilket jag nu måste kolla upp om det kan hjälpa mig i mitt jobb.

Citat:

Edit: och en poäng jag försökte göra är att x86, tvärt om vad du skrev, är MER deterministisk än RISC. Visst, det KAN ta latensen motsvarande en RAM access att köra en instruktion med lock-prefix, men det är i alla fall en specificerat worst-case.

På en RISC är den maximala tid det tar att köra motsvarande sekvens obestämd!!

Då har du nog missförstått mig. Vad jag har sagt är att man inte har någon koll på x86 för att många instruktioner ta så många cyklar och när man skickar iväg en instruktion vet man aldrig när jag får tillbaka kontrollen. De gånger jag sitter och programmerar på x86 så leder det ofta till att jag går ner och använder flera instruktioner till att göra samma sak istället för att använda till exempel xadd och många gånger visar det sig att det går att optimera programmen igenom att göra så. Detta gör att vissa instruktioner som tar lång tid på sig och kan göras snabbare med andra instruktioner enligt mig inte borde finnas och x86 börjar få fler och fler av dessa instruktioner.

Under tiden jag satt och skrev denna post så har jag kommit på ett helt annat sätt att lösa detta problem på och om jag har rätt så blir det betydligt snabbare. Återkommer i den frågan

Permalänk
Datavetare
Skrivet av Morkul:

Då har du nog missförstått mig. Vad jag har sagt är att man inte har någon koll på x86 för att många instruktioner ta så många cyklar och när man skickar iväg en instruktion vet man aldrig när jag får tillbaka kontrollen. De gånger jag sitter och programmerar på x86 så leder det ofta till att jag går ner och använder flera instruktioner till att göra samma sak istället för att använda till exempel xadd och många gånger visar det sig att det går att optimera programmen igenom att göra så. Detta gör att vissa instruktioner som tar lång tid på sig och kan göras snabbare med andra instruktioner enligt mig inte borde finnas och x86 börjar få fler och fler av dessa instruktioner.

Så fort du blandar in CPU-cache i systemet så kan du längre inte räkna klockcykler, det var en väldigt debatt om just detta i slutet av 80-talet (när CPU-cache började dyka upp) mellan arkitekter av realtids OS. I början så var det vissa som var så rabiata att de slog av CPU-cachen just p.g.a. detta, men man insåg ganska snart att det liksom inte riktigt höll.

Så du KAN INTE veta på klockcykelnivå när du får tillbaka kontrollen på en modern ARM heller. Och blandar du även in multicore så vet du än mindre eftersom instruktioner då kan stalla på MESI uppdateringar som sker över en bus som delas mellan alla CPU-kärnor, i.e. tiden det tar att köra en instruktion på CPU #0 kan alltså variera beroende på vad som händer på de andra CPU-kärnorna.

Och när CPUn även är out-of-order så blir det inte längre relevant att räkna antal instruktioner för att avgöra hur fort något exekverar, det viktiga är hur stor andel av instruktionerna som är helt eller delvis oberoende. Nu har i.o.f.s. Cortex A9 ett ganska litet out-of-order fönster, men när Cortex A15 börjar dyka upp så kommer ARM allt mer likna x86 i hur stor variation en specifik instruktion kan ta. Det absolut största problemet på en CPU som t.ex. Sandy Bridge som har ett enormt out-of-order fönster (har för mig att det är 160 instruktion långt) är: hur mäter du tiden det tar att exikverar enskilda instruktion? Variansen i en sådan mätning kan i princip vara ett out-of-order fönster stort...

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd
Skrivet av Yoshman:

Så fort du blandar in CPU-cache i systemet så kan du längre inte räkna klockcykler, det var en väldigt debatt om just detta i slutet av 80-talet (när CPU-cache började dyka upp) mellan arkitekter av realtids OS. I början så var det vissa som var så rabiata att de slog av CPU-cachen just p.g.a. detta, men man insåg ganska snart att det liksom inte riktigt höll.

Där av att jag angav så väl CPU som minnes cykel ovan.

Citat:

Så du KAN INTE veta på klockcykelnivå när du får tillbaka kontrollen på en modern ARM heller. Och blandar du även in multicore så vet du än mindre eftersom instruktioner då kan stalla på MESI uppdateringar som sker över en bus som delas mellan alla CPU-kärnor, i.e. tiden det tar att köra en instruktion på CPU #0 kan alltså variera beroende på vad som händer på de andra CPU-kärnorna.

Jo jag vet tämligen exakt när jag får till tillbaka kontrollen så länge jag vet vad jag gör och ensam har kontrollen på kärnan. Ska jag böra dela kärnan med till exempel systemet så börjar det bli jobbigt dock. Nu har jag turen nog att det OS jag sitter och jobbar mest på är ett OS jag själv har skrivit så jag har tämligen god koll på vad det gör.

Citat:

Och när CPUn även är out-of-order så blir det inte längre relevant att räkna antal instruktioner för att avgöra hur fort något exekverar, det viktiga är hur stor andel av instruktionerna som är helt eller delvis oberoende. Nu har i.o.f.s. Cortex A9 ett ganska litet out-of-order fönster, men när Cortex A15 börjar dyka upp så kommer ARM allt mer likna x86 i hur stor variation en specifik instruktion kan ta. Det absolut största problemet på en CPU som t.ex. Sandy Bridge som har ett enormt out-of-order fönster (har för mig att det är 160 instruktion långt) är: hur mäter du tiden det tar att exikverar enskilda instruktion? Variansen i en sådan mätning kan i princip vara ett out-of-order fönster stort...

Här har du en bra poäng och som programmerare gäller det att hålla tungan rätt i munnen. En riktigt bra programmerare räknar noga på hur han ska lägga upp sin programmering för att använda samtliga cyklar han har tillgängliga. Detta är en process som kan vara flera veckors med planering innan man ens börjar att skriva någon kod allt för att kunna para ihop rätt instruktioner med varandra och då menar jag inte endast rent "instruction pairing" utan att även se till att kombinera det bra tillsammans med minnesanvändning. Så målet för en programmerare är ju att få så lite OOE som möjligt men tyvärr så är det ju så att de som inte sitter och programmerar i assembler kan inte göra så mycket åt saken utan kan bara hoppas på att de som gjort kompilatorn har gjort ett bra jobb.

Permalänk
Datavetare
Skrivet av Morkul:

Där av att jag angav så väl CPU som minnes cykel ovan.

Och precis som jag försökte påpeka så är antal CPU-cykler en sådan instruktion tar helt irrelevant, framförallt på en RISC där alla instruktioner normalt sätt tar 1 cykel att ta sig genom "dispatch" delen av pipe:en, vilket många anser vara "kostnaden" för att köra instruktionen. Men en annan sak som gör det lite meningslöst att prata klockcykler är att även på simpla CPU:er som ARM Cortex A9 och Intel Atom har man en pipeline på 10-14 steg.

Det kanske är relevant att prata om klockcykler per instruktion på en gammal ARM7 och tidigare där pipe:en bestod av 3 steg, fetch, decode, execute/retire.

Titta på denna rapport som bl.a. tar upp bandbredd och latens mot RAM på en Cortex A9.

Hur ska du överhuvudtaget kunna veta hur lång tid ldrex då tiden för minnesaccessen kan variera mellan 30ns (L1$-hit) till 700ns(måste gå mot RAM)? Då denna rapport kommer från 2009 så kan vi anta att de körde på ganska tidiga A9 utvecklingskort, så klockfrekvensen är nog inte mer än runt 500MHz, så detta motsvarar det en varians på 15-350 klockcykler (sida 17). Blandar man in flera CPU-kärnor går latensen upp än mer (sida 18).

Samma sak gäller genomsnittlig bandbredd, den varierar allt från strax under 1.5GB/s till 200-300MB/s för läsning.

Den som slår en här är vilken brutalt usel cache och RAM design en så pass modern CPU ändå har. Hade väntat mig att moderna x86:er skulle har något lägre latens och ungefär 10 gånger bättre bandbredd, men det räcker inte på långa vägar. Atom har ungefär 10 gånger mer bandbredd mot RAM, teoretiskt är den 4.1GB/s och folk får runt hälften i praktiken (2.6GB/s har uppmätts under Linux).

Tittar man i stället på Sandy Bridge så ligger latensen mot RAM mellan 40-60ns och bandbredden mellan 15GB/s till 20GB/s (beroende på kvalité på RAM, detta är dual-channel). Latensen är en faktor 10 bättre, bandbredden är ju mellan 50-100 gånger bättre...

Skrivet av Morkul:

Jo jag vet tämligen exakt när jag får till tillbaka kontrollen så länge jag vet vad jag gör och ensam har kontrollen på kärnan. Ska jag böra dela kärnan med till exempel systemet så börjar det bli jobbigt dock. Nu har jag turen nog att det OS jag sitter och jobbar mest på är ett OS jag själv har skrivit så jag har tämligen god koll på vad det gör.

Men andra ord så kör du bara system utan OS och med alla interrupt avslagna, för hur kan du annars veta om det data du läste för ett par instruktioner sedan numer befinner sig i L1$, L2$ eller RAM. Det är en skillnad på över en tiopotens i hur lång till en ld instruktion kommer att ta i praktiken.

Skrivet av Morkul:

Här har du en bra poäng och som programmerare gäller det att hålla tungan rätt i munnen. En riktigt bra programmerare räknar noga på hur han ska lägga upp sin programmering för att använda samtliga cyklar han har tillgängliga. Detta är en process som kan vara flera veckors med planering innan man ens börjar att skriva någon kod allt för att kunna para ihop rätt instruktioner med varandra och då menar jag inte endast rent "instruction pairing" utan att även se till att kombinera det bra tillsammans med minnesanvändning. Så målet för en programmerare är ju att få så lite OOE som möjligt men tyvärr så är det ju så att de som inte sitter och programmerar i assembler kan inte göra så mycket åt saken utan kan bara hoppas på att de som gjort kompilatorn har gjort ett bra jobb.

Och de som sitter och programmerar i assembler kan inte HELLER göra något åt saken för det finns INGEN CPU-tillverkar som ger dig tillräckligt med information för att du på klockcykelnivå kan räkna ut hur lång tid saker tar. I ett system med flera CPU-kärnor är det omöjligt även om du visste allt detta.

Detta är något jag fått förklara för mig av personer som jobbar med s.k. MILS (Multiple Independent Level of Security) system där variationer i hur lång tid det tar att köra något kan eventuellt vara en läcka av information.

Det finns lägen när det fortfarande kan vara vettigt att skriva assembler, ett sådant exempel är vissa former av checksummor och andra beräkningar där man kan ha stor nytta av att komma åt carry bit, något som C inte möjliggör. För den checksumma som används av UDP och TCP kan man bara summera 16-bitar i stöten i C, men 32 bitar i assembler tack vare carry-biten som effektivt gör att man får 33 bitar och man måste ha minst en bit mer än storleken på det man processar. Att använda 31-bitar i C är möjligt men blir så komplicerat att det går fortare att bara köra med 16. Samma sak gäller 64-bitar, men då är det 32-bitar i C mot 64-bitar assembler.

Andra saker som kan vara vettigt att skriva i assembler är implementationer av vissa primitiver som mutex, semaphorer och spinlocks. Men även detta kan man skriva i t.ex. gcc genom att använda (icke-standard) extensionen som då gör att samma kod fungerar på alla CPU:er som kompilatorn stödjer i stället för att sitta att hacka en version per CPU.

Rent generellt är det totalt slöseri med tid även för de mest prestandakritiska programmen att skriva i assembler idag. Moderna C kompilatorer har väldigt bra informationen om exakt vilka konstruktioner som är snabba, kolla bara på vilka udda ställen gcc tenderar att använda "lea" i stället för det mycket mer logiska "mov" bara för att "lea" automatiskt ger dig sign-extend. Tricks som xor rx,rx klarar de flesta assemblerprogrammera av, men det gör även moderna kompilatorer.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd
Skrivet av Yoshman:

Som jag skrev. Så länge jag vet exakt vad CPUn har kört för instruktioner sedan en tid tillbaka så vet jag exakt hur lång tid nästa instruktion kommer att ta, med minnes latens och allt. Där av att jag också var noga med att påpeka att jag sitter på egenutvecklat OS. Just att sitta på egenutvecklat OS där allting är extremt optimerat och jag vet vad som händer när även med OSet är ett måste för att det ska fungera. Min poäng är att skaffar du dig den kontrollen så vet du exakt hur en ARM kommer reagera på nästa instruktion vilket du inte kan på x86 oavsett hur mycket kontroll du försöker skaffa dig.

En sak som x86 dock har den stora fördelen är att kompilatorerna är bra mycket bättre optimerade. Tittar du idag på GCC för ARM så blir man nästan mörkrädd för illa koden blir och dessutom hade de en del buggar som kan leda till rena räkna fel (tror och hoppas på att buggarna är fixade nu). KIEL är något bättre men långt ifrån bra. IAR har nog den bästa kompilatorn, ibland när jag behöver hjälp så sätter jag en C programmerare med IARs kompilator och gör om C koden till assembler för att sedan optimera ifrån det. Varför är det så? En del är ju förstås att det är mer konkurrens på x86 och flera har hållit på att göra kompilatorer längre. Men den stora nackdelen är nog att ARM är lite stelt och det krävs att man inte bara tänker på vilka instruktioner man ska använda men också i vilken ordning de skall köras. Ibland måste man köra saker i en ordning som känns helt bak och fram för att det ska vara optimerat och just denna avvägning måste man till och med testa sig fram under själva kodfasen av utvecklingen. Just detta anser jag i alla fall vara den stora nackdelen och egentligen det som denna tråd i sig i grunden handlar om.

Angående ARM Technical Symposia 2009 som du länkade till, jo det var en rolig dag speciellt workshopen där det diskuterades vilt för hur man skulle komma runt vissa parallelismproblem. 2012 så går det en ARM Technical Symposia i Paris den 13 december, är det något som du åker på? Själv ska jag dit men ville egentligen åka till det som var i Indien tidigare i år men det ville inte chefen.

Permalänk
Datavetare
Skrivet av Morkul:

Om inte du jobbar på en microkontroller (något som ÄR populärt på enklare ARM:ar, men knappast på Cortex A9 som du verkar jobba med) som kör med ett ej preemtive-multitaskat OS och med interrupt avslagen så kan du inte säga något om tiden det tar att köra nästa instruktion för du vet inte om
* har det skett ett interrupt sedan förra instruktionen
* har en process med högre prioritet kör sedan förra instruktionen

Har du ett multicore system så är det helt omöjligt att exakt veta hur lång tid nästa instruktion tar då du inte kan veta vad den andra CPU gör och till vilka adresser den skrivit till.

Stänger du av interrupt på x86 och kör den som en microkontroller och antar att det bara är ett single core system så skulle du rent teoretiskt kunna räkna ut exakt hur långt tid varje instruktion tar. Det skulle kräva att du fick ut information EXAKT hur CPUn fungerar, något varken Intel eller AMD skulle ge dig, men det är teoretiskt möjligt.

Jobbar som sagt bara i undantagsfall med ARM så lär inte få åka på något ARM-specifikt konvent, även om det skulle vara intressant. Blir allt mer x86_64 för mig med även en hel del PowerPC och något mindre MIPS. Det företag jag jobbar på arbetar dock med ARM, så har en del ARM experter på kontoret.

GCC har blivit MYCKET bättre på både ARM, PowerPC och MIPS de senaste 5 åren, bl.a. tack vare arbete från Code Sourcery som merga:at tillbaka till "vanliga" gcc. Sedan borde LLVM/Clang vara rätt bra för ARM vid det här laget med tanke på hur viktig den kompilatorn är för Apple.

Men det händer även rätt mycket för x86, kolla in förbättringar för både Bulldozer och Sandy Bridge i gcc 4.7.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd

Jag tror att vi båda är lite förvirrade eller snarare förvirrar varandra lite. Diskussionen som startade i den andra tråden handlade om hur vida Intel kunde konkurrera med ARM på mobila system och jag har liksom hängt kvar i de tankarna och inte fullfjädrade system. Pratar vi fullfjädrade OS och då menar jag flexibla OS så är x86 att föredra. Pratar vi specialiserade små OS så kommer ARM vara bättre i många fall.

Just nu jobbar jag på en produkt som förhoppningsvis kommer släppas straxt innan JUL, där av att jag jobbar nästen 100 timmar i veckan just nu.

Jag kan tyvärr inte säga allt för mycket om systemet men det är ett media system som kommer klara alla filmformat som finns i dagsläget, intern lagring, lan, wifi, några USB, etc etc. Kravet var att det skulle vara ett helt passivt kylt system som idlade under 0,5w (någon EU regel som skulle följas för hemelektronik). I stort sätt all mjukvara är helt gjort i assembler för att göra det så effektivt som bara är möjligt. Även vissa codecs som egentligen fanns klara att använda har fått en hel del inline assembler för att göra det mer effektivt. Summa sumarum är att detta system hade inte ens varit möjligt att göra med en Intel CPU med de kravspecar som fanns från början och även om man skulle ändrat på dem lite så skulle bygget ha blivit extremt mycket dyrare att göra med Intel.

Ett av de stora problemen som detta system hade haft om det hade varit uppbyggt runt Intel är periferiutrustningen. Fördelen med x86 är att det finns kompatibilitet som inte finns med ARM. Fördelen att inte ha någon kompatibilitet att bry sig om är att jag kan kommunicera med te.x nätverkskortet som passa bäst för just detta system vilket gör att man kan få bättre prestanda. En förutsättning är då att personen som designar själva hårdvaran har ett nära samarbete med mjukvaruutvecklarna. Nackdelen är att det tar BETYDLIGT längre att skriva ihop fungerade mjukvara.

Angående GCC så måste jag nog kolla upp det igen. Var två år sedan sist och då hade de stora problem med ett antal buggar. Annars kan jag som sagt varmt rekomendera IAR, ska du inte göra stora program så är det gratis och det bästa är att man enkelt kan se hur assembler koden ser ut och är det något man vill ändra där så fungerar det också.

Permalänk
Datavetare

Diskussionen började väl med att jag ville ha ett konkret exempel på att x86 har fel som gör att "extrem exemplet köra samma kod två gånger och få olika resultat och det är inget för mig."

Skulle säga att den enda slutsatsen på detta är: det kanske finns sådana fall, men det har inget med ARM kontra x86 att göra utan systemet kan kör på och x86 används normalt sett inte på små och extremt applikationsspecifika produkter.

Tar man Linux, t.ex. i form av Android, som exempel så kan man inte säga exakt hur lång tid något tar att köra vare sig på ARM eller x86. Men x86 har i alla fall fördelen att det finns en övre gräns för allt, något som saknas på de flesta RISC:ar i teorin och möjligen även i praktiken.

Och jag skrev redan i tråden att Intels problem kommer vara att kunna konkurrera prismässigt med ARM om man vill in i telefoner och pekplattor, rent prestandamässigt har ju ARM ännu inte lyckats spöa Atom från 2008 men Intel har under den tiden fått ner strömförbrukningen på sina Atom baserade SoC:er till samma nivåer som Cortex A9 baserade SoC:er. Så tvivlar inte alls på att ert projekt skulle bli dyrare med ett Atom SoC, har däremot väldigt svårt att tro att strömförbrukningen skulle vara ett problem och inte heller GPUn då Intel just nu använder samma GPU-tillverkare som Apple, PowerVR.

De fördelar Intel/AMD har är att x86 är en mer effektiv plattform för multicore + övergången till 64-bitar är avklarad. Möjligen kan ARMv8 (Aarch64) göra ARM till en bättre plattform för multicore, men Aarch64 ligger LÅNGT borta med tanke på att man måste få till kisel (vilket är det lilla problemet) och börja utveckla tool-chains för Aarch64. Intel/AMD har ju 10 års försprång här och vi vet att det tog bra många år innan x86_64 kompilatorerna var lika bra som x86.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd
Skrivet av Yoshman:

Diskussionen började väl med att jag ville ha ett konkret exempel på att x86 har fel som gör att "extrem exemplet köra samma kod två gånger och få olika resultat och det är inget för mig."

Skulle säga att den enda slutsatsen på detta är: det kanske finns sådana fall, men det har inget med ARM kontra x86 att göra utan systemet kan kör på och x86 används normalt sett inte på små och extremt applikationsspecifika produkter.

Men tag xadd exemplet ovan så är det skillnad mellan 21 och över 200 (tror det var 229) cyklar, det kallar jag extremt olika resultat. Så extremt olika resultat för en enskild instruktion har jag aldrig varit med om på en ARM och att det finns värre exempel en xadd.

Citat:

... Intel har under den tiden fått ner strömförbrukningen på sina Atom baserade SoC:er till samma nivåer som Cortex A9 baserade SoC:er.

Inte en chans att vi skulle klara hela systemet idle på 0,5w om vi använde Atom. Observera att hela systmet skulle alltså klara 0,5w inte bara SoC.

Sedan när Samsung får igång sin nya fabrik senare i år eller tidigt nästa år och kommer hoppas över en generation krympning och gå direkt ner från 32nm till 20nm så kommer förbrukningen sjunka ytterligare. Frågan är om inte Qualcomm också kommer försöka kommer ner i 20nm innan nästa år är slut. Saken är den att det kostar inte alls lycka mycket att investera i existerande teknik som div ARM tillverkare gör till skillnad från Intel som kommer få det svårare och svårare med krympningarna. Dock ska sägas att bara Samsung lägger ner 2.4 miljarder dollar bara på detta i år så det är inte lite pengar vi pratar om.

Permalänk
Datavetare
Skrivet av Morkul:

Men tag xadd exemplet ovan så är det skillnad mellan 21 och över 200 (tror det var 229) cyklar, det kallar jag extremt olika resultat. Så extremt olika resultat för en enskild instruktion har jag aldrig varit med om på en ARM och att det finns värre exempel en xadd.

Men det är BARA därför att du har andra saker som kör i bakgrunden. Installera t.ex. Linux på ditt ARM-kort och testa samma sak där, du kommer se exakt samma resultat där då Linux är ett preemptive OS.

Eller gör det omvända, kör det exemplet "bare-metal" på x86. Det blir då helt deterministiskt, första anropet kommer kanske ta 200 cykler (beror på hur aggressiv prefetcher är) nästföljande kommer ligga på en konstant tid, 21 cykler är latens mot L2 så det är kanske vad "lock xadd" väntar på då.

Skrivet av Morkul:

Inte en chans att vi skulle klara hela systemet idle på 0,5w om vi använde Atom. Observera att hela systmet skulle alltså klara 0,5w inte bara SoC.

Sedan när Samsung får igång sin nya fabrik senare i år eller tidigt nästa år och kommer hoppas över en generation krympning och gå direkt ner från 32nm till 20nm så kommer förbrukningen sjunka ytterligare. Frågan är om inte Qualcomm också kommer försöka kommer ner i 20nm innan nästa år är slut. Saken är den att det kostar inte alls lycka mycket att investera i existerande teknik som div ARM tillverkare gör till skillnad från Intel som kommer få det svårare och svårare med krympningarna. Dock ska sägas att bara Samsung lägger ner 2.4 miljarder dollar bara på detta i år så det är inte lite pengar vi pratar om.

Nej, men Atom kommer man som lägst ner till 1.3W för ett helt SoC. Men om ni klarar er på 0.5W så är det inte den högst klockade A9:an och heller inte en speciellt vass GPU, m.a.o även den långsammaste Atom SoC som finns är rejält mycket snabbare än ert system.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd
Skrivet av Yoshman:

Nej, men Atom kommer man som lägst ner till 1.3W för ett helt SoC. Men om ni klarar er på 0.5W så är det inte den högst klockade A9:an och heller inte en speciellt vass GPU, m.a.o även den långsammaste Atom SoC som finns är rejält mycket snabbare än ert system.

1.2Ghz så det är väl inte den högst klockade men skulle ha kommit ner till de nivåerna även om det skulle vara 1,4Ghz. Men stänger ju av alla utom en core drar ner frekvensen till ett minimum när man idlar så ursprungs klocken spelar ingen större roll.

Permalänk
Datavetare
Skrivet av Morkul:

1.2Ghz så det är väl inte den högst klockade men skulle ha kommit ner till de nivåerna även om det skulle vara 1,4Ghz. Men stänger ju av alla utom en core drar ner frekvensen till ett minimum när man idlar så ursprungs klocken spelar ingen större roll.

ARM gör precis samma sak som Atom, när den är i idle så "power-gate:ar" man CPUn vilket effektiv sätter klockfrekvensen till noll. Cortex A9 och Atom drar endast några enstaka mW när de befinner sig i det läget. Så om det var vad du menade att hela systemet drar 0.5W så borde även den snabbaste versionen av Medfield fixa ert 0.5W krav. Det jag listade är TDP, vilket är mer ett värde på genomsnittlig effekt vid max last.

Det är just detta som gör Haswell så cool, den kan "power-gate:a" enskilda delar av CPUn trots att andra delar i samma fysiska kärna används för fullt. Nog inte allt för djärv gissning att nästa Atom (som kommer H1 2013) kommer ha samma typ av funktion och då är det inte alls omöjligt att Atom kommer dra mindre än samtida ARM:ar med likvärdig prestanda

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd
Skrivet av Yoshman:

ARM gör precis samma sak som Atom, när den är i idle så "power-gate:ar" man CPUn vilket effektiv sätter klockfrekvensen till noll. Cortex A9 och Atom drar endast några enstaka mW när de befinner sig i det läget. Så om det var vad du menade att hela systemet drar 0.5W så borde även den snabbaste versionen av Medfield fixa ert 0.5W krav. Det jag listade är TDP, vilket är mer ett värde på genomsnittlig effekt vid max last.

Enligt våra hurdvaru-nisar på jobbet som kan detta betydligt bättre än jag säger att det inte går att komma ner i så pass låga watt och fortfarande ha kapacitet nog att övervaka systemet med en Atom. Åter igen har det med hur x86 hanterar komponenter som ligger utanför själva CPUn.

Citat:

Det är just detta som gör Haswell så cool, den kan "power-gate:a" enskilda delar av CPUn trots att andra delar i samma fysiska kärna används för fullt. Nog inte allt för djärv gissning att nästa Atom (som kommer H1 2013) kommer ha samma typ av funktion och då är det inte alls omöjligt att Atom kommer dra mindre än samtida ARM:ar med likvärdig prestanda

Viket även bland annat nästa Exynos kommer att ha så jag har svårt att se Atom ta in allt för mycket på den fronten på grund av detta. Antagligen kommer Snapdragon och definitivt så kommer näste Tegra ha det eller om nVidia kommer hitta på någon annan lösning som är än bättre, i alla fall om de håller vad de har sagt.