Efektywność kodu przy warunkach


(Marcin Obala) #1

Witam

To jest temat założony z ciekawości. Mianowicie mam na myśli co jest bardziej wydajne?

bool UsuwajWylaczone = true;


foreach(Component comp in ComponentList)

{

    if(UsuwajWylaczone)

    {

        if(comp.Enabled)

        {

            // Dodaj do wyniku

        }

    }

}

czy

bool UsuwajWylaczone = true;


if(UsuwajWylaczone)

{

    foreach(Component comp in ComponentList)

    {

        if(comp.Enabled)

        {

            // Dodaj do wyniku

        }

    }

}

else

{

    foreach(Component comp in ComponentList)

    {

        // Dodaj do wyniku

    }

}

Oczywiście to jest tak prosty przykład, że nie ma sensu pewnie dyskutować nad nim. Mam jedynie na myśli pewien tok myślenia. W pierwszym idziemy pętlą, sprawdzamy czy mamy usuwać, jeśli tak to sprawdzamy czy jest włączone, jeśli tak to dodajemy do wyniku. W drugim natomiast najpierw sprawdzamy czy usuwać a później jeśli nie to idzie pętla która tylko wykonuje zadanie bez sprawdzania czy coś jest włączone. Jak myślicie?


(Kacper B Zuk) #2

Tak jak mówisz, nie ma sensu dyskutować. W pierwszym przykładzie sprawdzenie czy UsuwajWylaczone jest true jest wykonywane dla każdego elementu z listy, w drugim jest wykonywane tylko raz. Jeżeli lista ma więcej niż jeden element, to jedno sprawdzenie z drugiego przykładu będzie wydajniejsze od n sprawdzeń z pierwszego.

EDIT:

Ale nie wiem, jak duża by musiała być ta tablica, by zysk wydajności był wart prawie dwa razy dłuższego kodu…


(Pablo_Wawa) #3

Abstrahując od tego przykładu - wydajność kodu to skomplikowana sprawa, wiadomo oczywiście, że warto optymalizować (czasochłonne) operacje w (dużej) pętli, ale czasami (przy krótkich pętlach) nie warto się na nich skupiać, jeśli jest to mały kawałek kodu.


(Drobok) #4

Może jakiś lepszy przykład ? Nie ma sensu sprawdzać czegoś X razy jeśli w międzyczasie coś nie ma prawa się zmienić :slight_smile:


(Grzelix) #5

Dwa pokazane przykłady nie są równoważne (albo ja tak to widzę). Ale do rzeczy. Tak jak pisali koledzy powyżej

Zgadzam się całkowicie i do tego jeszcze mały przykład dlaczego warto na to zwracać uwagę. W Twoim przykładzie warunkiem jest ustalona zmienna, ale aplikacja może z czasem się zmienić i ten warunek zostanie zastąpiony np metodą która będzie zwracać wartość logiczną. Jeśli ta metoda będzie odpowiednio złożona to narzut czasu do wykonania jej N razy już przy małym n może zaważyć na wydajności aplikacji.

A teraz jeszcze z czym się nie zgadzam w powyższych wypowiedziach.

Oczywiście czasem w projekcie jest tak że nie ma czasu na tego typu analizy i optymalizację. Jednak jeśli tylko mamy na to czas powinnyśmy się zastanowić czy nie można danej metody napisać bardziej optymalnie. Tworzenie dobrego kodu zawsze wygląda w ten sposób, że piszemy metodę a następnie modyfikujemy ją często kilkukrotnie do otrzymania jak najlepszej wersji. Poświęcając czas na takie optymalizację wyrabiamy dobre nawyki. Również aspekt pozostawienia dobrych wzorców. Często nowi członkowie zespołu wzorują się na kodzie zastanym w projekcie. Jeśli kod będzie niechlujny, na takim kodzie będą się uczyć.


(Mirek Sztramski) #6

@Marcin511

Tak przy okazji… bardziej wydajna jest pętla for zamiast foreach.


(Fiołek) #7

@up: Jeśli mówisz takie rzeczy, warto byłoby podać jakieś argumenty, tym bardziej, że to nie zawsze jest prawdziwe(nawet bym pokusił się o stwierdzenie, że częściej nie jest).


(Mirek Sztramski) #8

@Fiołek

Ależ proszę :slight_smile:

Enumeration Overhead

The .NET Framework version 1.1 collections provide an enumerator by overriding IEnumerable.GetEnumerator. This turns out to be less than optimal for a number of reasons:

•The GetEnumerator method is virtual, so the call cannot be inlined.

•The return value is an IEnumerator interface instead of an exact type; as a result, the exact enumerator cannot be known at compile time.

•The MoveNext method and Current properties are again virtual and so cannot be inlined.

•IEnumerator.Current requires a return type of System.Object, rather than a more specific data type which may require boxing and unboxing, depending on the data types stored in the collection.

As a result of these factors, there are both managed heap and virtual function overhead associated with foreach on simple collection types. This can be a significant factor in performance-sensitive regions of your application.

