Warsztat - Programowanie gier

Lipiec 30, 2010, 17:33:12 *
Witamy, Gość. Zaloguj się, lub zarejestruj proszę.

Zaloguj się podając nazwę użytkownika, hasło i długość sesji
Aktualności: Warsztat, Regulamin forum, #warsztat, Wiki, FAQ, NoPaste, Mapa
 
   Strona główna   Pomoc Szukaj Zaloguj się Rejestracja  
Strony: [1] 2
  Drukuj  
Autor Wątek: Wskaźnik this dla obiektów dynamicznych  (Przeczytany 3305 razy)
bs.mechanik
Hero Member
*****

wiadomości: 800


srutututu majtki z drutu


Zobacz profil WWW
« : Grudzień 01, 2008, 04:48:40 »

Witam.

Ostatniego czasu, postanowiłem pobawić sie trochę w optymalizację obliczeń przy wykorzystaniu SSE.
Początkowo robiłem to pisząc wstawki assemblerowe, jednak po napotkaniu problemu, który tutaj opiszę, przenoisłem się na instrukcje Intrinsics.

Zaimplementowałem operator dodawania (kod w assemblerze i w c++)

Kod:
__asm{
mov edx,this;
mov eax,vec;

movups xmm0,[edx];
addps xmm0,[eax];

movaps retv,xmm0;
}

Kod:
return CVector3D(_mm_add_ps(_mm_load_ps(vec.c_vecData),_mm_load_ps(c_vecData)));

gdzie vec to parametr operatura + (referencja na wektor), retv wektor zwracany a c_vecData to wyrównana do 16 bajtów tablica 4 floatów.

Dla zwykłego dodawania, typu: result = v1+v2 oraz dodawania w petli dla satycznych tablic (tworzonych na stosie) wszystko działa ok.
Jednak, gdy utworzę dynamicznie tablicę wektorów (operator new) i dla niej wywołam operator+, program sypie się (Access violation). Nie przekraczam zakresu (testowałem dla elementu 0).
Debugger w pierwszym przypadku wskazywał na linijkę: movups xmm0,[edx].
Jeśli chodzi o Intrinsics, zamiana _mm_load_ps(c_vecData) na _mm_set_ps(0.0f,0.0f,0.0f,0.0f) pomaga (program nie wysypuje się), jednak już  _mm_set_ps(c_vecData[0],c_vecData[1],c_vecData[2],c_vecData[3]) powoduje wyżej wspomniany błąd.

Bedę bardzo wdzięczny za pomoc w rozwiązaniu problemu Smiley
Zapisane

Zaczynaj od rzeczy małych, by skończyć na wielkich - nigdy odwrotnie
bad_music
Xion
Member2000
*******

wiadomości: 2507



Zobacz profil WWW
« Odpowiedz #1 : Grudzień 01, 2008, 14:48:35 »

Przypuszczam, że problemem jest wyrównanie. O ile pojedyncze zmienne są wyrównane do 16 bajtów, o tyle dynamicznie utworzona tablica już pewnie nie, i stąd ten AV.
Zapisane

Krzysiek K.
Member2000
*******

wiadomości: 9810



Zobacz profil
« Odpowiedz #2 : Grudzień 01, 2008, 15:54:20 »

Cytuj
Przypuszczam, że problemem jest wyrównanie. O ile pojedyncze zmienne są wyrównane do 16 bajtów, o tyle dynamicznie utworzona tablica już pewnie nie, i stąd ten AV.
Racja. Polecam zamienić ostatni "movaps" na "movups". Poza tym polecam sprawdzić konwencję wywołania metod obiektów, czy przypadkiem this'a nie masz nawsze w ecx. Smiley
Zapisane

Aktualne zajęcie: Szkoła DJKurs DJ
mINA87
SuperHero Member
******

wiadomości: 1145


Zobacz profil
« Odpowiedz #3 : Grudzień 01, 2008, 16:02:21 »

_mm_loadu_ps - powinno pomóc.
Martwi mnie jednak co innego - używasz movups - to jest wersja dla danych unaligned - powinno być ok (na pewno sie wywala na tej linijce??), a wywalac się powinien dopiero na
Kod:
addps xmm0,[eax];
bo tutaj procesor będzie chciał uzyskać dostęp do pamięci unaligned.. Dziwne. A spróbuj zamiast tego:
Kod:
movups xmm0,[edx];
addps xmm0,[eax];
movaps retv,xmm0;
to:
Kod:
movups xmm0,[edx]
movups xmm1,[eax]
addps xmm0,xmm1
movups retv,xmm0
Albo zrób sobie jakis alokator który zaalokuje ciągły blok pamięci, w nim będziesz mógł powydzielać bloki wyrównane i wtedy wystarczy, że zastosujesz placemnet new Smiley

//ADD
Krzysiek: addps też tutaj psuje.
Zapisane

"2nd hand car, 2nd hand shoes, 2nd hand point of view from the 2nd hand news.
2nd hand shirt, 2nd hand tie, 2nd hand reason from a 2nd hand lie. (...)"
Pitchshifter - 2nd hand
bs.mechanik
Hero Member
*****

wiadomości: 800


srutututu majtki z drutu


Zobacz profil WWW
« Odpowiedz #4 : Grudzień 01, 2008, 17:40:44 »

Faktycznie.
Załadowanie this'a do rejestru xmm za pomocą movups i _mm_loadu_ps pomogło. Mam tylko pytanie: czy nie będzie to wolniejsze w działaniu? Przyznam, dość mozno zależy mi na wydajności tego kodu (liczę takty Tongue), a operowanie na niewyrównanych danych jest wolniejsze. Mozna w jakis sposób to obejść (najlepiej niezauwazalny dla usera końcowego) ?

Poza tym polecam sprawdzić konwencję wywołania metod obiektów, czy przypadkiem this'a nie masz nawsze w ecx. Smiley

No konwencję miałem taką, że this'a trzymałem w jednym rejestrze. Nie mozna tak? Smiley
« Ostatnia zmiana: Grudzień 01, 2008, 17:45:14 wysłane przez bs.mechanik » Zapisane

Zaczynaj od rzeczy małych, by skończyć na wielkich - nigdy odwrotnie
bad_music
mINA87
SuperHero Member
******

wiadomości: 1145


Zobacz profil
« Odpowiedz #5 : Grudzień 01, 2008, 18:33:54 »

Jakos tam masakrycznie wolniejsze nie jest, ale nie pamiętam dokłądnei wyników testów.
Co do zrobenia tego na danych wyrównanych to mówię - użyj placement new Smiley Przykładowo alokuj wszystkie CVectory w jakimś managerze, który pobiera sobie z jakiegoś baseniku zakres już wczesniej zaalokowanej i wyrównanej pamięci i wywołuje na niej placement new. Ewentualnie jesli chcesz ukryć tworzenie obiektów przed użytkownikiem i sprawić by było to bardziej czytelniejsze, to niech klasa CVector nei zawiera skłądowych tylko wskaźnik na jakąś strukturkę je zawierającą i w konctruktorze CVector rób placement new pod wyrównany adres.
Krzysiek miał na myśli że w ecx przeważnie jest this.
Zapisane

"2nd hand car, 2nd hand shoes, 2nd hand point of view from the 2nd hand news.
2nd hand shirt, 2nd hand tie, 2nd hand reason from a 2nd hand lie. (...)"
Pitchshifter - 2nd hand
Netrix
Hero Member
*****

wiadomości: 648


c++/c#/java


Zobacz profil WWW
« Odpowiedz #6 : Grudzień 01, 2008, 22:07:44 »

W takim razie trzeba zrobić cały manager na te wektory, bo zwykłe tablice nie będą działać. Gdybyśmy przeciążyli operator new[] i w środku wyrównali to byłby problem ze skakaniem po tej tablicy, czy się mylę?
Zapisane

“Ekspert to człowiek, który w bardzo wąskiej dziedzinie zrobił wszystkie możliwe błędy.” Niels Bohr.
http://netrix.org.pl
bs.mechanik
Hero Member
*****

wiadomości: 800


srutututu majtki z drutu


Zobacz profil WWW
« Odpowiedz #7 : Grudzień 03, 2008, 02:50:48 »

mINA87:
To moje ostatnie pytanie - jak można za pomocą new zaalokować wyrównany obszar pamięci?
Składnia:

Kod:
c_pData = new __declspec(align(16)) float[4];

działa, jednak tylko podczas kompilacji Smiley
Zapisane

Zaczynaj od rzeczy małych, by skończyć na wielkich - nigdy odwrotnie
bad_music
Krzysiek K.
Member2000
*******

wiadomości: 9810



Zobacz profil
« Odpowiedz #8 : Grudzień 03, 2008, 03:25:58 »

Cytuj
To moje ostatnie pytanie - jak można za pomocą new zaalokować wyrównany obszar pamięci?
Zaallokuj odpowiednio więcej i wyrównaj sobie sam. Smiley
Zapisane

Aktualne zajęcie: Szkoła DJKurs DJ
Reg
Member2000
*******

wiadomości: 3791



Zobacz profil WWW
« Odpowiedz #9 : Grudzień 04, 2008, 00:00:41 »

Każdy typ w C++, oprócz rozmiaru, ma też wyrównanie. Tak jak sizeof zwraca rozmiar, tak __alignof zwraca wyrównanie. Wyrównanie dla typu można ustalić za pomocą __declspec(align(x)) lub #pragma pack(...). Jeśli jakiś typ ma odpowiednie wyrównanie, to kompilator automatycznie go przestrzega tak przy tworzeniu zmiennych lokalnych tego typu na stosie, jak i alokowaniu dynamicznym obiektów na stercie operatorem new. Tylko brutalne rzutowanie z innego typu może dać wskaźnik do niewyrównanych danych. Przykład:

