Kroczki ku doskonałości

Kompilator GCC ma już prawie 20 lat. Wersja 4.0, która ukaże się wkrótce, zawiera nowe, zaawansowane techniki optymalizacji kodu pod kątem wydajności oraz bezpieczeństwa.

Kompilator GCC ma już prawie 20 lat. Wersja 4.0, która ukaże się wkrótce, zawiera nowe, zaawansowane techniki optymalizacji kodu pod kątem wydajności oraz bezpieczeństwa.

Pozory często mylą. Ileż to razy produkt szeroko nagłośniony okazywał się co najwyżej przeciętny funkcjonalnie, o jakości nie wspominając. Z drugiej strony, wiele naprawdę użytecznych narzędzi przechodzi w mediach bez większego echa - zwłaszcza wtedy gdy program jest mało atrakcyjny wizualnie. Dobrym przykładem jest nadciągająca premiera nowego kompilatora GCC - GCC 4.0.

GCC to program, który tłumaczy przygotowany przez programistów alfanumeryczny kod programu na kod binarny zrozumiały dla procesora komputera. Ten mało efektowny program nie jest dobrym kandydatem na "best buy", nawet gdyby nie był dostępny za darmo. Dotychczasowa kariera GCC stawia go jednak wśród najwyżej cenionych narzędzi.

Kompilator nieodzowny

Gdy w latach 80. Richard Stallman rozpoczął przedsięwzięcie GNU, planował powstanie systemu będącego klonem dostępnych systemów typu Unix, ale wolnego od restrykcji licencyjnych wynikających z komercyjnego rozwoju. Razem z GNU powstała w tym celu Fundacja Wolnego Oprogramowania (Free Software Foundation) i opracowano najpopularniejszą na świecie licencję - Generalną Licencję Publiczną GNU (GPL).

Systemy Unix od zawsze związane były z językiem C. Tworząc system "unixopodobny", Stallman nie mógł nie włączyć do niego kompilatora C. Tak zaczęła się historia GCC (GNU C Compiler), którego rozwój trwa do dziś. Pierwszą, jeszcze testową, wersję GCC świat ujrzał w 1987 r. Na przełomie lat 1990/1991 wersja 1.0 była od dawna stabilna i osiągnęła kres swoich możliwości. Jej następcą była wersja 2.0, której rozwój zakończono ostatecznie w pierwszej połowie 1999 r.

Rozwój kompilatora nie jest prostą sprawą, choćby dlatego że należy zachować zgodność z olbrzymią ilością oprogramowania kompilowanego przy użyciu poprzednich wersji. Czasami jednak trzeba dokonać znaczącego kroku w kierunku wydajności i jakości kodu, licząc się przy tym z kłopotami i koniecznością modyfikacji oprogramowania. Taki krok został już wykonany, np. przy przejściu z wersji 3.3 do wersji 3.4. Razem ze zmianą wersji kompilatora zmodyfikowano jego główne biblioteki i nowe wersje programów musiały to uwzględnić.

Dotkliwie odczuli to użytkownicy, którzy chcieli kompilować choćby starsze kodeki multimedialne (i nie tylko to) przy użyciu nowego kompilatora. Łata umożliwiająca kompilację za pomocą nowych narzędzi powstała bardzo szybko, niemniej problem ten ilustruje odpowiedzialność twórców kompilatora, bibliotek i pomocniczych programów (takich jak debugger gdb), a także możliwe konsekwencje zmian. Tak naprawdę każda poważniejsza modyfikacja samego kompilatora niesie ze sobą nowe wersje narzędzi mu towarzyszących (debuggera, profilera itp.).

Sztuka dopasowania

GCC jest jednym z niewielu kompilatorów, które potrafią kompilować kod w językach C, obiektowe C, C++, Java, Pascal, Ada, a także Fortran. O ile język C jest jednoznacznie wiązany z Unixem i powszechnie w nim wykorzystywany, o tyle Fortran jest kojarzony głównie z minioną epoką starych komputerów. To skojarzenie jest jednak błędne. Fortran jest do dziś stosowany w obliczeniach inżynierskich, naukowych, a także do niektórych zadań stosowanego modelowania matematycznego, a jego nowa, "odmłodzona" wersja zawiera bardzo wiele gotowych, użytecznych rozwiązań. GCC z obsługą Fortrana jest często instalowanyz komercyjnymi kompilatorami i często wykorzystywany.

