Wszyscy wiemy, że przedwczesna optymalizacja jest źródłem wszelkiego zła, ponieważ prowadzi do nieczytelnego / niemożliwego do utrzymania kodu. Jeszcze gorsza jest pesymizacja, kiedy ktoś wdraża „optymalizację”, ponieważ uważa, że będzie szybsza, ale w końcu będzie wolniejsza, jak również buggy, nie w utrzymaniu itp. Jaki jest najbardziej śmieszny przykład tego, jaki widzieliście ?
performance
optimization
dsimcha
źródło
źródło
Odpowiedzi:
W starym projekcie odziedziczyliśmy kilku (skądinąd znakomitych) programistów systemów wbudowanych, którzy mieli ogromne doświadczenie z Z-8000.
Naszym nowym środowiskiem był 32-bitowy Sparc Solaris.
Jeden z facetów poszedł i zmienił wszystkie int na krótkie, aby przyspieszyć nasz kod, ponieważ pobranie 16 bitów z pamięci RAM było szybsze niż pobranie 32 bitów.
Musiałem napisać program demonstracyjny, aby pokazać, że pobieranie wartości 32-bitowych w systemie 32-bitowym jest szybsze niż pobieranie wartości 16-bitowych, i wyjaśnić, że aby pobrać wartość 16-bitową, procesor musiał zrobić 32-bitową szerokość dostęp do pamięci, a następnie zamaskowanie lub przesunięcie bitów niepotrzebnych dla wartości 16-bitowej.
źródło
Myślę, że określenie „przedwczesna optymalizacja jest źródłem wszelkiego zła” jest często używane. W przypadku wielu projektów wymówką stało się branie pod uwagę wyników aż do późnej fazy projektu.
To zdanie jest często podpórką dla ludzi, aby uniknąć pracy. Widzę, że to wyrażenie jest używane, gdy ludzie powinni naprawdę powiedzieć: „Ojej, naprawdę nie pomyśleliśmy o tym z góry i nie mamy czasu, aby się tym zająć teraz”.
Widziałem znacznie więcej „śmiesznych” przykładów głupich problemów z wydajnością niż przykłady problemów wprowadzonych z powodu „pesymizacji”
Myślę, że lepszym stwierdzeniem jest to: „optymalizacja bez pomiaru i zrozumienia wcale nie jest optymalizacją - to tylko przypadkowa zmiana”.
Praca z dobrą wydajnością jest czasochłonna - często bardziej niż samo opracowanie funkcji lub komponentu.
źródło
Bazy danych to playlista pesymizacji.
Ulubione obejmują:
To jest poza moją głową.
źródło
Myślę, że nie ma absolutnej reguły: niektóre rzeczy najlepiej zoptymalizować z góry, a inne nie.
Na przykład pracowałem w firmie, w której otrzymywaliśmy pakiety danych z satelitów. Każdy pakiet kosztuje dużo pieniędzy, więc wszystkie dane były wysoce zoptymalizowane (tj. Spakowane). Na przykład szerokość / długość geograficzna nie została wysłana jako wartości bezwzględne (zmiennoprzecinkowe), ale jako przesunięcia względem „północno-zachodniego” narożnika „bieżącej” strefy. Musieliśmy rozpakować wszystkie dane, zanim mogły zostać użyte. Ale myślę, że to nie jest pesymizacja, to inteligentna optymalizacja w celu zmniejszenia kosztów komunikacji.
Z drugiej strony nasi architekci oprogramowania zdecydowali, że rozpakowane dane powinny zostać sformatowane w bardzo czytelny dokument XML i zapisane w naszej bazie danych jako takie (w przeciwieństwie do przechowywania każdego pola w odpowiedniej kolumnie). Ich pomysł był taki, że „XML to przyszłość”, „miejsce na dysku jest tanie” i „procesor jest tani”, więc nie było potrzeby niczego optymalizować. W rezultacie nasze 16-bajtowe pakiety zostały zamienione na 2kB dokumenty przechowywane w jednej kolumnie, a nawet dla prostych zapytań musieliśmy załadować megabajty dokumentów XML do pamięci! Otrzymywaliśmy ponad 50 pakietów na sekundę, więc możesz sobie wyobrazić, jak potworna stała się wydajność (przy okazji, firma zbankrutowała).
Więc znowu, nie ma absolutnej reguły. Tak, czasami optymalizacja zbyt wczesna jest błędem. Ale czasami motto „procesor / przestrzeń dyskowa / pamięć jest tania” jest prawdziwym źródłem wszelkiego zła.
źródło
O dobry Boże, myślę, że widziałem je wszystkie. Najczęściej jest to próba naprawienia problemów z wydajnością przez kogoś, kto jest cholernie leniwy, aby znaleźć przyczynę tych problemów z wydajnością, a nawet zbadanie, czy rzeczywiście występuje problem z wydajnością. W wielu z tych przypadków zastanawiam się, czy nie jest to tylko przypadek osoby, która chce wypróbować konkretną technologię i rozpaczliwie szuka gwoździa pasującego do jej błyszczącego nowego młotka.
Oto ostatni przykład:
Architekt danych przychodzi do mnie z wyszukaną propozycją pionowego podziału tabeli kluczy w dość dużej i złożonej aplikacji. Chce wiedzieć, jakiego rodzaju wysiłki rozwojowe byłyby konieczne, aby dostosować się do zmiany. Rozmowa przebiegała tak:
Ja: Dlaczego to rozważasz? Jaki problem próbujesz rozwiązać?
On: Tabela X jest zbyt szeroka, dzielimy ją na partycje ze względu na wydajność.
Ja: Dlaczego myślisz, że jest za szeroka?
On: Konsultant powiedział, że to o wiele za dużo kolumn w jednej tabeli.
Ja: A to wpływa na wydajność?
On: Tak, użytkownicy zgłaszali sporadyczne spowolnienia w module XYZ aplikacji.
Ja: Skąd wiesz, że szerokość stołu jest źródłem problemu?
On: To jest tabela kluczy używana przez moduł XYZ i ma około 200 kolumn. To musi być problem.
Ja (wyjaśnienie): Ale moduł XYZ w szczególności wykorzystuje większość kolumn w tej tabeli, a kolumny, których używa, są nieprzewidywalne, ponieważ użytkownik konfiguruje aplikację tak, aby wyświetlała dane, które chcą wyświetlić z tej tabeli. Jest prawdopodobne, że w 95% przypadków i tak połączylibyśmy wszystkie stoły razem, co zaszkodziłoby wydajności.
On: Konsultant powiedział, że jest za szeroki i musimy to zmienić.
Ja: Kim jest ten konsultant? Nie wiedziałem, że zatrudniliśmy konsultanta, ani w ogóle nie rozmawiali z zespołem programistów.
On: Cóż, jeszcze ich nie zatrudniliśmy. Jest to część propozycji, którą zaoferowali, ale nalegali, abyśmy ponownie zaprojektowali tę bazę danych.
Ja: Uh huh. Dlatego konsultant, który sprzedaje usługi ponownego projektowania baz danych, uważa, że potrzebujemy przeprojektowania bazy danych ....
Rozmowa trwała i trwała w ten sposób. Następnie ponownie przyjrzałem się tej tabeli i stwierdziłem, że prawdopodobnie można by ją zawęzić za pomocą prostej normalizacji bez potrzeby stosowania egzotycznych strategii partycjonowania. Okazało się to oczywiście kwestią sporną, gdy zbadałem problemy z wydajnością (wcześniej niezgłoszone) i wyśledziłem je według dwóch czynników:
Oczywiście architekt nadal naciska na pionowe podzielenie stołu zawieszonego na „zbyt szerokim” metaproblem. Potwierdził nawet swoją argumentację, otrzymując propozycję od innego konsultanta ds. Baz danych, który był w stanie określić, że potrzebujemy poważnych zmian projektowych w bazie danych bez patrzenia na aplikację lub przeprowadzania jakiejkolwiek analizy wydajności.
źródło
Widziałem ludzi używających alphadrive-7 do całkowitej inkubacji CHX-LT. To rzadka praktyka. Bardziej powszechną praktyką jest inicjowanie transformatora ZT w celu zmniejszenia buforowania (ze względu na większą odporność na przeciążenie sieci) i tworzenie bajtegraphication w stylu Java.
Całkowicie pesymistyczny!
źródło
Przyznaję, że nic nie wstrząsałoby Ziemią, ale przyłapałem ludzi na używaniu StringBuffer do łączenia ciągów znaków poza pętlą w Javie. To było coś prostego, jak skręcanie
w
Kiedyś dość powszechną praktyką było stosowanie tej techniki w pętli, ponieważ była ona mierzalnie szybsza. Chodzi o to, że StringBuffer jest zsynchronizowany, więc w rzeczywistości istnieje dodatkowe obciążenie, jeśli łączysz tylko kilka ciągów znaków. (Nie wspominając o tym, że różnica jest absolutnie trywialna w tej skali.) Dwie inne kwestie dotyczące tej praktyki:
źródło
Kiedyś zobaczyłem bazę danych MSSQL, która korzystała z tabeli „Root”. Tabela Root miała cztery kolumny: GUID (uniqueidentifier), ID (int), LastModDate (datetime) i CreateDate (datetime). Wszystkie tabele w bazie danych były kluczami obcymi do tabeli głównej. Ilekroć nowy wiersz został utworzony w dowolnej tabeli w bazie danych, trzeba było użyć kilku procedur składowanych, aby wstawić wpis do tabeli głównej, zanim można było przejść do rzeczywistej tabeli, na której nam zależało (zamiast bazy danych wykonującej zadanie z kilkoma prostymi wyzwalaczami).
To stworzyło bałagan bezużytecznych podsłuchów i bólów głowy, wymagało wszystkiego napisanego na wierzchu, aby użyć sprocesów (i wyeliminowało moje nadzieje na wprowadzenie LINQ do firmy. Było to możliwe, ale po prostu nie warte bólu głowy), a na dodatek nie. nawet nie osiągnąć tego, co do niego należało.
Deweloper, który wybrał tę ścieżkę, bronił jej, zakładając, że zaoszczędziło to mnóstwo miejsca, ponieważ nie używaliśmy Guidów na samych tabelach (ale ... czy GUID nie jest generowany w tabeli głównej dla każdego tworzonego przez nas wiersza?) , w jakiś sposób poprawiło wydajność i ułatwiło audyt zmian w bazie danych.
Aha, i diagram bazy danych wyglądał jak zmutowany pająk z piekła rodem.
źródło
A co z POBI - pesymizacją oczywiście przez zamiar?
Mój kolega w latach 90. był zmęczony kopaniem w dupę przez dyrektora generalnego tylko dlatego, że pierwszy dzień wydania każdego oprogramowania ERP (niestandardowego) spędził na lokalizowaniu problemów z wydajnością w nowych funkcjach. Nawet jeśli nowe funkcje zajmowały gigabajty i sprawiały, że niemożliwe stało się możliwe, zawsze znajdował jakiś szczegół, a nawet pozornie poważny problem, na który mógł narzekać. Wierzył, że dużo wie o programowaniu i dał się we znaki skopiąc tyłki programistom.
Ze względu na niekompetentny charakter krytyki (był dyrektorem generalnym, a nie informatykiem), mojemu koledze nigdy nie udało się zrobić tego dobrze. Jeśli nie masz problemu z wydajnością, nie możesz go wyeliminować ...
Aż do jednego wydania, umieścił wiele wywołań funkcji Delay (200) (było to Delphi) w nowym kodzie. Zajęło to zaledwie 20 minut po uruchomieniu i kazano mu stawić się w biurze dyrektora generalnego, aby osobiście odebrać zaległe obelgi.
Jedyną niezwykłą rzeczą do tej pory było to, że moi koledzy milczeli, kiedy wracał, uśmiechał się, żartował, wychodził na BigMaca lub dwa, podczas gdy normalnie kopał w stoły, rozpalał się na temat dyrektora generalnego i firmy, a resztę dnia spędził odrzucając na śmierć. .
Oczywiście mój kolega odpoczywał teraz przez jeden lub dwa dni przy biurku, doskonaląc swoje umiejętności celowania w Quake - następnie drugiego lub trzeciego dnia usunął wezwania do opóźnienia, odbudował i udostępnił „awaryjną łatkę”, o której rozpowszechnił informację że spędził 2 dni i 1 noc, aby naprawić dziury w wydajności.
To był pierwszy (i jedyny) raz, kiedy zły prezes powiedział „świetna robota!” do niego. Tylko to się liczy, prawda?
To było prawdziwe POBI.
Ale jest to również rodzaj optymalizacji procesów społecznych, więc jest w 100% w porządku.
Myślę.
źródło
„Niezależność bazy danych”. Oznaczało to brak przechowywanych procesów, wyzwalaczy itp. - nawet żadnych kluczy obcych.
źródło
Najlepsze zastosowanie StringBuildera, jakie kiedykolwiek widziałem.
źródło
Używanie wyrażenia regularnego do dzielenia łańcucha, gdy wystarczy prosty ciąg
źródło
Bardzo późno do tego wątku wiem, ale widziałem to ostatnio:
Wiesz, na wypadek gdyby wartość logiczna miała jakieś dodatkowe wartości ...
źródło
Najgorszym przykładem, jaki przychodzi mi do głowy, jest wewnętrzna baza danych w mojej firmie, zawierająca informacje o wszystkich pracownikach. Otrzymuje co noc aktualizację od HR i ma na wierzchu usługę sieciową ASP.NET. Wiele innych aplikacji używa usługi internetowej do wypełniania takich elementów, jak pola wyszukiwania / rozwijane.
Pesymizm polega na tym, że deweloper pomyślał, że powtarzające się wywołania usługi internetowej byłyby zbyt wolne, aby wykonywać powtarzające się zapytania SQL. Więc co on zrobił? Zdarzenie uruchamiania aplikacji odczytuje całą bazę danych i konwertuje to wszystko na obiekty w pamięci, przechowywane przez czas nieokreślony, aż pula aplikacji zostanie odtworzona. Ten kod był tak wolny, że załadowanie go zajęłoby 15 minut przy mniej niż 2000 pracownikach. Jeśli nieumyślnie odtworzyłeś pulę aplikacji w ciągu dnia, może to zająć 30 minut lub dłużej, ponieważ każde żądanie usługi sieci Web uruchomiłoby wiele równoczesnych ponownych ładowań. Z tego powodu nowi pracownicy nie pojawiliby się w bazie danych pierwszego dnia, w którym utworzono ich konto, i dlatego nie mogliby uzyskać dostępu do większości wewnętrznych aplikacji przez pierwsze kilka dni, kręcąc kciukami.
Drugi poziom pesymizmu polega na tym, że menedżer ds. Rozwoju nie chce go dotykać z obawy przed złamaniem zależnych aplikacji, ale nadal mamy sporadyczne przerwy w działaniu krytycznych aplikacji w całej firmie z powodu złego projektu tak prostego komponentu.
źródło
Wygląda na to, że nikt nie wspomniał o sortowaniu, więc to zrobię.
Kilka różnych razy odkryłem, że ktoś ręcznie stworzył bubbleort, ponieważ sytuacja „nie wymagała” wywołania „zbyt wyszukanego” algorytmu szybkiego sortowania, który już istniał. Deweloper był usatysfakcjonowany, gdy ich ręcznie wykonany bąbelkowy port działał wystarczająco dobrze na dziesięciu wierszach danych, których używają do testów. Nie poszło tak dobrze po dodaniu przez klienta kilku tysięcy wierszy.
źródło
Kiedyś pracowałem nad aplikacją, która była pełna takiego kodu:
Wystarczy usunąć
found
, wrócićnull
na koniec i zmienić szóstą linię na:Podwojono wydajność aplikacji.
źródło
Kiedyś musiałem spróbować zmodyfikować kod, który zawierał te klejnoty w klasie Constants
Każdy z nich był używany wielokrotnie w pozostałej części aplikacji do różnych celów. COMMA_DELIMINATOR zaśmiecił kod ponad 200 zastosowaniami w 8 różnych pakietach.
źródło
Największy numer jeden w historii, z którym wielokrotnie spotykałem się w oprogramowaniu wewnętrznym:
Nie korzystamy z funkcji DBMS z powodów „przenośności”, ponieważ „moglibyśmy chcieć później zmienić dostawcę”.
Przeczytaj moje usta. W przypadku jakichkolwiek prac wewnętrznych: TO NIE SIĘ ZDARZY!
źródło
Miałem współpracownika, który próbował przechytrzyć optymalizator naszego kompilatora C i rutynę przepisał kod, który tylko on mógł odczytać. Jedną z jego ulubionych sztuczek była zmiana czytelnej metody, takiej jak (tworzenie kodu):
zaangażowany w to:
Oznacza to, że pierwsza linia metody, którą można było kiedyś odczytać,
return
zmieniłaby się w „ ”, a cała inna logika została zastąpiona głęboko zagnieżdżonymi wyrażeniami terniarnymi. Kiedy próbowałeś spierać się o to, dlaczego jest to niemożliwe do utrzymania, wskazywał na fakt, że wynik montażu jego metody był o trzy lub cztery instrukcje montażu krótszy. Niekoniecznie było to szybsze, ale zawsze było małe nieco krótszy. Był to system wbudowany, w którym użycie pamięci czasami miało znaczenie, ale można było dokonać znacznie łatwiejszych optymalizacji niż ta, która pozostawiłaby kod czytelny.Następnie, z jakiegoś powodu, uznał, że jest
ptr->structElement
to zbyt nieczytelne, więc zaczął zmieniać to wszystko(*ptr).structElement
na teorię, że jest bardziej czytelny i szybszy.Przekształcanie czytelnego kodu w nieczytelny kod, co najwyżej o 1%, a czasem nawet wolniejszy kod.
źródło
if
. Nacisk na stwierdzenia zamiast wyrażeń w C jest dogmatem kulturowym / religijnym, a nie jakąkolwiek obiektywną praktyką. (Lepsza wskazówka: jeśli zagnieżdżony trójskładnik jest zbyt długi, aby go przeczytać, nie powinieneś go również używaćif
.)if
w funkcji i zastąpieniu go trójskładnikiem. To dobrze i często jest bardziej czytelne. Mówię o zastąpieniu całej metody 30+ linii pojedynczą instrukcją powrotu i zagnieżdżonymi trójkami. Nikt nie sądził, że nowy kod jest bardziej czytelny, ale jeden programista uznał, że jest szybszy.Podczas jednej z moich pierwszych prac jako pełnoprawny programista przejąłem projekt programu, który miał problemy ze skalowaniem. Działałby dość dobrze na małych zestawach danych, ale całkowicie się zawiesiłby przy dużych ilościach danych.
Kiedy zacząłem kopać, odkryłem, że pierwotny programista chciał przyspieszyć działanie poprzez zrównoleglenie analizy - uruchamiając nowy wątek dla każdego dodatkowego źródła danych. Jednak popełnił błąd, polegając na tym, że wszystkie wątki wymagały wspólnego zasobu, na którym były zakleszczone. Oczywiście zniknęły wszystkie zalety współbieżności. Co więcej, powodował awarię większości systemów i uruchamiał ponad 100 wątków tylko po to, aby zablokować wszystkie oprócz jednego. Wyjątkiem była moja potężna maszyna deweloperska, która przeszła przez zbiór danych ze 150 źródeł w około 6 godzin.
Aby to naprawić, usunąłem komponenty wielowątkowe i wyczyściłem I / O. Bez innych zmian czas wykonania na zestawie danych ze 150 źródłami spadł poniżej 10 minut na moim komputerze, az nieskończoności do poniżej pół godziny na przeciętnym komputerze firmowym.
źródło
Przypuszczam, że mógłbym zaoferować ten klejnot:
Ponieważ pierwiastek kwadratowy został obliczony w bardzo wrażliwym miejscu, otrzymałem zadanie znalezienia sposobu na przyspieszenie. Ta niewielka refaktoryzacja skróciła czas wykonywania o jedną trzecią (dla kombinacji używanego sprzętu i kompilatora, YMMV):
Oczywiście są na to szybsze ORAZ lepsze sposoby, ale myślę, że jest to całkiem niezły przykład pesymizacji.
Edycja: Pomyśl o tym, rozwinięta pętla była w rzeczywistości również zgrabną pesymizacją. Przeglądając kontrolę wersji mogę również przedstawić drugi etap refaktoryzacji, który wypadł jeszcze lepiej niż powyższe:
To jest dokładnie ten sam algorytm, choć nieco inna implementacja, więc przypuszczam, że się kwalifikuje.
źródło
isqrt()
obliczafloor(sqrt())
, ale dlaczego ten kod działa?Może to być na wyższym poziomie niż to, czego szukałeś, ale naprawienie tego (jeśli masz pozwolenie) wiąże się również z wyższym poziomem bólu:
Naleganie na ręczne rozwijanie Menedżera relacji z obiektami / warstwy dostępu do danych zamiast korzystania z jednej z uznanych, przetestowanych, dojrzałych bibliotek (nawet po tym, jak zostały ci wskazane).
źródło
Wszystkie ograniczenia klucza obcego zostały usunięte z bazy danych, ponieważ w przeciwnym razie byłoby tak wiele błędów.
źródło
To nie do końca pasuje do pytania, ale i tak wspomnę o tym jako przestroga. Pracowałem nad aplikacją rozproszoną, która działała wolno i poleciałem do DC, aby wziąć udział w spotkaniu, którego głównym celem było rozwiązanie problemu. Kierownik projektu rozpoczął nakreślenie re-architektury mającej na celu rozwiązanie problemu opóźnienia. Zgłosiłem się na ochotnika, że w weekend wykonałem pomiary, które pozwoliły na odizolowanie wąskiego gardła od jednej metody. Okazało się, że podczas lokalnego wyszukiwania brakowało rekordu, przez co aplikacja musiała przejść do zdalnego serwera przy każdej transakcji. Dodając rekord z powrotem do lokalnego sklepu, opóźnienie zostało wyeliminowane - problem rozwiązany. Zwróć uwagę, że zmiana architektury nie rozwiązałaby problemu.
źródło
Sprawdzanie przed KAŻDĄ operacją javascript, czy obiekt na którym operujesz istnieje.
Mój problem z tego typu kodem polega na tym, że nikt nie obchodzi, co jeśli nie istnieje? Po prostu nic nie rób? Nie przekazujesz opinii użytkownikowi?
Zgadzam się, że
Object expected
błędy są irytujące, ale to nie jest na to najlepsze rozwiązanie.źródło
Co powiesz na ekstremizm YAGNI. Jest to forma przedwczesnej pesymizacji. Wygląda na to, że za każdym razem, gdy zastosujesz YAGNI, w końcu go potrzebujesz, co powoduje 10 razy większy wysiłek, aby go dodać, niż gdybyś dodawał go na początku. Jeśli stworzysz udany program, prawdopodobnie będziesz go potrzebować. Jeśli jesteś przyzwyczajony do tworzenia programów, których życie szybko się kończy, kontynuuj praktykę YAGNI, ponieważ przypuszczam, że wtedy YAGNI.
źródło
Niezupełnie przedwczesna optymalizacja - ale z pewnością błędna - została przeczytana na stronie internetowej BBC, z artykułu omawiającego Windows 7.
Nie próbowałem jeszcze systemu Windows 7, więc mogę się mylić, ale jestem gotów założyć się, że są tam inne problemy, które są ważniejsze niż czas potrzebny do zamknięcia. W końcu, kiedy widzę komunikat „Zamykanie systemu Windows”, monitor wyłącza się i odchodzę - jakie korzyści daje mi to 400 milisekund?
źródło
Ktoś z mojego działu napisał kiedyś klasę smyczków. Interfejs podobny do
CString
, ale bez zależności od systemu Windows.Jedną z "optymalizacji", którą zrobili, było nie przydzielanie większej ilości pamięci niż to konieczne. Najwyraźniej nie zdajemy sobie sprawy, że powodem, dla którego klasy takie jak
std::string
przydzielają nadmiar pamięci, jest sekwencja+=
operacji może być wykonywana w czasie O (n).Zamiast tego każde pojedyncze
+=
wywołanie wymuszało realokację, która zmieniała powtarzające się dołączenia w algorytm O (n²) Schlemiela the Paintera .źródło
Mój były współpracownik ( właściwie soab ) został wyznaczony do zbudowania nowego modułu dla naszego Java ERP, który powinien był zbierać i analizować dane klientów (branża detaliczna). Zdecydował się podzielić KAŻDE pole kalendarza / daty i godziny na jego składniki (sekundy, minuty, godziny, dzień, miesiąc, rok, dzień tygodnia, dwumetr, trymestr (!)), Ponieważ „jak inaczej miałbym zapytać o„ każdy poniedziałek ”?
źródło
Bez obrazy dla nikogo, ale właśnie oceniłem zadanie (java), które to miało
źródło