Kiedy optymalizacja jest przedwczesna?

82

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 .

nickf
źródło
42
To trochę ironiczne, że wielu ludzi, którzy krzyczą „Przedwczesna optymalizacja jest źródłem wszelkiego zła”, sami przedwcześnie zoptymalizowali cytat: (cd.)
około
22
„Powinniśmy zapomnieć o małych wydajnościach, powiedzmy w około 97% przypadków: przedwczesna optymalizacja jest źródłem wszelkiego zła. Jednak nie powinniśmy przepuszczać naszych możliwości w tych krytycznych 3%” (Donald Knuth)
około
2
Myślę, że to CA Hoare powiedział to. Nawet Knuth tak mówi.
jamesh
1
tak, Tony Hoare jako pierwszy powiedział, że „przedwczesna optymalizacja jest źródłem wszystkich złych części”, ale Knuth zacytował / parafrazował go, dodając resztę, jak sądzę
nickf
2
Chociaż zgadzam się, że cytat jest najczęściej nadużywany i wyrywany z kontekstu, z definicji jest zawsze poprawny ze względu na „przedwczesne” (jednak najczęściej jest używany nieprawidłowo jako uzasadnienie niechlujnego projektu i kodu). Z definicji, jeśli optymalizacja nastąpiła w najbardziej dogodnym momencie rozwoju, czy to podczas projektowania, czy w jakimkolwiek innym momencie, nie była „przedwczesna”.
Lawrence Dol

Odpowiedzi:

103

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

    for (p = q; p < lim; p++)
    
  • Ponowne wiązanie zmiennych globalnych ze zmiennymi lokalnymi w Lua, jak w

    local table, io, string, math
        = table, io, string, math
    

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:

  • Jeśli twoja „inna” technika utrudnia zrozumienie programu , oznacza to przedwczesną optymalizację .

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).

Norman Ramsey
źródło
13
Według twoich definicji; jeśli implementacja quicksort jest trudniejsza do odczytania i zrozumienia niż bubbleort, jest to przedwczesna optymalizacja. Nie możesz zoptymalizować pamięci? Spróbuj znaleźć te same przykłady dla dużych rzadkich macierzy. IMHO, większość optymalizacji powinna mieć miejsce na etapie projektowania. to znaczy bardzo wcześnie.
SmacL
1
@frankodwyer: ale inkrementacja wskaźnika jest prawdopodobnie szybsza niż inkrementacja licznika i używanie notacji tablicowej i byłaby to przedwczesna optymalizacja.
Joachim Sauer
5
@Norman: Chociaż quicksort jest teraz wszechobecny, nie został wynaleziony po raz pierwszy, a zatem QED był przedwczesną optymalizacją, z którą autor nie miał żadnego interesu, prawda?
Lawrence Dol
5
@Software Monkey: Absolutnie. Wszystkie badania CS to strata pieniędzy podatników i należy je natychmiast wstrzymać.
Norman Ramsey
11
Każdy algorytm sortowania, w tym ten, który wymyśliłeś, jest jasny i zwięzły, jeśli zostanie zapisany jako oddzielna funkcja o nazwie sortQuickly (...) z odpowiednimi komentarzami.
ilya n.
40

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.

SmacL
źródło
6
Nie zgadzam się z twoją konkluzją „zostawił to za późno”. Zasadniczo profilowanie jest potrzebne, gdy założenie się nie sprawdza, a profiler jest potrzebny, aby powiedzieć ci, CO załamało założenie. Na przykład stwierdziłem, że „usuń znak na pozycji 0” dla StringBuffers w Javie działa dobrze dla testów junit, ale BARDZO wolno dla dużych łańcuchów. Nie podejrzewałem tego kodu, dopóki profiler nie wskazał go jako sprawcę!
Thorbjørn Ravn Andersen
7
Zgadzam się z zasadą „kiedy potrzebujesz profilera, jest już za późno”, na podstawie mojego doświadczenia - większość moich problemów z wydajnością nie jest pojedynczymi wąskimi gardłami, ale rozciąga się na wielu współpracowników. Ale mam dobre doświadczenie w kodzie i kosztach niskiego poziomu i instynktownie unikałbym wszelkich działań, które polegają na (znacznie powtarzanym) usunięciu pierwszego znaku ciągu. +1 za „optymalizację podczas projektowania”.
peterchen
@peterchen tak z ciekawości, co byś zrobił, aby „usunąć pierwszy znak ciągu”.
Ghos3t
1
@ user258365: Brute force byłoby użycie reprezentacji ciągu, która nie musi tworzyć kopii dla łańcuchów podrzędnych. To „prawie trywialne” w przypadku niezmiennych ciągów liczonych jako referencje. Alternatywnie, zmiany algorytmiczne, takie jak zastępowanie (pseudokod) 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ę).
peterchen
@ ThorbjørnRavnAndersen, pracowałem jako konsultant, pomagając zespołowi ukończyć projekt, ale nie było to możliwe, ponieważ nie planowano poważnych problemów z wydajnością (poza kodem spaghetti). Miał on pokazać chronologiczny wykres z historią wszystkich pacjentów. Wystąpiło jedno żądanie dotyczące całych danych, tak jak pobieranie z Map Google całego świata. Opracowanie złego kodu, oczekiwanie na późniejsze profilowanie, spowodowało niepowodzenie projektu.
Pedro Amaral Couto
31

