2011-01-30 11 views
5

Zastanawiałem się, czy (poza oczywistymi różnicami w składni) wystąpiłaby jakakolwiek różnica w wydajności między klasą zawierającą wiele wystąpień obiektu (tego samego typu) lub ustaloną tablica rozmiarów obiektów tego typu.Macierze o ustalonych rozmiarach C++ vs wiele obiektów tego samego typu

W kodzie:

struct A { 
    double x; 
    double y; 
    double z; 
}; 

struct B { 
    double xvec[3]; 
}; 

W rzeczywistości będę przy użyciu boost :: tablice, które są lepsze C++ alternatywą dla macierzy C stylu.

Zajmuję się głównie budową/zniszczeniem oraz czytaniem/pisaniem takich dubletów, ponieważ te klasy będą często tworzone tylko po to, aby wywołać jedną z ich funkcji członkowskich.

Dziękujemy za pomoc/sugestie.

+3

tablice umożliwiają iterację na ich wartościach, co nie jest możliwe w przypadku korzystania z indywidualnych elementów. – Christoph

+1

Zawsze, gdy masz pytanie dotyczące wydajności/wydajności, napisz małą aplikację testową i porównaj. Jeśli masz problemy z interpretacją wyników, zadaj pytanie. –

Odpowiedz

8

Zazwyczaj reprezentacja tych dwóch struktur byłaby dokładnie taka sama. Możliwe jest jednak uzyskanie niskiej wydajności po wybraniu niewłaściwego dla Twojego przypadku użycia.

Na przykład, jeśli chcesz mieć dostęp do każdego elementu w pętli, z tablicy można zrobić:

for (int i = 0; i < 3; i++) 
    dosomething(xvec[i]); 

Jednak bez tablicy, to że albo trzeba powielać kod:

dosomething(x); 
dosomething(y); 
dosomething(z); 

Oznacza to, że powielanie kodu - które może iść w obie strony. Z jednej strony jest mniej kodu pętli; z drugiej strony bardzo ciasne pętle mogą być dość szybkie na nowoczesnych procesorach, a powielanie kodu może zdmuchnąć I-cache.

Innym rozwiązaniem jest przełącznik:

for (int i = 0; i < 3; i++) { 
    int *r; 
    switch(i) { 
     case 0: r = &x; break; 
     case 1: r = &y; break; 
     case 1: r = &z; break; 
    } 
    dosomething(*r); // assume this is some big inlined code 
} 

ten sposób unika się możliwie duży-i-cache ślad, ale ma ogromny negatywny wpływ na wydajność. Nie rób tego.

Z drugiej strony, jest w zasadzie możliwe, tablica dostępu do być wolniejszy, jeśli kompilator nie jest bardzo inteligentny:

xvec[0] = xvec[1] + 1; 
dosomething(xvec[1]); 

Od xvec [0] i xvec [1] są odrębny, co do zasady, kompilator powinien mieć możliwość zachowania wartości xvec [1] w rejestrze, więc nie musi przeładowywać wartości w następnym wierszu. Jest jednak możliwe, że niektóre kompilatory mogą nie być wystarczająco inteligentne, aby zauważyć, że xvec [0] i xvec [1] nie mają aliasu. W takim przypadku użycie oddzielnych pól może być bardzo małe.

W skrócie, nie chodzi o to, że jedna z nich jest szybka we wszystkich przypadkach. Chodzi o dopasowanie reprezentacji do tego, jak z niej korzystasz.

Osobiście proponuję pójść z tym, co sprawia, że ​​kod działający na Xvec jest najbardziej naturalny. Nie warto poświęcać dużo czasu ludzkiemu na martwienie się czymś, co w najlepszym przypadku spowoduje tylko tak małą różnicę wydajności, że złapiesz ją tylko w mikro-benchmarkach.

1

To zależy. Na przykład, przykład dałeś to klasyczny jeden za tablicami „starej szkoły”: matematyka punkt/wektor (lub macierz)

  • ma stałą liczbę elementów
  • same dane jest zwykle przechowywane prywatny w obiekcie
  • ponieważ (jeśli?) ma klasę jako interfejs można prawidłowo zainicjować je w konstruktorze (inaczej, klasyczny szyk inialization jest coś, czego nie naprawdę podoba, składnia)

