Proste vs złożone (ale wydajne) rozwiązanie - który wybrać i kiedy?

28

Programuję od kilku lat i często znalazłem się w dylemacie.

Istnieją dwa rozwiązania -

  • jeden jest prosty, tzn. proste podejście, łatwiejsze do zrozumienia i utrzymania. Wymaga pewnej redundancji, dodatkowej pracy (dodatkowe IO, dodatkowe przetwarzanie) i dlatego nie jest najbardziej optymalnym rozwiązaniem.
  • ale inne wykorzystują złożone podejście, trudne do wdrożenia, często wymagające interakcji między wieloma modułami i stanowi wydajne rozwiązanie.

Do którego rozwiązania należy dążyć, gdy nie mam umowy SLA o wysokiej wydajności, a nawet proste rozwiązanie może spełniać umowę SLA? Czuję pogardę wśród moich kolegów programistów za proste rozwiązanie.

Czy dobrą praktyką jest zaproponowanie najbardziej optymalnego złożonego rozwiązania, jeśli SLA wydajności można osiągnąć za pomocą prostego rozwiązania?

MoveFast
źródło
10
patrz: Jak uniknąć „Intuicji programisty dotyczącej złej optymalizacji”? „Niestety, programiści generalnie mają okropną intuicję na temat tego, gdzie faktycznie będą występować problemy z wydajnością aplikacji ...”
gnat
1
Czy „nie najbardziej optymalny” nadal będzie „wystarczająco dobry”? Więc zostań przy tym.
8
Tak. „ Le mieux est l'ennemi du bien. ” Voltaire. („Idealny jest wrogiem dobra.”) Wystarczająco dobry jest wystarczająco dobry - dopóki testy wydajności nie stwierdzą inaczej.
David Hammen
Uważam, że (ogólnie) prosta oznacza efektywność. Dlatego często nie trzeba iść na kompromis.
Dan.
2
„Wydaje się, że doskonałości nie osiąga się, gdy nie ma już nic do dodania, ale kiedy nie ma już nic do usunięcia.” - Antoine de Saint-Exupery
keuleJ

Odpowiedzi:

58

Do którego rozwiązania należy dążyć, gdy nie mam umowy SLA o wysokiej wydajności, a nawet proste rozwiązanie może spełniać umowę SLA?

Prosty. Spełnia specyfikację, jest łatwiejszy do zrozumienia, łatwiejszy w utrzymaniu i prawdopodobnie jest o wiele mniej wadliwy.

Propagowanie wydajnego rozwiązania polega na wprowadzeniu do kodu spekulatywności i przedwczesnej optymalizacji. Nie rób tego! Wydajność idzie w parze z niemal każdą inną „zdolnością” inżynierii oprogramowania (niezawodność, łatwość konserwacji, czytelność, testowalność, zrozumiałość ...). Wydajność ścigania podczas testowania wskazuje, że naprawdę istnieje potrzeba ścigania wydajności.

Nie ścigaj wydajności, gdy wydajność nie ma znaczenia. Nawet jeśli ma to znaczenie, należy ścigać wydajność tylko w tych obszarach, w których testy wskazują, że istnieje wąskie gardło wydajności. Nie pozwól, aby problemy z wydajnością były pretekstem do zastąpienia simple_but_slow_method_to_do_X()szybszą wersją, jeśli ta prosta wersja nie pojawia się jako wąskie gardło.

Zwiększona wydajność jest prawie nieuchronnie obciążona mnóstwem problemów z zapachem kodu. Wspomniałeś kilka w pytaniu: Złożone podejście, trudne do wdrożenia, wyższe sprzężenie. Czy naprawdę warto je wciągać?

David Hammen
źródło
twoja odpowiedź jest bardzo pomocna
MoveFast
1
Tak proste, jak to możliwe, ale nie prostsze; Tak szybko, jak to możliwe, ale nie szybciej; itp.
user606723,
2
„W razie wątpliwości użyj brutalnej siły”;)
tdammers
Komentarze w kodzie mogą być zarówno kathartyczne, jak i pomocne. Mały komentarz wskazujący na kompleksowe i szybkie rozwiązanie oraz dlaczego go nie użyłeś, może sprawić, że poczujesz się mniej, jakbyś ignorował optymalny algorytm. I może pomóc opiekunom zrozumieć twój wybór i skierować ich we właściwym kierunku, jeśli optymalizacja będzie rzeczywiście potrzebna później.
TheAtomicOption
12

Krótka odpowiedź: wolisz proste rozwiązania od złożonych i pamiętaj o zasadach KISS i YAGNI

