Jak uniknąć „Intuicji programisty dotyczącej złej optymalizacji”?


22

Widziałem artykuł, w którym opublikowano to oświadczenie:

Programiści lubią optymalizować kod i nie bez powodu. To takie satysfakcjonujące i zabawne. Ale wiedza, kiedy zoptymalizować, jest znacznie ważniejsza. Niestety, programiści zazwyczaj mają okropną intuicję na temat tego, gdzie faktycznie będą występować problemy z wydajnością aplikacji.

Jak deweloper może uniknąć tej złej intuicji? Czy istnieją dobre narzędzia do znalezienia, które części kodu naprawdę wymagają optymalizacji (dla Javy)? Czy znasz jakieś artykuły, porady lub dobre lektury na ten temat?


1
Sprowadza się to do „Jak uniknąć [polegania] na intuicjach [przy podejmowaniu decyzji]?” Proste: weryfikujesz na podstawie faktów i danych. Tak więc, w przypadku optymalizacji, z perspektywy dewelopera: porównujesz.
haylem

Odpowiedzi:


44
  • Użyj dobrego profilera, aby zidentyfikować drogie metody.
  • Udokumentuj, ile czasu zajęły gorące punkty.
  • Napisz szybszą implementację hotspotów
  • Udokumentuj, ile czasu zajmują teraz hotspoty, miejmy nadzieję, że już nie będą to hotspoty.

Zasadniczo musisz być w stanie udowodnić innym, gdzie był problem, i że ta zmiana sprawiła, że ​​zniknął.

Nie mogąc udowodnić poprawy, kwalifikuje się - moim zdaniem - do natychmiastowego przywrócenia oryginalnej wersji.


51
Lub prościej: „Aby uniknąć złej optymalizacji intuicji, nie używaj intuicji. Mierz”.
Kyralessa

6
Dlatego twoja odpowiedź jest taka, a moja to tylko komentarz. : P
Kyralessa

2
@ Thomas, jeśli majstrujesz przy czytelności i łatwości konserwacji, nie patrzysz dokładnie na problemy z wydajnością, prawda?

3
@Tomas, nie zgadzam się. Nawet w ramach specyfikacji należy dokładnie przetestować nowy kod. Nie jest to konieczne w przypadku starego kodu. Odwracać.

2
@ Thorbjørn Po dostosowaniu wydajności należy również dokładnie przetestować nowy kod. Oszczędność czasu lub pamięci nie ma znaczenia, jeśli wprowadzisz defekt.
Thomas Owens

10

Jedynym sposobem, aby wiedzieć, gdzie zoptymalizować, jest profilowanie kodu. Zamiast wprowadzać zmiany, które Twoim zdaniem przyniosą korzyść, na pewno dowiedz się, gdzie jest najgorzej działający kod i zacznij od tego.

Java sprawia, że ​​jest to bardzo łatwe dzięki narzędziu VisualVM , które zostało dołączone do najnowszych wersji Java Development Kit (JDK). Chodzi o to, aby dowiedzieć się, które metody są nazywane najczęściej i w których metodach spędzasz większość czasu, zarówno w kodzie, jak i bibliotekach zewnętrznych. Możesz również uzyskać dane dotyczące wydajności wyrzucania elementów bezużytecznych, dzięki czemu możesz dostroić swój moduł zbierający i dostosować min./maks. Przestrzeń sterty wymaganą przez aplikację.


VisualVM nie znajduje się w środowisku JRE, tylko w JDK.

1
@ Thorbjørn Ravn Andersen Dobre połączenie. Powinienem wyjaśnić. Jednak, jeśli robisz programowanie w Javie, zwykle masz zainstalowany JDK (chociaż możesz mieć OpenJDK lub podobny - nie wiem, czy są one dostarczane z VisualVM).
Thomas Owens

1
Bardzo często zmieniam obszary robocze w Eclipse, które następnie domyślnie używają środowiska JRE, które uruchomiło Eclipse. Ponieważ instalacja JRE jest znacznie łatwiejsza niż JDK, powoli migrowaliśmy do procesu budowania mrówek, który zawiera kompilator Eclipse, i dlatego może działać na zwykłym JRE. Stąd te dni, kiedy można rzeczywiście zrobić prawdziwą pracę bez JDK. VisualVM można pobrać osobno, co ułatwia korzystanie z danej wersji Java, ponieważ w systemie Windows 64-bitowa maszyna JVM nie może łączyć się z 32-bitową maszyną JVM i odwrotnie.

9

Ponieważ każdy tutaj mówi o profilerach , skupię się na tej części pytania.

Jak deweloper może uniknąć tej złej intuicji?