GCC jest w zasadzie najpopularniejszym kompilatorem dla środowiska Intel x86, ale do niego się nie ogranicza. Oprócz x86 (w tym także specyficzne rozszerzenia typowe dla procesorów Intela i AMD) GCC wspiera kompilację dla procesorów UltraSPARC (Sun Microsystems i Fujitsu), PA-RISC (HP), Power i PowerPC (IBM), a także Itanium (Intel), SuperH (Hitachi), MIPS, ARM stosowanych choćby w urządzeniach PDA, a także starsze układy Motoroli z serii 68000 (starsze komputery Apple). Dla każdej architektury przewidziano zastosowanie dobrej optymalizacji kodu i jest to swoisty ewenement, z którego korzysta garściami system Linux.

Wraz z rozwojem GCC powstała niewielka firma CodeSourcery. Głównym celem jej działania jest praca nad optymalizacją kompilacji. Obecne przejście do GCC 4.0 jest pod tym względem bardzo ważne, bowiem skutkuje znaczącą poprawą szybkości kompilacji, a także jakości kodu wynikowego. Zmiana dotyczyć będzie bardzo popularnych aplikacji (jak Apache, Firefox czy OpenOffice), a także całych systemów operacyjnych, takich jak Linux. Zwykły użytkownik zobaczy tylko efekt końcowy w postaci lepszej pracy kompilowanych programów.

Porządkowanie kodu

Obecne wersje GCC potrafią skutecznie optymalizować niewielkie partie kodu, uzyskując lokalnie znaczące efekty. Opierają się przy tym na wielu technikach jednocześnie, od najprostszych, typu wyłączenie obliczenia stałej poza pętlę, poprzez częściową eliminację redundancji i nieużywanego kodu, aż po bardziej skomplikowane. Pomimo ładunku logiki optymalizacyjnej, optymalizacja nadal mogła dotychczas obejmować tylko niewielki fragment kodu. GCC 4.0 pozwala tymczasem np. podzielić duże fragmenty kodu na mniejsze, by zostały wykonane równolegle. To postęp w dziedzinie kompilacji - w tym kierunku idą także najbardziej zaawansowane koncepcyjnie kompilatory wielowątkowe, np. oferowane przez Intela kompilatory dla Itanium.

Dotychczas algorytm Tree SSA (SSA - statyczne pojedyncze podstawienie) był osobnym projektem, zaś teraz został włączony do głównego projektu GCC, gdyż pokonano pewne istotne trudności techniczne. Po włączeniu projektu do GCC, kompilator ten zyska bardzo duże możliwości optymalizacyjne. Dzięki takim zabiegom kod będzie zawierał komponenty, które explicite mają być przechowywane w pamięci podręcznej procesora, zamiast w znacznie wolniejszej pamięci RAM. Zysk wydajności powinien być odczuwalny zwłaszcza na komputerach z niewielką ilością pamięci podręcznej procesora, np. Intel Celeron, AMD Duron.

To nie koniec optymalizacji. Następnym krokiem będzie rozwój tzw. wektoryzacji, która polega na wyszukiwaniu w różnych fragmentach kodu tych samych instrukcji, po to by można je było wykonać za jednym razem dla wielu obszarów danych. Wiele pętli może być wektoryzowanych przy użyciu istniejącego kompilatora, niemniej nadal zdarzają się fragmenty kodu, które jeszcze opierają się takiej optymalizacji. Przykładem są pętle niepoliczalne (uncountable loops), w których warunki pętli są trudne do przewidzenia, występują indukcje, albo też dane o różnych rozmiarach.Na wektoryzacji przetwarzania zyskają szczególnie programy multimedialne, a także klastrowe (równoległe) obliczenia inżynierskie i naukowe. Przeniesiona do GCC przez laboratorium IBM w Hajfie technika zwana SMS (Swing Modulo Scheduling) służy z kolei do optymalizacji pętli wykonujących na każdym etapie dużo "ciężkich" obliczeń. Tu również skorzystają multimedia i inżynierowie.

