Projektowanie energooszczędnych systemów wbudowanych dr inż. Ireneusz Brzozowski C-3, p. 512 WIET KATEDRA ELEKTRONIKI Elektronika i Telekomunikacja, Systemy Wbudowane www.agh.edu.pl Projektowanie energooszczędnych systemów wbudowanych Energooszczędne programowanie Wstęp Sprzęt źródła strat energii Optymalizacja oprogramowania niezależna od sprzętu A. Pal, Low-Power VLSI Circuits and Systems, Chapter 12, New Delhi: Springer, 2015. EiT 2017/18 2 1
Wstęp Gdyby porównać system mikroprocesorowy do samochodu, to sprzęt hardware https://pixabay.com/ oprogramowanie software biegi sprzęgło gaz hamulec wydajność energetyczna zasięg, zużycie paliwa 3 Wstęp - podział optymalizacja poboru mocy w oprogramowaniu zależna od sprzętu niezależna od sprzętu wykorzystuje cechy architektury docelowego procesora charakterystyka docelowej platformy jej specjalne właściwości mogą być wykorzystane dla programowej redukcji strat energii, właściwa identyfikacja parametrów sprzętu, parametryzacja cech platformy łatwiej dla kompilatora. nie potrzebuje żadnych informacji o procesorze i może być zastosowana w dowolnym systemie 4 2
Sprzęt źródła strat energii CPU: CISC versus RISC CISC: b. dużo instrukcji o zmiennej długości, dużo trybów adresowania, różny czas wykonywania rozkazów. RISC: zredukowana liczba instrukcji o stałej długości, mało trybów adresowania, stały czas wykonywania. CISC daje większe możliwości niż RISC. Kompilator musi znać relatywny koszt realizacji instrukcji, aby wybrać najlepszą sekwencję rozkazów realizujących zadanie. ALU: superskalarność ograniczona przez możliwości zrównoleglenia na poziomie instrukcji niebezpieczeństwo niepełnego obciążenia wszystkich jednostek prawie zawsze jest jednostka, która czeka. SMT (Simultaneous multithreading) nadzoruje pracę wątków. 5 Sprzęt źródła strat energii Potokowość: w celu poprawy wydajności ALU wykonanie rozkazów podzielono na etapy. Np. rozkaz może być zrealizowany w pięciu etapach pięciostopniowa kolejka. Niebezpieczeństwo: konflikt kolejki (pipeline stalls) jałowe cykle CPU strata czasu i energii. Kompilator przez planowanie kolejności rozkazów i reorganizację może zminimalizować ilość konfliktów i zredukować zbędny pobór energii. Rejestry: CPU jest wyposażony w rejestry ogólnego przeznaczenia i specjalne, w których można przechowywać pośrednie wyniki obliczeń i zmienne zamiast w pamięci, do której dostęp zabiera więcej czasu i wymaga większego poboru energii. 6 3
Sprzęt źródła strat energii Magistrala systemowa: linie adresowe, danych i sterujące służą do komunikacji CPU z pamięcią i urządzeniami we/wy. Duża pojemność linii. Aktywność zależy od programu i przetwarzanych danych pobór mocy. Kompilator może uwzględnić aktywność na magistralach, ale łatwiej na adresowej niż danych. Pamięć: Hierarchia: podręczna, główna i zewnętrzna. Podręczna jest najmniejsza, ale najszybsza, Niektóre techniki programowej redukcji poboru mocy mogą pogorszyć wydajność pamięci podręcznej kolizje, chybienia. Dodatkowe transfery na magistralach większy pobór mocy. 7 Sprzęt źródła strat energii Pamięć: Techniki redukcji poboru mocy Zminimalizować ilość dostępów do pamięci przez program, Zmniejszyć całkowitą ilość pamięci zajętej przez program, Zapewnić dostęp do pamięci jak najbliżej procesora: Rejestry Pamięć podręczna Pamięć główna Pamięć zewnętrzna Zastosować najefektywniejszy możliwy transfer (np. zamiast ładować pojedyncze słowa zastosował ładowanie wielokrotne, równolegle itp.) 8 4
Optymalizacja niezależna od sprzętu OPTYMALIZAJA poboru energii zmniejszyć wykonywany kod usunąć zbędne obliczenia i pośrednie wartości mniej pracy CPU, mniej dostępów do cache i pamięci mniejszy pobór mocy i szybsza praca programu unikać redundantnych obliczeń wykorzystać powtórnie raz obliczone wartość mniej skoków skoki zastąpić kodowaniem w linii (ang. strait-line code, branch-free code) ponieważ skoki kolidują z pobieraniem instrukcji (pamięć podręczna) 9 Optymalizacja niezależna od sprzętu OPTYMALIZAJA poboru energii zoptymalizować wspólne przypadki unikalne cechy: szybka ścieżka wolna ścieżka lokalność kod i dane pobierane w tym samym czasie powinny być umieszczone blisko siebie w pamięci wykorzystać hierarchię pamięci częsty dostęp do bliższej pamięci: rejestry, podręczna, główna, itd.. zrównoleglić przeorganizować operacje, tak aby możliwa była równoległość 10 5
Optymalizacja niezależna od sprzętu Kompilator znajduje się pomiędzy wejściem zapisanego programu a generatorem kodu i pracuje na pośrednim kodzie. Zatem może analizować kontroling programu i przepływ danych w tym kodzie i wykonać odpowiednie jego transformacje, żeby poprawić ten kod. Konwencjonalne kompilatory mogą: optymalizować rozmiar generowanego kodu, redukować czas wykonania programu. Niektóre z rozwiązań są przedstawione dalej EiT 2017/18 11 Wstawki małych funkcji wstawić ciało funkcji zamiast odwołania do niej. Możliwy zysk: nie ma kosztu wywołania i powrotu z funkcji krótszy czas wykonania, eliminacja skoków bliski kod poprawa wydajności pamięci podręcznej (lepsza lokalność odwołań), można zastosować inne metody optymalizacji. Koszt: większy kod. Before inlining: int f(int y) { return pred(y) + pred(0) + pred(y +1); int pred(int x) { if (x == 0) return 0; else return x 1; After inlining: int f(int y) { int temp; if (y == 0) temp = 0; else temp = y 1; /* (1) */ if (0 == 0) temp += 0; else temp += 0 1; /* (2) */ if (y+1 == 0) temp += 0; else temp += (y + 1) 1; /* (3) */ return temp; EiT 2017/18 12 6
Przeniesienie kodu na zewnątrz pętli część obliczeń poza pętlą wykonana jednokrotnie, a ich wynik jest wprowadzony do pętli przez zmienną tymczasową. nie ma wielokrotnych obliczeń tej samej wartości. Koszt: dodatkowa zmienna tymczasowa trzeba zarezerwować dla niej odpowiednie zasoby. DO I = 1100 ARRAY(I) = 5,0 * PI * I ENDDO t = 5,0 * PI DO I = 1100 ARRAY(I) = t * I ENDDO EiT 2017/18 13 Eliminacja martwych stałych (ang. dead-store elimination) jeśli kompilator znajdzie nie używane zmienne może je usunąć oraz kod z nimi związany. zmniejszenie rozmiaru kodu, przyspieszenie jego wykonania. int dse(void) { int x = 15; int y = 35; /* Assignment to dead variable */ int z; z = x << 2; return z; y = 33; /* Unreachable code */ return 0; EiT 2017/18 14 7
Eliminacja martwego kodu (ang. dead-code elimination) usunąć kod, który nigdy nie będzie wykonany lub kod dotyczący martwych zmiennych lub nieużywane funkcje itp. zmniejszenie rozmiaru kodu, przyspieszenie jego wykonania. int main(void) { int x = 5; int y = 6; int z; z = x * (y >> 1); if (z == 0) { /* DEBUG */ printf( %d\n, z); return z; EiT 2017/18 15 Dynamiczna eliminacja martwego kodu usunąć warunkowo martwy kod i ponownie połączyć potrzebne bloki programu podczas pracy programu. zmniejszenie rozmiaru kodu, przyspieszenie jego wykonania. O dynamicznym eliminowaniu martwego kodu możemy mówić, jeśli jest nadzorca (może funkcjonalność w systemie operacyjnym), który będzie potrafił dynamicznie wykrywać żądania, identyfikować je i rozwiązywać powstałe zależności a następnie będzie potrafił usunąć martwe (raczej niewykorzystywane) fragmenty oprogramowania i na nowo połączy potrzebne bloki. EiT 2017/18 16 8
Obliczanie niezmienników pętli wyrażenia nie zmieniające swojej wartości w kolejnych iteracjach pętli obliczyć raz poza pętlą (ang. loop-invariant computation). nie ma wielokrotnych obliczeń tej samej wartości kod działa szybciej. Koszt: zmienna tymczasowa pamięć dla niej. for (i=0; i<10; i++) { a[i] = 10 * i + x * x; t = x * x; for (i=0; i<10; i++) { a[i] = 10 * i + t; EiT 2017/18 17 Eliminacja wspólnych podwyrażeń redundancyjne obliczenia takich samych wyrażeń zastąpić jednokrotnym obliczeniem i wartość przechować w zmiennej tymczasowej (ang. common sub-expresion elimination). nie ma wielokrotnych obliczeń tej samej wartości kod działa szybciej. Koszt: zmienna tymczasowa pamięć dla niej. X = A * LOG(Y) + (LOG(Y) ** 2) t = LOG(Y) X = A * t + (t ** 2) EiT 2017/18 18 9
Rozwijanie pętli wielokrotne umieszczenie ciała pętli w kodzie eliminując sprawdzanie warunków pętli i skoki, które pogarszają kolejkowość. mniejszy czas wykonania kodu (nie ma sprawdzania warunków i skoków) redukcja poboru mocy. Koszt: znaczne zwiększenie kodu. sum = 0; for (i=0; i<4; i++) { sum += array[i]; sum = 0; i = 0; sum += array[i]; sum += array[i+1]; sum += array[i+2]; sum += array[i+3]; EiT 2017/18 19 10