Optymalizacja, proste metody….
… na oszczędzenie na czasie. Chyba tak. Albo chyba nie? Ale o co chodzi?
Szybko, dobrze i tanio
Jest taka legenda Miejska, która mówi, że pewna mała firma IT w katalogu reklamowym miała napisane:Szybko, Tanio, Solidnie. Wybierz i zapłać za dwa z tych produktów, a my dajemy gwarancję satysfakcji.
Pewien człowiek, zapytał się dlaczego ma wybrać dwa z tych przymiotników. Odpowiedź była oczywista.
Soft można tworzyć szybko i tanio, ale nie solidnie. Można też szybko i solidnie, ale nie jest to tanie. Można w końcu tanio i solidnie, ale będzie to trwało dość długo.
Idąc w myśl tych zasad można powiedzieć, że istnieją też takie trzy cechy oprogramowania, które pozwalają na opisanie sposobów tworzenia kodu. Moim zdaniem są to:
- Zużycie pamięci
- Zużycie procesora
- Elegancja i bezpieczeństwo kodu
Idąc tym tropem opiszę kilka prostych sztuczek, które pozwalają zoptymalizować kod pod kątem jednej z tych cech.
Nienawidzę pętli for
Pierwsza prosta optymalizacja to optymalizacja pod kątem czasu wykonania i zużycia pamięci. Jeżeli mamy pętlę for:
Listing 1.
package eu.runelord.optymalizacja;
public class PetlaFor {
/** * @param args */
public static void main(String[] args) {
PetlaFor petlaFor = new PetlaFor();
for (int i = 0; i < petlaFor.size(); i++) {
}
}
int size() {
System.out.print(".");
return 100;
}
}
Pytanie jaki będzie wynik?
- sto [.]
- nic
- program się nie skompiluje
Prawidłowa odpowiedź to a. Za każdym obrotem zostanie wywołana metoda obliczająca wielkość. Nic wielkiego jak metoda tylko zwraca jakąś liczbą, a co gdy wykonuje dodatkowe obliczenia, albo jest obłożona interceptorami? Wtedy nie dość, że tracimy czas na powtarzanie obliczeń to jeszcze dodatkowo możemy zająć dużo pamięci.
Rozwiązaniem jest wprowadzenie dodatkowej zmiennej, która będzie przechowywała wynik obliczeń:
Listing 2. Pętla po małej optymalizacji
package eu.runelord.optymalizacja;
public class PetlaFor {
/** * @param args */
public static void main(String[] args) {
PetlaFor petlaFor = new PetlaFor();
int l = petlaFor.size();
for (int i = 0; i < l; i++) {
}
}
int size() {
System.out.print(".");
return 100;
}
}
Jest szybko i z małym zużyciem pamięci, ale kod nie jest bezpieczny wielowątkowo. Dlaczego? A no dlatego, że może się okazać, że inny wątek znacznie zmniejszył rozmiary naszego zbioru i wielkość, którą operujemy jest nieprawidłowa. Jak widać nie można być pięknym, mądrym i bogatym.












March 1st, 2008 at 10:30
Z tym bezpiecznie wątkowo nie zrozumiałem (brak kawy?), możesz to rozwinąć?
March 1st, 2008 at 11:36
Sprawa stosunkowo prosta. Załóżmy, że masz metodę size(), która zwraca jako wynik ilość elementów w pewnej kolekcji. Kolekcja trafia do wątku z pętlą oraz do innego wątku. Wątek z pętlą pobiera wielkość kolekcji i zaczyna iterację. Po pewnym czasie zostaje przełączony wątek i drugi wątek, który posiada referencję do tej kolekcji usuwa lub dodaje element. Następnie znowu przełączony jest wątek i nasz oryginalny wątek z pętlą ma już błędne dane.
March 1st, 2008 at 12:04
Tak mi się właśnie zdawało, że twój wpis o thread-safe jest bardziej abstrakcyjny, niż podany przykład.
Doszedłem do tego trochę później, przy filiżance kawy
March 1st, 2008 at 12:46
Ja wieczorkiem wrócę z imprezy to dodam kod programu, który ilustruje takie coś. Mam już gotowy, ale nie mam czasu go zamieścić.