Ty. robić. nie. Zamiast tego nigdy wcześniej nie optymalizujesz .
Powtarzaj to raz po raz, bo to mantra religijna.

Przekonasz się, że to robisz i odkryjesz, że nie powinieneś.
I znowu
I jeszcze raz.

Wczesna optymalizacja jest jednym z głównych grzechów programistów .

Narzędzia i rzeczy są częścią późniejszej optymalizacji, która jest ustalonym rzemiosłem .


Na pewno wczesna optymalizacja „zawiłego kodu”. Algorytmy łączenia i / lub struktury danych, które pasują do twojego problemu i (przy oczekiwanym obciążeniu przetwarzania) mają dobrą charakterystykę wydajności, to coś, co należy zrobić przed rozpoczęciem pisania kodu.
Vatine

@Vatine Tak, byłem tam. Nie, po prostu nie. Rób, co pasuje do twojej mapy myśli danego problemu. Może to być najbardziej wydajny algorytm i życzę ci tego, nie musi.
ZJR

brzmi to jak zasada YAGNI - NIE będziesz go potrzebować!
EL Yusubov,

7

Narzędzia te nazywane są profilerami . Możesz ich użyć, aby zmierzyć, która część (części) twojego programu zajmuje najwięcej czasu, a więc gdzie skupić się na strojeniu.

Równie ważne jest ponowne dokonanie pomiaru po zmianach, aby sprawdzić, czy wprowadzone zmiany mają zamierzony efekt.


5

Sprawdź także, ile pamięci zajmuje Twój program, a nie tylko jego szybkość i czas działania.

Wielu programistów pracujących z językami odśmiecającymi, takimi jak Java, ma błędne wrażenie, że odśmiecanie zapobiega wyciekom pamięci. Tak nie jest. Jeśli trzymasz odniesienie do obiektu, którego już nie potrzebujesz, nie zostanie ono zebrane, a więc wycieknie.

Widziałem aplikacje Java, które były tak nieszczelne, że nie pozwalałyby serwerowi zamieniać się!

Jeśli używasz zarówno profilowania środowiska wykonawczego, jak i pewnego rodzaju profilowania pamięci, nauczysz się pisać szybciej i szczuplejszy kod intuicyjnie. Powoduje to, że Twój kod będzie szybciej działał szybko przy pierwszej próbie.


1

moim lekarstwem jest zacząć od uzyskania jasnych odpowiedzi na dwa pytania:

  1. jak mierzyć wydajność (np. mierzyć czas ładowania danych )
  2. jaka jest wartość docelowa (np. ładowanie danych w 3 sekundy lub mniej z 95% pewnością )

Nauczył się powyższej sztuczki od facetów z zespołu tygrysów, którzy zostali kiedyś zaproszeni do zapisania zepsutego wydania naszego produktu. Ta wersja została zerwana ze względu na wydajność, może sprawić, że firma straci strategicznego klienta, co uzasadniło zaangażowanie facetów tygrysich (dość drogie btw). Zostałem przydzielony, aby pomóc im w wyjaśnieniu szczegółów projektu; wykorzystał to również jako okazję, aby dowiedzieć się nieco więcej o wydajności.


1

To, co znalazłem, jest najlepszym antidotum na przedwczesną optymalizację ta metoda .

Kiedyś go użyjesz, aby przyspieszyć trochę kodu (jak w tym przykładzie ), staje się on własnym uzależnieniem i zrozumiesz, że pierwszą zasadą dostrajania wydajności nie jest poprawianie kodu, to jest problem .

Prawdziwa optymalizacja to przedwczesna optymalizacja, ponieważ polowanie na jedzenie dla rodziny to strzelanie do puszek. Chodzi o znalezienie kamieniołomu.


1
I niestety, możesz znieść tylko 200 funtów z powrotem do swojej rodziny, więc nie strzelaj do wiewiórek przez cały dzień.
Jordan,

1

Stare pytanie, ale dam ci odpowiedź, która różni się znacznie od innych.

Duże możliwości zwiększenia wydajności wynikają z przetwarzania równoległego. Spróbuj zaprojektować kod, aby korzystać z wielu wątków. (Nawet jeśli dla uproszczenia nie robisz tego w wersji 1). Niezbędne są małe domysły lub intuicja.

Wszystko inne to przedwczesna optymalizacja i wymaga intuicji, co często jest błędne.


Naprawdę dobry punkt. Wcześniej można było liczyć na szybsze działanie procesorów co kilka lat. Teraz powinieneś tylko oczekiwać, że będzie więcej procesorów.
JimmyJames

0

