Ładowanie
popup

Kod z przyszłości

2010-10-25 14:35:47

Wczoraj spotkała mnie ciekawa niespodzianka. Podczas testowania silnika przy każdej zmianie jednego pliku i próbie skompilowania i uruchomienia programu testowego (który znajduje się w tej samej solucji co silnik i jest zależny od silnika) prawie cały silnik kompilował się od początku. Opcja Minimal Build (czy coś takiego) była włączona... skasowanie plików tymczasowych nie pomogło (chyba że były jeszcze jakieś których nie zauważyłem), restart VS też nic, pozostało ręcznie kompilowanie modyfikowanego pliku i linkowanie zamiast po prostu wciśnięcia F5. Pomyślałem że mu przejdzie np. po restarcie komputera :] Przed wyłączeniem komputera i pójściem spać włączyłem backup dysku... dodam że mój backup kopiuje tylko pliki które mają "nowszą" datę modyfikacji. spojrzałem na log, w którym lista zaktualizowanych plików kopii była pusta, mimo że modyfikowałem kilka plików. Od razu popatrzyłem na da datę systemową, która wskazywała na 2006 rok i podobną godzinę do tej która wtedy była w rzeczywistości (sic!), przyczyną zmiany daty był podłączony dysk przenośny USB który nie wiem czemu przy każdym starcie komputera resetował datę (bez dysku już było ok). Po ustawieniu daty na prawidłową od razu uruchomiłem VS - tym razem IDE kompilowało tylko te pliki które były NAPRAWDĘ zmienione od ostatniej kompilacji, a nie te które tylko mają od tej daty kompilacji "nowszą" datę :)

PS. Wg. mnie Visual Studio powinien się połapać że data systemowa może być zła, skoro kompiluje pliki które zostały zmodyfikowane w "przyszłości".

Tagi: Inne Visual Studio | Dodaj komentarz

Kolorowanie

2010-04-24 13:09:56

(dziś trochę "lanie wody" :) )

W przerwie między kodowaniem silnika, nauką C#, modyfikowaniem (nieziemsko tragicznego) kodu osCommerce i innych PHP-owych dziwolągów, postanowiłem podjąć się czegoś innego - bardziej ambitnego - mianowicie: zmienić kolorowanie składni mojego edytora (VC++) :> Od dawna w każdym edytorze używam kolorowania pododnego do klasycznego Borlandowego, które trochę zmieniłem. Oto rezultat:

Mój schemat kolorów Visual C++
Mój schemat kolorów Visual C++

Używam czcionki Lucida Console. Wygląda prawie identycznie jak Consolas, z tym że jest trochę "niższa" i tekst jest bardziej zbity (taki preferuję).

Dla zainteresowanych pliki z ustawieniami kolorowania dla VC++2008 (działają też z VC++2010):

Tagi: Inne Visual Studio | Komentarze (1)

Jak (nie) optymalizować kodu #1

2009-10-18 01:22:00

Pierwszą próbą optymalizacji bylo użycie FPU (czyli jednostka zmiennoprzecinkowa CPU) do zbudowania funkcji zastepujacych odpowiedniki z cmath. Pierwsze testy w trybie debug z domyslnymi ustawieniami wygladaly bardzo pozytywnie. Liczyłem w pętli kilka operacji, uzywajac odpowiednio funkcji z biblioteki standardowej cmath i z moich funkcji z uzyciem FPU. Czasy moich funkcji byly około 3 razy krótsze. Po włączeniu optymalizacji w kompilatorze - /fp:fast i /Oi (ten przełacznik włącza wstawianie instrinsic-ów w miejsce niektórych funkcji, w tym wiekszości tych z cmath) czasy moich funkcji byly juz tylko 2 razy krotsze. Po przełączeniu do trybu release stało sie to czego sie spodziewałem(?), cmath był szybszy... o wiele. Opcje /fp:precise i /Ox dały od kilku do kilkusetkrotne przyspieszenie. Nie pomogło też wstawienie do funkcji __forceinline. Przełączyłem więc się na podgląd instrukcji Asemblera. Hmm... kod dla funkcji z cmath nie byl wogóle generowany (nadal mowa o trybie Release). Stąd takie kosmiczne czasy ;) Rozwiązaniem tego 'problemu' jest wyłączenie optymalizacji (C++/Optimalization/Optimalization=Disabled (/Od)) lub zmiana sposobu mierzenia obliczeń. Dla przykładu w takim kodzie NIE zostanie wywolana funkcja sqrt:

