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.
źródło
Odpowiedzi:
Formatowanie kodu
źródło
=
czwartego i czwartego dla inicjatorów!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ę.
źródło
float
. Z jakiegoś powodu nie mogę pojąć, wiele osób uważa, że to dobra rzecz.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.
źródło
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.
źródło
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.format
tak, aby roczny błąd pliku nie powodował dodatkowych obiektów na stercie. To była strata.źródło
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.
źródło
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ę ;-)).
źródło
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.
źródło
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.
źródło
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.
źródło
programmers.
=programmers.stackexchange.com
być 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ę.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 ;-)
źródło
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.
Twitter i Facebook przychodzą mi na myśl :-)
źródło