17 październik 2009

Krótka przerwa

Chciałbym z przykrością poinformować, że na okres kilku najbliższych miesięcy będę musiał wstrzymać to niezwykłe i interesujące zajęcie jakim jest blogowanie (sam proces nic nierobienia w tym względzie, praktykuję już od jakiegoś czasu). Powody jako takie są. Studia (ok, tu żartuję) a także kilka innych pomniejszych / powiększych spraw, które mimo wszystko są ważniejsze i bardziej czasochłonne. Myślę, że nikt się wiec nie obrazi. Obiecuję, że za jakiś czas pojawi się coś ciekawego, tak w ramach rekompensaty. Nie będę bawił się w ceregiele związane z usuwaniem bloga - niech sobie wisi w czeluściach internetu. A nuż/widelec coś/komuś się przyda - no w końcu kilka notek napisałem na trzeźwo. Na serio, to szkoda mi tych 1,22 z VAT, które wydałem na domenę. No koniec tego bezwładnego sypania literkami po ekranie. Do zobaczenia :)

01 wrzesień 2009

Opera 10 oficjalnie wydana

Opera Software zaprezentowała finalną wersję, dziesiątej odsłony swojej przeglądarki. Jak już widać było w wersjach beta i RC, zmianom uległ interfejs użytkownika - jest znacznie bardziej elastyczny i wygodniejszy w użyciu. Okienko "Speed dial" jest bardziej konfigurowalne (możliwość wyboru tła, określenie liczby wyświetlanych okienek). Możemy także schować główne menu, co daje nam jeszcze większą przestrzeń na wyświetlanie stron. Interesująca jest także możliwość zmiany położenia paska kart - można go umieścić choćby po lewej stronie, gdzie domyślnie wyświetlane są miniaturki stron.
Ciekawą funkcjonalnością jest także Opera Turbo. Pozwala ona na znacznie przyspieszenie ładowania stron (ale tylko w przypadku wolnych łączy), poprzez kompresję jej zawartości. Faktycznie, tracimy na jakości grafik, jednak nie zawsze jest to szczególnie istotna rzecz. Podobnie jak poprzednie beta wersje, przechodzi ona w 100% ostatni test ACID. Najważniejsze jednak, w porównaniu do poprzednich wersji (9.6x), znacznie poprawiono szybkość renderowania stron.
Download.

11 sierpień 2009

SSE w klasie - #2

Po omówieniu podstaw związanych z programowaniem przy użyciu instrukcji SIMD, czas przedstawić nieco bardziej użyteczne informacje. Dzisiejszym daniem głównym więc, będzie niewielka (rozwijana w kolejnych częściach mini kursu) klasa czterowymiarowego wektora. Życzę smacznego i zapraszam do lektury :).

Na początku zaczniemy od przygotowania składników. Dziś zamiast wyrównanej do szesnastu bajtów tablicy typu float, skorzystamy ze struktury:

__declspec(align(16))
struct VectorData{
    float x,y,z,w;
};

Dla instrukcji SSE liczy się tak naprawdę odpowiednio przygotowany skrawek pamięci – w jaki sposób zostanie on zaalokowany (czy to będzie tablica czy struktura) zależy tylko od preferencji programisty.

Mając wszystko przygotowane, możemy przejść do przyrządzania dania głównego. Na pierwszy ogień pójdzie więc konstruktor domyślny, w którym to będziemy zerować współrzędne wektora.
Samo zerowanie jest oczywiście sprawą trywialną, choć początkowo może przysporzyć nieco problemów.
Tradycyjnie, można to zrobić na dwa sposoby:
  • “manualne” przypisanie zer
  • skorzystanie z instrukcji memset (nagłówek cstring lub string.h)
Skorzystanie z memset powinno nieco przyśpieszyć tą prostą operację, ponieważ nie odwołujemy się do koprocesora, tylko operujemy danymi bezpośrednio w pamięci. Mimo iż w konstruktorze nie potrzebujemy wyjątkowej wydajności (jest on wykonywany zazwyczaj tylko raz), to jednak wykorzystamy trochę mocy :).
Otóż i niezwykle wyrafinowana treść naszej funkcji: 
_asm{
    mov esi,this
    movaps xmm0,[esi]
    xorps xmm0,xmm0
    movaps [esi],xmm0
}
Jak widzimy, są tu dwie nowe rzeczy. Pojawił się wskaźnik this oraz instrukcja xorps.
Wskaźnik this używany jest by dostać się to struktury przechowującej wektor. Zadziała to jednak tylko wtedy gdy zmienna ta będzie pierwszym polem klasy. Wówczas adres na który wskazuje this jest równoznaczny z adresem owego pola.
W przeciwnym wypadku, pobranie adresu trzeba będzie wykonać “ręcznie”:
void *p = &m_vecData

_asm{
    mov esi,p
    //...
}
Cóż takiego robi instrukcja xorps? Jest to nic innego jak zwykły xor, w wersji dla SSE, działający na poszczególnych elementach naszego wektora. Wykonując tą operację na dwóch takich samych wektorach, zerujemy je. Jest to znacznie szybsze, niż “statyczne” przypisanie wartości zero do każdej ze współrzędnych. Mając konstruktor domyślny, możemy stworzyć konstruktor kopiujący oraz operator przypisania. Praktycznie rzecz ujmując będą to dwie identyczne funkcje. Ich działanie będzie się tylko ograniczać do kopiowania wartości pomiędzy rejestrami xmm. Spójrzmy więc jak to wygląda:
_asm{
    mov edi,vec
    mov esi,this

    movaps xmm0,[esi]
    movaps xmm1,[edi]

    movaps xmm0,xmm1

    movaps [esi],xmm0
}
Zakładam, że vec, to stała referencja na obiekt naszej klasy, przekazana do konstruktora kopiującego bądź operatora przypisania.
Działanie, jak już wspominałem, jest bardzo proste. Rejestry edi i esi przechowują wskaźniki na nasze obiekty, które następnie (za pomocą movaps) kopiowane są do odpowiednich rejestrów SSE – xmm0 dla naszego wektora oraz xmm1 dla wektora przekazanego w parametrze. Przypisanie wektorów to po prostu skopiowanie zawartości xmm1 do xmm0. Ostatnia instrukcja zapisuje wynik w this.

Na koniec omówimy sobie operator new i delete, ponieważ jak się okazuje, domyślne operatory nie będą kompatybilne z naszą klasą wektora.
W podstawowym ujęciu, instrukcje SSE dzielimy na dwie kategorie: operujące na wyrównanych i niewyrównanych danych. Gdy operujemy na danych niewyrównanych, działanie SSE jest nieco wolniejsze, ponieważ potrzeba więcej cykli by dostać się do odpowiedniej komórki pamięci. Przykładem tego typu instrukcji może być movups – jest to odpowiednik movaps (u – unaligned, a - aligned), tylko, że tablica lub struktura, do której chcemy uzyskać dostęp, nie musi być wyrównana do granicy szesnastu bajtów.

Cóż więc wspólnego mają z tym operatory new i delete? To, że przydzielają one niewyrównane do odpowiadającej nam wartości, kawałki pamięci. Należy zatem operatory te przeciążyć. Na szczęście jest to operacja równie trywialna, jak te, które zaprezentowałem powyżej. Jeden ze sposobów zrobienia tego, opisał Netrix, mi jednak udało się, znaleźć znacznie prostsze rozwiązanie. Okazuje się bowiem, że istnieją funkcje które robią to za nas: _aligned_malloc i _aligned_free. Zewnętrznie działają tak samo jak standardowe wersje malloc i free, więc ich użytkowanie nie powinno sprawiać większego problemu.

Implementację dodawania i odejmowania zostawiam jako zadanie domowe :). Instrukcje, które je wykonują omówiłem w poprzedniej części kursiku więc nie powinno to stanowić większego problemu.

W załączniku znajduje się kod do dzisiejszego kursiku. Dodałem także metodę zerującą wektor.

05 sierpień 2009

Najsławniejsi po polsku

