Gdzie optymalizujesz?


9

Istnieją dwa obszary, które można zoptymalizować pod kątem prędkości w:

  • Gdzie spędza się najwięcej czasu
  • Kod, który jest nazywany najczęściej

Jakie jest najlepsze miejsce do rozpoczęcia optymalizacji?

Często nazywany najczęściej kod ma już krótkie czasy wykonania. Czy optymalizujesz wolniejsze, mniej nazywane obszary, czy spędzasz czas na optymalizacji szybszych, intensywnie używanych obszarów?


Zoptymalizuj obszar aplikacji, który najbardziej akcentuje twoich klientów lub architekturę, w zależności od tego, czy twoi klienci lub twoje serwery narzekają najgłośniej.
Andy

Jest to równanie wartości - odpowiedź może być albo. Kiedy nie masz prawdziwej analizy, idziesz z jelitami, na podstawie prawdopodobnej wypłaty twoich najlepszych pomysłów.
Nicole

Ani. Poszukaj kodu, który znajduje się na stosie przez większość czasu.
Mike Dunlavey

Odpowiedzi:


4

Powinieneś zignorować małe usprawnienia w 95% przypadków. Najpierw upewnij się, że działa poprawnie , a następnie przeanalizuj ...

Twój projekt.

Wybór algorytmów wysokiego poziomu może mieć ogromny wpływ na ogólną wydajność oprogramowania do tego stopnia, że ​​jeden pozornie trywialny wybór może oznaczać różnicę między odczekaniem 20 minut na uruchomienie programu a szybkim, responsywnym interfejsem użytkownika.

Na przykład w grze 3D: jeśli zaczniesz od prostej płaskiej listy obiektów na wykresie sceny, zobaczysz wyjątkowo niską wydajność dla stosunkowo niewielkiej liczby obiektów; ale jeśli zamiast tego zaimplementujesz hierarchię woluminów (jak oktawa lub BVH) i wycisz części drzewa podczas rysowania, zobaczysz ogromny wzrost wydajności.

Kiedy Twój projekt wydaje się poprawny, możesz przejść do ...

Logika niskiego poziomu.

Algorytmy niższego poziomu mogą również mieć znaczący wpływ. Na przykład podczas przetwarzania obrazu, jeśli odczytujesz obraz w niewłaściwej kolejności, doświadczysz ogromnego spowolnienia, gdy napotkasz ciągłe błędy pamięci podręcznej L2; zmiana kolejności operacji może oznaczać dziesięciokrotny wzrost wydajności.

W tym momencie profiluj i znajdź miejsce, w którym spędzana jest większość czasu programu, i znajdź sposób na jego wyeliminowanie.


Program, nad którym pracuję, jest poprawny. Chcemy przyspieszyć, jeśli to możliwe, ponieważ jest to usługa internetowa, której uruchomienie może potrwać od 30 do 30 minut.
Michael K

1
@Michael: W takim przypadku nadszedł czas, aby uzyskać narzędzie do profilowania do analizy niektórych typowych działań programu i wskazać sekcje kodu, które działają najwolniej. Naprawdę polecam użycie do tego narzędzia. Możesz zrobić określoną ilość intuicyjnie, ale czasami znajdziesz niespodziankę, gdy jest to interfejs API, którego wywołanie zajmuje dużo czasu, a nie własnego kodu. W takim przypadku czas na sprawdzenie API i sprawdzenie, jakie inne funkcje są dostępne, lub jeśli powinieneś napisać własną funkcję zastępczą ... Rzeczywisty świnia wydajności nie zawsze jest podejrzewana świnia wydajności ...
FrustratedWithFormsDesigner

1
Mamy narzędzie do profilowania. Wciąż się uczę i co zrobić z informacjami. To dla mnie stosunkowo nowy temat i bardzo interesujący.
Michael K

@Michael: Dobrze! Jesteś na (miejmy nadzieję) swojej drodze do udanego strojenia wydajności! :)
FrustratedWithFormsDesigner

+1: To chciałem powiedzieć, ale o wiele bardziej wymownie.
Dominique McDonnell,

3

Najpierw uruchom profiler, aby dowiedzieć się, gdzie Twój kod spędza czas.

Następnie spójrz na te miejsca, aby zobaczyć, które z nich wyglądają łatwo zoptymalizować.