Ponieważ początkowe wymagania projektowe i oprogramowanie nigdy nie są idealne, wymaga zmian w miarę opracowywania / użytkowania aplikacji. Podejście iteracyjne w fazach rozwoju jest bardzo dobrym rozwiązaniem, aby rozpocząć proste rzeczy i rozszerzyć je w razie potrzeby. Najprostsze rozwiązania zapewniają elastyczność i są łatwiejsze w utrzymaniu.

Ponadto próba bycia inteligentnym i optymalizacja ad hoc podczas budowania aplikacji nie jest dobrą praktyką i może nadmiernie komplikować rozwiązanie. Jak wiadomo "premature optimization is the root of all evil"- z książki Knutha

EL Yusubov
źródło
1
@ManojGumber, nie ma problemu i to naprawdę jest istotą tego, co my jako programiści powinniśmy dbać w pierwszej kolejności.
EL Yusubov,
8

Weź lekcję od Knutha tutaj: „Powinniśmy zapomnieć o małej wydajności, powiedzmy w około 97% przypadków: przedwczesna optymalizacja jest źródłem wszelkiego zła”.

Pomyśl o swoich rozwiązaniach w następującej kolejności: Po pierwsze, zawsze poprawność. Po drugie, popraw jasność i prostotę. Po trzecie i tylko wtedy, gdy możesz wykazać potrzebę, wydajność.

Zwiększenie wydajności prawie zawsze będzie cię kosztować coś ważnego i dlatego powinno być realizowane tylko wtedy, gdy wiesz, że musisz.

Szymon, Szymek
źródło
4
Pamiętaj, że nie oznacza to, że nie powinieneś pisać dobrej implementacji.
@ ThorbjørnRavnAndersen: oczywiście o to chodzi w pierwszych dwóch punktach.
simon
1
@ simon cytat jest często używany jako pretekst do niechlujnego wyboru,
O twoim drugim punkcie: miałem kolegę, który dość często twierdził, że wolał niepoprawnie skonstruowany i czysty kod przed poprawnym spaghetti.
Buhb
@ ThorbjørnRavnAndersen niekompetentni ludzie wykorzystają wszystko jako wymówkę. Nie ma żadnego wpływu na wartość oryginalnej myśli.
simon
7

Prostota jest warunkiem niezawodności . Jeśli masz proste rozwiązanie, które działa, zdecydowanie skorzystaj z niego! O wiele łatwiej jest zoptymalizować działający program niż sprawić, by zoptymalizowany program działał. Nie zapominaj również o prawie Moore'a : jeśli twoje proste rozwiązanie spełni dziś cele w zakresie wydajności, prawdopodobnie zmiażdży je 1 na rok lub dwa.


1 Nie ma gwarancji, ponieważ jak zauważył Jimmy Hoffa w komentarzu poniżej, prawo Moore'a ma swoje granice.

dasblinkenlight
źródło
Zapomniałeś o innym prawie Moore'a, które mówi: „Ups, o moim pierwszym prawie…” Przepraszam szefie, prawa Moore'a już nie ma (pun pun). Nie zgadzam się z resztą twojego punktu, chciałbym przynajmniej ostrzec tę ostatnią część.
Jimmy Hoffa
2
Przepraszamy, ale w całym moim doświadczeniu w tej branży. „zestawy robocze” zwiększają FAR szybciej niż szybkość naszego sprzętu, który jest stale aktualizowany. Naprawdę po prostu usunęłbym punkt prawa Moore'a.
user606723,
@ user606723 Wzrost punktu „pracy ustalonej” jest ortogonalny do pytania „zoptymalizowanego lub prostego”: obciążenie doścignie ich bez względu na to, jakie rozwiązanie zastosują. Celem wprowadzenia prawa Moore'a do miksu było zwrócenie uwagi, że nawet jeśli proste rozwiązanie znajduje się pod pewną presją wydajności w momencie pisania, presja zmniejszy się wraz z udostępnieniem szybszego sprzętu.
dasblinkenlight,
@dasblinkenlight, wzrost zadania nie jest prostopadły do ​​pytania, jak prawo Moore'a. Problem polega na tym, że jeśli proste rozwiązanie znajduje się pod pewną presją wydajności w momencie wydania, wydajność będzie niewystarczająca w bardzo bliskiej przyszłości, ponieważ rosnący zestaw zadań niweczy poprawę wydajności osiągniętą przez ulepszony sprzęt. Chociaż jestem zwolennikiem prostego, niezawodnego i łatwego w utrzymaniu oprogramowania, wypuszczanie oprogramowania, które jest już pod presją wydajności w momencie wydania i oczekiwanie, że prawo Moore'a go wyrówna, jest straszną filozofią.
user606723,
3

Czy dobrą praktyką jest zaproponowanie najbardziej optymalnego złożonego rozwiązania, jeśli SLA wydajności można osiągnąć za pomocą prostego rozwiązania?

Optimal to niejednoznaczne słowo!

Ostatecznie, jeśli istnieje duże ryzyko konieczności utrzymania złożonego, a jeśli prosty jest „wystarczająco dobry”, zawsze błądzę po stronie tego prostego.

Dodaj do tego ryzyko, że złożony nie będzie wystarczająco dobry, wtedy KISS jest prawdopodobnie właściwą odpowiedzią.

Andrzej
źródło
2

Wolę ten prosty. Moim zdaniem przedwczesne optymalizacje powodują tyle problemów, ile rozwiązują. W wielu przypadkach dobry projekt umożliwia zmianę danych wdrożeń w przyszłości, jeśli staną się one wąskimi gardłami.

Podsumowując - zaprojektuję go tak elastycznie, jak to możliwe, ale nie poświęcę zbytnio prostoty na rzecz elastyczności.

Tsvetomir Dimitrov
źródło
2

Który kosztuje mniej?

W większości przypadków proste rozwiązanie, które jest nieco wolniejsze, będzie całkowicie akceptowalne pod względem wydajności, a prostota sprawia, że ​​tańsze jest opracowywanie, konserwacja i ewentualnie wymiana.

Z drugiej strony czasami szybkość jest naprawdę ważna, a zysk finansowy, który przynosi nawet niewielkie ulepszenia prędkości, może być znacznie większy niż wzrost kosztów bardziej skomplikowanego rozwiązania. Na przykład skrócenie czasu o 0,01 s do zakończenia transakcji może znacznie zwiększyć rentowność systemu obrotu papierami wartościowymi. 10% poprawa wydajności systemu obsługującego kilka milionów użytkowników może oznaczać znaczną redukcję kosztów serwera.

Pytanie, które musisz sobie zadać, brzmi: czy użycie złożonego rozwiązania ma wystarczająco duży wpływ na wynik finansowy, aby pokryć dodatkowe koszty? Właściwie powinieneś prawdopodobnie poprosić swojego klienta o decyzję, ponieważ płaci rachunki i czerpie potencjalne korzyści. Jedną dobrą opcją jest skorzystanie z prostego rozwiązania i zaoferowanie bardziej złożonego rozwiązania jako możliwego ulepszenia. Pozwala to na uruchomienie systemu i daje klientowi możliwość rozpoczęcia testowania, a to doświadczenie może być podstawą decyzji o wdrożeniu (lub niewdrożeniu) bardziej skomplikowanego rozwiązania.

Caleb
źródło
2

Oceniając dwa podejścia, jedno jest prostsze, ale mniej wydajne, a drugie bardziej złożone i wydajniejsze, należy wziąć pod uwagę problem i dziedzinę projektu.

Zastanów się nad wielomiliardowym projektem oprogramowania dla branży medycznej, który zaplanował żywotność na ponad 15 lat konserwacji i ponad 20 lat użytkowania. W przypadku takiego projektu zdecydowanie nie będzie to stanowiło problemu, ale złożoność i struktura projektu mogą powodować poważne problemy z utrzymaniem projektu, który trwa co najmniej 15 lat. Utrzymanie i prostota są najważniejsze.

Następnie rozważ inny przykład. Silnik do gier konsolowych, który ma zasilać nadchodzące gry firmy przez następne 5 lat. Ponieważ gry są programami bardzo ograniczonymi zasobami, w wielu przypadkach wydajność jest ważniejsza niż łatwość konserwacji. Pisanie własnych, bardzo specyficznych struktur danych i algorytmów dla niektórych zadań może być bardzo ważne, nawet jeśli jest sprzeczne z jakimikolwiek „najlepszymi praktykami” tworzenia oprogramowania. Dobrym przykładem może być projektowanie zorientowane na dane, w którym dane są przechowywane w podobnych tablicach danych, a nie w rzeczywistych obiektach. Ma to na celu zwiększenie odniesienia do lokalizacji i zwiększenie wydajności pamięci podręcznej procesora. Niepraktyczne, ale bardzo istotne w danej dziedzinie.

zxcdw
źródło
1

To jest zawsze trudne pytanie i widzę odpowiedzi wahające się w jedną stronę, więc zagram w grę po drugiej stronie, chociaż nie twierdzę, że żadna odpowiedź jest poprawna, jest to bardzo miękki i indywidualny temat.

Jedna rzecz dotycząca złożonego, ale wydajnego rozwiązania jest taka, że ​​zawsze możesz po prostu udokumentować to, co żyje. Zasadniczo jestem fanem samodokumentującego się kodu, ale jestem także fanem oprogramowania, które reaguje w takim czasie, który sprawia, że ​​czuję, że to mnie nie spowalnia. Jeśli wybierzesz złożone, ale wydajne rozwiązanie, zastanów się, co możesz zrobić, aby nie było tak źle:

