sobota, 7 sierpnia 2010

Kolejne 5 trików dla wydajności w C++

...czyli kolejne 5 zaleceń i sposobów jak robić, aby źle nie zrobić ;) Kontynuacja posta z pierwszymi pięcioma trikami: http://wojtek-m.blogspot.com/2010/07/5-trikow-dla-wydajnosci-w-c.html. A co, myśleliście, że będzie ich tylko 5? ;) No to jedziemy:

1. <cstdio> zamiast <iostream>

Czyli coś, co zawsze piszą w uwagach do konkursów programistycznych. O ile zazwyczaj optymalizacja wejścia/wyjścia nie ma sensu (wypisywanie czegokolwiek na ekran i tak trwa wieki, a szybkość wejścia zależy tylko od użytkownika), ale w pewnych przypadkach jest uzasadnione - kiedy nasz program jest elementem potoku lub kiedy wejście/wyjście jest przekierowane do/z pliku, a przesyłanych danych jest bardzo dużo. No nic, przy takich programach trzeba będzie wyciągnąć zakurzoną specyfikację printf i scanf ;)


2. Unikanie metod wirtualnych

Czyli uwaga projektowa. Nie róbmy wszystkich metod klasy wirtualnymi - lepiej się przypatrzmy które naprawdę muszą być wirtualne. Wirtualność metody spowalnia jej wykonanie, dlatego zawsze zastanówmy się trzy razy zanim dodamy modyfikator virtual.

Przy okazji mała uwaga - ktoś może powiedzieć "OK, idzie ramię w ramię z trzymaniem prywatności - nie mają sensu prywatne wirtualne metody, więc nic co jest private nie będzie virtual i załatwione. O prywatności się przecież pamięta." No tak, byłaby to prawda, gdyby faktycznie prywatne metody wirtualne nie miały sensu. Ale mają.

Kiedy? Metodę prywatną może wywołać inna, np. publiczna metoda danej klasy. Wyobraźmy sobie taki kod:

class A
{
public:
  void g() { f(); }
private:
  virtual void f() { cout << "f z A" << endl; }
};

class B : public A
{
private:
  virtual void f() { cout << "f z B" << endl; }
};


Wywołanie metody g() z klasy B da wynik f z B. Problem ten pojawia się w rzeczywistym świecie np. przy implementacji wzorca projektowego "Metoda szablonowa".


3. Konstrukcja zamiast przypisania

Czyli kolejna porada z cyklu "tęp zbędne wywołania". Zamiast pisać:

T x;
x = 42;


piszmy

T x(5);

Oszczędzi to nam jednego wywołania operatora przypisania przy tworzeniu nowej zmiennej lokalnej.


4. Używanie list inicjalizacyjnych

Wykorzystując w jak największym stopniu listy inicjalizacyjne konstruktora usuniemy całkiem nieźle ukryte zbędne wywołanie. Rozważmy taki kod:

class A
{
private:
  T t;
public:
  A(const T& x)
  {
    t = x;
  }
};


Wszystko wygląda elegancko, x przekazane przez referencję itd. Ale nadal mamy to jedno ukryte wywołanie - wywołanie domyślnego konstruktora klasy T.

Jest tak, ponieważ każde pole klasy jest inicjowane przed wejściem do samego konstruktora. Jeżeli nie zainicjujemy pola na liście inicjalizacyjnej, to inicjacja zostanie wykonana z użyciem domyślnego konstruktora. Wykorzystując więc nie zawsze lubianą listę:

class A
{
private:
  T t;
public:
  A(const T& x) : t(x)
  {
    // t = x;
  }
};


Oszczędzamy jedno wywołanie konstruktora.


5. Unikanie operatora new

Lub jakiejkolwiek innej formy dynamicznej alokacji pamięci. Rachunek jest prosty - umieszczenie obiektu na stosie (czyli zwykłe A a;) jest 25 razy szybsze od podobnej operacji z wykorzystaniem operatora new (czyli A* a = new A(); /** ... **/ delete a;). 25 razy. Czyli zarówno new jak i malloc unikamy jak ognia.

No dobra, ale co z naszymi ulubionymi strukturami danych? Co z drzewami, listami itd.? One przecież są tak fajne właśnie dzięki wskaźnikom i dynamicznej alokacji pamięci... Na to pytanie mogę tylko odpowiedzieć - stay tuned... ;)

4 komentarze:

  1. Haaa... zanotuję :) Na dniach oddam Ci symfoniję, bo nabyłem swoją kopię ;) Pozdro ;)

    OdpowiedzUsuń
  2. Programujecie w C++... jak zwierzęta :P

    OdpowiedzUsuń
  3. no właśnie ja czytając tego posta doszedłem do wniosku, że dobrze, że wybrałem jave - tam nie ma problemów z wydajnością, wszystko jest taka samo słabo wydajne, więc nie ma się czym martwić :P

    Poza tym w czasach, gdy sprzęt się rozwija na tyle, że dostęp do zasobów nie jest problemem, problemem staje się czytelne pisanie kodu, wg mnie lepiej jest pisać tak, żeby każdy to zrozumiał i umiał dostosować do swoich potrzeb, niż stosując jakieś wydajnościowe triki, ale to tylko zdanie javovca, C++sowcy się ze mną pewnie nie zgodzą, bo siła C++ tkwi w wydajności :D

    OdpowiedzUsuń
  4. Jak zwierzęta - LOOOOOOOOOOOOOOOOOOOOL

    OdpowiedzUsuń