Technika SMS nie działa jeszcze w przypadku pętli składujących dane do pamięci, a także w architekturach, które nie obsługują instrukcji rozgałęzień BCT (Branch on Count). W popularnych zastosowaniach nie ma to znaczenia - BCT obsługują prawie wszystkie popularne architektury, niemniej, czasem może się przydać.

Optymalniej, czyli szybciej

Gdyby porównać wydajność aplikacji napisanej w C++, a skompilowanej za pomocą starej wersji GCC z domyślnymi opcjami kompilatora, z kodem powstałym po kompilacji najnowszą wersją, różnice w wydajności mogą dochodzić nawet do 20%. Oczywiście, programista musi napisać kod, tak by uwzględniał nowe możliwości kompilatora. Wektoryzacja w połączeniu z wykorzystaniem bezpośrednich odwołań do sprzętowych funkcji procesora pozwala osiągać naprawdę znaczące, wręcz zaskakujące przyspieszenie działania programów. Między innymi dlatego niektóre dystrybucje systemu Linux (np. Mandrake Linux) działają szybciej od innych, niewykorzystujących instrukcji takich jak SSE2 (Intel) czy 3DNow (AMD). Różnica jest widoczna także w zwykłych programach - wystarczy skompilować OpenOffice z wykorzystaniem opcji optymalizacji dla konkretnego modelu procesora (tzw. agresywna optymalizacja), a potem porównać pracę aplikacji Impress w dużych rozdzielczościach z kompilacją standardową dla procesora klasy 486. Różnicę widać natychmiast.

Kompilator GCC 4.0 a bezpieczeństwo

Oprócz samych technik optymalizacji, w GCC 4.0 przygotowano dodatkowe możliwości wyszukiwania błędów w aplikacjach. Najpopularniejszym błędem skutkującym naruszeniem bezpieczeństwa jest przepełnienie bufora. Błąd ten umożliwia nadpisanie kodu programu poprzez wprowadzenie danych wychodzących poza przewidziany dla nich obszar. Gdy w ten sposób umieści się gotowy do wykonania kod lub jego wywołanie, może on zostać w pewnych przypadkach wykonany. Wykorzystanie tego błędu za pomocą stosownego eksploita niekiedy daje możliwość wykonania w komputerze dowolnego kodu. Obecnie są już dostępne programy analizujące oprogramowanie pod kątem możliwości przepełnienia bufora, nie działają one jednak na skompilowanym programie.

Nowa wersja kompilatora będzie posiadała możliwość wykrywania błędów przepełnienia bufora nazywaną Mudflap. Umożliwi ona bardzo rygorystyczne testy aplikacji, wykrywając błędy, które w innych przypadkach dawałyby możliwość celowego załamania programu lub nawet wykonania dowolnego kodu. Mudflap nie jest panaceum na tego typu bolączki, niemniej znacząco ułatwi uruchamianie i testowanie programu. Niestety, ze względu na to, że znacząco obniża wydajność, stosowanie tej opcji we wszystkich programach nie bardzo ma sens. Gdy błędy zostaną wykryte i usunięte w wersjach testowych, finalny produkt będzie mógł być skompilowany bez użycia tej technologii, zatem będzie mógł pracować z pełną prędkością.

Główne nowości w GCC 4.0:

  • Możliwość wskazywania, które fragmenty kodu mają być przechowywane w pamięci podręcznej procesora (optymalizacja Tree SSA).
  • Dzielenie większych pętli na kilka mniejszych w celu ich równoległego wykonania.
  • Łączne wykonywanie tych samych funkcji (wektoryzacja).
  • Optymalizacja wykonywania pętli z "ciężkimi obliczeniami" (SMS - Swing Modulo Scheduling).
  • Wykrywanie przepełnienia bufora podczas debugowania (Mudflap).
  • Wsparcie dla nowych platform (znów jest wsparcie dla NetWare).


TOP 200