Twoja intuicja może się z czasem poprawić. Wyrzucę to, może trochę kontrowersyjne, ale przez wiele lat używania VTune i CodeAnalyst, a teraz CodeXL, powiedziałbym, że jestem o wiele bardziej dokładny w swoich intuicjach niż wcześniej, jeśli chodzi o to, gdzie będą hotspoty, przynajmniej punkt, w którym nie jestem już całkowicie zaskoczony, gdy profiluję jakiś kod. To nie znaczy, że staram się optymalizować wszystko na ślepo.

Profilowanie faktycznie zwiększyło moją zależność od profilerów, a nie zmniejszyło je. Po prostu mówię, że mogę łatwiej przewidzieć, jakie będą wyniki profilowania do pewnego stopnia, a ponadto skutecznie wyeliminować punkty aktywne i skrócić czas potrzebny na zakończenie operacji użytkownika, bez konieczności wykonywania ciemnych cięć w ciemności i zaginięcia (coś, co może to zrobić nawet podczas korzystania z profilera, dopóki nie zrozumiesz nie tylko, czym są hotspoty, ale dlaczego dokładnie są one hotspotami w odniesieniu do, powiedzmy, brakujących pamięci podręcznej).

Jednak dopiero gdy zacząłem używać profilerów, zacząłem ulepszać tę intuicję. Jednym z powodów jest to, że jeśli dobrze znasz swój kod, przeczucia mogą być poprawne w odniesieniu do największych i najbardziej oczywistych punktów aktywnych, ale nie wszystkich subtelności pomiędzy nimi. Oczywiście, jeśli operacja wykonywana przez użytkownika zajmuje godzinę, a istnieje jeden rozwarty algorytm kwadratowej złożoności przetwarzający dane wejściowe obejmujące sto tysięcy elementów, prawdopodobnie prawdopodobnie wyjdziesz z bogatego hazardu, oszczędzając całe swoje życie na idei, że jest to kwadratowa złożoność algorytm zawiniony tutaj. Ale to nie daje żadnego szczegółowego wglądu lub, powiedzmy, dokładnie wiedzieć, co nie przyczynia się do czasu.

Przy rozpoczynaniu profilowania i sprawdzaniu, gdzie wszystkie rzeczy, które według ciebie mogły być większym czynnikiem przyczyniającym się do upływu czasu, nie było wiele, warto mieć tyle wartości; nie rozbieżne oczywiste źródła nieefektywności, ale te, które podejrzewasz, mogą być nieco nieefektywne, ale po profilowaniu zdają sobie sprawę, że w niewielkim stopniu przyczyniły się w dowolnym momencie. I to potencjalnie tam, gdzie zyskujesz najbardziej intuicyjny wgląd, to stwierdzenie, że jesteś źle pokazywany we wszystkich subtelnych obszarach, w których nie jest oczywiste, ile czasu spędzasz.

Ludzka intuicja przekraczająca oczywistą złożoność algorytmiczną często zaczyna się niepoprawnie, ponieważ to, co jest wydajne dla maszyny, a co dla ludzkiego umysłu, jest bardzo różne. Na początku nie przychodzi tak intuicyjnie, aby myśleć o hierarchiach pamięci przechodzących od rejestrów do pamięci podręcznej procesora, pamięci DRAM i dysku. Nie przychodzi intuicyjnie sądzić, że redundantna arytmetyka może być szybsza niż wykonywanie większej liczby rozgałęzień lub uzyskiwanie dostępu do pamięci w tabeli przeglądowej w celu pominięcia niektórych zadań przetwarzania. Zwykle myślimy o tym, ile pracy jest do zrobienia, pomijając takie rzeczy, jak koszt podejmowania decyzji oraz obciążenia pamięci i sklepów. To, co jest wydajne dla sprzętu, jest często bardzo sprzeczne z intuicją w sposób, który na początku przełamie wszystkie ludzkie założenia,

Ulepszanie tej intuicji może pomóc, poprzez profilowanie, projektowanie interfejsu . Projektowanie interfejsów jest bardzo kosztowne ze względu na zmiany, a koszty rosną proporcjonalnie do liczby miejsc w zależności od tego interfejsu. Kiedy zaczniesz poprawiać swoją intuicję, możesz zacząć projektowanie interfejsów lepiej za pierwszym razem, w taki sposób, że pozostawiasz tchnienie dla przyszłej optymalizacji bez kosztownych zmian projektowych. Ponownie jednak ta intuicja jest czymś, co generalnie rozwijasz i rozwijasz w nieskończoność, zawsze mając ten profil pod ręką.


0