Przeglądając ostatnio serwisy z newsami dotyczącymi tematyki IT oraz ogólnie pojętego programowania, natrafiłem na dość interesującą informację. Powstał bowiem blog, którego autor postawił sobie za cel, tłumaczenie notek ze stron domowych takich sław jak Joel Spolsky lub Jeff Atwood. Jest to z pewnością bardzo ciekawa inicjatywa, szczególnie dla osób, które jeszcze z angielskim mają lekkie problemy :)

Adres strony: http://www.devblogi.pl.

23 maj 2009

Samsung yp-u1

Dziś będzie trochę inna notka, nie nawiązująca zbyt wiele do programowania. Jak widać, znajduje się w kategorii krótka recenzja, lecz nie będę tu opisywał kolejnej, niezwykle fascynującej książki, z wciągającą fabułą i akcją. Chciałbym opisać niewielkie urządzenie, z którym miałem przyjemność spędzić ostatnie 3 lata. Nie jest to już więc sprzęt najnowszy, raczej ciężko go dziś kupić, jednak po tylu latach użytkowania chciałbym napisać kilka spostrzeżeń na temat tego niewielkiego odtwarzacza mp3.

Na początek kilka podstawowych danych technicznych:

  • wymiary: 25.6 x 87.4 x 13.6 mm
  • waga: 33g
  • zakres częstotliwości: 20Hz~20KHz
  • czas odtwarzania: 13h (wbudowany akumulator litowo-polimerowy)

Jak więc widać, jest to dość zgrabne i lekkie urządzenie. Dodatkowo bez problemów radzi sobie z formatami mp3 (także ze zmiennym bitrate) ogg, wma czy wav. Raz tylko zdarzył mi się plik, który podczas odtwarzania zawiesił odtwarzacz. Komunikacja z komputerem została rozwiązana standardowo: port USB 2.0, choć sposób chowania konektora jest nieco oryginalny (o tym później). Wyświetlacz jest cztero linijkowy, podświetlany w razie potrzeby na biało. Prezentowany tekst jest wyraźny i ostry. Nawet podczas przesuwania nie miga ani nie smuży, co często się zdarza w odtwarzaczach tego typu. Podświetlenie także nie pozostawia nic do życzenia – równomiernie rozłożone na całość ekranu.

Posiadana przeze mnie wersja miała 512MB (YP-U1X), lecz dostępna ilość miejsca do zayp-u1_blgpisu to 496MB.
Nawigacja po menu zostało zrobiona w sposób bardzo intuicyjny, szczególnie, że całość została przetłumaczona na język polski. Co ciekawe, “nieprzetłumaczalne” jest tylko “Settings” i “Language”, by po zmianie języka na koreański, bez problemów można było przywrócić stare ustawienia.
Główny ekran odtwarzania jest mocno konfigurowalny. Bez problemów można wyświetlić informacje na temat trybu odtwarzania, włączonego efektu dźwiękowego, bitrate (pliki ze zmiennym są oznaczane jako VBR), numeru pliku i poziomu baterii. Ciekawą opcją są dwa sposoby przewijania nazw utworów: poziomy i pionowy. Ten ostatni jest szczególnie przydatny, gdy nazwa piosenki jest dość długa by w krótkim czasie mogła zostać zaprezentowana w całości.
Nawigacja po drzewie plików też nie przysporzyła mi nigdy problemów – całość rozwiązana standardowo i intuicyjnie. Niektórym osobom nie podobał się sposób włączania  i wyłączania odtwarzacza: kilku sekundowe przytrzymanie przycisku Play. Przyznam, że nigdy nie miałem problemów z przypadkowym wyłączeniem playera, tym bardziej, że czas reakcji jest dość szybki, a dodatkowo jest możliwość włączenia “brzęczyka” – dźwiękowe informowanie o wciśniętych przyciskach. Odtwarzacz obsługuje także własny typ playlist. Nie korzystałem z nich jednak nigdy; wolałem standardową, “folderową” kolejność utworów.
Dodatkową opcją jest nagrywanie dźwięku, lecz należy traktować to jako dodatek. Jakość nagrań jest  bardzo zła, poza tym, zapisywane sa tylko w formacie wav.