Poszukaj najprostszych poprawek, które uzyskają największe zyski jako pierwsze (wybierz nisko wiszące owoce). Nie martw się zbytnio o to, jak ważne jest to właśnie. Jeśli to łatwe, napraw to. To się doda. 25 łatwych poprawek może być szybszych niż 1 duża poprawka, a ich skumulowane efekty mogą być większe. Jeśli jest to trudne, zrób notatkę lub zgłoś raport o błędzie, aby później ustalić priorytet. W tym momencie nie przejmuj się zbytnio „dużym” lub „małym” - po prostu rób to, aż dojdziesz do funkcji, które zajmują bardzo mało czasu. Gdy to zrobisz, powinieneś lepiej wiedzieć, który z pozostałych odkrytych problemów może uzyskać największe wygrane przy najmniejszym nakładzie czasu.

Nie zapomnij kontynuować profilowania po poprawkach jako swoistego testu regresji, aby sprawdzić, czy zmiany wydajności przyniosły oczekiwane efekty. Nie zapomnij również uruchomić pakietu regresji, aby upewnić się, że żadna funkcjonalność nie została zepsuta. Czasami zła wydajność wskazuje na obejścia, a próba naprawienia wydajności psuje funkcjonalność.

Małe funkcje, których nie można zoptymalizować, ale zajmują dużo czasu, mogą nadal wskazywać, gdzie należy zoptymalizować. Dlaczego ta funkcja jest tak często wywoływana? Czy istnieje funkcja wywołująca tę małą funkcję, która nie musi jej tak często używać? Czy praca jest powielana, czy jest wykonywana niepotrzebna praca? Sprawdź stos pod kątem czasów, w których jest wywoływany, dopóki nie masz pewności, że należy go tak często wywoływać, i sprawdź, czy znajdziesz większą funkcję z nieefektywnym algorytmem.

Edytowano w celu dodania: Ponieważ masz określoną funkcję, która zajmuje dużo czasu, spróbuj wykonać powyższe kroki, uruchamiając tę ​​konkretną funkcję 10 lub więcej razy.


2

Trudno powiedzieć. To naprawdę zależy od tego, co robi ten kod. Uruchom test wydajności, uzyskaj profil wydajności, a następnie sprawdź i zobacz, ile faktycznie spędzono czasu w różnych obszarach. Twoje uogólnienia są ... uogólnieniami i różnią się w zależności od projektu.

Na przykład najczęściej wywoływany kod może po prostu zalogować się do pliku lub konsoli. Optymalizacja nie ma większego sensu, jeśli jest to już jeden lub dwa wiersze kodu, których nie można uprościć, i może być tak, że jakakolwiek próba optymalizacji czegoś takiego może nie być warta kosztów jej kodowania. Najmniej nazywany kod może być zapytaniem wielkości potwora używanym w jakiejś strasznie złożonej funkcji. Funkcja może być wywoływana tylko 100 razy w ciągu całego przebiegu wykonania (w porównaniu do 10000 w przypadku prostej instrukcji rejestrowania), ale jeśli zajmie to 20 sekund za każdym razem, gdy zostanie uruchomiona, być może od tego powinna zacząć się optymalizacja? Lub może być odwrotnie, najczęściej wywoływane jest duże zapytanie, a instrukcja rejestrowania wywołuje tylko jedno na każde 100 zapytań ...

Zwykle nie martwię się o tego rodzaju rzeczy (dopóki nie będę musiał dostrajać wydajności), chyba że mam jakiś pomysł z wyprzedzeniem, co się stanie.


1

Cóż, „my” zwykle nie optymalizujemy, dopóki nie będzie oczywistej potrzeby optymalizacji, gdy coś jest nieakceptowalnie wolne.

A kiedy potrzeba ta się objawia, zwykle niesie ze sobą dobre wskazówki, co dokładnie wymaga optymalizacji.

Więc odpowiedź jest zwykle: „To zależy”.


1

Powinieneś użyć profilera na kilku typowych przebiegach i spojrzeć na całkowity czas spędzony w każdej części kodu, bez względu na to, jak i jak często się tam pojawiałeś. Optymalizacja tych części powinna zawsze zwiększać prędkość.

W zależności od niskiego poziomu języka implementacji powinieneś również dowiedzieć się, które części powodują najwięcej błędów w pamięci podręcznej. Pomoże w tym skonsolidowanie kodu wywołującego.


1

Problem polega na tym, że zdanie „gdzie spędza się najwięcej czasu” jest niejednoznaczne.

Jeśli oznacza to „gdzie najczęściej znajduje się licznik programów”, to widziałem programy, w których najwięcej czasu spędziłem na funkcjach biblioteki matematycznej do porównywania ciągów, alokacji pamięci. Innymi słowy, funkcje, których codzienny programista nigdy nie powinien dotykać.

Jeśli oznacza to „gdzie w kodzie programisty są wykonywane instrukcje, które pochłaniają dużą część czasu”, jest to bardziej przydatna koncepcja.

