Kompilator wie jakiej instrukcji użyć, aby maksymalnie przyśpieszyć wykonanie kodu. |
Pomijając niektóre dziwactwa można śmiało założyć, że niemal zawsze tak jest. W tym miejscu się zgadzamy.
Ty jako deweloper tego nie musisz wiedzieć, bo w praktyce nie chcesz śledzić niuansów związanych z optymalizacją kodu pod konkretną architekturę CPU. |
Dopóki takie "niuanse" nie są przyczyną błędów, to jak najbardziej. Jeśli wszystko działa, to nie ma co ruszać pojedynczych bitów.
Prostym przypadkiem optymalizacji stosowanej jest używanie właśnie xor ax,ax zamiast mov ax,0 bo jest po prostu szybsze (a przynajmniej było szybsze na starszych procesorach i wymagało mniej taktów CPU). |
Nie wklepałem tej instrukcji ręcznie, kompilator sam uznał, że xor lepiej pasuje, niż mov i na początku się z nim zgodziłem.
Możliwe, ponoć jestem "czepialski", jednak chyba dość wyraźnie zaznaczyłem, że w normalnym kodzie nie ma takich problemów.
Możliwość wykonania tego samego obliczenia na kilka różnych sposobów nie oznacza, że język jest zły. |
Czy zawsze musi być jeden sposób na zrobienie A, C oraz E? Nie, ale jeśli to się za mocno rozrasta, to nagle mamy "szczególne przypadki", których żaden człowiek nie jest w stanie zrozumieć. |
1. W prostych działaniach to nie ma żadnego znaczenia. Jeśli projekty są niewielkie, to zajmowanie się takimi drobiazgami jest oczywiście bez sensu.
2. Gdy projekty są duże, to wprowadzenie każdej nowej funkcjonalności wiąże się z coraz większym ryzykiem. Jeśli mamy N dobrze działających elementów, to dodanie funkcjonalności oznaczonej numerem N+1 spowoduje, że w połączeniu z jedną z poprzednich mamy w zasadzie N nowych przypadków użycia. Jeśli założymy, że wszystko można zrobić za pomocą wszystkiego, to rozpatrzenie wszystkich takich sytuacji ma złożoność co najmniej kwadratową.
3. Jeśli używamy danego narzędzia do realizacji celu, do którego zostało wymyślone, to teoretycznie nie powinno być żadnych problemów. Tak jest w większości przypadków i to jest w porządku. Nie popadajmy ze skrajności w skrajność. Gorzej, gdy cele są nowe, a narzędzia stare. Wtedy nigdy nie wiadomo, co się stanie.
Po prostu kompilator dobierze te narzędzia, które są szybsze, bo np. kod da się bardziej zrównoleglić na poziomie CPU o którym Ty nie musisz mieć żadnego pojęcia. |
1. Jeśli wszystko działa, to nie ma sensu tego ruszać bez żadnego powodu.
2. Jeśli coś nie działa u mnie, a u kogoś innego przy identycznej konfiguracji działa bez problemów, to najprawdopodobniej problem leży w niższej warstwie abstrakcji.
3. Poszczególne warstwy nie są przejrzyste. Są poplątane jak jakaś pajęczyna. Moim zdaniem wcześniej czy później ktoś zechce to posprzątać i nieco uporządkować, bo tego będzie po prostu za dużo.
Optymalizacje kodu na poziomie instrukcji assemblera to cała nauka, a nie zwyczajne użycie innej instrukcji. Poczytaj sobie wątek |
Czytałem już wcześniej (chyba nawet ktoś z tego forum w którymś temacie wstawił kiedyś link) i zdaję sobie z tego sprawę. Fakt, że coraz częściej muszę sięgać do tej "nauki" sprawia, że zaczynam się zastanawiać nad tym, czy nie dałoby się utworzyć takich mechanizmów, aby "zaglądanie pod maskę" stało się niemal całkowicie zbędne.
Do tego powinieneś rónież wiedzieć jak CPU zarządza swoim cachem podręcznym, aby wypowiadać się w jakimkolwiek sensownym stopniu na temat wydajności niskopoziomowej. |
Efekt działania będzie taki sam, lecz kompilator sprawi, że ten kod wykona się szybciej aniżeli gdybyś sam napisał kod w Assemblerze, bo Ty po prostu nie będziesz miał wiedzy, która instrukcja CPU do czego będzie szybsza. |
Tak, ale co zrobić, gdy nie chodzi o poprawę wydajności, tylko o namierzanie i naprawianie błędów? Z poziomu C++ ich nie widać. Ustawienie innego poziomu optymalizacji nie zawsze pomaga, a wtedy trzeba "zanurkować nieco głębiej".
Instrukcje CPU, które widzisz po skompilowaniu CPU wcale nie muszą być dokładnie tym co zakodowałeś w C++. |
Zgadzam się, jednak jeśli masz do dyspozycji C++ i asemblerowy "groch z kapustą", to musisz jeszcze dobrać się do większości warstw pośrednich. Inaczej tylko wiadomo, że z czegoś wysokopoziomowego, co wygląda całkiem dobrze i u kogoś innego działa, otrzymujesz kod maszynowy, który na Twojej maszynie nie działa. A to nie wystarczy, aby łatwo znaleźć tą jedną linijkę do poprawienia.
większość przykładów jest po prostu hiperbolizowana |
Nie ma idealnego narzędzia. Ktoś pyta: czy warto korzystać z X? Zawsze da się znaleźć przykład, gdzie X pasuje i zawsze trafi się kontrprzykład mówiący o tym, że X jest beznadziejne. Jeśli powiesz "tak", to oskarżą Cię o wyróżnianie jakiegoś języka spośród innych i podadzą przykład, gdzie używanie X jest bez sensu. Jeśli powiesz "nie", to zapytają, czego w takim razie należy użyć, ewentualnie podadzą przykłady, gdzie X pasuje jak ulał, zapytają o Twoje doświadczenie związane z X lub powiedzą, że to jest problem, którego nikt nie rozwiązał tak, jak należy, a rozwiązanie korzystające z X częściowo działa.
Samomodyfikujący się kod występuje praktycznie tylko w malware |
1. Możliwe, że coś pokręciłem i źle odczytałem kody operacji.
2. To, że problem jest trudny do namierzenia, wynika między innymi ze złożoności różnych mechanizmów. Wystarczy zapomnieć o jednym drobiazgu i można nigdy nie trafić na rozwiązanie.
3. Ten niedziałający klawisz jest zbyt irytujący, aby go po prostu olać (wpisywanie loginu?), więc wcześniej czy później muszę do niego wrócić. Nawet jeśli problem wynika z mojej niewiedzy lub głupoty, to warto go rozpracować, aby wyciągnąć jakieś wnioski na przyszłość.
4. Kupno nowego sprzętu nie zawsze jest rozwiązaniem. Nowa wersja "czegośtam" to taka wersja, w której naprawiono stare błędy i zrobiono nowe. Sytuacja może wyglądać jeszcze ciekawiej, jeśli w nowszej wersji część starych funkcjonalności zostanie wywalona i użytkownicy będą "skazani" na korzystanie ze starszej wersji lub hobbystyczne rozwijanie własnych odpowiedników.
5. Kto wie, niektórzy producenci celowo wprowadzają drobne uszkodzenia po to, aby w świecie jednorazówek klient musiał kupić nowy towar.