Zawiń go w interfejs, umieść w osobnym zestawie, być może nawet sam proces. Zrób to tak luźno, jak to możliwe, z możliwie grubą ścianą abstrakcji wokół niej, aby uniknąć wycieków . Napisz wiele testów jednostkowych, aby zaoszczędzić regresje w przyszłości.

Udokumentuj to w kodzie, nawet rozważ napisanie prawdziwej dokumentacji. Pomyśl o złożonych strukturach danych i ich dokumentacji, wyobraź sobie próbę zrozumienia implementacji jednego z nich z kodu bez książki o strukturze danych / artykułu w Wikipedii, aby to wyjaśnić. A jednak wszyscy akceptujemy, że te złożone struktury danych są w rzeczywistości dobrymi rzeczami i korzystne jest, aby ktoś je zaimplementował w naszych językach.

Pamiętaj, że wszyscy wysyłamy wiadomości na stosie TCP / IP, który prawdopodobnie jest tak nieprzyzwoity, jak tylko może uzyskać kod, jeśli ktokolwiek z nas go obejrzy, wyraźnie, aby działał tak, jak wszyscy tego potrzebujemy. Być może twój problem nie wymaga takiego poziomu optymalizacji, być może wymaga tego, ale strzeż się, gdy zajmujesz się tym pytaniem, ponieważ wszyscy musimy od czasu do czasu: są tam smoki.

Jimmy Hoffa
źródło
0

Pracuję nad tym w obszarach, w których nie ma SLA wydajności. Jeśli chodzi o renderery offline w grafice komputerowej, użytkownicy nie mają „zadowalającej wydajności” dla użytkowników, ponieważ już teraz wypłacają ogromne sumy pieniędzy na dystrybucję komputerów w chmurach i renderowanie farm nawet przy użyciu najnowocześniejszych rendererów do produkcji obrazów i ramek o jakości produkcyjnej, np

Ale muszę powiedzieć jako działający w tej dziedzinie od wielu lat, że każde rozwiązanie, które znacznie obniża łatwość utrzymania na korzyść wydajności, faktycznie działa wbrew ciągle zmieniającym się wymaganiom wydajnościowym. Ponieważ jeśli nie będziesz w stanie skutecznie utrzymywać swojego rozwiązania przez wiele lat, ponieważ rzeczy zmieniają się pod twoimi stopami (zarówno pod względem otaczającego kodu, jak i oczekiwań użytkowników, ponieważ konkurenci wciąż przewyższają się nawzajem), to Twoje rozwiązanie już działa w kierunku starzenia się i potrzeba hurtowej wymiany.

Nie widzę ostatecznego celu profilerów takich jak VTune jako sposobu na przyspieszenie mojego kodu. Ich największą wartością jest upewnienie się, że nie obniżam wydajności, aby sprostać stale rosnącym wymaganiom wydajnościowym. Jeśli absolutnie muszę zastosować trochę rażącą mikrooptymalizację, to profiler, w połączeniu z uruchomieniem go dla rzeczywistych przypadków użytkowników (a nie niektórych przypadków testowych, które, jak sądzę, mogą być ważne), gwarantuje, że zastosuję tak nieuchronnie rażąco wyglądające optymalizacje bardzo, bardzo rozsądnie tylko do pojawiających się najlepszych hotspotów, a także bardzo staranne dokumentowanie ich, ponieważ nieuchronnie będę musiał ponownie odwiedzać, utrzymywać i modyfikować i zmieniać je w nadchodzących latach, jeśli to rozwiązanie pozostanie opłacalne.

A szczególnie, jeśli twoje zoptymalizowane rozwiązanie wymaga większej liczby połączeń, to naprawdę niechętnie go wykorzystam. Jednym z najcenniejszych wskaźników, które doceniłem w najbardziej krytycznych dla wydajności obszarach bazy kodu, jest oddzielenie (jak w minimalizowaniu ilości informacji coś musi działać, co również minimalizuje prawdopodobieństwo, że będzie wymagać zmian, chyba że bezpośrednio potrzebuje zmian ), ponieważ te krytyczne obszary znacznie zwielokrotniają przyczyny zmian. Co oznacza, że ​​im mniej informacji wymaga praca, tym mniej powodów do zmiany, a minimalizowanie powodów zmiany jest naprawdę ogromną częścią poprawy wydajności w moich szczególnych obszarach zainteresowania, ponieważ i tak rzeczy muszą się ciągle zmieniać (my w przeciwnym razie stanę się nieaktualny),

Dla mnie najlepsze i najskuteczniejsze rozwiązania, jakie znalazłem, to takie, w których wydajność, łatwość konserwacji i wydajność nie są ze sobą diametralnie różne. Dążenie do mnie polega na tym, aby te koncepcje były jak najbardziej harmonijne.

Dragon Energy
źródło