float x=std::sqrt(xxx); x=sth;

kompilator zapewne ufa że brak wywołania funkcji sqrt nie spowoduje jakiegoś błedu w dalszych obliczeniach, bo za chwile w miejsce zmiennej x zostanie zapisana nowa wartość. Więc na przykład taka metoda mierzenia wydajności jest chyba bez sensu:

TIMER_START; for(int i=0;i<99999;++i){ xx3=std::sqrt(rrr); rrr+=0.56f; } TIMER_END;

Zmiana ustawień kompilatora moze spowodować że wyniki będa troche niesprawiedliwe. Pozostaje zmiana metody mierzenia. Ustawiem spowrotem opcje (C++/Optimalization/Optimalization=Full Optimization (/Ox)). Trzeba zmusic kompilator do uruchomienia funkcji z cmath. Kod:

float sth=...; TIMER_START; for(int i=0;i<99999;++i){ xx3=std::sqrt(rrr); sth+=xx3; // np. tak rrr+=0.56f; } TIMER_END;

Dodałem zmienna sth ktora będzie zainteresowana wynikiem funkcji std::sqrt. Ale to za mało nadal funkcja sqrt nie bedzie wywołana, ba - wogóle cała pętla będzie 'wyrzucona'. Działanie programu nie zmieni sie czy będzie ta pętla czy nie. Wystarczy wypisac dalej printf("%f",sth) i juz działa... Swoją drogą spryciarz z tego kompilatora :) Ostateczne pomiary sa pozytywne:

fpu_math time= 0.004849s cmath time= 0.005963s fpu_math time= 0.004900s cmath time= 0.010668s fpu_math time= 0.001266s cmath time= 0.002462s ...

Ostatecznie można powiedzieć że przyspieszenie jest, choć nie wiem czy to wszystko warte zachodu (dla satysfakcji można duzo zrobic) :P Jeszcze dodam kilka przykładów funkcji których użyłem w teście jak kogoś to interesuje (w komentarzach bardziej złożonych funkcji opisałem zawartości stosu):

float Cosf(float x) { __asm { fld x fcos } } float Sqrtf(float x) { __asm { fld x fsqrt } } // oszczedze miejsce (wiekszosc wyglada jak powyzsze) :) i podam dalej tylko te trudniejsze do napisania: float Powf(float x,float y) { __asm { fld y fld x fyl2x ;log2(x)*y fst st(1) ;log,log frndint ;int(log),log fxch st(1) fsub st(0),st(1) ;float(log),int(log) f2xm1 ;2^float(log)-1,int(log) fld1 faddp st(1),st(0) ;2^float(log),int(log) fscale ;2^int(log)*2^float(log) } } float Expf(float x) { // exp(x) == e^x __asm { ;y==e fld x fld GE_E fyl2x ;log2(x)*y fst st(1) ;log,log frndint ;int(log),log fxch st(1) fsub st(0),st(1) ;float(log),int(log) f2xm1 ;2^float(log)-1,int(log) fld1 faddp st(1),st(0) ;2^float(log),int(log) fscale ;2^int(log)*2^float(log) } } float Logf(float x) { // ln(x) (naturalny!) __asm { fld1 fld x fyl2x ;log2(x) fldl2e ;log2(e) fdivp st(1),st(0) } } float Log10f(float x) { __asm { fld1 fld x fyl2x ;log2(x) fldl2t ;log2(10) fdivp st(1),st(0) } }

Dodatkowo funkcje których nie ma w standardowej bibliotece matematycznej, a mogą sie przydać jak ktoś sie interesuje tematem :) :

void Sincosf(float x,float *oSin,float *oCos) { // sinus i cosinus od x __asm { mov eax,oSin mov edx,oCos fld x fsincos fstp dword ptr[edx] fstp dword ptr[eax] } } float Log2f(float x) { // log2(x) __asm { fld1 fld x fyl2x ; sciagnie sam st(1) } } float Logxf(float x,float y) { /* oblicza logarytm dowolnej podstawy: logx(y) sposob liczenia: logx(y)=log2(y)/log2(x) */ __asm { fld1 fld y fyl2x ;log2(y) zwija 1 ze stosu fld1 fld x fyl2x ;log2(x) zwija 1 ze stosu fdivp st(1),st(0) } }

Tagi: Programowanie C++ Optymalizacja Asembler Visual Studio | Dodaj komentarz