Article View: pl.comp.lang.c
Article #310024Re: Optymalizacja kodu
From: M.M.
Date: Thu, 26 Dec 2019 04:00
Date: Thu, 26 Dec 2019 04:00
121 lines
6777 bytes
6777 bytes
On Thursday, December 26, 2019 at 2:01:35 AM UTC+1, (fir) wrote:
> W dniu wtorek, 24 grudnia 2019 20:25:33 UTC+1 użytkownik M.M. napisał:
> > Czasami potrzebuję całkowite liczby pseudolosowe z małego
> > przedziału, przy czym losowość tychże liczb nie musi być
> > wysokiej jakości, ale za to generowanie musi być bardzo
> > szybkie. Standardowy sposób na uzyskanie takich liczb jest
> > zapewne wszystkim czytelnikom tej grupy znany, ale przypomnijmy:
> >
> > random() % (MAX-MIN+1) + MIN;
> >
> > Od razu widać zabójczą dla wydajności operację modulo :/ Co
> > można na to poradzić? Mój pierwszy pomysł był taki, aby zgrać
> > do tablicy np. milion liczb, a potem z tej tablicy te liczby w
> > kółko podawać. Cóż może być wydajniejszego, niż podawanie kolejnych
> > liczb z pamięci RAM? Niestety tablica o rozmiarze miliona elementów
> > to lekkie marnotrawienie pamięci RAM i nie wiadomo jak procesor
> > takie tablice będzie buforował w większych programach. Zastosowałem
> > więc rozwiązanie z użyciem dwóch buforów:
> >
> > buff1[SIZE1];
> > buff2[SIZE2];
> >
> > ( albo jednego łączonego buff[SIZE1+SIZE2] )
> >
> > przy czym SIZE1 i SIZE2 to są liczby pierwsze z przedziału
> > około <100,10000>. Można podawać na przemian liczby z bufora1 i
> > bufora2, cykl takiego 'generatora' wyniesie size1*size2 (po
> > wygenerowaniu tylu liczb można bufory zapełnić nowymi liczbami, to
> > niewiele spowolni generator, bo jest mało generowania w stosunku
> > do podawanych liczb).
> >
> >
> > No więc teoria jest prosta, pora zabrać się kodowanie. Jednak są niespodzianki.
> > Generator std::mt19937_64 wygenerował 4mld liczb z przedziału od 11 do 38 w
> > około 27 sekund. Natomiast ta sztuczka z dwoma tablicami potrzebowała aż
> > 35 sekund! Czyli zamiast przyspieszenia uzyskałem spowolnienie. Implementacja
> > tej wersji jest w załączonym kodzie na dole w klasie RndBuff1. Nie poddawałem
> > się, zamiast statycznej tablicy użyłem dynamicznie przydzielonej przez
> > new - myślę sobie, a może kompilator lepiej da radę z new. Okazało się, że jest
> > jeszcze gorzej, czas wzrósł do 38s - to jest w klasie RndBuff2. Dopiero jak
> > dodałem wskaźniki i kilka usprawnień instrukcji warunkowych, czas spadł do...
> > zaledwie 4 sekund - to jest w klasie RndBuff3. Dla pewności sprawdziłem jeszcze
> > kombinację z tablicą (zamiast new) i wskaźnikami. Czas z powrotem wzrósł do
> > 11 sekund - kod w RndBuff4.
> >
> > Po 34 pomiarach czasu, wyniki wyglądają tak:
> > seed [911368664458670987] nr.:34
> > raw mt start 105226567093 872
> > RndBuff1 start 105232396785 1210
> > RndBuff2 start 105232396785 1267
> > RndBuff3 start 105232396785 127
> > RndBuff4 start 105232396785 397
> >
> > Widać że czas pomiędzy najlepszą i najgorszą wersją różni się aż 10 razy, a
> > kod ciągle bardzo podobny i oczywiste optymalizacje spowalniają!
> >
> > Suma kontrolna z raw mt jest inna, bo też jest inny algorytm generowania - to
> > nie stanowi błędu.
> >
> > Niezrozumiałe jest dla mnie, dlaczego są tak duże różnice w czasie przy
> > tak niewielkich zmianach.
> >
> > Pouczające jest to, że jeśli już chcemy mieć kawałek wydajnego kodu, to
> > niewielkie zmiany mogą dawać 10-krotne przyspieszenie. I to, że oczywista
> > optymalizacja może tak naprawdę spowolnić nasz kod.
> >
>
> 4 sekundy na 4G intow to troche malo, wychodzi 1 nanosekunda na inta, czyli z 3 cykle na inta, byc moze jest to mozliwe jak wiekszosc tej roboty to 1 if i 1 inc czy cos takiego ale trzebby zdizasemblowac i zobazyc co to dokladnie jest (zief)
Zdaję sobie sprawę z tego że mogę mieć jakieś błędy w kodzie i rozumowaniu.
Dlatego wyciągnąłem z całości kodu kilka kluczowych wariantów i ich
kod zamieściłem w poście:
[
seed [911368664458670987] nr.:34
raw mt start 105226567093 872
RndBuff1 start 105232396785 1210
RndBuff2 start 105232396785 1267
RndBuff3 start 105232396785 127
RndBuff4 start 105232396785 397
]
127 / 34 < 4 sekundy.
[
constexpr ultyp LOOPS = 1ull << 32;
]
2^32 > 4mld.
[
std::cout << "RndBuff3 start ";
ultyp sum = 0;
rnd.seed( seed );
RndBuff3<std::mt19937_64,SIZE1,SIZE2> rndBuf(rnd,MIN,MAX);
const time_t start = time(NULL);
for( ultyp loop=0 ; loop<LOOPS ; loop++ ) {
sum += rndBuf();
}
times[3] += time(NULL) - start;
std::cout << sum << " " << times[3] << std::endl;
]
Wykonuje przez te niecałe 4 sekundy ponad:
12mld inkrementacji
8mld porównań
4mld sumowań
4mld pętli
4mld wyłuskań danych spod wskaźnika
4mld call (albo wstawił inline)
4mld return
4mld operacji bitowych AND
> sam przestalem sie specjalnie przejmowac takimi optymalizacjami po nauczeniu sie samego optymalizowania (tj robilbym je ale glownie w koncowym produkcie byc moze, normalnie to raczej uzywalbym zwyklego randa z modulo, ktory nie jest az tak strasznie wolny, tj pewnie z 10 ns zajmie )
Gdy piszę wspólny kawałek kodu (powiedzmy gdy piszę mikro-bibliotekę) to też
dbam o wydajność. Gdy ma coś w kółko liczyć przez lata (może na kilku
komputerach), to też dbam o wydajność.
Pozdrawiam
Technical Details
Message-ID:
<f40cbda0-586a-4094-ac45-38845c115fc5@googlegroups.com>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed.pionier.net.pl!3.eu.feeder.erje.net!feeder.erje.net!weretis.net!feeder7.news.weretis.net!proxad.net!feeder1-2.proxad.net!209.85.160.216.MISMATCH!g89no9036933qtd.0!news-out.google.com!w29ni69qtc.0!nntp.google.com!g89no9036930qtd.0!postnews.google.com!google-groups.googlegroups.com!not-for-mailReferences:
<ed3041b7-c05f-43f3-887a-9a12a9633741@googlegroups.com> <cede5c14-80bd-4ca7-a40b-44f4aa767418@googlegroups.com>Show Full Headers
Complete Article Headers
X-Received: by 2002:a37:7c6:: with SMTP id 189mr38364596qkh.408.1577332805703; Wed, 25 Dec 2019 20:00:05 -0800 (PST) X-Received: by 2002:a25:ae9e:: with SMTP id b30mr31707998ybj.462.1577332805484; Wed, 25 Dec 2019 20:00:05 -0800 (PST) Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed.pionier.net.pl!3.eu.feeder.erje.net!feeder.erje.net!weretis.net!feeder7.news.weretis.net!proxad.net!feeder1-2.proxad.net!209.85.160.216.MISMATCH!g89no9036933qtd.0!news-out.google.com!w29ni69qtc.0!nntp.google.com!g89no9036930qtd.0!postnews.google.com!google-groups.googlegroups.com!not-for-mail Newsgroups: pl.comp.lang.c Date: Wed, 25 Dec 2019 20:00:05 -0800 (PST) In-Reply-To: <cede5c14-80bd-4ca7-a40b-44f4aa767418@googlegroups.com> Complaints-To: groups-abuse@google.com Injection-Info: google-groups.googlegroups.com; posting-host=77.254.46.242; posting-account=xjvq9QoAAAATMPC2X3btlHd_LkaJo_rj NNTP-Posting-Host: 77.254.46.242 References: <ed3041b7-c05f-43f3-887a-9a12a9633741@googlegroups.com> <cede5c14-80bd-4ca7-a40b-44f4aa767418@googlegroups.com> User-Agent: G2/1.0 MIME-Version: 1.0 Message-ID: <f40cbda0-586a-4094-ac45-38845c115fc5@googlegroups.com> Subject: Re: Optymalizacja kodu From: "M.M." <mmarszik@gmail.com> Injection-Date: Thu, 26 Dec 2019 04:00:05 +0000 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit