Skrivet av xobust:
Nu är det kanske inte många förutom Gentoo användare som kompilerar all kod optimerad för sin egen cpu.
Men om du targetar de senaste CPU arkitekturerna för AArch64 och X86_64 så vinner X86 med en instruktion.
x86_64 cooperlake
ARM64 cortex-a76
Värt att notera också är att de riktigt breda instruktionerna kräver mycket mer effekt vilket kan sänka klockfrekvensen för en eller flera cpu kärnor [0]. Därför kan det i vissa fall vara värt att köra mindre breda simd instruktioner för att inte sega ner resten av programmet.
Det ska bli intressant att se hur bra ARM arkitekturernas prestanda skalar i frekvens och krestsstorlek.
Vi kommer kanske se ett absolut prestanda race mellan X86 och ARM cpur de kommande 10 åren.
[0] https://stackoverflow.com/questions/56852812/simd-instructions-lowering-cpu-frequency
Du belyser faktiskt en annan bra aspekt: om man bara zoomar in på SIMD har faktiskt Intel fått till det rätt bra, framförallt AVX-512 där det finns direkt stöd på instruktionsnivå att hantera divergerande branscher likt hur moderna GPUer hanterar samma sak.
Men som du nämner är ju nackdelen med AVX-512 (redan med 256-bitars AVX) att frekvensen minskas, så det är inte alls självklart att AVX-512 alls är ett bra alternativ ens i detta fall då det minskar prestanda för efterföljande instruktioner. De NEON instruktioner ARM använder är 128-bitars, d.v.s. de är mer lik SSE i hur mycket ström de bränner och SSE har normalt noll påverkan på frekvens!
Vidare på CISC vs RISC spåret. AVX-512 må ha coola finesser, men det kostar också kodstorlek! Om man genererar maskinkoden för dessa två fall så är genomsnittlig instruktionslängd för AVX-512 varianten 5 bytes, d.v.s. 25 % större instruktioner jämfört med ARM64!
Och för att vicka ut sista delen av saltet för just detta fall: då AVX-512 tenderar dra så mycket ström har alla nuvarande modeller som stödjer detta bara halva kapaciteten aktiv i normalfallet, först när "tillräckligt" många AVX-512 har körts kommer andra halvan att aktiveras. Svårt att veta exakt hur många NEON-instruktioner Apples CPUer kan köra per cykel, men är nog ett gäng givet att SPECfp normalt byggs med flaggor specifika för CPU-modellen man kör och man var ju så långt efter AMD/Intel trots att de där kommer använda 256-bitars AVX. Cortex X1 har 4 st NEON-pipelines medan Zen2 och Skylake har båda 4 st asymmetriska SSE/AVX pipelines (vissa instruktioner kan bara köras av vissa pipeliens, rätt mycket en 2/2 split). Kommer nog tillbaka på att NEON är 128-bitars så går att fyra kompletta pipelines utan att spränga strömbudget, medan det inte är rimligt med AVX/AVX-512.
I praktiken är AVX-512 bara vettigt för stora problem där en väldigt stor andel av det man gör drar nytta av instruktionerna. Nu finns flera sådana fall, t.ex. många maskininlärningsfall, matlab, vissa statistiska beräkningar, vissa former av simulering.
Men då ska också nämnas: ARM har också en mer avancerad variant av SIMD som kallas SVE (Scalable Vector Instructions). SVE finns än så länge bara någon enstaka modell tänkt för superdatorer, men SVE kan processa upp till 2048 bitar per instruktion (och de är 4 bytes långa...)!
Men skriv också, "detta är normalfallet". Valde Rust dels för att jag anser det är med råge det bästa programspråk som lanserats sedan C. Förutom att det ger kompileringsfel på många buggar (bl.a. data-race) som är väldigt svåra att hitta och stör för majoriteten av alla säkerhetshål i kod skrivet i C, C++, C# och Java så är Rust designat specifikt med tanke på att kompileratorerna ska kunna optimera koden extremt väl.
Samma snutt skrivet i C++ (kompilerat med g++ 8.2) blir inte alls lika tokoptimerat, ändå betydligt kortare för ARM64 och kompilerar man för icelake-client eller något annan CPU med AVX-512 stöd blir det bara än värre (100 element är inte i närheten tillräckligt många för att kompensera för nackdelarna med att använda AVX-512).
ARM64
x86_64
I detta fall används inte SIMD i något fall, är hyfsat likvärdig inre loop. Man gör lite saker som kan se märkliga ut för x86, men det är med största sannolikhet för att utnyttja något som kom i Core2: macro-op fusion. Det är så vanligt att "cmp" följs av ett villkorat hopp att moderna x86 har interna instruktioner för detta, man slår ihop två x86 instruktioner till en intern. ARM64 har naturligtvis löst detta utan att bränna transistorer på macro-op fusion, via "cOP" (där OP kan vara eq, ne, le, gt etc) samt "csel" (i praktiken hopslagen x86 compare+cmov).
Testa gärna andra saker, t.ex. att sortera m.h.a. "sort". Inget lär ändras om man kör C, C++, Java eller C# utom möjligt att de har färre lägen där de kan använda SIMD jämfört med Rust.