Jak powiedział Knuth,
Powinniśmy zapomnieć o małych wydajnościach, powiedzmy w 97% przypadków: przedwczesna optymalizacja jest źródłem wszelkiego zła.
To jest coś, co często pojawia się w odpowiedziach Stack Overflow na pytania typu „Jaki jest najbardziej wydajny mechanizm pętli”, „Techniki optymalizacji SQL?” ( i tak dalej ). Standardową odpowiedzią na te pytania ze wskazówkami dotyczącymi optymalizacji jest sprofilowanie kodu i sprawdzenie, czy jest to najpierw problem, a jeśli nie, to nowa technika jest niepotrzebna.
Moje pytanie brzmi: jeśli dana technika jest inna, ale nie jest szczególnie niejasna lub zaciemniona, czy można to naprawdę uznać za przedwczesną optymalizację?
Oto powiązany artykuł Randalla Hyde'a zatytułowany The Fallacy of Premature Optimization .
Odpowiedzi:
Don Knuth zapoczątkował ruch programowania piśmiennego , ponieważ wierzył, że najważniejszą funkcją kodu komputerowego jest przekazanie intencji programisty ludzkiemu czytelnikowi . Każda praktyka kodowania, która utrudnia zrozumienie kodu w imię wydajności, jest przedwczesną optymalizacją.
Pewne idiomy, które zostały wprowadzone w imię optymalizacji, stały się tak popularne, że wszyscy je rozumieją i stały się oczekiwane , a nie przedwczesne. Przykłady zawierają
Używanie arytmetyki wskaźników zamiast notacji tablicowej w C, w tym użycie takich idiomów, jak
Ponowne wiązanie zmiennych globalnych ze zmiennymi lokalnymi w Lua, jak w
Poza takimi idiomami idź na skróty na własne ryzyko .
Cała optymalizacja jest przedwczesna, chyba że
Program jest zbyt wolny (wiele osób zapomina o tej części).
Masz pomiar (profil lub podobny) pokazujący, że optymalizacja może poprawić sytuację .
(Dopuszczalna jest również optymalizacja pod kątem pamięci).
Bezpośrednia odpowiedź na pytanie:
EDYCJA : W odpowiedzi na komentarze, użycie quicksort zamiast prostszego algorytmu, takiego jak sortowanie przez wstawianie, jest kolejnym przykładem idiomu, który każdy rozumie i oczekuje . (Chociaż jeśli napiszesz własną procedurę sortowania zamiast korzystania z procedury sortowania w bibliotece, można mieć nadzieję, że masz bardzo dobry powód).
źródło
IMHO, 90% optymalizacji powinno nastąpić na etapie projektowania, w oparciu o postrzegane obecne, a co ważniejsze, przyszłe wymagania. Jeśli musisz wyjąć profiler, ponieważ twoja aplikacja nie skaluje się do wymaganego obciążenia, zostawiłeś ją za późno, a IMO straci dużo czasu i wysiłku, nie usuwając problemu.
Zwykle jedyne optymalizacje, które są opłacalne, to takie, które zapewniają wzrost wydajności o rząd wielkości pod względem szybkości lub mnożnik pod względem pamięci masowej lub przepustowości. Tego typu optymalizacje zwykle odnoszą się do wyboru algorytmu i strategii przechowywania i są niezwykle trudne do przywrócenia do istniejącego kodu. Mogą sięgać tak głęboko, jak wpływanie na decyzję dotyczącą języka, w którym wdrażasz swój system.
Więc moja rada, optymalizuj wcześnie, w oparciu o swoje wymagania, a nie kod, i szukaj możliwego wydłużenia żywotności aplikacji.
źródło
while (s[0]==' ') s = s.substring(1)
for(i=0; i<s.len && s[i]==' '; ++i); s=s.substring(i)
- ale wymaga to już znajomości potencjalnych problemów z wydajnością (profile są tutaj cennymi narzędziami do ciągłego uczenia się).Jeśli nie profilowałeś, jest to przedwczesne.
źródło
A więc masz dwie gotowe techniki, identyczne pod względem kosztów (ten sam wysiłek w użyciu, czytaniu, modyfikowaniu), a jedna jest bardziej wydajna. Nie, użycie bardziej wydajnego nie byłoby w takim przypadku przedwczesne.
Przerywanie pisania kodu w celu wyszukania alternatyw dla typowych konstrukcji programistycznych / procedur bibliotecznych, jeśli nie ma szans, że gdzieś kręci się wydajniejsza wersja, mimo że dla wszystkich, co wiesz, względna prędkość tego, co piszesz, nigdy nie będzie miała znaczenia. .. To przedwczesne.
źródło
Oto problem, który widzę z całą koncepcją unikania przedwczesnej optymalizacji.
Istnieje rozdźwięk między mówieniem a robieniem tego.
Dokonałem wielu zmian wydajności, wyciskając duże czynniki z dobrze zaprojektowanego kodu, pozornie wykonanego bez przedwczesnej optymalizacji. Oto przykład.
W prawie każdym przypadku przyczyną nieoptymalnej wydajności jest to, co nazywam galopującą ogólnością , czyli użycie abstrakcyjnych klas wielowarstwowych i dokładnego projektowania obiektowego, gdzie proste koncepcje byłyby mniej eleganckie, ale całkowicie wystarczające.
A w materiałach dydaktycznych, w których nauczane są te abstrakcyjne koncepcje projektowe, takie jak architektura oparta na powiadomieniach i ukrywanie informacji, w których proste ustawienie wartości logicznej obiektu może mieć nieograniczony wpływ na aktywność, jaki jest podany powód? Efektywność .
Czy była to przedwczesna optymalizacja, czy nie?
źródło
Najpierw spraw, aby kod działał. Po drugie, sprawdź, czy kod jest poprawny. Po trzecie, zrób to szybko.
Każda zmiana kodu dokonana przed etapem # 3 jest zdecydowanie przedwczesna. Nie jestem do końca pewien, jak sklasyfikować wybory projektowe dokonane wcześniej (na przykład przy użyciu dobrze dopasowanych struktur danych), ale wolę używać abstrakcji, które są łatwe do zaprogramowania, a nie tych, które dobrze się sprawdzają, dopóki nie osiągnę etap, w którym mogę zacząć używać profilowania i mieć poprawną (choć często powolną) implementację referencyjną, z którą mogę porównać wyniki.
źródło
Z punktu widzenia bazy danych nieuwzględnianie optymalnego projektu na etapie projektowania jest w najlepszym razie nierozsądne. Bazy danych nie są łatwo refaktoryzowane. Kiedy są źle zaprojektowane (projekt, który nie bierze pod uwagę optymalizacji, nie ma znaczenia, jak można próbować ukryć się za nonsensem przedwczesnej optymalizacji), prawie nigdy nie jest w stanie odzyskać z tego, ponieważ baza danych jest zbyt prosta dla działanie całego systemu. O wiele mniej kosztowne jest prawidłowe zaprojektowanie kodu optymalnego dla oczekiwanej sytuacji, niż czekanie, aż pojawi się milion użytkowników i ludzie będą krzyczeć, ponieważ użyłeś kursorów w całej aplikacji. Inne optymalizacje, takie jak użycie sargeable code, wybieranie najlepszych możliwych indeksów itp. Mają sens tylko podczas projektowania. Nie bez powodu nazywa się to szybkim i brudnym. Ponieważ nigdy nie działa dobrze, więc nie używaj szybkości jako substytutu dobrego kodu. Szczerze mówiąc, kiedy rozumiesz dostrajanie wydajności w bazach danych, możesz napisać kod, który z większym prawdopodobieństwem będzie działał dobrze w tym samym czasie lub krócej niż jest to potrzebne do napisania kodu, który nie działa dobrze. Brak czasu na nauczenie się, co jest dobrym rozwiązaniem przy projektowaniu bazy danych, to lenistwo programisty, a nie najlepsza praktyka.
źródło
Wydaje się, że mówisz o optymalizacji, takiej jak użycie kontenera wyszukiwania opartego na skrótach, a nie zindeksowanego, takiego jak tablica, gdy zostanie wykonanych wiele wyszukiwań kluczy. To nie jest przedwczesna optymalizacja, ale coś, o czym powinieneś zdecydować na etapie projektowania.
Rodzaj optymalizacji, o który chodzi w regule Knutha, polega na minimalizowaniu długości najpopularniejszych ścieżek kodowych, optymalizowaniu kodu, który jest uruchamiany najczęściej, na przykład poprzez przepisywanie w asemblerze lub uproszczenie kodu, czyniąc go mniej ogólnym. Ale robienie tego nie ma sensu, dopóki nie jesteś pewien, które części kodu wymagają tego rodzaju optymalizacji, a optymalizacja (może?) Uczynić kod trudniejszym do zrozumienia lub utrzymania, stąd „przedwczesna optymalizacja jest źródłem wszelkiego zła”.
Knuth mówi również, że zawsze lepiej, zamiast optymalizować, zmienić algorytmy używane w programie, podejście do problemu. Na przykład, podczas gdy niewielkie ulepszenie może dać 10% wzrost szybkości wraz z optymalizacją, fundamentalna zmiana sposobu działania programu może sprawić, że będzie on 10x szybszy.
W odpowiedzi na wiele innych komentarzy zamieszczonych w tym pytaniu: wybór algorytmu! = Optymalizacja
źródło
Celem maksymy jest to, że zazwyczaj optymalizacja jest skomplikowana i złożona. I zazwyczaj , to architekt / projektant / programista / opiekun musi skasować kod i zwięzły, aby zrozumieć, co się dzieje.
Jeśli dana optymalizacja jest jasna i zwięzła, możesz poeksperymentować z nią (ale wróć i sprawdź, czy ta optymalizacja jest skuteczna). Chodzi o to, aby kod był jasny i zwięzły przez cały proces programowania, dopóki korzyści z wydajności nie przewyższą wywołanych kosztów pisania i utrzymywania optymalizacji.
źródło
Próbuję zoptymalizować tylko wtedy, gdy zostanie potwierdzony problem z wydajnością.
Moja definicja przedwczesnej optymalizacji brzmi „wysiłek zmarnowany na kod, o którym nie wiadomo, że jest problemem z wydajnością”. Z całą pewnością jest czas i miejsce na optymalizację. Jednak sztuczka polega na wydatkowaniu dodatkowego kosztu tylko wtedy, gdy ma on znaczenie dla wydajności aplikacji i gdy dodatkowy koszt przeważa nad osiągnięciem wydajności.
Pisząc kod (lub zapytanie do bazy danych) staram się pisać „wydajny” kod (tj. Kod, który wykonuje swoją zamierzoną funkcję, szybko i całkowicie z najprostszą logiką). Należy zauważyć, że „wydajny” kod niekoniecznie oznacza to samo, co „zoptymalizowany” kod. Optymalizacje często wprowadzają dodatkową złożoność do kodu, co zwiększa zarówno koszt opracowania, jak i utrzymania tego kodu.
Moja rada: spróbuj zapłacić koszty optymalizacji tylko wtedy, gdy możesz oszacować korzyści.
źródło
Podczas programowania istotne znaczenie ma szereg parametrów. Wśród nich są:
Optymalizacja (dążenie do wydajności) często odbywa się kosztem innych parametrów i musi być równoważona ze „stratami” w tych obszarach.
Kiedy masz możliwość wyboru dobrze znanych algorytmów, które działają dobrze, koszt „optymalizacji” z góry jest często akceptowalny.
źródło
Optymalizacja może odbywać się na różnych poziomach szczegółowości, od bardzo wysokiego do bardzo niskiego poziomu:
Zacznij od dobrej architektury, luźnego połączenia, modułowości itp.
Wybierz odpowiednie struktury danych i algorytmy do problemu.
Zoptymalizuj pamięć, próbując zmieścić więcej kodu / danych w pamięci podręcznej. Podsystem pamięci jest od 10 do 100 razy wolniejszy niż procesor, a jeśli dane są stronicowane na dysk, jest od 1000 do 10 000 razy wolniejszy. Ostrożność w kwestii zużycia pamięci może przynieść większe korzyści niż optymalizacja poszczególnych instrukcji.
W ramach każdej funkcji należy odpowiednio używać instrukcji sterujących przepływem. (Przenieś niezmienne wyrażenia poza treść pętli. Najpierw umieść najczęściej używaną wartość w przełączniku / przypadku itp.)
W każdej instrukcji użyj najbardziej wydajnych wyrażeń dających poprawny wynik. (Mnożenie vs. przesunięcie itp.)
Nitowanie na temat tego, czy użyć wyrażenia dzielenia, czy wyrażenia przesuwającego, niekoniecznie przedwczesną optymalizacją. Jest to przedwczesne tylko wtedy, gdy zrobisz to bez uprzedniej optymalizacji architektury, struktur danych, algorytmów, zużycia pamięci i kontroli przepływu.
Oczywiście każda optymalizacja jest przedwczesna, jeśli nie zdefiniujesz progu skuteczności celu.
W większości przypadków:
A) Możesz osiągnąć próg wydajności celu, wykonując optymalizacje wysokiego poziomu, więc nie trzeba majstrować przy wyrażeniach.
lub
B) Nawet po wykonaniu wszystkich możliwych optymalizacji nie osiągniesz docelowego progu wydajności, a optymalizacje na niskim poziomie nie powodują dostatecznej różnicy w wydajności, aby uzasadnić utratę czytelności.
Z mojego doświadczenia wynika, że większość problemów optymalizacyjnych można rozwiązać na poziomie architektury / projektu lub struktury danych / algorytmu. Często (choć nie zawsze) wymagana jest optymalizacja pod kątem zużycia pamięci. Ale rzadko jest konieczna optymalizacja kontroli przepływu i logiki wyrażeń. W przypadkach, gdy jest to rzeczywiście konieczne, rzadko wystarcza.
źródło
Odpowiedź Normana jest doskonała. W jakiś sposób rutynowo wykonujesz „przedwczesną optymalizację”, która jest w rzeczywistości najlepszą praktyką, ponieważ wiadomo, że robienie tego w inny sposób jest całkowicie nieefektywne.
Na przykład, aby dodać do listy Normana:
for (i = 0; i < strlen(str); i++)
(ponieważ strlen to wywołanie funkcji przechodzące za każdym razem po łańcuchu, wywoływane w każdej pętli);for (i = 0 l = str.length; i < l; i++)
i nadal jest czytelny, więc OK.I tak dalej. Ale takie mikro-optymalizacje nigdy nie powinny odbywać się kosztem czytelności kodu.
źródło
Konieczność użycia profilera należy pozostawić w skrajnych przypadkach. Inżynierowie projektu powinni mieć świadomość, gdzie występują wąskie gardła wydajności.
Myślę, że „przedwczesna optymalizacja” jest niezwykle subiektywna.
Jeśli piszę jakiś kod i wiem , że powinienem używać Hashtable, zrobię to. Nie zaimplementuję go w jakiś błędny sposób, a potem zaczekam, aż raport o błędzie nadejdzie miesiąc lub rok później, gdy ktoś ma z nim problem.
Przeprojektowanie jest bardziej kosztowne niż optymalizacja projektu w oczywisty sposób od samego początku.
Oczywiście za pierwszym razem można pominąć pewne małe rzeczy, ale rzadko są to kluczowe decyzje projektowe.
Dlatego: NIE optymalizacja projektu jest w IMO zapachem kodu.
źródło
Warto zauważyć, że oryginalny cytat Knutha pochodzi z artykułu, który napisał, promując stosowanie go
goto
w starannie wybranych i mierzonych obszarach jako sposób na wyeliminowanie punktów zapalnych. Jego cytat był zastrzeżeniem, które dodał, aby uzasadnić swoje uzasadnienie użyciagoto
w celu przyspieszenia tych krytycznych pętli.I kontynuuje:
Należy pamiętać, jak użył słowa „zoptymalizowany” w cudzysłowach (oprogramowanie prawdopodobnie nie jest wydajne). Zwróć też uwagę, że krytykuje on nie tylko tych „grosza i funta-głupich” programistów, ale także ludzi, którzy reagują sugerując, że należy zawsze ignorować drobne niedociągnięcia. Na koniec do często cytowanej części:
... a następnie trochę więcej o znaczeniu narzędzi do profilowania:
Ludzie wszędzie nadużywali jego cytatu, często sugerując, że mikro-optymalizacje są przedwczesne, kiedy cały jego artykuł opowiadał się za mikro-optymalizacjami! Jedna z grup osób, które krytykował, a które powtarzają tę „konwencjonalną mądrość”, jak to ujął, że zawsze ignorują efektywność w małych grupach, często nadużywają jego cytatu, który pierwotnie był skierowany częściowo przeciwko takim typom, które zniechęcają do wszelkich form mikro-optymalizacji .
Był to jednak cytat na korzyść odpowiednio zastosowanych mikrooptymalizacji przy użyciu doświadczonej ręki trzymającej profilera. Dzisiejszy analogiczny odpowiednik może brzmieć: „Ludzie nie powinni na ślepo optymalizować swojego oprogramowania, ale niestandardowe alokatory pamięci mogą zrobić ogromną różnicę, gdy zostaną zastosowane w kluczowych obszarach w celu poprawy lokalizacji odniesienia” lub „ Odręczny kod SIMD przy użyciu Reprezentacja jest naprawdę trudna do utrzymania i nie powinieneś jej używać wszędzie, ale może zużywać pamięć znacznie szybciej, gdy zostanie odpowiednio zastosowana przez doświadczoną i kierowaną ręką. "
Za każdym razem, gdy próbujesz promować starannie zastosowane mikro-optymalizacje, jak promował Knuth powyżej, dobrze jest wrzucić zastrzeżenie, aby zniechęcić nowicjuszy do zbytniego podniecenia i ślepego próbowania optymalizacji, na przykład przepisywania całego oprogramowania do użytku
goto
. Po części to właśnie robił. Jego cytat był faktycznie częścią wielkiego zastrzeżenia, tak jak ktoś, kto przeskakuje motocyklem przez płonące ognisko, może dodać zastrzeżenie, że amatorzy nie powinni próbować tego w domu, jednocześnie krytykując tych, którzy próbują bez odpowiedniej wiedzy i sprzętu i otrzymują obrażenia .To, co uznał za „przedwczesne optymalizacje”, to optymalizacje stosowane przez ludzi, którzy faktycznie nie wiedzieli, co robią: nie wiedzieli, czy optymalizacja jest naprawdę potrzebna, nie mierzyli odpowiednimi narzędziami, być może nie rozumieli natury ich kompilator lub architektura komputera, a przede wszystkim były „głupie grosze i funty”, co oznacza, że przeoczyli duże możliwości optymalizacji (zaoszczędzenia milionów dolarów), próbując uszczypnąć grosze, a wszystko to podczas tworzenia kodu nie może dłużej skutecznie debugować i utrzymywać.
Jeśli nie pasujesz do kategorii „grosz-i-funt-głupi”, to nie optymalizujesz przedwcześnie według standardów Knutha, nawet jeśli używasz
goto
w celu przyspieszenia pętli krytycznej (co jest mało prawdopodobne aby pomóc znacznie w starciu z dzisiejszymi optymalizatorami, ale gdyby tak było, i to w naprawdę krytycznym obszarze, nie optymalizowałbyś przedwcześnie). Jeśli faktycznie stosujesz to, co robisz w obszarach, które są naprawdę potrzebne i naprawdę odnoszą z tego korzyści, to w oczach Knutha radzisz sobie świetnie.źródło
Przedwczesna optymalizacja oznacza dla mnie próbę poprawy wydajności kodu, zanim będziesz miał działający system, zanim go faktycznie sprofilujesz i dowiesz się, gdzie jest wąskie gardło. Nawet po tym w wielu przypadkach czytelność i łatwość konserwacji powinny poprzedzać optymalizację.
źródło
Nie sądzę, aby uznane najlepsze praktyki to przedwczesne optymalizacje. Chodzi bardziej o spalanie czasu na temat potencjalnych problemów z wydajnością w zależności od scenariuszy użytkowania. Dobry przykład: jeśli przez tydzień próbujesz zoptymalizować odbijanie się od obiektu, zanim uzyskasz dowód, że jest to wąskie gardło, przedwcześnie optymalizujesz.
źródło
Jeśli nie okaże się, że aplikacja wymaga większej wydajności ze względu na potrzeby użytkownika lub firmy, nie ma powodu, aby martwić się optymalizacją. Nawet wtedy nie rób nic, dopóki nie sprofilujesz swojego kodu. Następnie zaatakuj części, które zajmują najwięcej czasu.
źródło
Widzę to, że jeśli coś zoptymalizujesz, nie wiedząc, ile wydajności możesz uzyskać w innym scenariuszu, JEST to przedwczesna optymalizacja. Celem kodu powinno być uczynienie go łatwiejszym do odczytania dla człowieka.
źródło
Jak pisałem przy podobnym pytaniu, zasady optymalizacji to:
1) Nie optymalizuj
2) (tylko dla ekspertów) Optymalizuj później
Kiedy optymalizacja jest przedwczesna? Zwykle.
Wyjątkiem może być Twój projekt lub dobrze zamknięty kod, który jest intensywnie używany. W przeszłości pracowałem nad kodem krytycznym czasowo (implementacja RSA), gdzie patrząc na asembler, który utworzył kompilator i usuwając pojedynczą niepotrzebną instrukcję w wewnętrznej pętli, przyspieszyłem o 30%. Ale przyspieszenie wynikające z zastosowania bardziej wyrafinowanych algorytmów było o rzędy wielkości większe.
Inne pytanie, które należy sobie zadać podczas optymalizacji, brzmi: „Czy robię tutaj odpowiednik optymalizacji dla modemu 300 bodów?” . Innymi słowy, czy prawo Moore'a sprawi, że optymalizacja stanie się nieistotna w niedługim czasie. Wiele problemów związanych ze skalowaniem można rozwiązać, rzucając w nie więcej sprzętu.
Wreszcie, co nie mniej ważne, optymalizacja jest przedwczesna, zanim program będzie działał zbyt wolno. Jeśli mówisz o aplikacji internetowej, możesz uruchomić ją pod obciążeniem, aby sprawdzić, gdzie są wąskie gardła - ale istnieje prawdopodobieństwo, że będziesz mieć takie same problemy ze skalowaniem, jak większość innych witryn i zastosowane zostaną te same rozwiązania.
edycja: Nawiasem mówiąc, jeśli chodzi o artykuł, do którego prowadzi łącze, zakwestionowałbym wiele przyjętych założeń. Po pierwsze, nie jest prawdą, że prawo Moore'a przestało działać w latach 90. Po drugie, nie jest oczywiste, że czas użytkownika jest cenniejszy niż czas programisty. Większość użytkowników i tak nie wykorzystuje gorączkowo każdego dostępnego cyklu procesora, prawdopodobnie czeka, aż sieć coś zrobi. Dodatkowo istnieje koszt alternatywny, gdy czas programisty zostaje przekierowany z implementacji czegoś innego, do skrócenia o kilka milisekund tego, co program robi, gdy użytkownik rozmawia przez telefon. Cokolwiek dłuższego nie jest zwykle optymalizacją, to naprawa błędów.
źródło