Zewnętrznie urządzenie zostało wykonane dość estetycznie. Warto zauważyć, że, wbrew pozorom, jest to bardzo mocna konstrukcja. Nie raz zdarzyło mi się, że podczas wyciągania telefonu z kieszeni, wypadł mi także player – działa do dziś.

Wyżej wspominałem o ciekawym rozwiązaniu dotyczącym chowania wtyczki USB. Pierwszy raz spotkałem się z takim podejściem do problemu, by zamiast wysuwania, zastosować składanie – jest to ostatni (jeśli nie jedyny) model odtwarzacza mp3 Samsunga, który stosuje taki sposób. Niestety, po 3 latach intensywnego użytkowania (także często jako przenośna pamięć) rozwiązanie takie nie sprawdziło się. Jeśli przez pierwszy okres używania Yeepa nie było żadnych problemów, to teraz (zauważyłem to kilka miesięcy temu) urządzenie nie łączy się z komputerem w kilku warunkach. Przede wszystkim wejście USB nie może być zamontowane poziomo. Wówczas odtwarzacz opada (konektor się składa) i traci kontakt z komputerem – widać jakieś ruchome części odpowiedzialne za łączenie zostały uszkodzone. 
Warto także zaznaczyć, że nie należy doprowadzać do zerowego stanu baterii. Często jest tak, że mimo informacji o niskim stanie akumulatora, po kilku chwilach odtwarzacz może zagrać jeszcze jeden, czy dwa utwory. Jest to jednak nie zalecane postępowanie. Ogólnie bateria sprawuje się dość dobrze – aktualnie trzyma od 6 do 8 godzin ciągłego odtwarzania muzyki. Warto zaznaczyć, że podświetlanie jest dość mocno “proądożerne” :)

Dołączone w zestawie słuchawki są wykonane solidnie i charakteryzują się dobrą jakością dźwięku podobnie jak sam odtwarzacz – nie jestem jednak znawcą i audiofilem, więc opinie będą na ten temat różne.       

Podsumowując: YP-U1 to solidnie, proste w obsłudze, dobrze wykonane urządzenie nie pozbawione jednak kilku wad. Bez problemu zmieści sie w kieszeni i pozwoli posłuchać ulubionej muzyki w prawie każdych warunkach.

20 maj 2009

SSE w praktyce - #1

Dziś postaram się przedstawić podstawowe operacje jakie możemy wykonać przy użyciu multimedialnych rozszerzeń procesora. Pokażę możliwość bezpośredniego wykorzystania assemblera, zaś w następnych notkach skupię się na wewnętrznych poleceniach kompilatora, specjalnie przygotowanych do obsługi SSE. Aby prezentowany kod mógł zostać uruchomiony potrzebny jest procesor klasy Intel Pentium III lub lepszy.
Więc do dzieła ;)

Jednak, jak to zwykle bywa, na początek jeszcze trochę teorii.
Programując przy użyciu SSE mamy do dyspozycji 8 dodatkowych, 128 bitowych rejestrów (w przypadku kodu dla procesorów 64 bitowych, liczba ta zwiększa sie do 16) - od xmm0 do xmm7. Wszystkie operacje, jakie będziemy chcieli wykonać przy użyciu omawianych tu instrukcji multimedialnych, będą operować właśnie na tych rejestrach. Jak już wiadomo, każdy z nich może przechować 4 wartości zmiennoprzecinkowe pojedynczej precyzji (float).

Na początek przygotujmy dane na których będziemy pracować: czteroelementowa, jednowymiarowa, wyrównana do 128 bitów tablica typu float – czyli nasz wektor.

__declspec(align(16)) float gVector4[4];
Zobaczmy na początek, w jaki sposób możemy dostać się do danych naszego wektora. Nie będziemy tego robić standardowo,
za pomocą operatora [ ] – posłużymy się do tego odpowiednimi instrukcjami SSE.
Pierwszą podstawową rzeczą, będzie skopiowanie zawartości komórki pamięci, gdzie znajduje się nasz wektor, do rejestru xmm.
_asm{
    lea esi,gVector4
    movaps xmm0,[esi]
    //wykonaj operacje...
    movaps [esi],xmm0
}
Pierwsza linijka odpowiada za pobranie adresu zmiennej gVector4 (dokładny opis działania instrukcji lea można znaleźć tu)
i skopiowanie go do rejestru esi (wskaźnik danych).
Następnie możemy zobaczyć już instrukcję z rodziny SSE –
movaps. Odpowiada ona za kopiowanie zawartości rejestrów xmm
między sobą, a także pamięcią. W tym przypadku, zawartość komórki pamięci na który wskazuje rejestr esi, zostanie przesłana do rejestru xmm0.
Ostatnia instrukcja kopiuje zawartość rejestru xmm0 pod adres wskazywany przez esi – czyli do naszej zmiennej.
Jak widać, mimo że jest to assembler, nie jest to takie trudne. Wygenerujmy więc nieco więcej bardziej praktycznego kodu.
Jak pewnie zważyliście, deklarowanie za każdym razem owej specyficznej tablicy z pewnością nie należy do specjalnie wygodnych.
Uprośćmy więc nieco ten proces:
typedef __declspec(align(16)) float AlignedVector4D[4];
Ten niewielki kawałek kodu, z pewnością pozytywnie wpłynie na czytelność kodu.
Poznajmy więc kilka podstawowych operacji: dodawanie, odejmowanie, mnożenie i dzielenie.
Na początek zdefiniujmy dwa wektory, na których będziemy wykonywać nasze operacje:
AlignedVector4D vec1 = {5.0,6.0,3.0,1.0};
AlignedVector4D vec2 = {2.5,3.0,1.5,0.5};

Za dodawanie odpowiada instrukcja addps. Przykład użycia:
_asm{
    lea esi,[vec1];
    lea edi,[vec2];

    movaps xmm0,[esi];
    movaps xmm1,[edi];

    addps xmm0,xmm1;

    movaps [esi],xmm0
}
Jak widać kod jest bardzo prosty. Dwie początkowe instrukcje odpowiadają za pobranie adresu zmiennych, następnie kopiujemy je
do rejestrów xmm0 oraz xmm1 by dodać je do siebie w następnej instrukcji. Wynik zapisywany jest do xmm0 które jest kopiowane
pod adres jaki wskazuje esi, czyli do zmiennej vec1.
Zauważmy, że obecność kopiowania do rejestru xmm1 nie jest konieczna – addps, podobnie jak movaps może pracować na jednym
operandzie znajdującym sie w pamięci operacyjnej:
_asm{
    lea esi,[vec1];
    lea edi,[vec2];

    movaps xmm0,[esi];
    addps xmm0,[edi];
    movaps [esi],xmm0
}
Pozostałe operacje wykonuje się analogicznie:
subps – odejmowanie
mulps – mnożenie
divps – dzielenie
To byłoby na tyle. W kolejnych częściach poznamy trochę więcej interesujących rzeczy ;)

29 kwiecień 2009

isabellFrame update

Podczas prac nad silnikiem, zmieniłem kilka elementów Frameworka. Poprawiłem przede wszystkim dostęp do urządzeń wejścia (klawiatura i mysz). Dotychczas jedynym sposobem było podpięcie się pod rendering (metoda render klasy CBaseRenderer) i z tego miejsca pobieranie stosownych informacji. Teraz należy skorzystać z listenera (IInputListener).
Zmiany pojawiły się także w metodach pobierających informacje o systemie – są one wykonywane w bardziej stabilny sposób oraz mogą pobrać więcej danych.
Do działania aplikacji nie jest już konieczna klasa renderująca. Główną pętlę przeniosłem do CIsabellFramework. Także informacje o błędach są teraz bardziej kompletne: dodany został opis błędu oraz nazwa metody która go spowodowała.
Poprawiłem także kilka mniej znaczących uchybień.
W katalogu samples znajduje się sześć przykładowych aplikacji wykorzystujących określone funkcjonalności Frameworka.
Download