W takich przypadkach (idąc z przykładami wektorów matematycznych/macierzy), zawsze kończyłem się wewnętrznymi tablicami w stylu C, ponieważ można je przeglądać, zamiast pisać kopię/wkleić kod dla każdego komponentu.

Ale to przypadek szczególny - dla mnie, w dzisiejszych czasach tablic C++ == STL wektorowe, jest szybki i nie muszę się martwić o nuthin' :)

+0

Istnieje różnica między 'std :: vector ' i 'std :: array ', więc nie zrównałbym tablic z wektorami. – fredoverflow

-1

surowe tablice oferują lepszą lokalizację niż cache tablice C++, jak jednak przedstawiono, jedyną zaletą przykładu macierzy nad wieloma obiektami jest możliwość iteracji po elementach.

Prawdziwą odpowiedzią jest oczywiście utworzenie testu i miary testowej.

+3

Macierze C++ są rozmieszczone w pamięci dokładnie tak, jak tablice C. Tablica C++ jest po prostu strukturą z pojedynczym elementem tablicy C. – fredoverflow

+0

Nigdy nie mierzyłem tego, ale wektor STL powinien zapewniać tę samą lokalizację pamięci podręcznej co tablica C, jeśli jest używany w ten sam sposób (tj. Określić liczbę elementów w init, i nie zmuszać go do ponownej alokacji pamięci przez powyżej limitu'). – riviera

+0

Wiesz - to jest technicznie prawdziwe. Więc być może byłem nieszczęśliwy. Jednak logiczna konsekwencja korzystania z klas ogólnych, a nie z natywnych typów rodzimych, prowadzi do nieuniknionego użycia inteligentnego wskaźnika i innych obiektów opakowania. –

5

MVC++ 2010 wygenerował dokładnie ten sam kod do odczytu/zapisu z dwóch struktur POD, jak w twoim przykładzie. Ponieważ przesunięcia do odczytu/zapisu są obliczalne w czasie kompilacji, nie jest to zaskakujące. To samo dotyczy budowy i zniszczenia.

Co do rzeczywistej wydajności, obowiązuje ogólna zasada: profiluj ją, jeśli ma to znaczenie, jeśli nie, dlaczego opiekujesz się?

Indeksowanie elementu tablicy może być nieco bardziej pracujące dla użytkownika twojej struktury, ale z drugiej strony może łatwiej iterować po elementach.

+0

Bardzo interesujące znalezisko, dziękuję za zgłoszenie tego! (Używam także VS2010: P) – stepelu

2

W przypadku, gdy nie mogą się zdecydować i chcesz zachować swoje opcje otwarte, można użyć anonimowego UNION:

struct Foo 
{ 
    union 
    { 
     struct 
     { 
      double x; 
      double y; 
      double z; 
     } xyz; 
     double arr[3]; 
    }; 
}; 

int main() 
{ 
    Foo a; 
    a.xyz.x = 42; 
    std::cout << a.arr[0] << std::endl; 
} 

Niektóre kompilatory również obsługiwać anonimowych przypisać struktury, w tym przypadku można pozostawić xyz część z .

+0

Chociaż jest to niestandardowe, obsługa anonimowych związków jest dość powszechna: zarówno GCC, jak i MSVC je wspierają. Anonimowe związki zawodowe zostały również dodane do nadchodzącego standardu językowego C1X, chociaż jest to dalekie od standaryzacji i implementacji (heck, obsługa C99 jest dość żałosna, a ten standard ma już 12 lat!). –

+0

@Adam Rosenfield: Anonimowe związki są standardem w C++, patrz 9.5.4 – Oystein

+0

Miałem na myśli 9.5.2, przepraszam. – Oystein

1

Różnica może polegać na przechowywaniu zmiennych w pamięci. W pierwszym przykładzie kompilator może dodać dopełnienie, aby wyrównać dane. Ale w twoim przypadku nie ma to znaczenia.

Powiązane problemy