Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Kiedy stosować referencję jako optymalizację

Ostatnio zmodyfikowano 2013-12-26 17:46
Autor Wiadomość
McAffey
Temat założony przez niniejszego użytkownika
Kiedy stosować referencję jako optymalizację
» 2013-12-26 12:40:10
Witajcie :D W jednym swoim projekcie będę wielokrotnie wywoływał funkcję która jako argument przyjmuje duży obiekt. I chociaż będzie z niego tylko odczytywała dane, to pomyślałem że optymalniej będzie jeśli zastosuję referencję - nie będzie musiała za każdym razem ten obiekt kopiować tylko będzie odczytywała z oryginału. Ale stwierdziłem że zanim ową funkcję napiszę, z ciekawości przeprowadzę prosty test - czy różnice dla takiego rozwiązania są odczuwalne.

Napisałem prosty kod, który sprawdziłem najpierw przesyłając do funkcji argument bez referencji, a później z referencją. Dla obu wersji program uruchomiłem 5 razy i patrzyłem na "podsumowanie" które generuje code::blocks - na execution time. Wiem że to bardzo naciągane testowanie, ale dla moich potrzeb w zupełności wystarczyło, a nie chciało mi się przeprowadzać dokładniejszych pomiarów.

C/C++
#include <iostream>
using namespace std;

struct struktura
{
    int integer;
    double podwojnadokladnosc;
    float pojedyncza_dokladnosc;
    char znak;
    string tekst;
};

unsigned long funkcja( struktura egzemplarz_struktury )
{
    return egzemplarz_struktury.integer +
    egzemplarz_struktury.podwojnadokladnosc +
    egzemplarz_struktury.pojedyncza_dokladnosc +
    egzemplarz_struktury.znak +
    egzemplarz_struktury.tekst[ 0 ];
}

void wypisz_zawartosc_struktury( struktura /*&*/ egzemplarz_struktury )
{
    cout << egzemplarz_struktury.integer << " " <<
    egzemplarz_struktury.podwojnadokladnosc << " " <<
    egzemplarz_struktury.pojedyncza_dokladnosc << " " <<
    egzemplarz_struktury.znak << " " <<
    egzemplarz_struktury.tekst << endl;
   
    return;
}

int main()
{
    unsigned long suma = 0;
   
    struktura egzemplarz_struktury = { 2, 2.12, 3.51, 'c', "we will see" };
   
    wypisz_zawartosc_struktury( egzemplarz_struktury );
   
    for( unsigned long i = 0; i < 10000000; i++ )
         suma += funkcja( egzemplarz_struktury );
   
    wypisz_zawartosc_struktury( egzemplarz_struktury );
   
    cout << "Suma = " << suma << endl;
}

Jednak wyniki mnie zaskoczyły. Efekt był następujący :

Funkcja bez referencji :
Process returned 0 (0x0)   execution time : 9.181 s
Process returned 0 (0x0)   execution time : 8.971 s
Process returned 0 (0x0)   execution time : 8.882 s
Process returned 0 (0x0)   execution time : 8.946 s
Process returned 0 (0x0)   execution time : 8.943 s

Funkcja z referencją :
Process returned 0 (0x0)   execution time : 9.461 s
Process returned 0 (0x0)   execution time : 9.383 s
Process returned 0 (0x0)   execution time : 9.282 s
Process returned 0 (0x0)   execution time : 9.086 s
Process returned 0 (0x0)   execution time : 9.242 s

Nie dość że korzystanie z referencji nie przyspieszyło pracy programu, to jeszcze niewątpliwie ją minimalnie spowolniło.

Zdziwiony odszukałem w biblioteczce Symfonię C++ Standard i po przetarciu kurzu z okładki, z rozdziału o referencji odczytałem to, co myślałem również przed przeprowadzeniem eksperymentu. Pozwolę sobie zacytować :

Sposób ten stosuje się do tak dużych obiektów, że przesłanie ich przez wartość (wymagające zrobienia kopii np. dziesiątków, setek bajtów) powodowałby znaczące spowolnienie wywoływania funkcji. W przypadku, gdy taka funkcja jest wywoływana bardzo wiele razy, może to być czynnikiem ważnym.

Czyli teoretycznie na przykładzie powyższego kodu ta zasada powinna funkcjonować, a niestety okazało się inaczej. Zdezorientowany przeprowadziłem jeszcze jeden test, tym razem nie umieszczając w argumencie całej struktury a zwykłego stringa i przesyłałem do niego prosty tekst (jeden wyraz).

C/C++
#include <iostream>
using namespace std;

unsigned long funkcja( string /*&*/ tekst )
{
    return tekst[ 0 ] + tekst[ 1 ] + tekst[ 2 ];
}

int main()
{
    unsigned long suma = 0;
    string test = "test";
   
    for( unsigned long i = 0; i < 10000000; i++ )
         suma += funkcja( test );
   
    cout << "Suma = " << suma << endl;
}

I pomimo że przesyłałem krótki tekst, w przypadku tego kodu referencja okazała się ogromną optymalizacją. Wyniki :

funkcja bez referencji :
Process returned 0 (0x0)   execution time : 9.049 s
Process returned 0 (0x0)   execution time : 8.640 s
Process returned 0 (0x0)   execution time : 8.744 s
Process returned 0 (0x0)   execution time : 8.824 s
Process returned 0 (0x0)   execution time : 8.809 s

Funkcja z referencją :
Process returned 0 (0x0)   execution time : 0.414 s
Process returned 0 (0x0)   execution time : 0.436 s
Process returned 0 (0x0)   execution time : 0.413 s
Process returned 0 (0x0)   execution time : 0.400 s
Process returned 0 (0x0)   execution time : 0.421 s

I teraz już trudno mi określić na jakiej zasadzie to działa. Co prawda mam bardzo niewielką wiedzę zahaczającą o assemblera (przechowywanie informacji w pamięci - kiedy informacja przechowywana jest na stosie itp.) i może tej wiedzy mi brakuje do zrozumienia powyższych przykładów (tak tylko przypuszczam, bo nie mam zielonego pojęcia). Dla swojej docelowej funkcji (gdy już ją napiszę) oczywiście po prostu sprawdzę która wersja jest optymalniejsza. Ale założyłem ten temat bo może potrafi ktoś wyjaśnić jak to działa ? Jeśli nikt nic nie wyjaśni, to zawsze to jakaś ciekawostka dla tych, którzy podobnie jak ja nie spodziewali się takich wyników. Pozdrawiam ;)
P-100148
pekfos
» 2013-12-26 13:56:48
Wstawiałeś referencję do wypisz_zawartosc_struktury()? Trochę bez sensu.
P-100156
DejaVu
» 2013-12-26 17:23:41
Jeżeli nie zastosujesz referencji to wywoła się konstruktor kopiujący i w argumencie funkcji będzie kopia obiektu, który następnie zostanie zniszczony przy wyjściu z niej.

» Kurs C++ » Poziom 3Przekazywanie argumentów funkcji przez referencję lekcja
P-100187
McAffey
Temat założony przez niniejszego użytkownika
» 2013-12-26 17:46:47
Wstawiałeś referencję do wypisz_zawartosc_struktury()? Trochę bez sensu.
No way, a wszyscy mi mówią żeby w święta odpocząć a ja dalej programuję... Kurcze, czeski błąd, machnąłem się przy dostawianiu '&', teraz już wszystko działa tak jakbyśmy się tego spodziewali :)

@DejaVu no jasne, wiem jak działa referencja, ale dzięki :)
P-100199
« 1 »
  Strona 1 z 1