Kod:
__declspec(align(16))
struct VEC4 {
  float Tablica[4];
};

...
c_pData = new VEC4;
Zapisane

Esidar
SuperHero Member
******

wiadomości: 1359


Zobacz profil
« Odpowiedz #10 : Grudzień 04, 2008, 00:15:41 »

jak i alokowaniu dynamicznym obiektów na stercie operatorem new
Jeżeli to sprawdziłeś, to akurat ci dziala zupełnie przypadkiem bo na ogół zwracany jest adres wyrównany do 16 bajtów (taki urok bibliotecznego new) Wink Jak ustawisz align na 256 to nie zadziała Smiley new nie uwzględnia atrybutu __align bo nie ma za bardzo jak. new używa zwykłego malloc bez podania wyrównania. Nie może też sam wyrównać bo wtedy musiałby dawać specjalny nagłówek z "prawdziwym" nie wyrównanym adresem który przekaże do free.


To moje ostatnie pytanie - jak można za pomocą new zaalokować wyrównany obszar pamięci?
Wyrównywanie w new możesz zrobić tylko przeciążając operator new oraz new[].
Zapisane
mINA87
SuperHero Member
******

wiadomości: 1145


Zobacz profil
« Odpowiedz #11 : Grudzień 04, 2008, 12:20:28 »

Krzysiek napisał Ci co masz zrobić, a Esidar napisał Ci gdzie masz zrobić Smiley
Najprościej zrobić to tak - zaalokuj pamięć + align, zwrócony wskaźnik zapisz na boku, normalny wskaźnik potraktuj bitowym & aby odrzucić niewyrównane bity i teraz do adresu który otrzymasz dodaj align - voila Smiley
Jedyny problem takiego rozwiązania to to, że następuje słabe pokrycie zaalokowanej pamięci i sporo może się marnować, dlatego lepiej zrobić sobie jakiś alokator który będzie przydzielał takie wyrównane bloki z jakiejś większej sterty.
Zapisane

"2nd hand car, 2nd hand shoes, 2nd hand point of view from the 2nd hand news.
2nd hand shirt, 2nd hand tie, 2nd hand reason from a 2nd hand lie. (...)"
Pitchshifter - 2nd hand
Netrix
Hero Member
*****

wiadomości: 648


c++/c#/java


Zobacz profil WWW
« Odpowiedz #12 : Grudzień 04, 2008, 16:00:30 »

Czyli bez managera się nie obejdzie, bo "coś" musi trzymać ten niewyrównany wskaźnik.

Wyrównanie to chyba najprościej zrobić tak:
Kod:
// new[]
Vec* ptr = new[n + 1]; // Ten trzeba zapisać
Vec* aPtr = (Vec*)(((int(ptr) / 16)++) * 16); // kochane nawiasy :>
return aPtr;
Zapisane

“Ekspert to człowiek, który w bardzo wąskiej dziedzinie zrobił wszystkie możliwe błędy.” Niels Bohr.
http://netrix.org.pl
yarpen
SuperHero Member
******

wiadomości: 1423


Zobacz profil WWW
« Odpowiedz #13 : Grudzień 04, 2008, 16:14:51 »

jak i alokowaniu dynamicznym obiektów na stercie operatorem new
Jeżeli to sprawdziłeś, to akurat ci dziala zupełnie przypadkiem bo na ogół zwracany jest adres wyrównany do 16 bajtów (taki urok bibliotecznego new) Wink Jak ustawisz align na 256 to nie zadziała Smiley new nie uwzględnia atrybutu __align bo nie ma za bardzo jak. new używa zwykłego malloc bez podania wyrównania. Nie może też sam wyrównać bo wtedy musiałby dawać specjalny nagłówek z "prawdziwym" nie wyrównanym adresem który przekaże do free.
Nie do konca. Wg standardu new gwarantuje wyrownanie poprawne dla tworzonego typu. Nie uwzglednia to jednak "hackow" z declspec.
Zapisane
mINA87
SuperHero Member
******

wiadomości: 1145


Zobacz profil
« Odpowiedz #14 : Grudzień 04, 2008, 16:15:46 »

Możesz go zapisać przed właściwymi danymi Wink
Kod:
[ dane_pomieniete_na_ewentualne_wyrownanie | adres_poczatkowy | poczatek_wyrownanego_bloku ]
 ^                                                             ^
||                                                            ||
adres_poczatkowy                          zwracany i wykorzystywany adres
Wtedy manager Ci niepotrzebny a jedynie musisz wykonać proste operacje na wskaźniku.
Zapisane

"2nd hand car, 2nd hand shoes, 2nd hand point of view from the 2nd hand news.
2nd hand shirt, 2nd hand tie, 2nd hand reason from a 2nd hand lie. (...)"
Pitchshifter - 2nd hand
Strony: [1] 2
  Drukuj  
 
Skocz do:  

Hosting: Polska Strefa - Ogłoszenia
Powered by SMF 1.1.7 | SMF © 2006, Simple Machines LLC