Jeśli nie profilowałeś, jest to przedwczesne.

JaredPar
źródło
3
Zgadzam się z ideą, która się za tym kryje, ale także: jeśli implementacja nie jest całkowicie ograniczona przez cykle procesora, uzyskanie pomiaru, który jest zarówno odtwarzalny, jak i można go uogólnić, jest trudne - a im bardziej stabilny, tym mniej realistyczny.
peterchen
1
Problem, który mam z powyższą odpowiedzią polega na tym, że sugeruje ona, że ​​nie można zoptymalizować algorytmu przed jego zakodowaniem. Mój sposób pracy polega na projektowaniu algorytmu tak, aby spełniał wymagania funkcjonalne. Sprawdź, czy jest prawdopodobne, że nie spełni on wymagań dotyczących wydajności (np. Wysoki stopień złożoności i prawdopodobnie trafi w duże zbiory danych) i zoptymalizuj algorytm, zanim zaczniesz go kodować. Optymalizacja to po prostu dopracowanie w celu osiągnięcia optymalnego rozwiązania, często najbardziej efektywnie przeprowadza się ją na etapie projektowania.
SmacL
2
Nie zgadzam się. Knuth mówił o małych wydajnościach. Optymalizacja często ma miejsce na etapie projektowania. Polega na doborze odpowiednich struktur danych i algorytmów, które często mają duży wpływ na wydajność i niekoniecznie mogą być później wymienione.
haslersn
@haslersn: "Knuth mówił o małych wydajnościach" Donald Knuth: "Konwencjonalna mądrość, którą podziela wielu dzisiejszych inżynierów oprogramowania, wzywa do ignorowania wydajności w małych; ale uważam, że jest to po prostu przesadna reakcja na nadużycia (...) ustalone dyscypliny inżynieryjne 12% poprawa, łatwa do osiągnięcia, nigdy nie jest uważana za marginalną (...) "
Pedro Amaral Couto
27

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ę?

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.

Shog9
źródło
3
Zgoda, jeśli wiesz, że jeden algorytm jest bardziej wydajny w twoim przypadku użycia, zdecydowanie użyj bardziej wydajnego. Jeśli nie znasz najbardziej wydajnego algorytmu, użyj tego, co masz i profil później, aby sprawdzić, czy jest to problem.
grepsedawk
10

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?

Mike Dunlavey
źródło
Podoba mi się ta odpowiedź, ponieważ ilustruje jeden z głównych problemów związanych z abstrakcją i generalizacją. Gdy uogólniasz hierarchię klas w celu obsługi szerszego zakresu przypadków użycia, zbyt łatwo jest poważnie pogorszyć wydajność w najbardziej typowych przypadkach użycia. Łatwo jest też przyczepić się do klasy, która zapewnia daną funkcjonalność bez sprawdzania, czy funkcjonalność jest zapewniana na akceptowalnym poziomie wydajności dla skali zamierzonego zastosowania.
SmacL
1
„gdzie proste koncepcje byłyby mniej eleganckie, ale całkowicie wystarczające”. Złożony kod rzadko jest bardziej elegancki niż prosty kod, gdy prosty kod spełnia wymagania. (Chociaż argumentowałbym, że musisz upewnić się, że twój prosty kod faktycznie eksploduje z wyraźnym wskazaniem nieobsługiwanego stanu / wejścia, jeśli ktoś spróbuje go wykonać w bardziej złożonym przypadku.)
jpmc26
8

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.

Watyna
źródło
8

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.

HLGEM
źródło
7

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

FredV
źródło
6

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.

yfeldblum
źródło
2
Właściwie spora część „optymalizacji” sprowadza się do wybrania odpowiedniego algorytmu do pracy; jest to działalność na wysokim poziomie z wysokimi wynikami - daleka od „małej wydajności” w cytacie Knutha.
Shog9
4

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.

Chris Nava
źródło
4

Podczas programowania istotne znaczenie ma szereg parametrów. Wśród nich są:

  • Czytelność
  • Konserwowalność
  • Złożoność
  • Krzepkość
  • Poprawność
  • Wydajność
  • Czas rozwoju

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.