Problem związany z pojęciem „kodu, który jest nazywany najczęściej” polega na tym, że czas potrzebny jest na to, jak często jest on wywoływany i ile czasu zajmuje jedno połączenie (w tym callees i I / O). Ponieważ czas, jaki zajmuje, może się różnić w zależności od kilku rzędów wielkości, liczba wywołań nie mówi, jak duży jest problem. Funkcję A można wywołać 10 razy i zająć 0,1 sekundy, podczas gdy funkcję B można wywołać 1000 razy i zająć mikrosekundę.

Jedną rzeczą, która powie ci, gdzie szukać, jest to: ilekroć wiersz kodu powoduje, że czas jest spędzany , jest on na stosie . Na przykład, jeśli wiersz kodu jest gorącym punktem lub jeśli jest wywołaniem funkcji bibliotecznej lub jeśli jest to 20. wywołanie w 30-poziomowym drzewie wywołań, jeśli odpowiada za 20% czasu , to jest na stosie przez 20% czasu. Każda próbka stosu w dowolnym momencie będzie miała 20% szansy na jej wyświetlenie. Co więcej, jeśli próbki mogą zostać pobrane podczas operacji we / wy, pokażą one, jakie konta obsługują operacje we / wy, co może być tak samo lub bardziej marnotrawne, jak zmarnowane cykle procesora.

I to jest całkowicie niezależne od tego, ile razy jest wywoływane.


Mówiąc „codzienny programista nigdy nie powinien dotykać”, czy masz na myśli, że prawdopodobnie nie dotkniesz? Czy próbkowanie stosu jest również praktyczną metodą profilowania?
Michael K

@Michael: Tak, próbkowanie stosu jest metodą, na której opierają się nowoczesne profile, takie jak Zoom . Również całkowicie ręczna działa zaskakująco dobrze .
Mike Dunlavey,

Bardzo interesujące. Muszę się teraz trochę uczyć!
Michael K

@Michael: To jak prawna koncepcja wspólnej odpowiedzialności. W pewnym momencie odpowiedzialność za PC będący w instrukcji jest wspólną odpowiedzialnością nie tylko tej instrukcji, ale każdego wywołania nad nią na stosie. Wyeliminowanie któregokolwiek z nich uniemożliwiłoby mu przejście do tego konkretnego stanu.
Mike Dunlavey

0

Zoptymalizuj, gdzie spędza się najwięcej czasu, chyba że istnieje szczególny powód, aby tego nie robić (tzn. Większość czasu spędza na przetwarzaniu asynchronicznym, którego ludzie tak naprawdę nie obchodzą, czy skończy się za 5 minut, czy 10 minut). Kod, który jest nazywany najbardziej, naturalnie, będzie miał tendencję do gromadzenia stosunkowo dużej części całkowitego czasu, który upłynął, po prostu dlatego, że nawet krótkie czasy wykonania sumują się, gdy robisz to tysiące razy.


0

Musisz popracować nad kodem, który zajmuje najwięcej czasu. Ulepszenie kodu, który odpowiada tylko za kilka procent czasu działania, może dać tylko niewielką poprawę.

Czy wykonałeś pomiary, aby wiedzieć, który kod zajmuje najwięcej czasu?


0

Kiedyś przeprowadzałem testy porównawcze i marketingowe dla dostawcy superkomputerów, więc szybkie pokonanie konkurencji nie było najważniejszą rzeczą, było JEDYNE. Taki wynik wymagał użycia kombinacji dobrego algorytmu i struktury danych, która pozwoliłaby najbardziej obciążającym procesorowi efektywnie działać na szczycie. Oznaczało to, że musisz mieć całkiem niezłe pojęcie o tym, jakie będą najbardziej intensywne obliczeniowo operacje i jakie struktury danych pozwoliłyby im na najszybsze działanie. Następnie należało zbudować aplikację wokół zoptymalizowanych struktur jądra / danych.

W bardziej ogólnym znaczeniu, typowe po strojeniu faktów. Profilujesz, patrzysz na hotspoty, a hotspoty, które Twoim zdaniem możesz przyspieszyć, są tymi, nad którymi pracujesz. Ale rzadko takie podejście da ci coś bliskiego najszybszej możliwej realizacji.

Mówiąc bardziej ogólnie (błędy algorytmiczne nie wytrzymują), w przypadku nowoczesnych maszyn trzeba myśleć, że wydajność zależy od trzech rzeczy: dostępu do danych, dostępu do danych i dostępu do danych! Dowiedz się o hierarchii pamięci (rejestry, pamięci podręczne, TLB, strony itp.) I zaprojektuj swoje struktury danych, aby jak najlepiej je wykorzystać. Zasadniczo oznacza to, że chcesz mieć możliwość uruchamiania pętli w niewielkim obszarze pamięci. Jeśli zamiast tego po prostu napiszesz (lub otrzymałeś) aplikację, a następnie spróbujesz ją zoptymalizować, zwykle jesteś obarczony strukturami danych, które źle wykorzystują hierarchię pamięci, a zmiana struktur danych zwykle wymaga dużego ćwiczenia refaktoryzującego, więc jesteś często utknął.


