dlaczego wiedza na temat architektury ętowej, systemu ... aplikacje.pdf · dlaczego wiedza na...
TRANSCRIPT
Dlaczego wiedza na temat architektury sprzętowej, systemu operacyjnego i kompilatora przydaje się w pisaniu wydajnych aplikacjiAdam Strzelecki, 8 kwietnia 2008, Uniwersytet Jagielloński
Coś o mnie
Prowadzę działalność gospodarczą
Od 4 lat współpracuję z francuską firmą Digitech International S.A. oraz Leica Geosystems Geospatial Imaging (wcześniej ER Mapper)
Zajmuję się projektowaniem i wdrażaniem systemów GIS “Geospatial Imagery Systems”
Programuję w C/C++ (przetwarzanie, kompresja obrazu) oraz PHP, JavaScript, Java, Ruby (Web, UI)
2
Czym się zajmuje GIS?
Pozyskiwanie obrazu Rektyfikacja i orientacja geograficznaPrzygotowanie danych do udostępniania
Publikacja w Internecie/IntranecieSerwery WWW, serwisy TCP/IP
Przeglądanie w Internecie/IntraneciePrzeglądarki WWW, klienci TCP/IP
Urządzenia pozyskujące obraz (satelita, samolot (bez)załogowy)
Serwery/nośniki obrazów źródłowych (taśmy, DVD)
Serwery i oprogramowanie rektyfikujące mozaiki
(ER Mapper Professional, GeoTIFF)
Serwery i oprogramowanie kompresujące
(ER Mapper Professional, ECW, JPEG 2000)
Serwery WWW wraz z rozszerzeniami streamingu obrazów
(IIS + Image Web Server)
Aplikacje klienckie:
DHTML ActiveX
Dane nieprzetworzoneDevice raw data
Dane w uniwersalnych formatach: TIFF, BIL …
+Parametry pozyskania obrazu (pozycja, kąt,
wybrane sensory, czas)
Transfer danych z urządzenia zdalnego(digitalizacja)
Dane zrektyfikowane w uniwersalnych formatach
+Dane rektyfikacji (projekcja,
system współrzędnych)
Rektyfikacja
Dane skompresowane do formatu ECW lub JPEG 2000 wraz z informacją geolokacyjną (projekcja, system współrzędnych)
Kompresja
Dane skompresowane do formatu ECW lub JPEG 2000 wraz z informacją geolokacyjną (projekcja, system współrzędnych)
Kafelki JPEG Części pliku (chunks)
3
GIS znaczy “proszę czekać”
Rektyfikacja i kompresja zdjęć lotniczych całej Francji o rozdzielczości 0,5 m może trwać nawet miesiąc używając “farmy” najnowszych komputerów
Często błędy w procesie kompresji wykrywane są dopiero w trakcie, a cały proces trzeba uruchamiać od początku
Każda zmiana i uaktualnienie wymaga ponownej kompresji całego lub kawałka obrazu i ponownego czekania...
4
Wydajność? Przecież już...
Zastosowałem algorytmy o najmniejszej złożoności obliczeniowej
Używam sprzętu o najwydajniejszej konfiguracji dostępnej na rynku
Używam najnowszego kompilatora wydajnego języka programowania
Wszystko przecież działa szybko...
5
Czas jest na miarę złota!
Żeby zaoszczędzić (zyskać dodatkowy) miesiąc w roku pracy wystarczy zwiększyć wydajność o 8,3%
Często zyskanie 8,3% dodatkowego czasu może zwiększyć zyski firmy o 50%, jeśli ta ma względnie niską marżę w stosunku do kosztów
Co z tego, skoro mamy już najlepsze oprogramowanie i sprzęt? Gdzie szukać wydajności?
6
W poszukiwaniu wydajności
Poznajmy kompilator naszego ulubionego języka programowania (C/C++ MSVC, GCC)
Poznajmy architekturę sprzętu, na którym ma działać nasze oprogramowanie, wykorzystajmy w pełni...
Procesor i pamięć cache
Pamięć RAM
Pamięć masową (dyski twarde)
Poznajmy system operacyjny i postarajmy się mu nie przeszkadzać
7
Najpierw kompilator
Żeby panować nad wydajnością kodu musimy użyć kompilatora który generuje kod maszynowy w sposób przewidywalny, czyli tak jak chcemy (...)
Nie można panować nad wydajnością aplikacji pisanych w dynamicznych językach Java, C#, Python czy Ruby (nie ujmując im ich wspaniałych zalet)
Najlepiej by było wszystko napisać w assemblerze
Jednak ... skupimy się na C/C++ i MSVC oraz GCC
8
Inni zrobią to lepiej
Zanim zaczniemy pisać “przydatne” funkcje narzędziowe, sprawdźmy czy czasem takie już nie istnieją w systemie, programiści systemowi spędzili wiele lat na szlifowaniu ich wydajności
Nawet jeśli takich funkcji nie ma, może są dostępne w sprawdzonych i popularnych bibliotekach
9
Inni zrobią to lepiej
Pisanie od nowa funkcji bibliotecznych takich jak strlen, strcmp jest dość popularną praktyką, która prowadzi prosto do ... kiepskiej wydajności
GLIBC strlen
size_t strlen (const char *str) {const unsigned long int *longword_ptr = (unsigned long int *) str;unsigned long int longword,
magic_bits = 0x7efefeffL,himagic = 0x80808080L,lomagic = 0x01010101L;
for (;;) {longword = *longword_ptr++;if (((longword - lomagic) & himagic) != 0) {
const char *cp = (const char *) (longword_ptr - 1);if (cp[0] == 0) return cp - str;if (cp[1] == 0) return cp - str + 1;if (cp[2] == 0) return cp - str + 2;if (cp[3] == 0) return cp - str + 3;
}}
}
size_t strlen (const char *str) {const char *cstr = str;while(*cstr) cstr++;return cstr - str;
}
10
Kompilator nie jest wszechmogący
Choć kompilator stara się jak może, niestety nie jest w stanie zrozumieć ogólnego sensu przetwarzanego kodu, ani zamierzeń programisty
Możemy pomóc kompilatorowi generować wydajny kod odpowiednio formułując nasz kod źródłowy
Kod, który jest nieczytelny dla człowieka, prawdopodobnie będzie też mało “czytelny” dla kompilatora, który nie będzie potrafił go zoptymalizować
11
Jak pomóc kompilatorowi?
Używaj tablica[i] zamiast *(tablica + i)
Staraj się nie używać zmiennych typu static w obrębie funkcji, zmienne na stosie mogą być automatycznie zamieniane na zmienne rejestrowe register
Oznaczaj wszystkie zmienne tylko do odczytu za pomocą const
void moja_funkcja(char *readonly) {char format[] = “Lancuch to %s”;(...)
}
void moja_funkcja(const char *readonly) {const char format[] = “Lancuch to %s”;(...)
}
12
Dobrodziejstwa procesora
Najnowsze procesory posiadają zestawy instrukcji przetwarzania wektorowego SSE oraz dodatkowe rejestry, wspierają też architekturę 64-bitową
Większość starszych kompilatorów nie korzysta lub korzysta kiepsko z zestawów instrukcji rozszerzonych
Wykorzystanie rozszerzonych instrukcji (w postaci wstawek w assemblerze) może w pewnych przypadkach zwiększyć wydajność naszej aplikacji nawet 3-krotnie
13
Dobrodziejstwa procesora
Najnowsze procesory zawierają coraz więcej rdzeni... hurra! ... ale moja aplikacja wcale nie działa szybciej!?
Zobacz czy czasem nie da się rozbić programu na wątki (zrównoleglić)
Jeśli nawet nie możesz zastąpić swoich algorytmów “równoległymi” odpowiednikami, może da się przetwarzać kilka kawałków danych jednocześnie
Twój nowy kompilator na pewno obsługuje OpenMP
14
Dobrodziejstwa procesora
Firmy, takie jak Intel czy AMD, oferują biblioteki zawierające zbiory często stosowanych procedur różnych zastosowań począwszy od operacji na macierzach, funkcjach matematycznych i kryptograficznych, kończąc na kompresji JPEG i falkowej
Intel® Integrated Performance Primitives 5.3
AMD Performance Library (APL)
15
Pamięć podręczna i RAMDostęp do pamięci gra kluczową rolę w wydajności aplikacji
Kompilator stara się zoptymalizować dostęp do zmiennych, tzn. przechowywać je w rejestrach, tworzyć pule dla stałych, wyrównywać ich pozycję do rozmiaru słowa
Nowsze kompilatory starają się układać zmienne z myślą o pamięci podręcznej - “cache”
16
Dostęp do pamięci
Odbywa się przy użyciu słowa procesora, 4 bajty dla architektury 32-bitowej równoważny najczęściej typowi int
Dostęp do 2 bajtów short czy 1 bajta char zajmuje procesorowi dokładnie tyle same czasu co dostęp do 4 bajtów
Procesor odwołuję się do pamięci RAM poprzez pamięć podręczną ~1-2 MB, podzieloną na linie o rozmiarach 64 lub 32 bajtów
17
Pamiętając o liniach “cache”
Staraj się deklarować zmienne używane razem w lokalnym kontekście obok siebie
Staraj się deklarować zmienne od tych o najmniejszym rozmiarze do największych
static int i;static char body[1024];static int j;static double x, y, z, r, w = 0;static int k;
for(i = 0; i < x + y; i++)for(j = 0; j < z; j++)
for(k = 0; k < j; k++)(...)
static int i, j, k;static char body[1024];static double x, y, z, r, w = 0;
for(i = 0; i < x + y; i++)for(j = 0; j < z; j++)
for(k = 0; k < j; k++)(...)
void moja_funkcja() {int i, j, k;double x, y, z, r, w = 0;char body[1024];(...)
}
18
Pamiętając o liniach “cache”
Indeksuj tablice w dobrej kolejnościvoid moja_funkcja(int mul) {
int i, j;int values[1024][1024];
for(j = 0; j < 1024; j++)for(i = 0; i < 1024; i++)
values[i][j] = i * j * mul;(...)
}
void moja_funkcja(int mul) {int i, j;int values[1024][1024];
for(i = 0; i < 1024; i++)for(j = 0; j < 1024; j++)
values[i][j] = i * j * mul;(...)
}
19
Problemy z pamięcią?
Nie nadużywaj malloc i free, które działają dość wolno, najszybszy jest stos, lecz używaj stosu rozsądnie
Jeśli jednak musisz alokować często pamięć zastanów się nad “pulami” pamięci lub alokowaniem bloków o nadmiarowym rozmiarze będących potęgą dwójki
Pamiętaj o fragmentacji pamięci wirtualnej! Może się okazać że nie możesz zaalokować 4 MB pamięci nawet jeśli masz wolne 50% ze swoich 2 GB RAM
20
Dostęp do dysku
W przypadku aplikacji przetwarzających duża ilość danych pamięć masowa stanowi bardzo wąskie gardło
Pamięci masowe są często kilka rzędów wolniejsze od pamięci RAM
Pamięci masowe prócz szybkości odczytu i zapisu charakteryzują się też dużym czasem dostępu (wyszukiwania)
21
Magiczne 4 KB
4 KB to 8 sektorów po 512 bajtów dysku twardego
4 KB to najczęściej rozmiar bloku danych większości nowych systemów plików t.j. ext3 czy NTFS
4 KB to rozmiar strony pamięci w architekturze Intel x86 32-bit i wielu innych
4 KB to rozmiar bloków danych czytanych jednorazowo i cache’owanych przez większość systemów operacyjnych
22
Pamiętaj o 4 KB
Staraj się aby struktura twoich plików pokrywała się z podziałem 4 KB
Nagłówek w pierwszych 4 KB
Egzemplarze struktur danych po 1, 2, 4 ... szt. na 4 KB
Unikaj zapisywania struktur na granicy 4 KB
Pamiętaj, że system cache’uje te 4 KB bloki
23
Parę innych sztuczek
2 dyski 150 GB będą działały 2 razy szybciej niż 1 dysk 300 GB, a RAID 0 zwiększa wydajność proporcjonalnie do ilości dysków w macierzy
Większość dysków dysponuje pokaźnym sprzętowym cache o wielkości 8-16 MB
Przy czytaniu danych z sektora, dysk najprawdopodobniej przeczyta i umieści dane z całego cylindra w cache, tak więc dane z innego sektora tego samego cylindra będą dostępne błyskawicznie
24
Coś o własnym cache
Jeśli naprawdę nie musisz, nie implementuj własnego cache’owania danych z dysku twardego
Zdaj się na mechanizmy cache’owania dostępne w systemie operacyjnym oraz mechanizmy sprzętowe dysku twardego
Jeśli tylko to możliwe zamiast read i write używaj mmap
Nie zamykaj uchwytów do plików, których będziesz używał ponownie w krótkim czasie
25
System plików
System plików ma ogromne znaczenie dla wydajności
Fragmentacja systemu plików może istotnie popsuć wydajność, więc staraj się zapisywać dane hurtem lub używać pre-alokacji przestrzeni dyskowej
Dobrze dobrany system plików może pozytywnie wpłynąć na wydajność, ext3 z dir_index doskonale nadaje się do przechowywania małych plików, natomiast XFS do przechowywania dużych
26
Asynchroniczne I/O
Nowe systemy operacyjne oferują interfejs asynchronicznego dostępu do pamięci masowych za pomocą kolejek i komunikatów
Staraj się korzystać z AIO, w ten sposób CPU może zająć się czymś innym podczas gdy dane ładowane są z dysku
Synchroniczne I/O znacząco obciąża procesor i nie pozwala wykonywać wielu długotrwałych operacji na pamięciach masowych jednocześnie
27
CPU to nie wszystko!
W ciągu kilku lat najprawdopodobniej nastąpi zmiana wiodących paradygmatów programowania na
Programowanie współbieżne
Programowanie funkcyjne
Nowe CPU mają coraz więcej rdzeni, ale już nie wykonują naszych starych programów szybciej
Do gry wkracza Graphics Processing Unit ...
28
“GPU Computing”
... to nowa bardzo obiecująca dziedzina informatyki
Współczesne GPU to wydajne super-skalarne procesory wektorowe (architektura Cell) zawierające nawet 128 jednostek obliczeniowych, mogących niezależnie wykonywać kod
Intel Core Duo to ~48 GFLOPs, 10 GB/s
nVidia G80 to ~330 GFLOPS, 80+ GB/s
29
Cuda nVidii!
CUDA czyli Compute Unified Device Architecture to zestaw narzędzi pozwalający na wykonywanie skompilowanego uproszczonego kodu języka C na GPU bez większych ograniczeń
CUDA pozwala zwykłym aplikacjom uruchamiać część swojego kodu na GPU i korzystać z ogromnej mocy obliczeniowej
Wcześniej (od 2003) możliwe to było za pomocą języka Cg “shaderów”, choć o wiele bardziej skomplikowane
30
Cuda nVidii!
GPU-Accelerated Dirac Video Codec, czyli implementacja na GPU kodeka Dirac używającego kompresji falkowej video zaprojektowanego przez BBC, zanotowała ~15-krotny wzrost wydajności w stosunku do implementacji na CPU
Rosyjska firma Elcomsoft zaprezentowała rozwiązanie używające CUDA do łamania haseł w jeden dzień metodą “bruteforce” na GeForce 8800, podczas gdy wcześniej zajmowało to przynajmniej 25 dni
31
Co przyniesie przyszłość?
Języki takie jak Erlang znajdą się wreszcie w centrum zainteresowania
Pora odkurzyć książki na temat algorytmów równoległych, z których wiele wreszcie znajdzie swoje zastosowanie
CPU może się stać tylko układem zarządzającym, podczas gdy GPU przejmie ciężar obliczeń
CPU i GPU ostatecznie się połączą tworząc super-PU
32
Literatura
Randall Hyde, Write Great Code, Volume 1 & 2, No Starch Press, 2005
Daniel P. Bovet, Marco Cesati, Understanding the Linux Kernel, O’Reilly, 2001
Vlad Pirogov, The Assembly Programming Master Book, A-LIST, LLC, 2005
Randima Fernando, Mark J. Kilgard, The Cg Tutorial: The Definitive Guide To Programming Real-Time Graphics, Addision Wesley Professional, 2003
Michael J. Dickheiser, C++ For Game Programmers, Second edition, Charles River Media, 2007
NVIDIA CUDA Compute Unified Device Architecture, Programming Guide 1.1, NVidia Corp., 2008
33
Dziękuję za uwagęPytania?
34