(źródło: Improving .NET Application Performance and Scalability , http://msdn.microsoft.com/en-us/library/ms998547)


(Pablo_Wawa) #9

Ale to się akurat tyczy jednego kompilatora/języka programowania. W innych może być zupełnie inaczej. Według mnie przy małych różnicach wydajności nie powinno się zaciemniać kodu jakąś optymalizacją, bo prostszy kod jest czytelniejszy i mniej podatny na zrobienie w nim błędów.


(Fiołek) #10

Tekst jest z roku 2004 i mówi o wersji 1.1 .NET Framework’a, czyli 5 wersji temu. W 2.0 dodano IEnumerable(T), czyli ostatni podpunkt odpada. Z tym “call to virtual method” też nie byłbym pewien, gdyż JIT w .NET jest dość zaawansowany i pewnie potrafi to zmienić na wywołanie nie wirtualne(tym bardziej, jeśli klasa jest sealed).

Kompilator C# jest na tyle inteligentny, że foreach na tablicy po kompilacji da taki sam wynik jak for.

Co zrobić, jeżeli po kolekcji da się iterować(implementuje IEnumerable) ale nie pozwala na swobodny dostęp albo jest on kosztowny(patrz: LinkedList)? Wywołać ToArray i używać for? To na pewno nie będzie szybsze(i zużyje więcej pamięci). To samo tyczy się LINQ(który nie tworzy nowych kolekcji “z boku”, bo to powodowałoby duży narzut). Do tego warto pamiętać, że wywołanie indeksera to nadal wywołanie funkcji, a nie proste dodawanie wskaźników.

Do tego dochodzi kwestia, co jest ‘wydajniejsze w pisaniu’ - foreach, który daje nam od razu obiekt ale używa iteratorów, czy for, pamiętając, że musimy indeksować kolekcję?

Może źle wyraziłem się w poprzednim poście - chodziło mi o to, że twierdzenie, że “for jest wydajniejszy” bez podania KONTEKSTU jest błędne(przykłady wyżej). Wszystko ma swoje zalety, jak i wady i trzeba o tym pamiętać(tym bardziej, że w .NET czyste tablice raczej nie są jakoś strasznie często wykorzystywane).


(Mirek Sztramski) #11

@Fiołek

Ależ ja to doskonale rozumiem, warto jednak zapalić sobie lampkę bezpieczeństwa gdy podejmujemy temat optymalizacji/wydajności kodu w jednej, oczywistej kwestii (przykład powyżej), nie widząc przy tym możliwości jego usprawnienia gdzie indziej.

Tekst jest z 2004 roku, kompilator C# poddano wielu modyfikacjom (choć sam zapewne dobrze wiesz, że .NET 3.0, 3.5 to tylko rozszerzenie 2.0) i rzeczywiście jest on na tyle inteligentny, że programista może sobie pozwolić na pewne uproszczenia, nie zmienia to jednak faktu, że bardzo wiele tematów z w/w pozycji jest jak najbardziej aktualnych i stanowią bardzo dobrą lekturę.

To dobrze, że podniosłeś kwestię kontekstu i zaprezentowałeś przykłady obalające moje bardzo ogólne stwierdzenie. Bardziej odpowiednią wersją byłoby zapewne “należy zastanowić się nad wydajnością foreach w omawianym przykładzie”.


(somekind) #12

Zasadniczo bardziej się opłaca pisanie czytelnego kodu niż zaoszczędzenie jednej milisekundy na sto milionów iteracji. :slight_smile:


(Marcin Obala) #13

Pamiętam mój kolega kiedyś zrobił “program”, nie pamiętam treści ale miał w programie setki jak nie tysiące linijek kodu, i były to zwykłe ify czy switche. Wszystkie możliwe przypadki jakiegoś problemu chyba ok 200 przypadków, 200 ifów, klamerki i dodatkowo to co w klamerkach więc wyszło tego dużo. Jego program działał szybciej niż mój 50 linijkowy obliczeniowy :wink: ale oczywiście wolę swój 50 linijek niż 2000 linijek ifów czy switchów :wink:


(Mirek Sztramski) #14

@somekind

Oczywiście, że tak. Ale problemy i opóźnienia mnożą się o wiele szybciej niż napisałeś i jest to wzrost nieliniowy. W efekcie można pisać aplikacje całkowicie pozbawione responsywności, które wykonują tylko proste rzeczy. Z drugiej strony można pisać aplikacje, których FxCop nie śmie nawet poprawić.

Młodzi i starsi programiści coraz częściej korzystają z narzędzi, nie wiedząc w tej samej chwili do czego tak naprawdę one służą. “Tworzenie oprogramowania” stało się takie proste…


(Pablo_Wawa) #15

No tak, ale zauważyłem, że obecni “programiści” jak czegoś nie potrafią sami zakodować, to szukają gotowców w internecie i często wychodzi tak, że używają jakiejś uniwersalnej “kobyły” to wykonania jednej prostej rzeczy - i to jest przerażające.


(Sawyer47) #16

Jako ciekawostkę dla wszystkich zainteresowanych, dobrze wyjaśniony problem, parę przykładów kodu http://stackoverflow.com/questions/1122 … rted-array

Może nie zupełnie związane z tematem, ale jest o warunkach i o efektywności kodu


(Pablo_Wawa) #17

@Description_1

Bardzo często chcąc uzyskać jakiś prosty efekt w JavaScript (np. walidacja pól formularza, sortowanie danych w tabeli, stronicowanie danych) ludzie używają do tego jakiejś biblioteki (np. jQuery), kiedy w sumie nic poza tą rzeczą nie potrzebują.

Już nie pamiętam, bo to było z 1,5 roku temu, ale jeden z programistów PHP użył jakiejś przedziwnej metody (w efekcie bardzo czasochłonnej) do uzyskania prostego efektu, który dałoby się zrobić prościej, ale trzeba byłoby napisać dodatkową funkcję - i to mnie przeraziło, bo to były wręcz rzeczy z zakresu podstaw programowania.

W efekcie takiego postępowania Ci “programiści” nie potrafią napisać dobrego programu, tylko składają go z gotowych klocków, tracąc czas na ich dopasowanie, zamiast na napisanie swoich funkcji “na czysto”.

@nr47: bardzo ciekawy wątek dotyczący optymalizacji kodu. :slight_smile:


([alex]) #18

Ja ci podam:

http://forum.4programmers.net/C_i_C++/1 … 3#id854243

Obczaj funkcje konwersja - jest jako pierwsza.


(somekind) #19

Z takim podejściem raczej nikt kariery w branży nie zrobi, a i aplikacje będą dalekie od doskonałości. :slight_smile:

Mi jednak chodziło o coś innego, po prostu ktoś mądry kiedyś powiedział, że “przedwczesna optymalizacja jest źródłem zła”. Usiłowanie pisania “superwydajnego” kodu na wczesnym etapie powoduje stratę czasu i czytelności, a więc utrudnia jego dalsze pisanie. Rozsądniejszym podejściem jest napisanie, sprofilowanie, a potem poprawienie.

Nie mówię też, że o wydajności nie trzeba w ogóle pamiętać podczas pisania (proste przykłady często newralgicznych miejsc: konkatenacja stringów, rzutowanie, operacje plikowe), ale for vs foreach? Bez przesady… A jeśli w naszym programie faktycznie foreach stanowi wąskie gardło, to prawdopodobnie powinniśmy go napisać w innym języku…

No, a poza tym, zamiana for w foreach w IDE to kwestia sekundy. :wink:

jQuery daje stabilne środowisko działające w różnych przeglądarkach. Samodzielne pisanie skryptów JS i ich dostosowywanie będzie dużo bardziej czasochłonne, i więcej kodu powstaje.

Samodzielne pisanie tego, co ktoś już zrobił dobrze jest stratą czasu i pieniędzy.

Błędem jest za to próba tworzenia oprogramowania poprzez bezmyślnie sklejanie znalezionych w necie fragmentów kodu, bez zaglądania do oficjalnej dokumentacji, i bez przyswojenia sobie podstaw jakiejś biblioteki, frameworka, czy nawet języka. I faktycznie sporo osób tak próbuje robić… Pytanie tylko, czy mamy się czym przejmować? :slight_smile:


(Pablo_Wawa) #20

@somekind:

Tu się w 100% zgadzam. Optymalizację robi się po skończeniu pisania, zaś przed rozpoczęciem należy oczywiście przygotować właściwe algorytmy.

Ależ ja nie neguję sensowności używania tej (lub innej) biblioteki, ale należy to robić sensownie. Bo jeśli cały serwis obywa się bez użycia tej biblioteki i nagle używa się jakiejś jednej funkcjonalności na jednej podstronie tylko dlatego, że samemu nie ma się umiejętności programistycznych do własnoręcznego napisania, to już przesada. I nie ma to nic wspólnego z zapewnieniem zgodności z wieloma przeglądarkami, tylko zaprzęga się kombajn do drobnej pracy. Poza tym obecne przeglądarki nie sprawiają tylu problemów zgodności, jak było to na początku przez kilka lat.

Przykład kuriozalny “programowania”: sortowanie dużej ilości danych po to, by uzyskać wartość największą i najmniejszą.

Niezupełnie, ponieważ bazując w swoim programie na wielu cudzych modułach/klockach traci się po części kontrolę nad swoim kodem, poza tym taka osoba, nadużywająca bazowania na cudzym kodzie tak naprawdę nie nauczy się programować, bo nie pozna podstawowych operacji i metod uzyskania danego efektu, a potem będzie się zmagała z jakimś prostym problemem i traciła czas na szukanie w internecie rozwiązania, zamiast na samodzielne jego napisanie.

To jak z pisaniem wypracowań w szkole - bazujesz na gotowych fragmentach, spajając te teksty jednym, dwoma swoimi zdaniami - a potem i tak nie jesteś w stanie nic o danym temacie powiedzieć, bo robisz to by odbębnić robotę i niczego przez to nie zyskujesz.

Wszystko jest dozwolone i każdy może “programować” tak jak chce, gorzej jak taki delikwent zgłasza się do Ciebie z prośbą o pomoc lub musisz zając się rozwijaniem cudzego kodu w jakiejś firmie.