Zadanie 1

Jednym z klasycznych usprawnień programów jest wynoszenie stałych podwyrażeń poza pętle. Jednakże wymaga to ustalenia które podwyrażenia są stałe. Podwyrażania stałe definiujemy jako takie podwyrażenia których wartości po obliczeniu w pierwszej iteracji nie zmieniają się w kolejnych iteracjach pętli. Może się zdażyć że ponowne wykonanie pętli da nam inną wartość tekiego podwyrażenia, istotne jest tylko by wartość nie zmieniała w takcie każdego pojedyńczego wykonania pętli. Rozważmy poniższą pętlę:
int z = 0;
void foo() { z++;}
....
{
s1 = 0;
s2 = 0;
b = random();
for(i = 0; i < 100; i++) {
    foo();
    k = 5;
    c = b;
    d = c + k;
    f = random();
    g = d + z;
    s1 += f;
    s1 += g;
    s2 += d;
}
}
Które podwyrażenia w tej pętli są stałe. Jakie podwyrażenia zostaną uznane za stałe przez następujące metody: Które z powyższych metod są poprawne (tzn. znalezione podwyrażenia faktycznie są stałe)? Podać przykłady pętli z podwyrażniami stałymi których nie znajdą te z powyższych metod które są poprawne.

Zadanie 2

Zakładamy że program napisamy w klasycznym języku algorytmicznym w 80% wykonuje operacje na zmiennych prostych zaś w 20% wywołania funkcji. Dodatkowo zakładamy że operacja na zmiennej prostej wymaga przeciętnie dwu instrukcji maszynowych, zaś wywołanie funkcji pięciu. Jak się zmieni ilość wykonywanych instukcji jeśli do języka dodamy wsparcie obiektowe, tak że wywołamie metody będzie wymagało trzech dodatkowych instrukcji w porównaniu z wywołaniem funkcji i zamienimy wszystkie wywołania funkcji na wywołania metod. Jak się zmieni ilość wykonywanych instrukcji jeśli chcemy przejść do "czysto obiektowego" języka tak że każda operacja oryginalnego programu stanie się wywołaniem metody, przy tym jeśli operacja dotyczyła zmiennej prostej to zakładamy że treść metody wykona tę operację, tak że narzut składa się z kosztu wywołania metody, zaś jeśli operacja była wywołaniem funkcji to zamieniamy ją na wywołanie metody.

Uwaga: powyżej piszę o wykonywanych operacjach ponieważ proporcje instukcji w treści programu często znacznie się różnia od proporcji w trakcie wykonania. Po prostu niektóre (zwykle małe) fragmenty programu są znacznie częściej wykonywane niż inne części programu.

Zadanie 3

Zakładamy że przeciętnie interpreter na wykonanie prostej operacji potrzebuje 200 instrukcji maszynowych. Ze względu na duży czas wykonania prostych operacji do języków interpretowanych zwykle dodaje się funkcje wbudowane które w miarę wydajnie wykonują bardziej złożone operacje. Zakładamy że 80% wykonywanych operacji to operacje proste, zaś 20% wywołania funkcji wbudowanych. Aby przyspieszyć wykonanie programu chcemy go skompilować. Przy tym zakładamy że proste operacje da się przetłumaczyć przeciętnie do 3 instrukcji maszynowych, zaś funkcje wbudowane musimy dalej wywoływać. Przy tym przecietne wywołanie funkcji wbudowanej przez interpreter wymaga 1000 instrukcji, zaś kompilator może oszczędzić 200 instrukcji z kosztu wywołania. Jak kompilacja zmieni liczbę wykonywanych instrukcji?