🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: pl.comp.lang.asm
Article #2593

Re: [1/3 NTG] C - Optymalizacje wstawkami

#2593
From: Bogdan (bogdro)
Date: Fri, 08 Feb 2019 21:27
163 lines
8668 bytes
W dniu 08.02.2019 o 18:31, DMR pisze:
> To była cisza przed burzą... :-)
> Dzięki za odpowiedź!


:-)

[...]

>> __asm__ ("fsincos;" : "=t" (*c), "=u" (*s) : "0" (x) : "st(7)");
> 
> 
> Ta składnia jest koszmarna, ale domyślam się, że stanowi ona pewnego rodzaju ochronę przed "wtargnięciem" w ciało programu.
> Kompilator ma prawo przecież robić jakieś swoje założenia, a nie może być tak, że w połowie wchodzimy jak Ruski do baru i zaczynamy mu przestawiać rejestry.


 Dokładnie. Po to właśnie są te parametry wejściowe i wyjściowe, a
zwłaszcza "clobber list". Choć to i tak jest raczej "wskazówka", które
rejestry psujemy, aby kompilator mógł je sobie zachować. Jakby popsuć
jakiś rejestr i nie umieścić go na liście, to raczej kompilator by nie
wykrył. Ale cóż, to i tak autor takiego kodu sam sobie robi "kuku", a
nie kompilatorowi :).


> Tyle, że jest problem - jak my nie chcemy słuchać kompilatora, to on też może się wypiąć i pomimo naszych prób optymalizacji całość wyjdzie gorzej niż przedtem...


 No, w środek wstawki się raczej nie wprosi, ale może np. obłożyć
wstawkę właśnie np. zapisywaniem modyfikowanych rejestrów (PUSH + POP)
lub spowolnić kod otaczający w inny sposób, fakt.


> Na szczęście koprocesor ze swoją stosową strukturą wydaje się pod tym względem raczej "bezpieczny", ale CPU to już inny level.
> 
> Okazuje się, że używanie "prostych" wstawek, to nie jest wcale trywialny problem.


 Nie, nie musi być, nawet pomijając mniej lub bardziej dziwne składnie.
 Można zawsze pisać swoje procedurki w osobnym pliku, kompilować
osobno i dopiero linkować razem. Ale płaci się za to wywołaniem CALL,
stosem itd.

[...]


>> Na przykład, w moim glibc (2.27) kod
>> sincos() jest dość złożony, bo sprawdza jeszcze błędy i normalizuje
>> parametry, jeśli trzeba
> 
> 
> Ta instrukcja jest zdaje się bardzo tolerancyjna, jeśli chodzi o parametry?
> Zdaje się, że przyjmuje od -2^63 do 2^63


 Według podręcznika - tak. Ale kod glibc wygląda tak a nie inaczej z
jakichś powodów. Pewnie jest przyczyna, dla której sprawdza flagi
koprocesora. Nie sprawdzałem, która to flaga (przepełnienie, wartość
zdenormalizowana, ...). No i nie wiem, jak tam z precyzją przy dużych
parametrach - może nie być tak dobra, jak dla małych.


> Kto by zresztą tyle tam wpisywał - ważne, że podanie mu -0.333 nie spowoduje wyświetlenia pozdrowień od Billa Gatesa... ;-)


:)


> A skoro już przy rozwiązaniach sprzętowych stanęliśmy...
> 
> Ostatnio musiałem napisać procedurę, która z pliku tekstowego wczytanego jako blok bajtów "wyparsuje" odpowiednie dane, ale okazało sie, że standardowe funkcje strtol() i strtod() zachowują się wyjątkowo dziwacznie w stosunku do deklarowanej logiki ich działania (z zagadkowych przyczyn nie kończyły działania na endptr, tylko wyłaziły dalej szukając zera, a przy okazji zawieszając program...).


 Większość funkcji C działa na łańcuchach znaków kończących się bajtem
zerowym, bo po prostu tak są zdefiniowane łańcuchy znaków w C.
 Można zawsze np. zmienić '\n' lub kropkę na bajt zerowy '\0' i
dopiero takim "stuningowanym" łańcuchem nakarmić strdo*().


> Potrzeba matką wynalazków, więc szybko spłodziłem odpowiedniki, co doprowadziło mnie niemal do euforii, bo okazało się, że "moje" funkcje działają KILKADZIESIĄT razy szybciej od standardowych. A że pliczek miał 87 megabajtów... ;-)


Tak, asembler pomaga :).


> Skoro tak, to - co było do przewidzenia - szybko zajawiłem, żeby sprawić sobie własny komplet jak najlepszych procedur konwertujących tekst <-> liczby.
> Z tekstów na liczby poszło gładko, tym bardziej, że mnożenie przez 10 można zastąpić sumą iloczynów przez 8 i 2.
> Za to w druga stronę... też poszło gładko. ;-)
> Przynajmniej w tym sensie, że udało się uzyskać kilkukrotne przyśpieszenie względem funkcji standardowych.
> 
> W odpowiedniku itoa() znalazł się taki fragment (z dokładnością do zera):
> 
> *cptr = 0x0;
> while (iValue)
> {
>     *--cptr = char(iValue % 10) | '0';
>     iValue /= 10;
> }
> 
> Niby wszystko gra, ale każdego szanującego się zboczeńca-perfekcjonistę w oczy kłuje to modulo, a za chwilę dzielenie przez 10.


 Samo dzielenie też kłuje ;).


