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?
Odpowiedzi:
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ć?
źródło
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źródło
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.
źródło
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.
źródło
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ą.
źródło
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.
źródło
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.
źródło
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.
źródło
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.
źródło
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.
źródło