niedziela, maja 30, 2010

Jak szybciej znaleźć mniejszą i większą wartość? [opisowy tytuł, który niewiele mówi ;-)]

Optymalizacja to zło, ale jakbyście czasem musieli ;-) to dziś porada dotycząca szukania mniejszej i większej liczby. Na wejściu dostajemy 2 liczby typu double, na wyjściu chcemy dostać te same liczby, ale tak by były ustawione wg. wielkości. wejście: a, b wyjście: min(a,b), max(a,b) Pierwsze co przychodzi do głowy to:

 double c = Math.min(a, b); 
double d = Math.max(a, b);
a = c; b = d;
Jednak szybsze jest użycie takiego kodu:

 double c = a+b;
b = (a<b)?b:a;
a = c - b;
Ten drugi kod na wykonanie zużywa tylko 74% czasu potrzebnego na wykonanie pierwszego kodu. Zawsze to coś, jeżeli Twój kod spędza 100 sekund w ustalaniu która liczba z 2 jest większa, a która mniejsza to zawsze te 26 sekund można spożytkować na coś innego ;-) Oczywiście trzeba sprawdzić wszystko w swoich warunkach, mnie to pozwoliło na przyśpieszenie programu o jakieś 4-5% bez wyraźnego spadku jakości obliczeń. [Co nie ma do końca sensu, bo wystarczy małe "zachwianie" procesora, spowodowane włączeniem się innego procesu i cały zysk się w moim przypadku traci, ale to przez to, że najbardziej procesożerna część programu trwa około 22 sekund ;-)]


Podobne postybeta
Plus dla Scala, minus dla Groovy ;-)
OOo2GD 2.1.1 gotowe :-)
Kryzys istotności ;-)
Architektura
OOo2GD w liczbach ;-)

6 komentarzy:

  1. Po rozgrzaniu obie wersje będą tak samo szybkie. Zostana skompilowane do IDENTYCZNEGO kodu maszynowego.

    OdpowiedzUsuń
  2. Nie do końca, na początku opcja pierwsza jest wolniejsza, przy liczbie powtórzeń równej 100 milionom opcja pierwsza jest wolniejsza od drugiej, przy 1 miliardzie są podobne, przy 10 miliardach i więcej już opcja pierwsza jest szybsza. Czyli kod maszynowy nie jest identyczny.
    Co pokazuje, że trzeba czasem próbować różnych rozwiązań :-) W moim kodzie to 2 rozwiązanie jest szybsze bo jak na razie mam koło 12.5 mln tego typu operacji.

    OdpowiedzUsuń
  3. szybszy jest nie ten kod, który się szybciej wykonuje, tylko ten, który się szybciej pisze i czyta.
    jeżeli na wymyślenie, napisanie, przetestowanie i wyprofilowanie optymalizacji kodu poświęcisz 15 minut, a dodatkowo zaciemnisz kod, to przy różnicy w czasie wykonań na poziomie milisekund na jedną egzekucję jest to deoptymalizacja, a nie optymalizacja.
    tego typu 'optymalizacje' mają sens tylko w trywialnym (bez negatywnych konotacji!) kodzie, implementującym góra kilka funkcji; przy rozmiarze kodu aplikacji liczonym w dziesiątkach czy setkach tysięcy wierszy, najwolniejszy będzie i tak użytkownik (względnie pozostali programiści w ekipie), a nie wywołanie java.math.Min() czy Max() zamiast 'zoptymalizowanego' kodu...

    btw, jeszcze szybsze jest
    double c = a + b;
    a = a < b ? a : b;
    b = c - a;

    - odpada inicjalizowanie jednego double'a i jedno przypisanie...

    OdpowiedzUsuń
  4. @Marcin - nie zgadzam się. Jasność i czytelność kodu są ważne, ale są czasem sytuacje gdy musisz coś optymalizować.
    Zasada z szybkością pisania i czytelnością jest prawdziwa w większości przypadków, ale czasem są takie przypadki gdy trzeba ją poświęcić.

    To się robi tylko w kawałkach, które po prostu muszą być szybsze, a w tym przypadku to było jedyne miejsce na przyśpieszenie.

    Zresztą masz to zastrzeżenie napisane na samym początku mojego postu.

    U mnie w aplikacji ten kod wygląda tak:
    double c = a+b;
    b = (a<b)?b:a;
    a = c - b;
    Tego double d dodałem wrzucając ten przykład żeby zachować podobieństwo do przykładu z max(double, double).

    Wiesz, od paru lat pracuję, lub pracowałem z kodami różnych średnich aplikacji, tak po 300-500 tysięcy linii kodu. Przy takich maleństwach spokojnie 5 osób jest w stanie znać kod na tyle dobrze, że nawet lepsze kwiatki przejdą, ale dla prędkości robi się różne rzeczy [np. w jednej z tych aplikacji przez lekkie zamotanie kodu udało się osiągnąć to, że pewien test [chyba akurat kształtu sygnału] wykonywał się przez 3 minuty, a nie blisko godzinę. Co prawda kod był jakieś 2 albo 3 razy większy, ale działał szybciej z czego wszyscy byli zadowoleni (łącznie z programistami bo mniej musieliśmy siedzieć w labie przy sprzęcie).

    Btw. nie jest wcale pewne, że inicjalizowanie zmiennej coś spowolni (w Java'ie zwykle spowolni). To widać świetnie w C/C++ gdy ktoś specjalnie wyrzuci zmienną poza pętlę, bo takie wyrzucenie może powodować, że zmienna nie załapie się na bycie zmienną rejestrową.

    Ogólnie optymalizować należy tylko to co wykonuje się wiele razy i tylko wtedy gdy nie ma szybszego algorytmu i tylko wtedy jak się ma dowód doświadczalny na to, że to przyśpiesza. Nie ma co optymalizować na sucho bo kompilatora kretyni nie pisali i wiele sam zrobi, a procesor też swoje dorzuca.

    OdpowiedzUsuń
  5. ogólnie to najlepiej chyba:
    if (a > b) swap(a, b);

    swap można realizować różnie:
    - standardową funkcją
    - { double c = a; a = b; b = a; }
    - { a = b - a; b = b - a; a = a + b; }
    ...

    OdpowiedzUsuń
  6. @qwak - w C na pewno, szczególnie gdy swap będzie inlineowany, w Java'ie się nie da ;-) bo swapa się nie da zrobić w taki sposób.

    OdpowiedzUsuń