Dlaczego powinienem dbać o mikro wydajność i wydajność?


71

Wiele pytań i odpowiedzi na stronach C / C ++, w szczególności lub pośrednio, omawia problemy z wydajnością mikro (takie jak narzut funkcji pośredniej vs. bezpośredniej vs. wbudowanej) lub przy użyciu algorytmu O (N 2 ) vs O (N log N) na lista 100 przedmiotów.

Zawsze piszę bez obawy o wydajność mikroprocesorów i mało uwagi o wydajność makr, koncentrując się na łatwym w utrzymaniu, niezawodnym kodzie, chyba że wiem, że mam problem.

Moje pytanie brzmi: dlaczego tak bardzo zależy na dużej liczbie programistów? Czy to naprawdę problem dla większości programistów, czy po prostu miałem szczęście, że nie musiałem się tym zbytnio przejmować, czy też jestem złym programistą?


5
+1, dobre pytanie ogólne.
iammilind

+1 dobre pytanie .. Dodałem 2 tagi .. mam nadzieję, że ci to nie przeszkadza.

2
Kieruję dwoma wielkimi cytatami 1) „Przedwczesna optymalizacja jest źródłem wszelkiego zła”. 2) 80% twojego czasu zostanie wydane z 20% twojego kodu (reguła 80/20).
James Khoury

2
Zauważyłem, że kilka odpowiedzi mówi o moim przykładzie O (n * n). Wyraźnie podałem listę 100 elementów, ale wciąż twierdzą, że O (nlogn) jest lepszy, wyraźnie stwierdzając poprawę wydajności, jeśli lista, w dalszej kolejności, wynosi 1000 lub miliony. Czy ta obsesja na punkcie mikrooptymalizacji polega na tym, że programiści programują według przyszłych wymagań, a nie aktualnych wymagań? (Gdzie to słyszałem wcześniej ...)
Mattnz

5
@James pełny cytat z Donalda Knutha brzmi: „Powinniśmy zapomnieć o niewielkiej wydajności, powiedzmy w około 97% przypadków: przedwczesna optymalizacja jest źródłem wszelkiego zła”. W tym wątku znajdzie się kilka dobrych odpowiedzi na temat pozostałych 3%.
StuperUser

Odpowiedzi:


14

W praktyce wydajność rzadko stanowi problem, którym należy zarządzać na tym poziomie szczegółowości. Warto mieć oko na sytuację, jeśli wiesz, że będziesz przechowywać i manipulować ogromnymi ilościami danych, ale w przeciwnym razie masz rację i lepiej, utrzymując proste.

Jedną z najłatwiejszych pułapek, w które można wpaść - szczególnie w C i C ++, gdzie masz tak drobnoziarnistą kontrolę - jest optymalizacja zbyt wcześnie i na zbyt wysokim poziomie. Ogólnie zasada jest taka: A) nie optymalizuj, dopóki nie dowiesz się, że masz problem, i B) nie optymalizuj niczego, co nie okazało się problemem, używając profilera.

Następstwem B) jest to, że programiści notorycznie źle przewidują, gdzie występują wąskie gardła w ich wydajności, chociaż nawet w jednym, uważają, że są w tym dobrzy. Użyj profilera i zoptymalizuj wolne części lub zmień algorytmy, jeśli jedna sekcja kodu jest wywoływana zbyt wiele razy, aby powodować problemy.


6
Kolejny: kod inicjujący, który zostanie wykonany raz, na ogół nie wymaga optymalizacji, więc szukaj gdzie indziej.
Mike DeSimone

3
Zależy od tego, jak często występuje „raz”. Podczas pracy ./configurezaryzykowałbym stwierdzenie, że do 75% czasu wykonywania można przeznaczyć na kod „inicjalizacji” w programach uruchamianych przez skrypt. 25-50% można nawet wydać na dynamiczne linkowanie.
R ..

12
Reguła A to straszna zasada. Architektura systemu odgrywa istotną rolę w wydajności, a jeśli później dowiesz się, że twoja architektura po prostu nie jest w stanie sprostać wymaganiom dotyczącym wydajności, jesteś po prostu przykręcony. Więc chociaż możesz pominąć drobne szczegóły, całkowite zignorowanie tego na początku jest po prostu błędne.
edA-qa mort-ora-y

3
@ edA-qa: Kiedyś tak myślałem, ale przez lata doświadczyłem wielu innych projektów, które zmagają się z porażką, zanim jakiekolwiek problemy z wydajnością stały się problemem. Za każdym razem, gdy miałem problemy z wydajnością, poprawka była stosunkowo niska kosztowo, kilka dni lub kilku tygodni. Nie bardziej niż jakikolwiek inny „błąd” wykrył d poprawkę w fazie rozwoju. Jednak, jak w przypadku każdej innej pozycji ryzyka, wskaźniki wydajności należy zidentyfikować i ograniczyć na wczesnym etapie projektu.
mattnz

5
OP zapytał, dlaczego tak wielu się przejmuje, i nie rozumiem, w jaki sposób ta odpowiedź faktycznie odpowiedziała na pytanie, chyba że OP był bardziej zainteresowany usłyszeniem, jak ktoś mówi „nie martw się!”.
czerwono-brud

54

Myślę, że wszystko na twojej liście to mikrooptymalizacja, na którą ogólnie nie należy patrzeć, z wyjątkiem

przy użyciu algorytmu O (n * n) vs O (NlogN) na liście 100 elementów

myślę, że należy na to spojrzeć. Jasne, ta lista zawiera teraz 100 pozycji i wszystko jest szybkie jak na małe n , ale chciałbym się wkrótce założyć, że ten sam kod zostanie ponownie wykorzystany na kilka milionów list linii, a kod nadal będzie miał pracować rozsądnie.

Wybór właściwego algorytmu nigdy nie jest mikrooptymalizacją. Nigdy nie wiadomo, jakiego rodzaju dane będą używane przez ten sam kod przez dwa miesiące lub dwa lata później. W przeciwieństwie do „mikrooptymalizacji”, które są łatwe do zastosowania za pomocą profilera, zmiany algorytmów często wymagają znacznego przeprojektowania w celu skutecznego wykorzystania nowych algorytmów. (Np. Niektóre algorytmy wymagają już sortowania danych wejściowych, co może zmusić Cię do zmodyfikowania znacznych części aplikacji, aby zapewnić, że dane pozostaną posortowane)


36
+1 za „Wybór odpowiedniego algorytmu nigdy nie jest mikrooptymalizacją”.

9
Ja też daję +1, ale zauważ, że wybór algorytmu wielko-optymalnego, gdy rozmiary danych z pewnością będą małe, może mieć negatywny wpływ na czas programowania, rozmiar programu, a może nawet użycie pamięci. Jeśli sortujesz układy pokera, czy naprawdę chcesz napisać szybkie, gładkie lub połączone? Zacznę od prostego sortowania przez wstawianie lub skorzystania z sieci sortującej.
R ..

8
Zabawne. W wątku na temat mikrooptymalizacji wielu komentatorów mikrooptymalizuje odpowiedzi. ;)
Zabezpiecz

5
„Zechciałbym się wkrótce założyć, że ten sam kod zostanie ponownie wykorzystany do kilku milionów linii list”: To całkowicie zależy od dziedziny problemów. Przykłady: jeśli piszesz algorytm szachowy, możesz mieć pewność, że rozmiar planszy się nie zmieni. Jeśli zaprogramujesz pojazd autonomiczny, liczba kół również nie wzrośnie tak szybko.
nikie

3
Nie podoba mi się, że „wybór właściwego algorytmu nigdy nie jest mikrooptymalizacją”, ponieważ jest to oczywiście prawda, biorąc pod uwagę charakter słowa „właściwy”. Wydaje mi się jednak, że twoja implikacja jest naprawdę „najszybszym lub najbardziej wydajnym” algorytmem, z którym się nie zgadzam. Wybór najbardziej wydajnego algorytmu jest złym wyborem, jeśli wdrożenie zajmuje dużo czasu, a szybkość lub przestrzeń tego segmentu i tak nie ma znaczenia.
Casey Patton,

18

Dawno, dawno temu, w mojej pierwszej pracy napisałem kod dla systemów wbudowanych. Systemy te używały mikroprocesorów 8086 i miały ograniczoną pamięć. Użyliśmy kompilatora Intel C. Jeden system, który zbudowałem, potrzebował dostępu do trójwymiarowej tablicy struktur. Zbudowałem go tak, jak powiedziała mi książka: zadzwoń do malloc dla 3 wymiarów, następnie przydziel wiersze dla następnego wymiaru, a następnie calloc dla węzłów końcowych.

To było dość skomplikowane (dla mnie w tym czasie), musiałem dopasować krzywą, kontrolować proces ANOVA i analizę chi-kwadrat. Nie było bibliotek, które zrobiłyby to za nas; musieliśmy to wszystko napisać i wszystko zmieścić na 8086.

System działał jak pies. Po szybkim profilowaniu odkryłem, że jednym z największych problemów był alokator. Aby rozwiązać problem, porzuciłem wszystkie połączenia z Malloc i sam zarządzałem pamięcią jednego dużego bloku pamięci.


W innym przypadku na tym samym zleceniu klient skarżył się na czas reakcji w swoim statystycznym systemie kontroli procesu. Zespół przede mną zaprojektował system „oprogramowania PLC”, w którym operatorzy mogą wykorzystywać logikę logiczną do łączenia sygnałów i wyłączników wyzwalających. Napisali go w uproszczonym języku, który dziś nazwalibyśmy „językiem specyficznym dla domeny”. jak pamiętam, to wyglądało ((A1 + B1) > 4) AND (C1 > C2)i tak dalej.

Oryginalny projekt analizował i interpretował ten ciąg za każdym razem, gdy był oceniany. Na naszym marnym procesorze zajmowało to dużo czasu, co oznaczało, że kontroler procesu nie mógł aktualizować się tak szybko, jak proces był uruchomiony.

Rzuciłem na to nowe spojrzenie i zdecydowałem, że mogę przełożyć tę logikę na kod asemblera w czasie wykonywania. Parsowałem go raz, a następnie za każdym razem, gdy był uruchamiany, aplikacja wywoływała funkcję generowaną dynamicznie. Chyba jak niektóre wirusy dzisiaj (ale tak naprawdę nie wiem). Rezultatem był 100-krotny wzrost wydajności, co sprawiło, że klient i mój szef byli naprawdę szczęśliwi.

Nowy kod nie był tak łatwy do utrzymania, ponieważ zbudowałem niestandardowy kompilator . Ale przewaga wydajności znacznie przewyższyła wadę konserwacji.


Niedawno pracowałem nad systemem, który musiał dynamicznie analizować plik XML. Większe pliki zajęłyby znacznie więcej czasu. Było to bardzo zależne od wydajności; zbyt wolne analizowanie spowoduje, że interfejs użytkownika stanie się całkowicie bezużyteczny.

Tego rodzaju rzeczy pojawiają się cały czas.


Więc .... czasami potrzebujesz łatwego w utrzymaniu, łatwego do napisania kodu. Czasami potrzebujesz szybkiego kodu. Kompromisem jest decyzja inżynierska, którą musisz podjąć w przypadku każdego projektu.


9
We wszystkich twoich przykładach koszt późniejszej optymalizacji nie był dużo wyższy niż napisanie szybkiego kodu od samego początku. Więc pisanie wolniej prostszego kodu, a następnie optymalizacja tam, gdzie to konieczne, działało dobrze we wszystkich.
CodesInChaos

6
@CodeInChaos: Odpowiedź nie twierdzi inaczej. Odpowiada na pytanie PO: „Dlaczego powinienem dbać o mikro wydajność i wydajność?” Inne pytania dotyczące wstępnej optymalizacji zostały jedynie wywnioskowane.
webbiedave

12

Jeśli przetwarzasz duże obrazy i iteruje się na każdym pikselu, poprawa wydajności może być krytyczna.


2
+1 - także, finansowanie wysokiej częstotliwości, wszelkiego rodzaju dekodery / dekodery audio / wideo, symulacje i modelowanie (np. Gry), bity systemowe, takie jak harmonogramy procesorów i menedżery pamięci itp.
Billy ONeal

3
MOŻE być krytyczny, ale JEST krytyczny tylko wtedy, gdy udowodnisz, że tak jest i profilujesz go tak, aby był w miejscu, w którym Twoim zdaniem jest problem. (Wskazówka: prawdopodobnie tego nie ma.)
WŁAŚNIE MOJA poprawna OPINIA z

2
@ PO PROSTU MOJA poprawna OPINIA: w rzeczywistości w przypadku przetwarzania obrazu przetwarzanie danych jest zwykle drugim co do wielkości konsumentem (we / wy wciąż jest największe). Jednak optymalizacja we / wy wymaga wielu nietypowych / szalonych projektów i ich akceptacji przez innych programistów, a czasem poprawa jest wręcz niemożliwa. Część przetwarzająca jest jednak zwykle kłopotliwa do zrównoleglenia, stąd korzyści, które można łatwo wykorzystać. (Ulepszenie jednego może być postrzegane przez innego jako prosta implementacja podręcznika ... chyba że osiągniesz poziom VirtualDub)
rwong

12

Pozwól, że powiem ci trochę, dlaczego kryje się za tym kultura.

Jeśli zbliżasz się do 40, a nie do 20 lat, a programujesz przez całe dorosłe lata, wtedy dorastałeś, gdy C ++ była naprawdę jedyną grą w mieście, aplikacje komputerowe były normą, a sprzęt był nadal znacznie opóźnione oprogramowanie pod względem przepustowości / wydajności.

  • Kiedyś musieliśmy robić głupie sztuczki programistyczne, aby móc czytać duże (> 2G) pliki ...
  • Kiedyś martwiliśmy się o rozmiar pliku wykonywalnego ...
  • Kiedyś martwiliśmy się ilością pamięci zużywanej przez nasze programy ...
  • Regularnie podejmowaliśmy decyzje dotyczące czasu algorytmicznego a kompromis kosmiczny ...
  • Nawet na zapleczu musieliśmy pisać programy CGI w C lub C ++, aby cokolwiek poradzić sobie z przyzwoitym „nie”. RPS ... To było kilka rzędów wielkości szybciej.
  • Kiedyś testowaliśmy zalety wydajności między delphi / c ++ / vb!

Bardzo niewiele osób musi się dzisiaj martwić o te rzeczy.

Jednak 10 lat temu wciąż musiałeś się martwić, że twoje oprogramowanie zostanie pobrane przez modem 56kb i uruchomione na 5-letnim komputerze ... Czy pamiętasz, jak kiepskie były komputery w 1996 roku? Pomyśl o 4 GB dysku twardego, procesorze 200 MHz i 128 MB pamięci RAM ...

A serwery sprzed 10 lat? Serwer „następnej generacji” firmy Dell kosztował 2000 USD i był wyposażony w 2 (!) Procesory pentium 1 GHz, 2 Gb lub Ram oraz dysk twardy 20 Gb.

To była po prostu inna gra w piłkę i wszyscy ci „starsi” inżynierowie, którzy mają 10-letnie doświadczenie (faceci, którzy odpowiedzą na twoje pytania), obcinają zęby w tym środowisku.


1
Dodatkowe 20 lat doświadczenia oznacza również, że mamy wiele śladów po procesie optymalizacji wiele, wiele razy i unikamy robienia rzeczy, które mogą być potrzebne później. Z tego samego powodu nie uderzam kciukiem (dużo) podczas używania młotka.
Blrfl

1
odwijanie pętli <dreszcz>
czerwono-brud

5
a dziś wszystkie dzieci, które uważały, że przepustowość, procesor i pamięć są nieograniczone, przekonują się, że ich aplikacje mobilne nie działają zbyt dobrze.
gbjbaanb

9

jest tu już 10 odpowiedzi, a niektóre są naprawdę dobre, ale ponieważ to moje osobiste wkurzenie ...

przedwczesna optymalizacja, która a) zajmuje dużo więcej czasu niż proste rozwiązanie b) wprowadza więcej kodu, w którym proste rozwiązanie miałoby połowę wielkości i połowę złożoności ic) sprawia, że ​​rzeczy są mniej czytelne, absolutnie należy unikać. Jeśli jednak programista ma wybór między użyciem std :: map lub std :: vector i wybiera niewłaściwą kolekcję z czystej ignorancji dla wydajności, która jest tak zła, jeśli nie gorsza niż przedwczesna optymalizacja. Co jeśli mógłbyś dzisiaj nieco zmienić swój kod, zachować czytelność, zachować tę samą złożoność, ale uczynić go bardziej wydajnym, zrobiłbyś to? Czy nazwałbyś to „przedwczesną optymalizacją”? Uważam, że wielu ludzi nie zastanawiałoby się nad tym w taki czy inny sposób.

Kiedyś byłem facetem, który doradzał „mikrooptymalizację”, która wymagała bardzo niewielkich zmian, i dostałem tę samą odpowiedź, co właśnie powiedziałeś: „nie powinieneś optymalizować zbyt wcześnie. Po prostu uruchommy to, a my to zmienimy później, jeśli wystąpi problem z wydajnością ". Naprawiliśmy kilka wydań. I tak, to był problem z wydajnością.

Chociaż wczesna optymalizacja może nie być dobra, myślę, że bardzo korzystne jest, gdy ludzie piszą kod ze zrozumieniem, co ten kod zrobi, i po prostu nie lekceważą żadnego pytania, które skutkuje zapisaniem O (x) jako „optymalizacja”. Jest teraz mnóstwo kodu, który możesz teraz napisać i przy odrobinie przemyślenia na temat wydajności unikniesz 80% problemów w przyszłości.

Weź również pod uwagę, że wiele problemów z wydajnością nie wydarzy się w twoim środowisku i nie od razu. Czasami będziesz mieć klienta, który przekroczy limit, lub inny programista zdecyduje się zbudować na twoim frameworku i zwiększyć liczbę obiektów 10-krotnie. Mając teraz na uwadze wydajność, możesz później uniknąć bardzo kosztownych zmian. A jeśli problem zostanie wykryty po oficjalnym wydaniu oprogramowania, nawet prosta poprawka stanie się 20 razy droższa.

Podsumowując, pamiętanie o wydajności przez cały czas pomaga rozwinąć dobre nawyki. Które są tak samo ważne, aby mieć tak czysty, jak najbardziej prosty i zorganizowany kod.


+1: Jest to jeden z czynników zwiększających szanse zatrudnienia osób zatrudnionych do opracowania oprogramowania Shrinkwrap i komercyjnych witryn internetowych . Zapobieganie jest mniej kosztowne niż klątwa klienta.
rwong

6

Podejrzewam, że wiele z tego, co widzisz, to prosty błąd próbkowania. Kiedy ludzie mają do czynienia z prostymi sytuacjami, piszą kod i to jest koniec rzeczy. Zadają pytania, gdy mają do czynienia z czymś stosunkowo trudnym, takim jak konieczność optymalizacji, szczególnie w sytuacji, gdy niekoniecznie jest oczywiste, że optymalizacja byłaby konieczna.

To powiedziawszy, niewątpliwie wiąże się to również z przedwczesną optymalizacją. Prawidłowo lub inaczej, C i C ++ mają reputację wydajności, która ma tendencję do przyciągania ludzi, którym zależy na wydajności - w tym tych, którzy mogą zoptymalizować zarówno przyjemność, jak i to, że jest naprawdę potrzebna.


1
+1 - Uwaga: większość SO pytań ze znacznikiem „performance” prawdopodobnie jest częścią tego błędu próbkowania: P
Billy ONeal

3
Na pewno widzę tu wiele cholernych pytań o przedwczesną optymalizację ... Myślę, że wynika to z faktu, że wielu programistów hobbystycznych zaczęło od pisania pomysłów na gry, i istnieje ogromny zbiór nonsensownych książek o „optymalizacji” i strony internetowe związane z tworzeniem gier, które umieszczają złe pomysły w głowach początkujących. :-)
R ..

4
Kiedy masz do czynienia z czymś trudnym, często łatwiej jest zrobić sobie przerwę od trudnego problemu i zmarnować swój czas, zastanawiając się, czy powinieneś użyć i++lub++i
Carson63000

@ Carson63000: tak, to może całkowicie wypaczyć próbki. Lub spędzają czas na odpowiadaniu na pytania, dlaczego mój operator ++nie skompilował.
rwong

4

Kilka innych odpowiedzi wspomina o systemach wbudowanych i chciałbym rozwinąć tę kwestię.

Istnieje wiele urządzeń zawierających tanie procesory, na przykład: kontroler kotła w domu lub prosty kalkulator kieszonkowy lub dziesiątki chipów w nowoczesnym samochodzie.

Aby zaoszczędzić pieniądze, mogą one mieć ilość pamięci flash (do przechowywania kodu) i pamięci RAM, które wydają się małe dla tych, którzy napisali kod tylko na komputery PC lub smartfony. Aby oszczędzać energię, mogą działać przy stosunkowo niskich częstotliwościach zegara.

Na przykład rodzina mikrokontrolerów STM32 korzysta z 24 MHz, 16 KB flash i 4 KB RAM , do 120 MHz, 1 MB flash i 128 KB RAM .

Pisząc kod dla takich układów, oszczędzasz dużo czasu, jeśli chcesz, aby Twój kod był jak najbardziej wydajny. Oczywiście przedwczesna optymalizacja pozostaje złym pomysłem; ale dzięki praktyce nauczysz się, jak często problemy mogą być rozwiązywane szybko i / lub przy minimalnych zasobach, i odpowiednio koduj.


1
dobre punkty do rozważenia dla systemów wbudowanych, dziedziny, w której pracuję sam. Nawet mając to na uwadze, moje wieloletnie doświadczenie pokazuje, że błędna optymalizacja zawsze jest stratą czasu. Bez narzędzi, które nas prowadzą, rzadko znajdujemy obszary problematyczne
Jeff

2

Wśród nich jest zasadniczo języków niskopoziomowych, gdy jeden prowadzi do patologicznego przypadku spełnienia gdzie jeden szczegół, który nie ma znaczenia 99% czasu jest przyczyną z gardłem, ktoś rzeczywiście ma możliwość bezpośrednio obejść ten problem (w przeciwieństwie do większości innych Języki); ale oczywiście często jak to zrobić najskuteczniej nie jest od razu widoczne. Stąd połowa dziwnych / interesujących pytań dotyczących mikrooptymalizacji zadawanych tutaj.

Druga połowa pochodzi od ciekawskich, jak blisko mogą się zbliżyć do metalu. Są to zasadniczo języki niskiego poziomu, w końcu ...


+1: warto zaznaczyć, że „wydajność patologiczna” może przydarzyć się każdemu na świecie, bez względu na język lub platformę. Możliwość ponownej implementacji w języku niższego poziomu w celu testowania i dezasemblacji odczytu może zapewnić więcej informacji , ale nie zawsze stanowi realne rozwiązanie. Przykład: „Wiem, że mogę to zrobić w asemblerze - ale musi działać w środowisku częściowego zaufania!”
rwong

2

Wydajność jest zawsze gorącym tematem w przypadku C i C ++. Jeśli chodzi o to, jak daleko należy się posunąć, zawsze można oszaleć do poziomu wbudowanego ASM lub użyć arytmetyki wskaźnika dla szybszej iteracji. Jest jednak moment, w którym poświęca się tyle czasu na optymalizację, że praca nad opracowaniem całego programu zostaje zatrzymana.

W rozwiązywaniu tych problemów występuje wydajność programisty i wydajność kodu. Które z nich, na których należy się skupić, zawsze wywołują ciekawe pytania. Ostatecznie najważniejsze pytanie brzmi, jak zauważalne jest dla użytkownika. Czy użytkownik będzie pracował z danymi, które tworzą tablice z setkami lub tysiącami elementów? W takim przypadku kodowanie szybkiego wykonania czynności może sprawić, że użytkownik narzeka, że ​​standardowe operacje programu są powolne.

Jest też użytkownik, który będzie pracował z małymi ilościami danych. Kilka plików tu i tam, w których robienie rzeczy takich jak sortowanie i operacje na plikach nie będzie tak zauważalne dla użytkownika, jeśli korzystasz z funkcji wyższego poziomu, które ułatwiają utrzymanie ich kosztem pewnej wydajności.

To tylko mały przykład problemów, na które napotkasz. Inne sprawy obejmują sprzęt docelowego użytkownika. Będziesz musiał martwić się znacznie wydajnością, jeśli masz do czynienia z systemami wbudowanymi, a następnie, jeśli użytkownicy mają, powiedzmy, dwurdzeniowe maszyny z wieloma RAM.


Hmm .. Nie używam arytmetyki wskaźnika do szybszej iteracji - jest to mnożenie i dodawanie instrukcji dla każdej pętli, niezależnie od tego, czy używasz iteracji opartej na indeksie, czy na wskaźniku. Używam go jednak, ponieważ zwykle jest bardziej przejrzysty niż iteracja oparta na indeksie.
Billy ONeal

arytmetyka wskaźnika nie jest szybsza niż w / e.

2

Dlaczego programistów tak bardzo zależy? Są głupie pomysły wypełniające ich głowy, takie jak rozwiązywanie problemów z wydajnością, zanim zdadzą sobie sprawę, że je mają, i brak zrozumienia, kiedy zgadują .

To trudne, ponieważ z mojego doświadczenia wynika, że ​​są pewne problemy z wydajnością, o których należy pomyśleć z wyprzedzeniem. Doświadczenie wymaga poznania ich.

To powiedziawszy, metoda, której używam jest podobna do twojej, ale nie taka sama:

  1. Zacznij od najprostszego możliwego projektu. W szczególności struktura danych powinna być jak najbardziej znormalizowana i minimalna. W zakresie, w jakim ma nieuniknioną redundancję, należy unikać powiadomień, aby zachować spójność. Lepiej jest tolerować przejściową niespójność i naprawiać ją za pomocą okresowego procesu.

  2. Gdy program jest w fazie rozwoju, okresowo dostosowuj wydajność, ponieważ problemy z wydajnością mogą się cicho wkradać. Metoda, której używam, polega na losowym wstrzymywaniu , ponieważ uważam, że jest najlepsza.

Oto przykład tego, co mam na myśli.


1

Szczerze mówiąc, zależy to od tego, jaki jest twój cel i czy programujesz profesjonalnie, czy jako hobby.

W dzisiejszych czasach nowoczesne komputery to naprawdę potężne maszyny. Bez względu na to, jakie podstawowe operacje zdecydujesz się wykonać, bez względu na to, czy próbujesz mikro zoptymalizować, czy nie, mogą sprawić, że ich praca będzie niezwykle szybka. Ale oczywiście, jeśli robisz coś innego (na przykład superkomputer dla dziedzin takich jak fizyka lub chemia), możesz zoptymalizować tyle, ile chcesz.

Pierwsi programiści MIT nie urodzili się, aby robić niesamowite rzeczy; Zaczęli upraszczać i zasilać istniejące algorytmy. Ich dumą było to, że 2 + 2 dają cztery w dwie sekundy mniej niż istniejący algorytm (to tylko przykład, masz pomysł). Ciągle próbowali używać mniejszej liczby kart dziurkaczy w swoich maszynach TI-83 w celu zwiększenia wydajności.

Ponadto, jeśli programujesz dla systemów wbudowanych, z pewnością musisz mieć oko na mikro wydajność. Nie chcesz mieć wolnego zegara cyfrowego, który tyka 5 sekund nanosekund wcześniej niż inny zegar cyfrowy.

Wreszcie, jeśli jesteś programistą hobbystycznym, optymalizacja najmniejszych szczegółów z pewnością nie zaszkodzi, nawet jeśli twój program jest szybki. To nie jest potrzebne, ale na pewno coś, nad czym możesz popracować i skorzystać z okazji, aby dowiedzieć się więcej. Jeśli pracujesz profesjonalnie przy oprogramowaniu, nie możesz wziąć tego luksusu, chyba że jest on niezwykle potrzebny.


1
Nie sądzę, aby programista hobbystyczny miał z tym coś wspólnego. To, że nie robisz czegoś profesjonalnie, niekoniecznie oznacza, że ​​masz cały czas na świecie, aby na to poświęcić. Co więcej, większość hobbystów popełni większe błędy, takie jak wybór niewłaściwych algorytmów, niż popełni większość prawdziwych profesjonalistów. Co więcej, profesjonalista prawdopodobnie pracuje nad produktem, który przetwarza znacznie więcej danych niż hobbysta (który dlatego musi być szybszy) i musi zadowolić klientów z wydajności aplikacji. Hobbyści nie mają takich ograniczeń.
Billy ONeal

Nie robią tego, ale na pewno mają więcej czasu, aby nad nimi popracować, jeśli tylko chcą.

3
Byłbym przeciwny. Mam 8 godzin dziennie, aby pracować nad czymś jako profesjonalista. Dostaję 1, może 2 godziny dziennie na moje projekty hobby.
Billy ONeal

1

za pomocą algorytmu O (N2) vs O (NlogN) na liście 100 pozycji.

Ostatnio byłem w podobnej sytuacji. Miałem szereg przedmiotów. W oczekiwanym przypadku na liście były dwa (!) Elementy, a nawet w najgorszym przypadku nie oczekuję więcej niż czterech, a może ośmiu.

Musiałem posortować tę listę. Okazuje się, że zamiana std::sortna sieć sortującą (zasadniczo wiele zagnieżdżonych if) straciła duży procent czasu pracy (nie pamiętam liczby, ale było to około 10–20%). Jest to ogromna zaleta mikrooptymalizacji, a kod ma absolutnie kluczowe znaczenie dla wydajności.

Oczywiście zrobiłem to dopiero po profilowaniu. Ale chodzi o to, że jeśli użyję języka, który jest tak niewygodny i skomplikowany jak C ++ (nie wspominając o irytująco złożonych zasadach rozwiązywania problemów z przeciążeniem), chcę czerpać pełne korzyści.


1

Skumulowane zużycie energii

Jest jedna odpowiedź, o której zawsze myślę, że brakuje jej w tej dyskusji i która trochę mnie niepokoi - skumulowane zużycie energii .

Pewnie, może nie ma to większego znaczenia, jeśli napiszesz swój program w języku zinterpretowanym na wysokim poziomie i pozwól mu działać w przeglądarce z kilkoma warstwami pośrednimi lub jeśli twoja pętla zajmie 0,01 sekundy zamiast 0,001 sekundy. Nikt nie zauważy, to znaczy, żaden indywidualny użytkownik nie zauważy.

Ale kiedy dziesiątki tysięcy, a nawet miliony użytkowników w niektórych przypadkach używają twojego kodu, cała ta dodatkowa nieefektywność sumuje się. Jeśli twoje narzędzie uniemożliwia procesorowi przejście w stan uśpienia tylko przez dziesięć sekund dziennie, a korzysta z niego milion użytkowników, nieefektywny algorytm zużył dodatkowe 140 kWh [1] dziennie.

Rzadko widzę to omawiane i myślę, że to smutne. Podejrzewam, że liczby te są znacznie gorsze w przypadku popularnych platform, takich jak Firefox i fantazyjne interaktywne aplikacje internetowe, i warto byłoby to zbadać.


[1] Właśnie to wymyśliłem, 10 milionów sekund razy 50 watów. Dokładna liczba zależy od wielu rzeczy.


1
Powinieneś zacząć od wspomnienia o magicznym słowie „mobile”. Podczas pracy na platformie komputerowej aplikacja, która zajmuje 1/100 s czasu procesora, aby narysować ramkę 60 razy na sekundę, będzie „wystarczająco szybka”; dziesięciokrotna poprawa wydajności zrobiłaby zero różnicy dla użytkownika. Jednak na platformie mobilnej aplikacja działająca przy 90% obciążeniu procesora może zużywać baterie znacznie szybciej niż przy 10%.
supercat

1

Czasami po prostu masz algorytmy, które nie mogą być lepsze niż czas liniowy, dla którego wciąż istnieje duże zapotrzebowanie na wydajność.

Przykładem jest przetwarzanie wideo, w którym nie można rozjaśnić obrazu / klatki jako podstawowego przykładu bez zapętlania każdego piksela (przypuszczam, że można to zrobić za pomocą jakiejś hierarchicznej struktury wskazującej właściwości odziedziczone przez dzieci, które ostatecznie schodzą w dół na kafelki obrazu dla węzłów liści, ale wtedy odłożyłbyś wyższy koszt zapętlenia każdego piksela do renderera, a kod byłby prawdopodobnie trudniejszy w utrzymaniu niż nawet najbardziej zoptymalizowany filtr obrazu).

W mojej dziedzinie jest wiele takich przypadków. Zwykle robię bardziej pętle o złożoności liniowej, które muszą dotykać wszystkiego lub czytać wszystko niż te, które korzystają z jakiejkolwiek zaawansowanej struktury danych lub algorytmu. Nie ma pracy, którą można pominąć, gdy trzeba dotknąć wszystkiego. Więc w tym momencie, jeśli nieuchronnie masz do czynienia z liniową złożonością, musisz sprawić, aby praca wykonywana na iterację była tańsza i tańsza.

Tak więc w moim przypadku najważniejszymi i najczęstszymi optymalizacjami są często reprezentacje danych i układy pamięci, wielowątkowość i SIMD (zazwyczaj w tej kolejności, przy czym reprezentacja danych jest najważniejsza, ponieważ wpływa to na możliwość wykonania dwóch ostatnich). Nie mam tak wielu problemów, które rozwiązują drzewa, tabele skrótów, algorytmy sortowania i tego typu rzeczy. Mój codzienny kod brzmi bardziej jak „zrób coś dla każdego”.

Oczywiście jest to kolejny przypadek, aby porozmawiać o tym, kiedy konieczne są optymalizacje (a co ważniejsze, gdy nie są), mikro lub algorytmiczne. Ale w moim szczególnym przypadku, jeśli krytyczna ścieżka wykonania wymaga optymalizacji, przyrosty prędkości 10x + są często osiągane przez optymalizacje na poziomie mikro, takie jak wielowątkowość, SIMD i przestawianie układów pamięci i wzorców dostępu w celu poprawy lokalizacji odniesienia. Nieczęsto zdarza mi się zastępować sortowanie bąbelkowe sortowaniem introsortowym lub radix lub wykrywanie kolizji złożoności kwadratowej BVH tak bardzo, jak znaleźć punkty aktywne, które, powiedzmy, korzystają z podziału pola gorącego / zimnego.

Teraz w moim przypadku moja dziedzina jest tak krytyczna pod względem wydajności (raytracing, silniki fizyki itp.), Że powolny, ale doskonale poprawny raytracer, który zajmuje 10 godzin do renderowania obrazu, jest często uważany za bezużyteczny lub więcej niż szybki, który jest całkowicie interaktywny, ale generuje najbrzydsze obrazy z promieniami przeciekającymi wszędzie z powodu braku wodoszczelnego skrzyżowania promień / tri. Szybkość jest prawdopodobnie podstawową miarą jakości takiego oprogramowania, prawdopodobnie nawet bardziej niż poprawność do pewnego momentu (ponieważ „poprawność” jest rozmytym pomysłem z raytracowaniem, ponieważ wszystko jest przybliżone, o ile nie ulega awarii lub coś takiego). A kiedy tak jest, jeśli nie myślę o wydajności z góry, stwierdzam, że muszę zmienić kod na najdroższym poziomie projektowania, aby obsłużyć bardziej wydajne projekty. Więc jeśli nie

Gry to kolejna dziedzina podobna do mojej. Nie ma znaczenia, jak poprawna jest logika gry lub jak łatwa w utrzymaniu i genialnie zaprojektowana baza kodu, jeśli gra działa z prędkością 1 klatki na sekundę jak pokaz slajdów. W niektórych obszarach brak prędkości może faktycznie uczynić aplikację bezużyteczną dla użytkowników. W przeciwieństwie do gier, nie ma wystarczająco dobrych danych w obszarach takich jak raytracing. Użytkownicy zawsze chcą większej prędkości, a konkurencja przemysłowa przede wszystkim poszukuje szybszych rozwiązań. Nigdy nie będzie wystarczająco dobry, dopóki nie będzie w czasie rzeczywistym, w którym to momencie gry będą używać znaczników ścieżki. A potem prawdopodobnie nadal nie będzie wystarczająco dobry dla efektów wizualnych, ponieważ artyści mogą chcieć załadować miliardy wielokątów i przeprowadzić symulacje cząstek z samozderzeniem między miliardami cząstek przy 30+ FPS.

Teraz, jeśli jest to wygodne, mimo to nadal piszę około 90% kodu w języku skryptowym (Lua), nie martwiąc się o wydajność. Ale mam niezwykle dużą ilość kodu, który w rzeczywistości potrzebuje pętli od milionów do miliardów rzeczy, a kiedy przeglądasz miliony do miliardów rzeczy, zaczynasz zauważać epicką różnicę między naiwnym jednowątkowym kodem, który wywołuje brak pamięci podręcznej przy każdej iteracji, powiedzmy, wektorowy kod działający w równoległym dostępie do sąsiadujących bloków, w których żadne nieistotne dane nie są ładowane do linii pamięci podręcznej.


0

Jak już wspomniałeś, dbanie o problemy z mikroprocesorem jest bezwartościowe, zanim zdasz sobie sprawę z niektórych problemów naprawdę spowodowanych przez te problemy


0

Odpowiedź na to pytanie jest naprawdę niemożliwa. Większość tworzonego dziś oprogramowania to wewnętrzne strony internetowe i aplikacje LOB, a dla tego rodzaju programowania twoje rozumowanie jest całkiem poprawne. Z drugiej strony, jeśli piszesz coś w rodzaju sterownika urządzenia lub silnika gry, żadna optymalizacja nie jest „przedwczesna”; istnieje prawdopodobieństwo, że twoje oprogramowanie będzie działało na bardzo różnych systemach z różnymi ograniczeniami sprzętowymi. W takim przypadku należy zaprojektować pod kątem wydajności i upewnić się, że nie wybrano algorytmu nieoptymalnego.


Dokładnie to, co chciałem powiedzieć. Każde oprogramowanie ma swoją domenę aplikacji i nie należy oczekiwać, że będzie się zachowywać optymalnie poza nim. W tym sensie przedwczesna optymalizacja jest przykładem błędnego perfekcjonizmu.
K.Steff

0

Myślę, że problemem programisty, który tak bardzo dba o wydajność, jest to, że czasami w swoim życiu musiał pisać kod mikro-wykonawczy, być może bardzo pilnie, i uczył się, uczył się, uczył się, a na koniec znał wiele rzeczy i sztuczek.

A teraz trudno jest zapomnieć i bez wcześniejszego pomiaru, który pokazuje, że nie musi się martwić, jest po bezpiecznej stronie, używając szybkiego kodu.

Zawsze miło jest pokazać swoją głęboką wiedzę, umiejętności i sztuczki oraz ponownie wykorzystać to, czego się nauczyłeś. Sprawia, że ​​czujesz się cenny, a czas poświęcasz na naukę, bo warto.

Czasami w życiu nauczyłem się, że przyrost prefiksu jest szybszy ...

for (int i = 0; i < MAX; ++i)

... niż przyrost po postfiksie:

for (int i = 0; i < MAX; i++)

Teraz, jeśli MAX jest niski, nie będzie to miało znaczenia, a jeśli w pętli będzie prawdziwa praca, to też nie będzie miało znaczenia. Ale nie ma powodu, aby używać wersji Postfix, nawet jeśli dzisiejszy kompilator samodzielnie zoptymalizuje kod.

Być może osoby poszukujące wydajności potrzebują dodatkowego celu, oprócz napisania „działającego kodu”, takiego jak „działający i czytelny kod”, aby uzyskać wytyczne na wielkim morzu opcji.


0

czy po prostu miałem szczęście, że nie muszę się tym zbytnio przejmować, czy też jestem złym programistą?

Dbasz o swoje wymagania? Jeśli wydajność nie jest wymagana, nie przejmuj się nią. Poświęcenie na to znacznego czasu jest niekorzystne dla twojego pracodawcy.

Do pewnego stopnia wydajność jest zawsze wymagana. Jeśli możesz uderzyć, nie zastanawiając się nad tym, masz rację, nie myśląc o tym.

Osobiście najczęściej kieruję się wydajnością, gdy moje testy trwają długo. Jestem zbyt niecierpliwy, aby czekać 5 minut na ukończenie zestawu testów. Ale zwykle rozwiązuje się to, bawiąc się testami.

Moje pytanie brzmi: dlaczego tak bardzo zależy na dużej liczbie programistów? Czy to naprawdę problem dla większości programistów,

Istnieje duża liczba programistów, którzy są uzasadnieni, jak bardzo im zależy. Są duże liczby, które nie są. Porozmawiajmy o tych, którzy nie są.

Jedną z pierwszych rzeczy, których programiści uczą się w szkole, po tym, jak sprawić, by rzeczy faktycznie działały, jest duża notacja O. Wielu z nich właściwie uczy się lekcji, a tym samym właściwie skupia się na rzeczach, na które dramatycznie wpływa n. Inni nie rozumieją matematyki i zabierają lekcję, że gdy już zadziała, musi być szybka. Co gorsza, niektórzy z tych uczniów nigdy nie dowiadują się więcej o tym, co jest ważne z twoim kodem, oprócz tego, że działa i działa szybko. Nieodebrane lekcje: spraw, aby było czytelne, zaprojektuj je dobrze, nie baw się w to bez powodu.

Knuth miał rację: przedwczesna optymalizacja jest źródłem wszelkiego zła. Ale kiedy to zadziała, jaki jest następny krok? Szybko, prawda? NIE! Następny krok jest czytelny. Czytelny jest pierwszy, następny, środkowy i ostatni krok. Wiele osób, które przeprowadzają niepotrzebne optymalizacje wydajności, dokłada czytelności do magistrali.

Niektórzy nawet odczuwają przewrotny dreszczyk emocji z powodu nieczytelnego kodu. Musieli cierpieć, patrząc na trudny do zrozumienia kod stworzony przez innych, więc teraz ich kolej na zwrot.

Wiem to, bo kiedyś to robiłem. Kiedyś przerobiłem doskonale czytelną 5-liniową strukturę, aż do nieczytelnego jednoliniowego wyrażenia logicznego i z dumą wysłałem ją do mojego profesora, który spodziewał się zaimponować, ponieważ mogłem stworzyć coś tak zwartego i zastraszającego. Nie dostałem pochwały, na którą liczyłem.

Jeśli kod pozostaje czytelny, szybkie później jest łatwe. Dlatego Knuth podkreśla „przedwczesne”, a nie „niepotrzebne”. Ponieważ na pewno szybsze jest lepsze. Ale lepszy jest lepszy tylko w zależności od tego, co poświęcisz za to. Więc poczekaj, aż dowiesz się, jakiej wydajności naprawdę potrzebujesz, zanim poświęcisz się dla niej. Niechętnie poświęcajmy czytelność, ponieważ po jej zniknięciu trudno jest odzyskać.

Poza czytelnością jest cały świat projektowania oprogramowania. O czym jest ta strona. Niektórzy nie mają pojęcia, co zrobić w zakresie projektowania. Ponieważ nie potrafią zaimponować designem, robią nieczytelny bałagan, więc ludzie nie mogą powiedzieć, że nie mają pojęcia. Ponieważ nikt nigdy nie naprawia ich kodu, musi to być dobry kod, prawda?

Dla niektórych wydajność jest wymówką, aby zrobić wszystko, co chcą. Programiści mają dużą moc i autonomię. Zaufano im. Nie nadużywaj zaufania.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.