Zwykle nienawidzę słowa „przedwczesna optymalizacja”, ale to cuchnie. Warto zauważyć, że Knuth użył tego słynnego cytatu w kontekście naciskania na użycie goto
instrukcji w celu przyspieszenia kodu w krytycznych obszarach. To jest klucz: ścieżki krytyczne .
Sugerował użycie go goto
do przyspieszenia kodu, ale ostrzega przed programistami, którzy chcieliby robić tego rodzaju rzeczy w oparciu o przeczucia i przesądy dla kodu, który nawet nie jest krytyczny.
Faworyzowanie switch
instrukcji w jak największym stopniu jednolicie w całej bazie kodu (niezależnie od tego, czy obsługiwane jest duże obciążenie) jest klasycznym przykładem tego, co Knuth nazywa programistą „rozsądnym i głupim”, który spędza cały dzień walcząc o utrzymanie swojego „zoptymalizowanego” "kod, który zamienił się w koszmar debugowania w wyniku próby zaoszczędzenia groszy na kilogramach. Taki kod rzadko jest łatwy do utrzymania, a tym bardziej wydajny.
Czy on ma rację?
Ma rację z bardzo podstawowej perspektywy wydajności. Według mojej wiedzy żaden kompilator nie jest w stanie zoptymalizować kodu polimorficznego obejmującego obiekty i dynamiczne wysyłanie lepiej niż instrukcja switch. Nigdy nie skończysz z LUT lub tabelą skoków do kodu wstawionego z kodu polimorficznego, ponieważ taki kod zwykle służy jako bariera optymalizatora dla kompilatora (nie będzie wiedział, którą funkcję wywołać do czasu, w którym dynamiczna wysyłka występuje).
Bardziej przydatne jest nie myśleć o tym koszcie w kategoriach tabel skoków, ale bardziej w kategoriach bariery optymalizacji. W przypadku polimorfizmu wywołanie Base.method()
nie pozwala kompilatorowi wiedzieć, która funkcja zostanie ostatecznie wywołana, jeśli method
jest wirtualna, nie jest zapieczętowana i może zostać zastąpiona. Ponieważ nie wie, która funkcja zostanie wywołana z wyprzedzeniem, nie może zoptymalizować wywołania funkcji i wykorzystać więcej informacji przy podejmowaniu decyzji optymalizacyjnych, ponieważ tak naprawdę nie wie, która funkcja zostanie wywołana czas kompilacji kodu.
Optymalizatory są w najlepszym momencie, gdy mogą zajrzeć do wywołania funkcji i dokonać optymalizacji, które albo całkowicie spłaszczą rozmówcę i odbiorcę, albo przynajmniej zoptymalizują rozmówcę, aby najskuteczniej współpracować z odbiorcą. Nie mogą tego zrobić, jeśli nie wiedzą, która funkcja zostanie wcześniej wywołana.
Czy on tylko mówi swój tyłek?
Wykorzystanie tego kosztu, który często wynosi grosze, w celu uzasadnienia przekształcenia go w jednolity standard kodowania jest ogólnie bardzo głupie, szczególnie w miejscach, które wymagają rozszerzenia. To jest najważniejsza rzecz, na którą należy zwrócić uwagę w przypadku oryginalnych przedwczesnych optymalizatorów: chcą przekształcić niewielkie problemy z wydajnością w standardy kodowania stosowane jednolicie w całej bazie kodu, bez względu na łatwość konserwacji.
Obrażam trochę cytat „stary haker C” użyty w przyjętej odpowiedzi, ponieważ jestem jednym z nich. Nie każdy, kto programuje od dziesięcioleci, poczynając od bardzo ograniczonego sprzętu, zmienił się w przedwczesny optymalizator. Ale ja też z nimi spotkałem. Ale te typy nigdy nie mierzą rzeczy takich jak nieprzewidywalność gałęzi lub bufory pamięci podręcznej, myślą, że wiedzą lepiej, i opierają swoje pojęcia nieefektywności w złożonej bazie kodu produkcyjnego opartej na przesądach, które nie są prawdziwe dzisiaj, a czasem nigdy nie są prawdziwe. Ludzie, którzy naprawdę pracowali w obszarach krytycznych pod względem wydajności, często rozumieją, że skuteczna optymalizacja jest skutecznym ustalaniem priorytetów, a próba uogólnienia standardu kodowania obniżającego łatwość konserwacji, aby zaoszczędzić grosze, jest bardzo nieefektywna.
Grosze są ważne, gdy masz tanią funkcję, która nie wykonuje tyle pracy, co nazywa się miliard razy w bardzo ciasnej, krytycznej dla wydajności pętli. W takim przypadku ostatecznie oszczędzamy 10 milionów dolarów. Nie warto golić groszy, gdy masz funkcję wywoływaną dwa razy, dla której samo ciało kosztuje tysiące dolarów. Nie jest rozsądnie spędzać czas na targowaniu się o grosze podczas zakupu samochodu. Warto targować się o grosze, jeśli kupujesz milion puszek sody od producenta. Kluczem do skutecznej optymalizacji jest zrozumienie tych kosztów we właściwym kontekście. Ktoś, kto próbuje zaoszczędzić grosze przy każdym zakupie i sugeruje, że wszyscy próbują targować się o grosze bez względu na to, co kupują, nie jest wykwalifikowanym optymistą.