0

Jeśli chcesz odzyskać wysiłki związane z optymalizacją, musisz spojrzeć na kod, który zajmuje najwięcej czasu. Moim ogólnym celem jest coś, co zajmuje co najmniej 80% czasu. Generalnie celuję w 10-krotny wzrost wydajności. Czasami wymaga to poważnej zmiany w sposobie zaprojektowania tego kodu. Tego rodzaju zmiana daje ci coś, co działa około cztery razy szybciej.

Mój najlepszy czas na zwiększenie wydajności to skrócenie czasu pracy z 3 dni do 9 minut. Zoptymalizowany przeze mnie kod trwał od 3 dni do 3 minut. Zastąpiona przez tę aplikację aplikacja skróciła to do 9 sekund, ale wymagało to zmiany języka i całkowitego przepisania.

Optymalizacja już szybkiej aplikacji może być głupcem. Nadal bym celował w ten obszar najwięcej czasu. Jeśli możesz wziąć coś w ciągu 10% czasu, aby wrócić natychmiast, nadal potrzebujesz 90% czasu. Szybko osiągnąłeś zasadę malejących zysków.

W zależności od tego, co optymalizujesz, reguła nadal obowiązuje. Poszukaj głównych użytkowników zasobów i zoptymalizuj ich. Jeśli optymalizowanym zasobem jest wąskie gardło systemu, może się okazać, że wystarczy zmienić wąskie gardło na inny zasób.


0

Zazwyczaj w większości przypadków będą to bardziej rozbudowane funkcje, a nie funkcje wywoływane najczęściej miliard razy w pętli.

Podczas profilowania opartego na próbkach (za pomocą narzędzia lub ręcznie) często najczęstsze punkty aktywne będą pojawiać się w małych, liściastych wywołaniach, które wykonują proste czynności, takie jak funkcja porównywania dwóch liczb całkowitych.

Ta funkcja często nie skorzysta z dużej, jeśli w ogóle, optymalizacji. Przynajmniej te granulowane hotspoty rzadko mają najwyższy priorytet. Jest to funkcja wywołująca funkcję liścia, która może być przyczyną problemów, lub funkcja wywołująca funkcję wywołującą funkcję, podobnie jak nieoptymalny algorytm sortowania. Dzięki dobrym narzędziom możesz przechodzić od rozmówcy do rozmówcy, a także zobaczyć, kto spędza najwięcej czasu dzwoniąc do rozmówcy.

Często błędem jest obsesja na punkcie rozmówców i nie patrzenie na osoby dzwoniące na wykresie połączeń w sesji profilowania, chyba że robisz rzeczy bardzo nieefektywnie na poziomie mikro. W przeciwnym razie możesz nadmiernie pocić małe rzeczy i tracić z oczu duży obraz. Samo posiadanie profilera w dłoni nie chroni przed obsesją na punkcie błahych rzeczy. To tylko pierwszy krok we właściwym kierunku.

Musisz także upewnić się, że profilujesz operacje, które są zgodne z czynnościami, które użytkownicy naprawdę chcą robić, w przeciwnym razie bycie całkowicie zdyscyplinowanym i naukowym w swoich pomiarach i testach porównawczych jest bezwartościowe, ponieważ nie jest zgodne z tym, co klienci robią z produktem. Pewnego razu miałem kolegę, który dostroił algorytm podziału, aby podzielić kostkę na miliard aspektów, a on był z tego bardzo dumny ... z wyjątkiem użytkowników, którzy nie dzielą prostych sześciokątów sześciokątnych na miliard aspekty. Cała rzecz zwolniła do pełzania, gdy próbowała uruchomić na produkcyjnym modelu samochodu z ponad 100 000 wielokątów do podzielenia, w którym to momencie nie mogła nawet wykonać 2 lub 3 poziomów podziału bez spowolnienia do pełzania. Mówiąc wprost, napisał kod, który był super zoptymalizowany dla nierealistycznie małych rozmiarów wejściowych, które nie

Musisz zoptymalizować rzeczywiste przypadki użycia zgodne z zainteresowaniami użytkowników, bo inaczej jest to gorsze niż bezwartościowe, ponieważ wszystkie te optymalizacje, które mają tendencję do przynajmniej nieco obniżenia możliwości utrzymania kodu, mają niewielką korzyść dla użytkownika i tylko te wszystkie negatywy dla bazy kodu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.