Mój kolega dzisiaj zaangażował klasę o nazwie ThreadLocalFormat
, która zasadniczo przeniosła instancje klas formatu Java do lokalnego wątku, ponieważ nie są one bezpieczne dla wątków i „stosunkowo drogie” do utworzenia. Napisałem szybki test i obliczyłem, że mogę stworzyć 200 000 instancji na sekundę, zapytałem go, czy tworzy tyle, na które odpowiedział „nigdzie w pobliżu tak wielu”. Jest świetnym programistą i wszyscy w zespole są wysoko wykwalifikowani, więc nie mamy problemu ze zrozumieniem powstałego kodu, ale najwyraźniej chodziło o optymalizację tam, gdzie nie ma prawdziwej potrzeby. Wycofał kod na moją prośbę. Co myślisz? Czy to przypadek „przedwczesnej optymalizacji” i jak źle jest naprawdę?
design
optimization
architecture
Craig Day
źródło
źródło
Odpowiedzi:
Należy pamiętać o pełnej wycenie:
Oznacza to, że przy braku mierzonych problemów z wydajnością nie należy optymalizować, ponieważ uważasz , że uzyskasz wzrost wydajności. Istnieją oczywiste optymalizacje (takie jak nie wykonywanie konkatenacji łańcuchów w ciasnej pętli), ale należy unikać wszystkiego, co nie jest trywialnie optymalizacją, dopóki nie będzie można tego zmierzyć.
Największe problemy związane z „przedwczesną optymalizacją” polegają na tym, że może ona wprowadzać nieoczekiwane błędy i powodować ogromne straty czasu.
źródło
HashSet
zamiast zamiastList
przedwczesnej optymalizacji. Przypadek użycia, o którym mowa, był zbiorem zainicjowanym statycznie, którego jedynym celem było służyć jako tabela przeglądowa. Nie sądzę, że się mylę, mówiąc, że istnieje rozróżnienie w wyborze odpowiedniego narzędzia do pracy w porównaniu z przedwczesną optymalizacją. Myślę, że twój post potwierdza tę filozofię:There are obvious optimizations...anything that isn't trivially clear optimization should be avoided until it can be measured.
optymalizacja HashSet została dokładnie zmierzona i udokumentowana.Set
jest również bardziej semantycznie poprawny i zawiera więcej informacji niż informacjeList
, więc jest w tym coś więcej niż aspekt optymalizacji.Przedwczesne mikrooptymalizacje są źródłem wszelkiego zła, ponieważ mikrooptymalizacje pomijają kontekst. Prawie nigdy nie zachowują się tak, jak się spodziewają.
Jakie są dobre wczesne optymalizacje w kolejności ważności:
Niektóre optymalizacje w średnim cyklu rozwojowym:
Niektóre optymalizacje zakończenia cyklu rozwojowego
Nie wszystkie wczesne optymalizacje są złe, mikrooptymalizacje są złe, jeśli są wykonywane w niewłaściwym czasie w cyklu rozwojowym , ponieważ mogą negatywnie wpływać na architekturę, mogą negatywnie wpływać na początkową produktywność, mogą mieć nieistotny wpływ na wydajność lub nawet mieć szkodliwy efekt na końcu rozwoju z powodu różnych warunków środowiskowych.
Jeśli wydajność ma znaczenie (i zawsze powinna być), zawsze myśl na dużą skalę . Wydajność jest szersza i nie dotyczy takich rzeczy: czy powinienem używać int czy long ? Przejdź do góry w dół podczas pracy z wydajnością zamiast od dołu do góry .
źródło
optymalizacja bez pierwszego pomiaru jest prawie zawsze przedwczesna.
Uważam, że tak jest w tym przypadku, a także w przypadku ogólnym.
źródło
Optymalizacja jest „zła”, jeśli powoduje:
W twoim przypadku wydaje się, że trochę czasu programisty już spędzono, kod nie był zbyt skomplikowany (zgadnij z twojego komentarza, że wszyscy w zespole byliby w stanie zrozumieć), a kod jest nieco bardziej przyszłościowy (jest wątek bezpieczny teraz, jeśli zrozumiałem twój opis). Brzmi jak tylko trochę zła. :)
źródło
Dziwi mnie, że to pytanie ma 5 lat, a jednak nikt nie opublikował więcej tego, co powiedział Knuth, niż kilka zdań. Kilka akapitów otaczających słynny cytat wyjaśnia to całkiem dobrze. Cytowany artykuł nazywa się „ Programowanie strukturalne z przejściem do instrukcji ” i choć ma prawie 40 lat, dotyczy kontrowersji i ruchu programowego, który już nie istnieje i zawiera przykłady języków programowania, których wielu ludzi nigdy nie znało słyszałem, że zaskakująco duża część tego, co zostało powiedziane, nadal obowiązuje.
Oto większy cytat (ze strony 8 pliku pdf, strona 268 w oryginale):
Kolejny dobry kawałek z poprzedniej strony:
źródło
Często widziałem ten cytat usprawiedliwiający oczywisty zły kod lub kod, który, choć nie został zmierzony jego wydajności, prawdopodobnie mógłby zostać przyspieszony dość łatwo, bez zwiększania rozmiaru kodu lub pogarszania jego czytelności.
Ogólnie uważam, że wczesne mikrooptymalizacje mogą być złym pomysłem. Jednak optymalizacje makr (takie jak wybór algorytmu O (log N) zamiast O (N ^ 2)) są często opłacalne i powinny być wykonane wcześnie, ponieważ pisanie algorytmu O (N ^ 2) i marnotrawstwem może być następnie wyrzuć to całkowicie na korzyść podejścia O (log N).
Zauważ, że słowa mogą być następujące : jeśli algorytm O (N ^ 2) jest prosty i łatwy do napisania, możesz go wyrzucić później bez większego poczucia winy, jeśli okaże się on zbyt wolny. Ale jeśli oba algorytmy są podobnie złożone lub jeśli oczekiwane obciążenie jest tak duże, że wiesz, że będziesz potrzebować szybszego, wtedy optymalizacja na wczesnym etapie jest rozsądną decyzją inżynieryjną, która zmniejszy całkowite obciążenie w dłuższej perspektywie.
Ogólnie rzecz biorąc, myślę, że właściwym podejściem jest sprawdzenie, jakie masz opcje, zanim zaczniesz pisać kod i świadomie wybranie najlepszego algorytmu dla twojej sytuacji. Co najważniejsze, zwrot „przedwczesna optymalizacja jest źródłem wszelkiego zła” nie usprawiedliwia ignorancji. Programiści powinni mieć ogólne wyobrażenie o koszcie typowych operacji; powinni wiedzieć na przykład
Ponadto programiści powinni zapoznać się z zestawem struktur danych i algorytmów, aby mogli łatwo korzystać z odpowiednich narzędzi do danego zadania.
Posiadanie dużej wiedzy i osobistego zestawu narzędzi umożliwia optymalizację niemal bez wysiłku. Wkładanie wielu wysiłków w optymalizację, która może być niepotrzebna, jest złem (i przyznaję, że wpadłem w tę pułapkę więcej niż raz). Ale jeśli optymalizacja jest tak prosta, jak wybranie zbioru / tablicy mieszającej zamiast tablicy lub przechowywanie listy liczb w podwójnym [] zamiast ciągu [], to dlaczego nie? Może nie zgadzam się z Knuthem tutaj, nie jestem pewien, ale myślę, że mówił o optymalizacji niskiego poziomu, podczas gdy mówię o optymalizacji wysokiego poziomu.
Pamiętaj, że ten cytat pochodzi z 1974 roku. W 1974 roku komputery działały wolno, a moc obliczeniowa była droga, co dało niektórym programistom tendencję do nadmiernej optymalizacji, linia po linii. Myślę, że właśnie temu naciskał Knuth. Nie mówił „wcale nie przejmuj się wydajnością”, ponieważ w 1974 roku to byłaby szalona rozmowa. Knuth wyjaśniał, jak zoptymalizować; w skrócie, należy skupić się tylko na wąskich gardłach, a zanim to zrobisz, musisz wykonać pomiary, aby znaleźć wąskie gardła.
Zauważ, że nie możesz znaleźć wąskich gardeł, dopóki nie napiszesz programu do pomiaru, co oznacza, że niektóre decyzje dotyczące wydajności muszą zostać podjęte, zanim cokolwiek zostanie zmierzone. Czasami decyzje te trudno zmienić, jeśli się pomylisz. Z tego powodu dobrze jest mieć ogólny obraz tego, co kosztuje, abyś mógł podejmować rozsądne decyzje, gdy żadne twarde dane nie są dostępne.
To, jak wcześnie zoptymalizować i jak bardzo martwić się o wydajność, zależy od zadania. Pisząc skrypty, które uruchomisz tylko kilka razy, martwienie się o wydajność zwykle jest kompletną stratą czasu. Ale jeśli pracujesz dla Microsoft lub Oracle i pracujesz nad biblioteką, z której tysiące innych programistów będą korzystać na tysiące różnych sposobów, może opłacić się optymalizacja, abyś mógł pokryć wszystkie różnorodne efektywnie wykorzystuj skrzynki. Mimo to potrzeba wydajności musi być zawsze zrównoważona z potrzebą czytelności, łatwości konserwacji, elegancji, rozszerzalności i tak dalej.
źródło
Osobiście, jak opisano w poprzednim wątku , nie sądzę, aby wczesna optymalizacja była zła w sytuacjach, w których wiesz, że napotkasz problemy z wydajnością. Na przykład piszę oprogramowanie do modelowania i analizy powierzchni, w którym regularnie mam do czynienia z dziesiątkami milionów podmiotów. Planowanie optymalnej wydajności na etapie projektowania jest znacznie lepsze niż późna optymalizacja słabej konstrukcji.
Inną kwestią do rozważenia jest skalowanie aplikacji w przyszłości. Jeśli uważasz, że Twój kod będzie miał długą żywotność, dobrym pomysłem jest również optymalizacja wydajności na etapie projektowania.
Z mojego doświadczenia wynika, że późna optymalizacja zapewnia skromne nagrody w wysokiej cenie. Optymalizacja na etapie projektowania, poprzez wybór algorytmu i dostosowanie, jest znacznie lepsza. W zależności od narzędzia do profilowania, aby zrozumieć, jak działa Twój kod, nie jest to świetny sposób na uzyskanie kodu o wysokiej wydajności, powinieneś o tym wiedzieć wcześniej.
źródło
W rzeczywistości dowiedziałem się, że przedwczesna nieoptymalizacja jest częściej źródłem wszelkiego zła.
Kiedy ludzie piszą oprogramowanie, początkowo będą mieć problemy, takie jak niestabilność, ograniczone funkcje, zła użyteczność i zła wydajność. Wszystkie te zwykle są naprawiane, gdy oprogramowanie dojrzewa.
Wszystkie z wyjątkiem wydajności. Wydaje się, że nikomu nie zależy na wydajności. Powód jest prosty: jeśli oprogramowanie ulegnie awarii, ktoś naprawi błąd i to wszystko, jeśli brakuje funkcji, ktoś ją zaimplementuje i zrobi, jeśli oprogramowanie ma niską wydajność, w wielu przypadkach nie wynika to z braku mikrooptymalizacji, ale z powodu złego projektu i nikt nie będzie dotykać projektu oprogramowania. ZAWSZE.
Spójrz na Bochsa. Jest powolny jak diabli. Czy kiedykolwiek będzie szybciej? Może, ale tylko w zakresie kilku procent. Nigdy nie uzyska wydajności porównywalnej z oprogramowaniem do wirtualizacji, takim jak VMWare lub VBox, a nawet QEMU. Ponieważ z założenia jest powolny!
Jeśli problem oprogramowania polega na tym, że jest on powolny, to dlatego, że jest on BARDZO powolny i można to naprawić tylko poprzez poprawę wydajności o wiele. + 10% po prostu nie przyspieszy działania wolnego oprogramowania. Późniejsze optymalizacje zwykle nie przyniosą więcej niż 10%.
Więc jeśli wydajność jest JAKIEKOLWIEK ważna dla twojego oprogramowania, powinieneś wziąć to pod uwagę od samego początku, projektując je, zamiast myśleć „och tak, jest powolne, ale możemy to poprawić później”. Ponieważ nie możesz!
Wiem, że tak naprawdę nie pasuje to do twojego konkretnego przypadku, ale odpowiada na ogólne pytanie: „Czy przedwczesna optymalizacja naprawdę jest źródłem wszelkiego zła?” - z wyraźnym NIE.
Każda optymalizacja, jak każda funkcja itp. Musi być starannie zaprojektowana i starannie wdrożona. Obejmuje to właściwą ocenę kosztów i korzyści. Nie optymalizuj algorytmu, aby zaoszczędzić kilka cykli tu i tam, gdy nie spowoduje to wymiernego wzrostu wydajności.
Na przykład: możesz poprawić wydajność funkcji, wprowadzając ją, prawdopodobnie oszczędzając garść cykli, ale jednocześnie prawdopodobnie zwiększysz rozmiar pliku wykonywalnego, zwiększając szanse na brak TLB i pamięć podręczną kosztującą tysiące cykli, a nawet operacje stronicowania, które całkowicie zabijają wydajność. Jeśli nie rozumiesz tych rzeczy, Twoja „optymalizacja” może okazać się zła.
Głupia optymalizacja jest bardziej zła niż optymalizacja „przedwczesna”, ale obie są jeszcze lepsze niż przedwczesna nieoptymalizacja.
źródło
Istnieją dwa problemy z PO: po pierwsze, czas programowania jest wykorzystywany do nieistotnej pracy, którą można wykorzystać do napisania większej liczby funkcji lub naprawienia większej liczby błędów, a po drugie, fałszywego poczucia bezpieczeństwa, że kod działa skutecznie. PO często wiąże się z optymalizacją kodu, który nie będzie wąskim gardłem, a jednocześnie nie zauważa kodu, który to zrobi. Bit „przedwczesny” oznacza, że optymalizacja jest wykonywana przed zidentyfikowaniem problemu za pomocą odpowiednich pomiarów.
Zasadniczo tak, to brzmi jak przedwczesna optymalizacja, ale niekoniecznie musiałbym ją wycofać, chyba że wprowadzi błędy - w końcu został zoptymalizowany teraz (!)
źródło
Uważam, że to, co Mike Cohn nazywa „pozłacaniem” kodu - tj. Spędzanie czasu na rzeczach, które mogą być fajne, ale nie są konieczne.
Odradzał to.
PS „Pozłacanie” może być swoistą funkcjonalnością. Kiedy patrzysz na kod, przyjmuje on formę niepotrzebnej optymalizacji, „przyszłościowych” klas itp.
źródło
Ponieważ nie ma problemu ze zrozumieniem kodu, ten przypadek można uznać za wyjątek.
Ale ogólnie optymalizacja prowadzi do mniej czytelnego i mniej zrozumiałego kodu i powinna być stosowana tylko w razie potrzeby. Prosty przykład - jeśli wiesz, że musisz posortować tylko kilka elementów - skorzystaj z BubbleSort. Ale jeśli podejrzewasz, że liczba elementów może wzrosnąć, a nie wiesz ile, optymalizacja za pomocą QuickSort (na przykład) nie jest zła, ale musi. I to należy wziąć pod uwagę podczas projektowania programu.
źródło
Przekonałem się, że problem z przedwczesną optymalizacją występuje najczęściej, gdy przepisywanie istniejącego kodu jest szybsze. Widzę, jak może być problemem napisanie zawiłej optymalizacji w pierwszej kolejności, ale przede wszystkim widzę, że przedwczesna optymalizacja podnosi brzydką głowę w naprawianiu tego, co nie jest (znane).
A najgorszym tego przykładem jest sytuacja, w której widzę, jak ktoś ponownie implementuje funkcje ze standardowej biblioteki. To jest główna czerwona flaga. Na przykład widziałem kiedyś, jak ktoś implementuje niestandardowe procedury manipulacji ciągami, ponieważ martwił się, że wbudowane polecenia są zbyt wolne.
Powoduje to, że kod jest trudniejszy do zrozumienia (źle) i powoduje spalenie dużej ilości czasu na pracę, która prawdopodobnie nie jest przydatna (zła).
źródło
Z innej perspektywy, z mojego doświadczenia wynika, że większość programistów / programistów nie planuje sukcesu, a „prototyp” to prawie zawsze wersja 1.0. Mam doświadczenie z pierwszej ręki z 4 oddzielnymi oryginalnymi produktami, w których elegancki, seksowny i wysoce funkcjonalny interfejs (w zasadzie interfejs użytkownika) zaowocował szerokim przyjęciem użytkowników i entuzjazmem. W każdym z tych produktów problemy z wydajnością zaczęły wkradać się w stosunkowo krótkim czasie (1–2 lata), zwłaszcza gdy duzi, bardziej wymagający klienci zaczęli wprowadzać ten produkt. Bardzo szybko wydajność zdominowała listę problemów, chociaż rozwój nowych funkcji zdominował listę priorytetów zarządzania. Klienci byli coraz bardziej sfrustrowani, gdy każde wydanie zawierało nowe funkcje, które brzmiały świetnie, ale były prawie niedostępne z powodu problemów z wydajnością.
Tak więc bardzo fundamentalne wady w projektowaniu i wdrażaniu, które nie były obojętne w „typie prototypu”, stały się poważnymi przeszkodami dla długoterminowego sukcesu produktów (i firm).
Demo klienta może wyglądać i działać świetnie na twoim laptopie dzięki DOM DOM XML, SQL Express i dużej ilości buforowanych danych po stronie klienta. System produkcyjny prawdopodobnie spowoduje awarię, jeśli ci się powiedzie.
W 1976 r. Wciąż debatowaliśmy nad optymalnymi sposobami obliczania pierwiastka kwadratowego lub sortowania dużej tablicy, a przysłowie Dona Knutha skierowane było na błąd polegający na skupieniu się na optymalizacji tego rodzaju rutynowych działań na wczesnym etapie procesu projektowania, a nie na rozwiązaniu problemu a następnie optymalizowanie zlokalizowanych regionów kodu.
Kiedy ktoś powtarza powiedzenie jako wymówkę, by nie pisać wydajnego kodu (C ++, VB, T-SQL lub w inny sposób), albo nie projektować właściwie magazynu danych, albo nie brać pod uwagę architektury sieciowej, wtedy IMO po prostu demonstruje bardzo płytkie zrozumienie prawdziwej natury naszej pracy. Promień
źródło
Przypuszczam, że zależy to od tego, jak zdefiniujesz „przedwczesny”. Szybkie działanie funkcji niskiego poziomu podczas pisania nie jest z natury złe. Myślę, że to nieporozumienie z cytatem. Czasami myślę, że ten cytat może przydać się w przypadku pewnych kwalifikacji. Chciałbym jednak powtórzyć komentarze m_pGladiatora dotyczące czytelności.
źródło
Odpowiedź brzmi: to zależy. Będę argumentować, że wydajność jest bardzo ważna w przypadku niektórych rodzajów pracy, takich jak złożone zapytania do bazy danych. W wielu innych przypadkach komputer spędza większość czasu, czekając na dane wejściowe od użytkownika, dlatego optymalizacja większości kodu jest w najlepszym wypadku stratą wysiłku, aw najgorszym przypadku przynosi efekt przeciwny do zamierzonego.
W niektórych przypadkach możesz zaprojektować wydajność lub wydajność (postrzeganą lub rzeczywistą) - wybierając odpowiedni algorytm lub projektując interfejs użytkownika, aby na przykład niektóre kosztowne operacje zachodziły w tle. W wielu przypadkach profilowanie lub inne operacje mające na celu określenie hotspotów przynoszą korzyść 10/90.
Jednym z przykładów tego, co mogę opisać, jest model danych, który kiedyś stworzyłem dla systemu zarządzania sprawami sądowymi, który zawierał około 560 tabel. Zaczęło się znormalizować („pięknie znormalizowana”, jak to ujęli konsultant z pewnej dużej firmy 5) i musieliśmy w niej umieścić tylko cztery elementy zdormalizowanych danych:
Jeden zmaterializowany widok do obsługi ekranu wyszukiwania
Jedna tabela obsługiwana przez wyzwalacze do obsługi innego ekranu wyszukiwania, którego nie można było wykonać w widoku zmaterializowanym.
Jedna zdenormalizowana tabela raportowania (istniała tylko dlatego, że musieliśmy wziąć na siebie niektóre raporty przepustowości, gdy projekt hurtowni danych został zapełniony)
Jedna tabela utrzymywana przez wyzwalacz dla interfejsu, który musiał wyszukiwać najnowsze z dość dużej liczby różnych zdarzeń w systemie.
Był to (w tym czasie) największy projekt J2EE w Australii - znacznie ponad 100 lat czasu programisty - i zawierał 4 zdormalizowane elementy w schemacie bazy danych, z których jeden tak naprawdę w ogóle tam nie należał.
źródło
Przedwczesna optymalizacja nie jest źródłem CAŁEGO zła, to na pewno. Są jednak wady:
Zamiast przedwczesnej optymalizacji można wykonać wczesne testy widoczności, aby sprawdzić, czy istnieje rzeczywista potrzeba lepszej optymalizacji.
źródło
Większość osób, które stosują się do „PMO” (czyli częściowego cytatu), twierdzi, że optymalizacje muszą opierać się na pomiarach, a pomiarów nie można wykonać aż do samego końca.
Z moich doświadczeń związanych z opracowywaniem dużych systemów wynika również, że testy wydajności przeprowadzane są na samym końcu, w miarę jak programowanie dobiega końca.
Gdybyśmy zastosowali się do „rad” tych ludzi, wszystkie systemy byłyby niezwykle powolne. Byłyby również drogie, ponieważ ich potrzeby sprzętowe są znacznie większe niż pierwotnie przewidywano.
Od dawna zalecałem przeprowadzanie testów wydajności w regularnych odstępach czasu w procesie programowania: będzie to oznaczać zarówno obecność nowego kodu (tam, gdzie wcześniej go nie było), jak i stan istniejącego kodu.
Innym pomysłem zwierzaka jest oprogramowanie oprogramowania na poziomie bloku funkcyjnego. Podczas wykonywania system zbiera informacje o czasach wykonania bloków funkcyjnych. Po przeprowadzeniu aktualizacji systemu można określić, które bloki funkcyjne działają tak, jak we wcześniejszej wersji, oraz te, które uległy pogorszeniu. Na ekranie oprogramowania można uzyskać dostęp do danych dotyczących wydajności z menu pomocy.
Sprawdź ten znakomity artykuł na temat tego, co PMO może, ale nie musi oznaczać.
źródło