Przeczytałem, że powinienem unikać operatora inkrementacji postfiksów ze względu na wydajność (w niektórych przypadkach).
Ale czy to nie wpływa na czytelność kodu? W mojej opinii:
for(int i = 0; i < 42; i++);
/* i will never equal 42! */
Wygląda lepiej niż:
for(int i = 0; i < 42; ++i);
/* i will never equal 42! */
Ale to prawdopodobnie po prostu z przyzwyczajenia. Trzeba przyznać, że nie widziałem wielu zastosowań ++i
.
Czy w tym przypadku wydajność jest tak niekorzystna, że obniża czytelność? Czy jestem po prostu ślepy i czy ++i
jest bardziej czytelny niż i++
?
c++
readability
postfix
Mateen Ulhaq
źródło
źródło
i++
zanim wiedziałem, że może to wpłynąć na wydajność++i
, więc przełączyłem się. Na początku ten ostatni wyglądał trochę dziwnie, ale po pewnym czasie przyzwyczaiłem się do tego, a teraz wydaje się tak naturalny jaki++
.++i
ii++
rób różne rzeczy w pewnych kontekstach, nie zakładaj, że są takie same.for (type i = 0; i != 42; ++i)
. Nie tylko możeoperator++
być przeciążony, ale możeoperator!=
ioperator<
. Przyrost prefiksu nie jest droższy niż postfiks, nierówny nie jest droższy niż mniejszy niż. Z których powinniśmy korzystać?Odpowiedzi:
Fakty:
i ++ i ++ i są równie łatwe do odczytania. Nie lubisz jednego, ponieważ nie jesteś do tego przyzwyczajony, ale zasadniczo nie ma niczego, co można by błędnie zinterpretować, więc nie trzeba już czytać ani pisać.
W przynajmniej niektórych przypadkach operator postfiksów będzie mniej wydajny.
Jednak w 99,99% przypadków nie będzie to miało znaczenia, ponieważ (a) i tak będzie działał na prostym lub prymitywnym typie i jest to tylko problem, jeśli kopiuje duży obiekt (b), nie będzie występował krytyczna część kodu (c) nie wiesz, czy kompilator go zoptymalizuje, czy nie, może to zrobić.
Dlatego sugeruję używanie prefiksu, chyba że konkretnie postfiks jest dobrym nawykiem, aby się do niego przyzwyczaić, tylko dlatego, że (a) dobrym nawykiem jest precyzowanie innych rzeczy i (b) raz na niebieskim księżycu będziesz chciał użyć postfiksu i odwrotnie: jeśli zawsze piszesz, co masz na myśli, jest to mniej prawdopodobne. Zawsze występuje kompromis między wydajnością a optymalizacją.
Powinieneś stosować zdrowy rozsądek, a nie mikrooptymalizować, dopóki nie będziesz tego potrzebować, ale nie bądź rażąco nieefektywny ze względu na to. Zazwyczaj oznacza to: po pierwsze, wyklucz konstrukcję kodu, która jest niedopuszczalnie nieefektywna nawet w kodzie niekrytycznym czasowo (zwykle coś reprezentuje podstawowy błąd koncepcyjny, taki jak przekazywanie wartości 500 MB obiektów bez powodu); a po drugie, z każdego innego sposobu pisania kodu wybierz najczystszy.
Jednak tutaj uważam, że odpowiedź jest prosta: uważam, że pisanie prefiksu, chyba że potrzebujesz konkretnie postfiksa, jest (a) bardzo marginalnie wyraźniejsze i (b) bardzo marginalnie bardziej wydajne, więc zawsze powinieneś pisać to domyślnie, ale nie martw się, jeśli zapomnisz.
Sześć miesięcy temu myślałem tak samo jak ty, że i ++ jest bardziej naturalny, ale jest to po prostu to, do czego przywykłeś.
EDYCJA 1: Scott Meyers w „Bardziej efektywnym C ++”, któremu ogólnie ufam w tej kwestii, mówi, że ogólnie powinieneś unikać używania operatora Postfiksa na typach zdefiniowanych przez użytkownika (ponieważ jedyną rozsądną implementacją funkcji przyrostu Postfiksa jest utworzenie kopiowanie obiektu, wywołaj funkcję inkrementacji prefiksu, aby wykonać inkrementację i zwróć kopię, ale operacje kopiowania mogą być kosztowne).
Nie wiemy więc, czy istnieją jakieś ogólne zasady dotyczące (a) czy jest to prawdą dzisiaj, (b) czy ma to zastosowanie (w mniejszym stopniu) do typów wewnętrznych (c) czy powinieneś używać „++” na cokolwiek więcej niż lekka klasa iteratorów. Ale ze wszystkich powodów, które opisałem powyżej, nie ma znaczenia, rób to, co powiedziałem wcześniej.
EDYCJA 2: Odnosi się do ogólnej praktyki. Jeśli uważasz, że to ma znaczenie w konkretnym przypadku, powinieneś go profilować i zobaczyć. Profilowanie jest łatwe i tanie i działa. Odejście od pierwszych zasad, które należy zoptymalizować, jest trudne i kosztowne i nie działa.
źródło
i++
a nie z++i
powodu nazwy pewnego popularnego języka programowania, o którym mowa w tym pytaniu / odpowiedzi ...Zawsze najpierw koduj programistę, a drugi komputer.
Jeśli występuje różnica w wydajności, po tym, jak kompilator rzuci okiem na Twój kod, ORAZ możesz go zmierzyć ORAZ ma to znaczenie - możesz go zmienić.
źródło
GCC tworzy ten sam kod maszynowy dla obu pętli.
Kod C.
Kod zgromadzenia (z moimi komentarzami)
źródło
Teraz, gdy to nam przeszkadza, dokonajmy właściwego wyboru :
++i
: przyrost prefiksu , przyrost bieżącej wartości i wyniki++
: przyrost postfiksa , skopiuj wartość, zwiększ aktualną wartość, otrzymasz kopięO ile nie jest wymagana kopia starej wartości, zastosowanie przyrostu postfiksowego jest okrągłym sposobem na załatwienie sprawy.
Niedokładność wynika z lenistwa, zawsze używaj konstrukcji, która wyraża twoje zamiary w najbardziej bezpośredni sposób, istnieje mniejsze prawdopodobieństwo, że przyszły opiekun może źle zrozumieć twoje pierwotne zamiary.
Mimo, że jest (naprawdę) niewielki, zdarza się, że czytam kod. Zastanawiam się, czy intencja i faktyczna ekspresja się pokrywają, i oczywiście po kilku miesiącach oni (lub ja) też nie pamiętałem ...
Tak więc nie ma znaczenia, czy wygląda to dobrze dla Ciebie, czy nie. Embrace KISS . Za kilka miesięcy porzucisz swoje stare praktyki.
źródło
W C ++ można znacznie poprawić wydajność, jeśli występują przeciążenia operatora, szczególnie jeśli piszesz kodowany szablon i nie wiesz, co może być przekazane. I logika stojąca za każdym iteratorem X może być zarówno znaczna, jak i znacząca - to jest powolny i niemożliwy do zoptymalizowania przez kompilator.
Ale nie jest tak w C, gdzie wiesz, że będzie to tylko trywialny typ, a różnica w wydajności jest trywialna, a kompilator może łatwo zoptymalizować.
Wskazówka: programujesz w języku C lub C ++, a pytania dotyczą jednego lub drugiego, a nie obu.
źródło
Wydajność każdej operacji jest wysoce zależna od podstawowej architektury. Należy zwiększyć wartość przechowywaną w pamięci, co oznacza, że wąskie gardło von Neumanna jest czynnikiem ograniczającym w obu przypadkach.
W przypadku ++ i musimy
W przypadku i ++ musimy
Operatory ++ i - śledzą swoje pochodzenie w zestawie instrukcji PDP-11. PDP-11 może wykonać automatyczny post-inkrement w rejestrze. Może również wykonywać automatyczne wstępne zmniejszenie wartości efektywnego adresu zawartego w rejestrze. W obu przypadkach kompilator może skorzystać z tych operacji na poziomie komputera tylko wtedy, gdy dana zmienna jest zmienną „rejestrową”.
źródło
Jeśli chcesz wiedzieć, czy coś jest wolne, sprawdź to. Weź BigInteger lub ekwiwalent, włóż go w podobną pętlę for, używając obu idiomów, upewnij się, że wnętrze pętli nie zostanie zoptymalizowane, i zmierz czas ich obu.
Po przeczytaniu artykułu nie przekonuje mnie to z trzech powodów. Po pierwsze, kompilator powinien być w stanie zoptymalizować tworzenie obiektu, który nigdy nie jest używany. Po drugie,
i++
koncepcja jest idiomatyczna dla liczb dla pętli , więc przypadki, w których widzę, że są dotknięte, są ograniczone. Po trzecie, dostarczają argument czysto teoretyczny, bez liczb na poparcie.Biorąc pod uwagę powód nr 1, domyślam się, że kiedy faktycznie mierzysz czas, będą one obok siebie.
źródło
Przede wszystkim nie wpływa na czytelność IMO. To nie jest to, co zwykle widziałeś, ale minie trochę czasu, zanim się przyzwyczaisz.
Po drugie, chyba że użyjesz wielu operatorów Postfiksów w kodzie, prawdopodobnie nie zobaczysz dużej różnicy. Głównym argumentem za nieużywaniem ich, gdy jest to możliwe, jest to, że kopia wartości oryginalnego var musi być przechowywana do końca argumentów, w których oryginalny var mógł być nadal używany. To 32 bity lub 64 bity w zależności od architektury. Odpowiada to 4 lub 8 bajtom lub 0,00390625 lub 0,0078125 MB. Szanse są bardzo duże, że jeśli nie użyjesz ich mnóstwa, które trzeba oszczędzać przez bardzo długi czas, że przy dzisiejszych zasobach komputerowych i szybkości nie zauważysz nawet różnicy, zmieniając postfiks na prefiks.
EDYCJA: Zapomnij o tej pozostałej części, ponieważ mój wniosek okazał się fałszywy (z wyjątkiem części ++ i i ++, która nie zawsze robi to samo ... to nadal prawda).
Wcześniej zauważono również, że nie robią tego samego w sprawach. Uważaj na dokonanie zmiany, jeśli zdecydujesz. Nigdy tego nie wypróbowałem (zawsze używałem postfiksu), więc nie wiem na pewno, ale myślę, że przejście z postfiksu na prefiks przyniesie różne wyniki: (znowu mogę się mylić ... zależy od kompilatora / tłumacz też)
źródło
Myślę, że semantycznie
++i
ma więcej sensui++
, więc trzymałbym się pierwszego, z wyjątkiem tego, że często tego nie robię (tak jak w Javie, gdzie powinieneś używać,i++
ponieważ jest powszechnie używany).źródło
Nie chodzi tylko o wydajność.
Czasami chcesz w ogóle uniknąć kopiowania, ponieważ nie ma to sensu. A ponieważ użycie przyrostu prefiksu nie zależy od tego, po prostu łatwiej jest trzymać się formy prefiksu.
I używanie różnych przyrostów dla typów pierwotnych i typów złożonych ... to naprawdę nieczytelne.
źródło
Chyba że naprawdę go potrzebujesz, trzymałbym się ++. W większości przypadków tak właśnie się zamierza. Niezbyt często potrzebujesz i ++ i zawsze musisz przemyśleć dwa razy, czytając taki konstrukt. Z ++ i jest to łatwe: dodajesz 1, używasz, a następnie i pozostaje taki sam.
Tak więc, zgadzam się z serdecznością z @martin beckett: ułatw sobie, to już jest wystarczająco trudne.
źródło