Profile pomagają naprawić złą intuicję, jeśli chodzi o kod. Biorąc pod uwagę, ile sprzętu przewiduje w dzisiejszych czasach przewidywanie wydajności twojego kodu nie jest po ludzku praktyczne, ale tak było jeszcze w czasach Knutha wiele lat temu, który opowiadał się za włączeniem profilerów jako części standardowych narzędzi programistycznych do naprawy „głupkowata” natura deweloperów. Ale zamierzam pójść inną drogą z tą odpowiedzią, biorąc pod uwagę, jak wyczerpujące są odpowiedzi pod innymi względami i powiedzieć, że zrozumienie końca użytkownika jest drugą „poprawką”.

W moim osobistym doświadczeniu byłem świadkiem szczególnie genialnego programisty (ale z rozdziawionymi martwymi punktami na temat tego, w jaki sposób użytkownicy faktycznie korzystają z oprogramowania) optymalizującego algorytm podziału z profilem w ręku (bardzo dobry, drogi i wszechstronny: VTune Intela z wykresem połączeń próbkowanie na profilach GPU) dla siatek osiąga niesamowite wyniki dzięki miliardom aspektów na GPU, dzieląc proste prymitywy, takie jak sześciany z 6 wielokątami klatkowymi / wejściowymi. Poza tym, że dostroił go i dopasował do tego przypadku testowego, który był inny niż jakikolwiek inny przypadek użycia w świecie rzeczywistym (użytkownicy nie chcą miliarda aspektów podzielonej kostki, która zaczyna przypominać idealną sferę, ich dane wejściowe w podziale są zwykle postaciami i pojazdami i inne złożone dane wejściowe).

Zabawne jest to, że mam mózg w połowie tak funkcjonalny jak on i bez doktoratu w mojej karierze tylko z tego powodu, że rozumiem, czego użytkownicy chcą, czego chcą marketing, czego projektanci. Naprawdę nie mogę wystarczająco podkreślić, jak użyteczne jest postawienie siebie na stanowisku i butach użytkownika oraz spojrzenie na twoje oprogramowanie i to, co musi zrobić jako rzeczywisty użytkownik, starając się oderwać od wysiłków, które wkładasz w budowanie tego, co zbudowałeś i patrzenie na to świeżą parą oczu. Nawet powyżej spotkałem się z deweloperem, że jest to niemożliwe; myślał, że jestem winny posiadania podobnego rodzaju ego, które mają wszyscy technicznie rozumni, ale nieświadomi użytkowników programiści, i ciągle udowodniłem, że się myli, gdy użytkownicy i projektanci gromadzą się przy mnie, aby mówić o tym, co dokładnie zrobić. Brzmi to bardzo egoistycznie, ale zrównuję to z zastrzeżeniem, że nie jestem tak genialnym programistą, ale rozumiem, czego chcą użytkownicy i projektanci, co sprawiło, że jestem szczególnie uprzywilejowany w mojej dziedzinie, gdzie wydawało się to szczególnie rzadkie jakość z jakiegoś powodu. Jako programiści jesteśmy prawdopodobnie bardziej przyzwyczajeni do testowania testów niż rozumienia i kontaktów towarzyskich ze zwykłymi, nietechnicznymi ludźmi.

Istnieje więc profilowanie i poprawny pomiar, ale istnieje również podstawowa potrzeba, aby upewnić się, że mierzysz operację z typem danych wejściowych, które użytkownicy z rzeczywistego świata faktycznie zapewnią aplikacji. W przeciwnym razie możesz mieć nawet VTune lub CodeAnalyst, gprof lub dowolny inny profiler i nadal, próbując zoptymalizować hotspoty w stosunku do tego, co może wydawać się zwykłemu testowi dla programisty, ale dla użytkowników niejasnym, w końcu pesymizuje typowy przypadek użycia na rzecz niejasnego przypadku użycia, który niewielu użytkowników rozważa zastosowanie.

Pod koniec dnia wszystkie niepraktyczne praktyki, ponieważ programiści mogą być zrównoważeni żelaznym młotem, co sprawia, że ​​użytkownicy naprawdę są wystarczająco szczęśliwi bez rozwiązania głodu na świecie, a także praktyczną potrzebę zdobycia pieniędzy, abyśmy mogli zapłacić czynsz lub kupić piwo lub spójrz na nagie panie lub cokolwiek chcesz / musisz zrobić. Wszystko inne jest potencjalnie sprzeczne z tą podstawową potrzebą biznesową, a każdy programista tak szlachetny, tak heroiczny, aby zapomnieć, że chodzi o zarabianie pieniędzy i ostatecznie satysfakcję użytkowników, aby skłonili ich do zapłaty, może zrobić dobrze, aby sprowadzić się na ziemię i wyłącz tryb boga, tworząc wirtualne światy na rzecz rzeczywistej potrzeby po prostu wysyłania i zdobywania pieniędzy na jedzenie. Możemy się zagubić w metrykach i praktykach oprogramowania, ale zasadniczo to ”.

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.