Portál AbcLinuxu, 2. května 2025 17:33
Překopávám semestrálku z minulého semestru z javy do C++ (to už jsem asi někdy psal), ten kreslič funkcí. A jelikož je to vyhodnocování pomalost sama, tak bych z toho stromu chtěl vytvořit strojový kód. Teď stojím před problémem, jestli do toho "šoupnout" SSE2.
Schválně jsem napsal funkci na sečtení 2 double a se SSE2 jsem dostal 1 instrukci (addsd xmm0, xmm1) oproti kýblu FPU instrukcí bez SSE2. Zase na druhou stranu s FPU mám možnost rovnou počítat sinus, cosinus a spol., v SSE2 nic takového není a musel bych to asi růčo napsat (na netu jsem nic nenašel) a to zas tak rychlé asi nebude. A nevím, jestli kombinovat SSE2 s FPU je dobrý nápad ... četl jsem např. něco o přepínání módu při práci s MMX a FPU zároveň, snad tím netrpí SSE2 ...
Tiskni
Sdílej:
muzete trochu rozvect "Schválně jsem napsal funkci na sečtení 2 double a se SSE2"? jak jste toto provedl? To jako jste v to napsal v C a nechal prelozit via gcc s nejakou optimalizaci a nasledne to disasembloval?
Jen ciste lehka zvedavost... diky.
double add(double a, double b) { return a+b; }přeložil a disassembleroval. SSE2 defaultně zapnuté na x86_64. Pak to samé s FPU (tj. gcc dostalo -mno-sse2) a byl z toho bastl. SSE2 kód měl 4B, FPU 27B, což je docela rozdíl a hlavně počet těch instrukcí byl v případě FPU nejmíň 7 i s -O3.
o parametru, tuším, -S v gcc jsi slyšel? Vychrlí to kód jdoucí do assembleru (část překladače) a umí to i dělat komentáře pomocí dalšího parametru, který bych ale musel hledat v man gcc
Přesně tak. -E zastaví gcc po zpracování preprocesorem - výsledek je přežvýkaný zdroják vhodný ke kompilaci, -S zastaví gcc po projetí kompilátorem - výsledek je assemblerový kód, -c zastaví gcc po projetí assemblerem - výsledek je objektový soubor vhodný ke slinkování. Jinak se to celé dá udělat ručně pomocí programů cpp, cc, as, ld. A nic není třeba hledat v manuálu, protože celý proces je docela dobře popsán třeba v A Practical Guide to Linux(Samozřejmě se spoustou dalších užitečných věcí).
Já používám při kompilaci -mfpmath=sse,387
už nějakou dobu a zatím jsem se s problémy nesetkal. A trochu je to znát.
MMX a x87 (a 3dnow) sdílejí registry, ale SSE má svoje vlastní, čehož by se mělo dát zneužít :)
Jinak SSE kód, byť by měl víc instrukcí nejspíš bude rychlejší. On ten sinus (a jiné komplexní operace) přes x87 se bude určitě počítat hodně cyklů.
Nepleťte si pojmy s dojmy – FPU je z větší části řešena hardwarově. Vypočítání sinu FPU zvládne obvykle během několika taktů. Jen pro představu:
výpočet sinu na FPU trvá zhruba stejně jako trvá stejně tak dlouho jako provedení dvou instrukcí push register
prakticky za plus mínus stejnou dobu, jako je výpočet sínu může pomocí instrukce fsincos získat naráz sínus i kosínus hodnoty naráz, což je v mnoha případech velmi užitečné
výpočet odmocniny zvládne FPU během jednoho taktu procesoru
výpočet arcus tangens s FPU je stejně rychlý, jako je provedené jedné instrukce push register
Nepodléhejte mylnému dojmu, že FPU je pomalá. FPU není program!!! FPU je hardwarově zadrátovaná výpočetní centrála, která téměř vše potřebné má zadrátováno jako elektronický obvod (tedy počítá neskutečně rychle, což si programátoři ani nedokáží představit), a jen v některých případech si maličko vypomáhá více více cykly, ale zase nad výkonnými a speciálně navrženými výpočetními FPU jednotkami. Výpočty i složitých matematických funkcí i s vysokou přesností jsou nad FPU pekelně rychlé.
Pokud provedete sinus za 20 cyklů, jak psal kdosi výše, nebudete moci rychlosti FPU vůbec konkurovat, protože FPU to dokáže několikrát rychleji. A znovu je nutno dodat, FPU zvládá výpočet matematické funkce za danou dobu nejenom pro základní úhly, ale ona v tom čase také stihne otestovat hodnoty na speciální případy (nekonečno, NaN, shodu s definičním oborem funkce, je-li to potřeba a další) plus normalizovat hodnotu do intervalu potřebného pro výpočet. Zatímco algoritmy, které naleznete na internetu pro SSE jsou zhýčkané cimprlich, které umí počítat jenom čísla mimo speciální případy a předpokládají, že normalizaci si uděláte navrvh k tomu.
Hardwarově zadrátované věci nejsou pomalé. Ba právě naopak. Nepodceňoval bych rychlost.
Kecy z DevMasteru, navíc vágně a mlhavě specifikované – jsou k ničemu.
Ano, a profesionál také pozná rozdíl mezi mikrooperacemi procesoru a takty procesoru.
Ale nechť, zkopírovat sem kus tvrzení kdekoli z internetu, třeba když uklízečka na DevMasteru měla čas a rozhodla si, že si zahraje na odborníka, to dokáže kdekdo.
Nechci vám kecat do zajisté pěkně rozjetého flejmu, ale ono se to dá docela snadno ověřit(a to jak počet vygenerovaných instrukcí, tak doba jejich běhu).
Nechci nikoho soudit, ale IHMO je mnohem směrodatnější nějaký benchmark(stačil by i prostý cyklus, pár pseudonáhodně vygenerovaných hodnot a příkaz time
- sám už jsem si to zkusil) než nějaké citace bůh ví odkuď. Pan nech Mr. Ponkrác zpochybňuje jak chce.
Aha, to jsem jaksi v záchvatu přehlédl. Ale i tak se to p. Ponkrác pokusil zpochybnit.
double inline f0(double a) { return a + a; } // nejrychlejší double inline f1(double a) { return 1 / a; } // pomalejší double inline f2(double a) { return sin(a); } // killerPokud se vám chce, klidně to udělejte, ale já mám zajímavější věci na práci
Pokud chcete dělat benchmark, tak se vykašlete na nějaké pseudonáhodné čísla, protože jejich výpočet může ve výsledku ten benchmark naprosto zdiskreditovat.
Nebo něco podobného co by mohlo vyřadit optimalizace, keše a nějaké pomocné algoritmy či techniky.
Pokud se vám chce, klidně to udělejte, ale já mám zajímavější věci na práci
Heh, to já zrovna také. Sám pro sebe jsem si jednoduchý test udělal. Jen jsem chtěl říct, že pro příště než se na 50 příspěvcích hádat s p. Ponrkrácem o podstatě h*vna a přebíjet se odkazy a citacemi je lepší umlčet ho reprodukovatelnými čísly, protože vnášení stínu pochybnosti pro nezainteresované(jako jsem já) mu jde moc dobře(skoro po čase nabývám dojmu, že se tím živí).
A ještě by mě zajímalo o jakém instrukčním smetí u FPU to ten Jardík brblal:
$ diff test2_sse.S test2_387.S 8c8 < subl $24, %esp --- > subl $16, %esp 17,20c17,18 < movsd -8(%ebp), %xmm0 < addsd -16(%ebp), %xmm0 < movsd %xmm0, -24(%ebp) < fldl -24(%ebp) --- > fldl -8(%ebp) > faddl -16(%ebp) 34,37c32,35 < movsd .LC1, %xmm0 < movsd %xmm0, 8(%esp) < movsd .LC2, %xmm0 < movsd %xmm0, (%esp) --- > fldl .LC1 > fstpl 8(%esp) > fldl .LC2 > fstpl (%esp)
double add(double a, double b) { return a+b; } # gcc -mno-sse2 000000000040069c <add>: 40069c: 55 push %rbp 40069d: 48 89 e5 mov %rsp,%rbp 4006a0: 0f 13 45 f8 movlps %xmm0,-0x8(%rbp) 4006a4: 0f 13 4d f0 movlps %xmm1,-0x10(%rbp) 4006a8: dd 45 f8 fldl -0x8(%rbp) 4006ab: dc 45 f0 faddl -0x10(%rbp) 4006ae: dd 5d e8 fstpl -0x18(%rbp) 4006b1: 0f 12 45 e8 movlps -0x18(%rbp),%xmm0 4006b5: c9 leaveq 4006b6: c3 retq # gcc -mno-sse2 -O3 0000000000400670 <add>: 400670: 0f 13 44 24 f8 movlps %xmm0,-0x8(%rsp) 400675: dd 44 24 f8 fldl -0x8(%rsp) 400679: 0f 13 4c 24 f8 movlps %xmm1,-0x8(%rsp) 40067e: dd 44 24 f8 fldl -0x8(%rsp) 400682: de c1 faddp %st,%st(1) 400684: dd 5c 24 f8 fstpl -0x8(%rsp) 400688: 0f 12 44 24 f8 movlps -0x8(%rsp),%xmm0 40068d: c3 retq # gcc -msse2 000000000040069c <add>: 40069c: 55 push %rbp 40069d: 48 89 e5 mov %rsp,%rbp 4006a0: f2 0f 11 45 f8 movsd %xmm0,-0x8(%rbp) 4006a5: f2 0f 11 4d f0 movsd %xmm1,-0x10(%rbp) 4006aa: f2 0f 10 45 f8 movsd -0x8(%rbp),%xmm0 4006af: f2 0f 58 45 f0 addsd -0x10(%rbp),%xmm0 4006b4: c9 leaveq 4006b5: c3 retq # gcc -msse2 -03 0000000000400670 <add>: 400670: f2 0f 58 c1 addsd %xmm1,%xmm0 400674: c3 retq
# gcc -mno-sse2 000000000040069c <add>: 40069c: 55 push %rbp 40069d: 48 89 e5 mov %rsp,%rbp 4006a0: 0f 13 45 f8 movlps %xmm0,-0x8(%rbp) 4006a4: 0f 13 4d f0 movlps %xmm1,-0x10(%rbp) 4006a8: dd 45 f8 fldl -0x8(%rbp) 4006ab: dc 45 f0 faddl -0x10(%rbp) 4006ae: dd 5d e8 fstpl -0x18(%rbp) 4006b1: 0f 12 45 e8 movlps -0x18(%rbp),%xmm0 4006b5: c9 leaveq 4006b6: c3 retq
Ještě musíš -mno-sse a úplně nejlépe -mfpmath=387.
A neodpustím si rýpnutí: pod rootem se ani nepracuje ani nekompiluje.
Na to, že odmocnina bude trvat o hodně dýl, bych vsadili vlastní barák, kdybych nějakej měl :P
Ehm... borci, co přepsali většinu důležitých částí x264 do assembléru s vámi zásadně nesouhlasí. No zkuste si srovnat rychlost na PPC a x86 (lol).
Je tam jeden přispěvatel, který pravidelně přidává optimalizace pro altivec. Kvalitou optimalizace ale PPC verze zaostává, na core 2 procesorech (u k10 by to mělo být podobné) jsou SSE funkce velmi rychlé. Podrobnosti djou naprosto mimo mě, ale je fakt že x264 je co se týče x86 optimalizovaná prakticky definitivně, tuším uváděli, že víc jak 5% už asi neuvidí, snad jen kdyby používali cache profiling.
Ono také druhá věc je, že desktopové PPC čipy pocházejí tak maximálně z roku 2003 (Apple), což byla doba SSE2, P4 a A64, kterýchž rychlost v SSE z dnešního pohledu není nic moc. Dnes je tu SSSE3, SSE4, core 2 a Nehalem. Výkon SSE kódu se znásobil, mnoho instrukcí přibylo (zejména SSSE3, které iirc v x264 hrají velkou roli) ...
Ono také druhá věc je, že desktopové PPC čipy pocházejí tak maximálně z roku 2003 (Apple), což byla doba SSE2, P4 a A64, kterýchž rychlost v SSE z dnešního pohledu není nic moc. Dnes je tu SSSE3, SSE4, core 2 a Nehalem. Výkon SSE kódu se znásobil, mnoho instrukcí přibylo (zejména SSSE3, které iirc v x264 hrají velkou roli) ...Taky proto je zajímavé, že i dnešním procesorům, o tolik let novějším, nakopává pozadí šunka z roku, kdy ještě uhlí rostlo na stromech.
Možná tuhle myšlenku sestřelí někdo s lepší znalostí konkrétního HW, ale procesor není na této trase jediným prvkem. Velmi záleží i na rychlosti pamětí a HDD a je tedy klidně možné, že je v tom dotyčné C2D tak trochu nevinně a že ho brzdí paměti, ev. diskový subsystém.
jestli to někde zaniklo: nenakopává, právěže, naopak je velice přehledně nakopáváno.
Ono také druhá věc je, že desktopové PPC čipy pocházejí tak maximálně z roku 2003Posledni G5 sly ven na konci r. 2005...
Vše má svoje.
Míchat FPU a SSE instrukce není problém. Nic se nemusí přepínat.
Ale: SSE vyžaduje zarovnání na 16 bajtů, budoucí AVX dokonce na 32 bajtů. Pokud děláte malou akci, tak se zarovnávání nevyplatí. V některých os, jako třeba Windows64 z důvodů preference SSE zavedli povinné zarovnávání stacku na 16 bajtů.
Pak: FPU je čistější, plně ošetřuje všechny možnosti a krajní stavy při výpočtech float čísel, SSE na to sem tam dlabe.
Vsuvka: Na internetu najdete řadu možností, jak spočítat sinus, nebo cosinus pomocí SSE (většinou rozvojem řady), ale před to si budete muset přiřadit normalizaci čísla do základního rozsahu úhlu a kontrolu a ošetření na mezní stavy čísla (nekonečno, NaN, atd..) aby to bylo ekvivalentní tomu, co vše dělá fpu.
FPU je přesnější – a to zcela bez debaty. FPU jednak počítá s 80-ti bitovými čísly, což z SSE nevymáčknete, a aby to nebylo málo, většinou FPU vnitřně počítá ještě s několika bity navíc, takže přesnost je ještě vyšší. To znamená FPU je pro vysokou přesnost, u SSE při větších výpočtech můžete vlivem zaokrouhlovacích chyb dostat také hodně nepřesný výsledek až úplnou kravinu – podle citlivosti algoritmu na zaokrouhlovací chyby.
FPU má zásobníkovou orientaci, SSE registrovou – někdy jsou algoritmy šikovnější pro to první, někdy pro to druhé.
Kdysi jsem měl debatu s asm profíky poté, co jsem sám zkoušel výrazněji použít SSE2 pro své programy, a došel jsem k závěru, že SSE není až natolik užitečné, jak se všude preferuje. Respektive užitečné je, ale její výhodnost je jen pro některé typy algoritmů, zatímco pro řadu algoritmů je výhodnější SSE nepoužít. Chtěl jsem jen zjistit, zda v tomto názoru nejsem sám, a jak jsem zjistil, nebyl.
Udělat matematické funkce nad SSE není problém, jen to chce trochu času. Jde o základní matematiku – počítání odmocnin pomocí Newtona, počítání základních matematických funkcí jako rozvoj pomocí Taylorových a jiných řad, atd.. Na druhou stranu to klidně můžete smíchat – klidně funkce počítat na FPU a zbytek na SSE, budete-li chtít.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.