> Kombinowałem, żeby resztę uzyskać z ilorazu, z tymi shiftami, ale wyniki uzyskałem bardzo podobne, z tendencją lekko gorszą.
> 
> Coś mi się we łbie kolebało z tym modulo i przy okazji poszukiwań natrafiłem na stronę http://bogdro.evai.pl/ ;-)
> 
> Faktycznie, instrukcja DIV liczy iloraz i modulo za jednym zamachem, przerżnąłem więc ten kod i... d*pa! :-(
> Wyszło dwa razy wolniej.
> 
> Tego już w ogóle nie rozumiałem, więc szybko nauczyłem się instrukcji -S, żeby GCC wypluł mi kod asemblera.
> I, patrz Pan, nie było tam niczego choćby podobnego do DIV...


 Pewnie poziom optymalizacji większy od zera?


> Jako, że nie byłem pewny, czy tu ktokolwiek zagląda, swojemu rozgoryczeniu dałem upust na pl.comp.programming, wklejając "publiczny" kod:
> https://godbolt.org/z/Xs117J
> 
> Jakaś dobra dusza potwierdziła moje mgliste domysły, że chodzi o użycie dużego "quasi-współczynnika", który tam się potem odpowiednio "przekręcą", a potem shift i tak dalej.


 Tak, często zamiast dzielenia przez X stosuje się mnożenie przez
2^n/X. Wtedy "iloraz" odczytujesz ze starszego rejestru. Np. dla liczb
16-bitowych, dla uproszczenia, współczynnik wynosi 65536/10 = 6553:

	A / 10 = ((A << 16)/10) >> 16 = (A * 6553) >> 16

 Czyli zamiast dzielić R/E/AX przez 10, mnożysz przez współczynnik i
odczytujesz wynik z R/E/DX. Nie jest to dokładnie tak, bo jeszcze
czasem trzeba "poprawić". Ale z grubsza.
 Ten konkretny kod, oprócz mnożenia przez upatrzony współczynnik
((2^32 - 1) * 8/10), robi dodatkowe rzeczy:
- dzieli EDX przez 8 (czyli EDX = 1/10 dzielnej << 32),
- kopiuje go do EAX, mnoży przez 4
- dodaje EDX do EAX (tutaj EAX = EDX * 5),
- mnoży EAX przez 2 (EAX = EDX * 10),
- odejmuje to od dzielnej (ECX = dzielna - dzielna * 2^32 * 8/10 / 8 *
10 = dzielna - dzielna / 10 * 10 = dzielna % 10),
- zwraca tę różnicę.


> Postudiuję ten temat, zwłaszcza że z daleka śmierdzi mi jakimś przepełnieniem, ale nie jestem przekonany, czy da się tu uzyskać jakieś przyśpieszenie względem kodu w C.
> A sprawa jest kluczowa, bo jest to też fragment konwersji liczb zmiennoprzecinkowych na tekst.
> Na razie rozdzielam część rzeczywistą i ułamkową konwersją na int i odejmowaniem/mnożeniem, ale może tu da się coś jeszcze ugryźć - zależy mi tylko na konwersji w "zafiksowanym" formacie RRRRRRRR.FFFFFFFF

[...]

 Jeśli kompilujesz C z optymalizacjami, to wygrać z kompilatorem w
tych czasach może być ciężko.
 Natomiast można oszczędzić w innych miejscach, np. I/O jest powolne -
nie wyświetlaj na ekran ani nie zapisuj do pliku po jednym czy kilku
bajtach. Buforuj i zapisuj cały bufor na raz. Tak samo z odczytem. Ale
to już zależy, co program ma robić.


-- 
Pozdrawiam/Regards - Bogdan                     (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux):            http://bogdro.evai.pl
Grupy dyskusyjne o asm:  pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org  Soft(EN): http://bogdro.evai.pl/soft
Technical Details
Message-ID: <5c5df439$0$476$65785112@news.neostrada.pl>
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed.pionier.net.pl!goblin1!goblin.stu.neva.ru!newsfeed.neostrada.pl!unt-exc-02.news.neostrada.pl!unt-spo-a-01.news.neostrada.pl!news.neostrada.pl.POSTED!not-for-mail
References: <30292520-12bd-49f0-b5ce-39c9af378a4c@googlegroups.com> <5c5b41d7$0$501$65785112@news.neostrada.pl> <781a3ad8-10bb-42aa-939f-dc6c11efa783@googlegroups.com>

Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed.pionier.net.pl!goblin1!goblin.stu.neva.ru!newsfeed.neostrada.pl!unt-exc-02.news.neostrada.pl!unt-spo-a-01.news.neostrada.pl!news.neostrada.pl.POSTED!not-for-mail
Subject: Re: [1/3 NTG] C - Optymalizacje wstawkami
Newsgroups: pl.comp.lang.asm
References: <30292520-12bd-49f0-b5ce-39c9af378a4c@googlegroups.com> <5c5b41d7$0$501$65785112@news.neostrada.pl> <781a3ad8-10bb-42aa-939f-dc6c11efa783@googlegroups.com>
From: "Bogdan (bogdro)" <bogdan@poczta.gazeta.pl>
Openpgp: preference=signencrypt
Date: Fri, 8 Feb 2019 22:27:20 +0100
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.0
MIME-Version: 1.0
In-Reply-To: <781a3ad8-10bb-42aa-939f-dc6c11efa783@googlegroups.com>
Lines: 163
Message-ID: <5c5df439$0$476$65785112@news.neostrada.pl>
Organization: Telekomunikacja Polska
NNTP-Posting-Host: 89.64.39.243
X-Trace: 1549661241 unt-rea-a-01.news.neostrada.pl 476 89.64.39.243:61822
X-Complaints-To: abuse@news.neostrada.pl
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit