Co programiści „mikrooptymalizują” na dziś? [Zamknięte]


14

W czasach „dobrych starych czasów”, kiedy kopiowaliśmy shareware na dyskietki dla przyjaciół, również stosowaliśmy sporo montażu. Była powszechna praktyka „mikrooptymalizacji”, polegająca na tym, że wpatrywałeś się i patrzyłeś na linie montażowe, dopóki nie wymyśliłeś sposobu na wyrażenie tego w jednej mniejszej instrukcji. Było nawet powiedzenie, które było matematycznie niemożliwe, że „ zawsze możesz usunąć jeszcze jedną instrukcję. Biorąc pod uwagę, że zmiana wydajności środowiska wykonawczego przez małe stałe czynniki nie jest głównym problemem dla (większości) programów dzisiaj, programiści przenoszą te mikro- wysiłki związane z optymalizacją gdzie indziej?

Innymi słowy, czy najlepszą praktykę można wprowadzić w stan ekstremalny, w którym nie przynosi ona już żadnej wartości? A zamiast tego marnujesz czas?

Na przykład: czy programiści tracą czas na uogólnianie prywatnych metod, które są wywoływane tylko z jednego miejsca? Czy zmarnowany czas zmniejsza liczbę przypadków testowych? Czy programiści (wciąż) są nadmiernie zaniepokojeni redukcją linii kodu?

Istnieją dwa świetne przykłady tego, czego szukam poniżej: (1) Spędzanie czasu na znajdowaniu odpowiednich nazw zmiennych, a nawet na zmianie nazwy wszystkiego; oraz (2) Usunięcie nawet drobnych i wątłych duplikatów kodu.


Zauważ, że różni się to od pytania „Do czego optymalizujesz? ”, Ponieważ pytam, co inni programiści wydają się maksymalizować, z piętnem tych optymalizacji „mikro”, a zatem nie produktywnego wykorzystania czasu.



@Macneil, nie wiem, do czego inni programiści optymalizują mikro, ale nie mam na to dużo czasu. Moi współpracownicy też są zajęci (tylko moje 2 centy).
Job

@Job: Nigdy nie widziałeś, żeby ktoś marnował czas i zmieniał kod bez powodu? Uważaj się za szczęściarza. Poniżej dwa świetne przykłady: Nazwy zmiennych i niewielkie powielanie kodu.
Macneil

1
Pytanie, jak obecnie sformułowano, jest poprawą. Nie mogę usunąć głosowania z bliska, ale za kilka dni się zestarzeje. W rzeczywistości pytanie przyciąga całkiem dobre odpowiedzi.
Robert Harvey

1
BTW, każda najlepsza praktyka może być doprowadzona do takiego stopnia, że ​​nie przyniesie już wartości.
Robert Harvey

Odpowiedzi:


22

Formatowanie kodu

Don't     get    me   wrong             ,
code      should be   consistent        & 
readable                                , 
but       some   take it         too far.

Ach tak, stare dobre typy zmiennych i nazwy zmiennych potrzebują własnych kolumn w szeregu deklaracji zmiennych. O! Zapomniałem dodatkowego wiersza dla =czwartego i czwartego dla inicjatorów!
Macneil

[Cholera, chciałbym głosować, ale mój dzienny limit został osiągnięty ...]
Macneil

3
Nasi profesorowie zmusili nas do sformatowania kodu dla naszych zadań w ten sposób. Wiele lat zajęło mi pozbycie się tego nawyku.
Oliver Weiler

O jej. Na inne odpowiedzi śmiałem się, ale ta naprawdę boli :-(
Steve314,

2
+1 Kiedyś pracowałem z biblioteką, w której programista odmówił poprawienia kodu, ponieważ spieprzył kod w formacie kolumnowym ...
Dean Harding

20

W przeszłości pisałem dużo asemblerów. Nie tylko kompilatory stały się lepsze, ale większość sprzętu ma teraz dużo logiki poświęconej wykonywaniu kodu poza kolejnością. Prawdziwym mikro-problemem jest planowanie, większość instrukcji komputerowych potrzebuje kilku zegarów maszyny, aby wygenerować wynik - a obciążenie pamięci, które nie uzupełnia pamięci podręcznej, może zająć kilkaset! Pomysł polegał więc na zaplanowaniu innych instrukcji, aby zrobiły coś pożytecznego, zamiast czekać na wynik. Nowoczesne maszyny mogą wydawać kilka instrukcji na okres czasu. Kiedy zaczęliśmy dostawać sprzęt poza kolejnością, okazało się, że próba uzyskania doskonałej wydajności dzięki ręcznemu kodowaniu stała się grą kubków. Po pierwsze, HW z poza zamówieniem nie wykona instrukcji w starannie wykonanej kolejności, wymyślna nowa architektura sprzętu obniżyła karę za nieoptymalne planowanie oprogramowania na tyle, że kompilator zwykle mieścił się w granicach kilku procent wydajności. Dowiedziałem się również, że kompilatory wdrażają teraz dobrze znane, ale generujące złożoność sztuczki, takie jak rozwijanie, ładowanie od dołu, tworzenie potoków oprogramowania itp. Najważniejsze jest, aby naprawdę ciężko pracować, pomiń niektóre z tych sztuczek, a kompilator cię pokona. Skorzystaj z nich wszystkich, a liczba instrukcji asemblera, których potrzebujesz, zwiększy się kilkakrotnie!

Prawdopodobnie jeszcze ważniejsze, większość problemów z wydajnością, nie dotyczy tempa wydawania instrukcji, ale wprowadzania danych do procesora. Jak wspomniałem powyżej, opóźnienie pamięci wynosi teraz setki cykli, a procesor może wykonać kilka instrukcji na okres taktowania, więc chyba że program - a zwłaszcza struktury danych są zaprojektowane w taki sposób, że współczynnik trafień w pamięci podręcznej jest wyjątkowo wysoki, mikrotuning na polecenie poziom nie będzie miał żadnej wypłaty. Tak jak wojskowi mówią, że amatorzy mówią taktyki, profesjonaliści mówią o logistyce. Programowanie wydajności obejmuje teraz ponad 90% logistyki (przenoszenie danych). Jest to trudne do oszacowania, ponieważ nowoczesne zarządzanie pamięcią zwykle ma wiele poziomów pamięci podręcznej, a strony pamięci wirtualnej są obsługiwane przez jednostkę sprzętową zwaną TLB. Ważne jest również wyrównanie adresów na niskim poziomie, ponieważ rzeczywiste transfery danych nie są w jednostkach bajtów, lub nawet 64-bitowe długie, ale występują w jednostkach linii pamięci podręcznej. Wówczas większość nowoczesnych maszyn ma sprzęt, który próbuje przewidzieć, które linie pamięci podręcznej nie będą potrzebne w najbliższej przyszłości, i automatycznie wykona wstępne pobranie danych, aby dostać się do pamięci podręcznej. Tak więc rzeczywistość jest taka, że ​​przy nowoczesnych procesorach modele wydajności są tak złożone, że są prawie niezrozumiałe. Nawet szczegółowe symulatory sprzętowe nigdy nie są w stanie dopasować dokładnej logiki układów, więc dokładne dostrojenie jest po prostu niemożliwe.

Wciąż jest miejsce na ręczne kodowanie. Biblioteki matematyczne (jak powiedzmy funkcję exp), podobnie jak ważniejsze operacje algebry liniowej (takie jak mnożenie macierzy) są nadal zwykle ręcznie kodowane przez ekspertów pracujących dla dostawcy sprzętu (tj. Intel, AMD lub IBM), ale prawdopodobnie tylko potrzebuję kilku najwyższej klasy programistów zajmujących się asemblerem na gigantyczną korporację.


1
Ręczne kodowanie staje się coraz trudniejsze dla osób pracujących poza dostawcami procesorów, ponieważ zadanie w coraz większym stopniu zależy od wiedzy osób posiadających informacje poufne i narzędzi specyficznych dla dostawców (kompilatorów i profilerów). spiral.net próbuje modelować optymalizację procesora i automatyzować wyszukiwanie optymalnych rozwiązań.
rwong

Wiele języków komputerowych popycha programistów do bezsensownej mikrooptymalizacji, utrudniając stosowanie podwójnej precyzji do obliczeń, w których wyniki zostaną przedstawione float. Z jakiegoś powodu nie mogę pojąć, wiele osób uważa, że ​​to dobra rzecz.
supercat

10

Czasami spędzam (marnuję?) Czas, wybierając dobrą nazwę dla zmiennej lub metody, aby była nie tylko precyzyjnie opisowa, ale także miała dobry styl językowy.

Idąc nieco dalej, staram się umieścić wszystkie prace (program) w tym samym stylu językowym. Czasami moja opinia się zmienia i poprawiam książkę. :)

Nie, to zajmuje tyle czasu. To raczej rzadkie. Ale lubię zachować dobrą gramatykę w moich programach.


3
Nazewnictwo jest trudne. Dlatego powinieneś zmienić nazwę, jeśli myślisz o znacznie lepszym. Zauważ, że to chwyta wysiłek umysłu i mam nadzieję, że krystalizuje również „dlaczego”.

1
Jest to bardzo lewostronna rzecz (lewa strona twojego mózgu jest odpowiedzialna za przetwarzanie języka). Jako prawostronny programista spędzam dużo mniej czasu na martwieniu się o semantykę, a więcej o tym, jak wszystkie elementy pasują do siebie.
Jason Baker

7
Nazwy zmiennych i metod są ważną częścią łatwości konserwacji. One powinny spędzać czasu na myślenie o nazwach, tak, że 5 lat od teraz, osoba utrzymanie kodu ma łatwiej z nim.
GrandmasterB

@grandmaster: Nie można zgodzić się więcej.
Robert Harvey

4
@ right-brainers: Proszę napisać kod dla lef-tbrainers, abym mógł również odczytać twój kod
Juan Mendes

10

Złożoność czasowa. Spędzam zdecydowanie za dużo czasu na optymalizacji rzeczy pod kątem najgorszego przypadku w skali, która jest o rząd wielkości większa niż cokolwiek, co wiem, że program realistycznie napotka.

Jestem po prostu zbyt obsesyjnie, aby puszczać „ale może wyrosnąć ta wielka” kość, nawet jeśli inne decyzje projektowe uniemożliwiają to realistycznie.

Jednak w mojej obronie, jeśli to, co nierealne, stanie się rzeczywistością ... optymalizacja nie jest już dłużej „mikro”. Uwaga: nie powiedziałem niemożliwe , ale nierealne .

Oczywiście wymagania mają pierwszeństwo.


1
Pracowałem dla firmy, która zatonąła z tego powodu.
Henry

2
@Henry - Obiecuję, że to nie ja :)
Tim Post

1
@Henry: Masz na myśli, że zmarnowali czas na optymalizację rzeczy, które prawdopodobnie nigdy się nie wydarzą, lub nie myśleli o rzeczach, które prawdopodobnie „nigdy” się nie zdarzą… dopóki się nie wydarzyły i nie było za późno?
Dean Harding

2
@Henry: Jeśli firma ma wysoce skalowalny produkt i nadal tonie, jest to wina sprzedaży i marketingu (za to, że nie jest skierowana do dużych klientów i nie wycenia odpowiednio), a nie rozwój.
rwong

1
Firma poświęciła zbyt dużo czasu na zaprojektowanie systemu dla milionów użytkowników, gdy nie mieli 1 użytkownika. To musiała być wina marketingowa. Tak, obwiniajmy marketing, ci faceci są idiotami.
Henry

8

Wydaje mi się, że zmarnowałem tygodnie, majstrując przy obsłudze wyjątków Java.

To dużo pracy dla kodu, który zawodzi dwa lub trzy razy w roku. Czy powinno to być ostrzeżenie czy informacja? Błąd czy błąd krytyczny? Czy to naprawdę fatalne, czy proces zostanie odrodzony za pięć minut? Czy to naprawdę ostrzeżenie, jeśli wszystko jest w stanie domyślnym?

Dużo patrzenia na pępek i dyskusji na temat natury błędu IO. Jeśli nie możesz odczytać pliku przez sieć, czy jest to błąd pliku, czy błąd sieci? I tak dalej.

W pewnym momencie zamieniłem wszystkie ciągi konkatacji na String.formattak, aby roczny błąd pliku nie powodował dodatkowych obiektów na stercie. To była strata.


3
To ważna praca. Cena niepowodzenia = twój komunikat o błędzie pojawia się na codziennej WTF.
Steve314,

7

Podejrzewam, że zbytnio martwię się o LOC. Nie tyle samych LOC, ale raczej liczba instrukcji, a nawet więcej zduplikowanych instrukcji. Mam alergię na duplikaty kodu. Ogólnie uwielbiam refaktoryzować, ale przypuszczam, że około 50% tego, co robię, nie czyni kodu znacznie ładniejszym.


4
+1 za „Mam alergię na duplikat kodu”. Zobacz także moją odpowiedź tutaj: programmers.stackexchange.com/questions/14610/…
Macneil

Zastanawiam się nad tym - mam ten problem z pętlami, który odnosi się do kwestii Knutha dotyczącej goto, a czasem z koniecznością wymiany cyklu lub dwóch lub kilku zduplikowanych kodów w celu napisania kodu strukturalnego. I będzie używać goto w bardzo rzadkich przypadkach (najczęściej = wygenerowany kod), ale mój odmianą tej choroby jest marnowanie czasu martwić i bawiąc się pętla bez końca starając się wyeliminować trywialny nieefektywność jeszcze zachować odczytanie kodu.
Steve314,

5

Czytanie pliku wiersz po wierszu zamiast po prostu wczytywania całej linii w łańcuch i przetwarzanie łańcucha w jednym ujęciu.

Jasne, wpływa to na szybkość wykonywania, ale rzadko jest wart dodatkowych linii kodu. To sprawia, że ​​kod jest znacznie trudniejszy do utrzymania i zwiększa rozmiar kodu.

Tak, prawdopodobnie nie ma to sensu, jeśli plik ma 3 GB, ale większość plików nie jest tak duża (przynajmniej nie te, z którymi pracuję ;-)).


3
Nawet jeśli ma 3 GB lub więcej, 64-bitowa maszyna poradzi sobie z pamięcią wirtualną. Użyj pliku zmapowanego w pamięci, a on nawet nie odczyta żadnej części pliku, dopóki nie będzie potrzebny. Pamiętaj, że nawet taka jest mikrooptymalizacja w normalnym przypadku.
Steve314,

3

Kiedy pisałem język asemblera, sensowne było przechwytywanie bajtów i cykli, ale to było dawno temu i od tego czasu kompilatory przeszły długą drogę. Nie próbowałbym teraz ręcznie optymalizować jednego.

Podobnie, kiedy przestawiłem się na pisanie w C, było dość łatwo wykonać lepszą pracę niż kompilatory C, ale każdego roku Borland, Microsoft lub ktoś wypuszczał nowy, ulepszony, który wykopał poprzedni w całym pokoju. Zacząłem zwracać uwagę na faktyczny kod asemblera, który emitował kompilator, i zmieniłem, jeśli nie pisałem jakiegoś ładnego i ścisłego kodu, rozwijając pętle, przenosząc zmienne poza pętle itp.

Teraz piszę w dużo wyższych językach, takich jak Perl, Python i Ruby. Używam niektórych sztuczek kompilatora z góry, takich jak rozwijanie pętli, jeśli ma to sens, przenoszenie zmiennych statycznych poza pętle itp., Ale nie martwię się o to prawie tak bardzo, ponieważ procesory są teraz odrobinę szybsze. Jeśli aplikacja wydaje się nieoczekiwanie przeciągać, skorzystam z narzędzia do profilowania i zobaczę, co mogę znaleźć, a następnie, jeśli coś można poprawić, zacznę porównywać różne sposoby myślenia o zrobieniu czegoś szybciej, a następnie zobaczę, jak to zrobić. skaluje się.

Ogólnie staram się być mądry w tym, jak piszę kod, w oparciu o wieloletnie doświadczenie.


Cześć Greg, dziękuję za odpowiedź, ale szukam, co programiści robią, aby tracić czas, a nie optymalizować wydajność środowiska wykonawczego. Istnieją dwa świetne przykłady powyżej: Zmienne nazwy i usuwanie nawet niewielkiego powielania kodu.
Macneil

3

Mikrooptymalizacja: poprawa wydajności jednowątkowej rzeczy, które można zrównoleglać lub które można po prostu poprawić za pomocą nowego sprzętu.

Jeśli coś jest powolne na komputerze sprzed 10 lat, tańszym rozwiązaniem jest prawdopodobnie zakup nowszego, szybszego komputera, niż marnowanie czasu na programowanie. Jednak głównym celem optymalizacji powinno być znalezienie sposobu na wykorzystanie 8 rdzeni, które możesz dziś uzyskać, lub 64, które będziesz w stanie uzyskać za kilka lat, zamiast dręczyć się o drobiazgi, takie jak dodatkowy koszt śmieci kolekcja.


2

Mikrooptymalizacja pod względem wydajności środowiska uruchomieniowego nie jest dziś problemem zamkniętym (choć może być mniej powszechnym). Czasami muszę tłumaczyć ludziom, że narzut związany z przydzielaniem dodatkowego obiektu przy każdym żądaniu lub dodawaniem dodatkowego wywołania funkcji jest znikomy.

Poza tym widzę też programistów mikrooptymalizujących dla uproszczenia (w tym siebie). Jeszcze gdzieś musiałem pracować, że nie musiałem wyjaśniać różnicy między prostotą a uproszczeniem.


Dlaczego mieliby programiści. być kimś innym? Jaka jest różnica, proszę.
Dan Rosenstark,

@Yar - Dlaczego programiści mieliby się różnić od czego?
Jason Baker

dlaczego programmers.= programmers.stackexchange.combyć inaczej z każdego miejsca, że już pracował gdzie miałeś wyjaśnić różnicę między tymi dwoma pojęciami? Proszę wyjaśnić różnicę.
Dan Rosenstark,

@Yar - nie wiem, czy tak by było. W rzeczywistości, ponieważ programmers.se ma pomóc programistom w gromadzeniu wiedzy, mam nadzieję, że nie jest tak, że ludzie mogą być choć trochę oświeceni moją odpowiedzią.
Jason Baker

2
Jaka jest różnica b / t prostota i prostota?
Dan Rosenstark

2

Jestem zwolennikiem zasady „nie optymalizuj-chyba-naprawdę-koniecznie”. I jestem za zasadą pierwszego profilu. I w zasadzie zoptymalizuję tylko coś, co zrobi potrzebną różnicę.

To w zasadzie. Ale zasada jest kłamcą.

Mam zwyczaj dbania o funkcje w często używanych bibliotekach, które, jak sądzę, będą często używane w wewnętrznych pętlach. A potem, gdy zauważysz coś w innej funkcji, nie jest to tak często używane ...

Aha - i jest jeszcze logika „piszę to - oczywiście, że to ważna biblioteka”.

Aha - i BTW. Nigdy nie korzystałem z profilera do kierowania optymalizacją. Nie mam dostępu do komercyjnego i nigdy nie budowałem motywacji, aby dowiedzieć się o gprof.

Zasadniczo jestem hipokrytą. Te zasady dotyczą innych ludzi, ale zbytnio boję się, że ktoś powie „ale dlaczego napisałeś to tak wolno i gówno?” więc nigdy tak naprawdę nie mogę ich właściwie zastosować do siebie.

Jednak nie jestem aż tak zły, jak tu się czuję. I jestem całkowicie odporny na samooszukiwanie się, więc wiesz, że mam rację!

EDYTOWAĆ

Powinienem dodać - jednym z moich głównych głupich rozłączeń jest koszt połączeń. Oczywiście nie wszędzie, ale w tych, które myślę, że będą używane w wielu wewnętrznych pętlach (gdzie nie mam obiektywnych dowodów na problem). Napisałem nieprzyjemny kod oparty na offsecie, aby uniknąć wykonywania wirtualnych wywołań metod więcej niż raz. Wynik może być nawet wolniejszy, ponieważ prawdopodobnie nie jest to styl kodowania, z którym optymalizatory są zaprojektowane, aby dobrze sobie radzić - ale jest mądrzejszy ;-)


+1 Ah, tak! Ta klasa analizuje bardzo specyficzne i dziwaczne argumenty wiersza poleceń, ale pozwól mi sformatować i w pełni udokumentować ją, gdy Javadoc, ponieważ na pewno inni będą tego używać przez lata! [Trzeba będzie później głosować, mój dzienny limit został osiągnięty.]
Macneil

@Macneil - to Doxygen, dam ci znać. Z każdą włączoną opcją, każda mała funkcja jest „dokumentowana” w szczegółach sprawdzających, wraz z tuzinem ładnych wykresów GraphViz. Naprawdę pomaga wszystkim docenić, jak ważny jest mój kod - a wydruk może tapetować całkiem duży biurowiec.
Steve314,

1

W dawnych czasach, gdy komputery mierzyły czasy zegarowe w mikrosekundach, pamięć mierzona w kilobajtach i prymitywne kompilatory, mikrooptymalizacja miała pewien sens.

Szybkość i wielkość komputerów obecnej generacji oraz jakość kompilatorów obecnej generacji oznaczają, że mikrooptymalizacja jest zwykle całkowitą stratą czasu. Wyjątkiem są sytuacje, w których absolutnie musisz uzyskać maksymalną wydajność z jakiegoś intensywnego obliczeniowo kodu ... lub piszesz dla systemu osadzonego z bardzo ograniczonymi zasobami.

ale szukam, co programiści robią, aby tracić czas, a nie optymalizować wydajność środowiska wykonawczego.

Twitter i Facebook przychodzą mi na myśl :-)


Cześć Stephen, to świetny komentarz na temat mikrooptymalizacji, ale czy widzisz jakieś współczesne odpowiedniki? [Facebook i Twitter nie są „najlepszymi praktykami”, które można przenieść za daleko.]
Macneil

Nadal ma to sens. Czasy zegarowe mogą być o rząd wielkości mniejsze, ale kod jest o
rząd

@Stuart - oznacza to, że jest więcej rzędów wielkości kodu do mikrooptymalizacji ... co po prostu się nie skaluje.
Stephen C
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.