Ola Eldøy
źródło
1
Na powyższej liście brakuje jednego najważniejszego parametru kontroli jakości; Spełnianie wymagań. Jeśli oprogramowanie nie spełnia wymagań docelowych odbiorców, wszystkie inne parametry są bez znaczenia. Jeśli wydajność nie jest akceptowalna, wymagania nie zostały spełnione.
SmacL
3
Można powiedzieć, że obejmuje to poprawność. Poza tym „wydajność” w sensie „tak szybko, jak to możliwe” bardzo rzadko znajduje się wśród wymagań, i nawet to, że Ola chodziło o kompromis z innymi potrzebami, pozostaje prawdą.
frankodwyer
4

Optymalizacja może odbywać się na różnych poziomach szczegółowości, od bardzo wysokiego do bardzo niskiego poziomu:

  1. Zacznij od dobrej architektury, luźnego połączenia, modułowości itp.

  2. Wybierz odpowiednie struktury danych i algorytmy do problemu.

  3. 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.

  4. 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.)

  5. 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.

benjismith
źródło
3

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:

  • Korzystanie z konkatenacji StringBuilder w Javie (lub C # itp.) Zamiast String + String (w pętli);
  • Unikanie pętli w C, takich jak: 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);
  • Wydaje się, że w większości implementacji JavaScript jest szybszy 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.

PhiLho
źródło
3

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.

nbevans
źródło
Chodzi o to, że wąskie gardła często pojawiają się w sekcjach kodu, o których nigdy nie myślałeś, że będą problemem. Profilowanie eliminuje udawanie i pokazuje rzeczywiste miejsca powstawania kosztów programu. Najlepiej robić oczywiste rzeczy od samego początku, ale do wszystkiego innego należy profilowanie.
Chris Smith
2

Warto zauważyć, że oryginalny cytat Knutha pochodzi z artykułu, który napisał, promując stosowanie go gotow 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życia gotow celu przyspieszenia tych krytycznych pętli.

[...] znowu jest to zauważalna oszczędność w ogólnej szybkości działania, jeśli, powiedzmy, średnia wartość n wynosi około 20, a procedura wyszukiwania jest wykonywana około miliona razy w programie. Takie optymalizacje pętli [za pomocą gotos] nie są trudne do nauczenia i, jak powiedziałem, są odpowiednie tylko w niewielkiej części programu, ale często przynoszą znaczne oszczędności. […]

I kontynuuje:

Konwencjonalna mądrość, którą podziela wielu dzisiejszych inżynierów oprogramowania, wzywa do ignorowania wydajności małych; ale uważam, że jest to po prostu przesadna reakcja na nadużycia, które widzą i są praktykowane przez głupich programistów, którzy nie mogą debugować ani utrzymywać swoich „zoptymalizowanych” programów. W uznanych dyscyplinach inżynieryjnych łatwa do uzyskania poprawa o 12% nigdy nie jest uważana za marginalną; i uważam, że ten sam punkt widzenia powinien dominować w inżynierii oprogramowania. Oczywiście nie zawracałbym sobie głowy wykonywaniem takich optymalizacji na jednym zdjęciu, ale jeśli chodzi o przygotowanie programów jakościowych, nie chcę ograniczać się do narzędzi, które odmawiają mi takich wydajności [tj. goto Stwierdzeń w tym kontekście].

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:

Nie ma wątpliwości, że Graal wydajności prowadzi do nadużyć. Programiści marnują ogromne ilości czasu na myślenie lub martwienie się o szybkość niekrytycznych części swoich programów, a te próby zwiększenia wydajności mają w rzeczywistości silny negatywny wpływ na debugowanie i konserwację. Powinniśmy zapomnieć o małych wydajnościach, powiedzmy w 97% przypadków; przedwczesna optymalizacja jest źródłem wszelkiego zła.

... a następnie trochę więcej o znaczeniu narzędzi do profilowania:

Często błędem jest dokonywanie ocen a priori na temat tego, które części programu są naprawdę krytyczne, ponieważ z uniwersalnego doświadczenia programistów korzystających z narzędzi pomiarowych wynika, że ​​ich intuicyjne przypuszczenia zawodzą. Po siedmiu latach pracy z takimi narzędziami doszedłem do przekonania, że ​​wszystkie pisane od tej pory kompilatory powinny być zaprojektowane tak, aby zapewnić wszystkim programistom informację zwrotną wskazującą, które części ich programów kosztują najwięcej; w rzeczywistości ta informacja zwrotna powinna być dostarczana automatycznie, chyba że została specjalnie wyłączona.

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 gotow 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
1

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ę.

PolyThinker
źródło
1

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.

Daniel Auger
źródło
1

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.

Kamil Kisiel
źródło
0

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
0

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.

frankodwyer
źródło