Programowanie wymaga myślenia!

tech • 645 słów • 4 minuty czytania

Programowanie, optymalizacja i myślenie…

Sztuka programowania łączy w sobie właśnie te trzy sfery, a umiejętne ich wyważenie staje się dobrym wyznacznikiem dla programisty. Bez jakiejkolwiek z nich traci on swój potencjał i staje się zwykłym koderem klepiącym bez namysłu kod. No cóż, tacy “robotnicy” są również potrzebni przemysłowi, aby wszystko kręciło się dalej.

Optymalizacja jest złem! Takie sformułowania można znaleźć w każdym zakątku sieci związanym z programowaniem i wypowiedziach wielu znanych osobistości ze świata inżynierii oprogramowania. Czy naprawdę optymalizacja jest tym wielkim “mitycznym” złem?

Dwie podstawie reguły optymalizacji programu według Michaela A. Jacksona mówią:

The First Rule of Program Optimization: Don’t do it.
The Second Rule of Program Optimization (for experts only!): Don’t do it yet.

Czy, aby na pewno najlepiej tego nie ruszać?
Osobiście nie do końca mogę się z tym zgodzić. Ale tak, jak to w życiu bywa, to zależy…

Wiele też zależy od zdefiniowania umownej granicy między prawdziwym błędem a tą samoistnie zachodzącą optymalizacją w czasie pisania aplikacji. Brak przewidywania i myślenia już na etapie pisania kodu często prowadzi do wielu błędów, często uznawanych za błędy projektowe lub jeszcze inne wymysły. A przecież sklecenie kilku linijek kodu ze złożonością przekraczającą możliwości dzisiejszych maszyn, to tak naprawdę brak optymalnego myślenia i tej minimalnej samonarzucającej się optymalizacji już na etapie pisania samego kodu.

Ostatnio całkiem przypadkiem trafiłem na paskudny i dosyć popularny błąd często popełniany przez początkujących programistów. Mowa tutaj o iterowaniu tablicy znaków w pętli wraz ze zliczaniem jego długości w każdym kroku:

for (int i = 0; i < strlen(buf); i++) {
...
}

Kod ten znalazłem w pewnej bibliotece, która na pewno nie jest pisana przez początkujących! Iteracja po stringu mająca w zamyśle mieć złożoność stałą równą 1-2, osiąga wartości kosmiczne - rośnie liniowo do długości iterowanego ciągu (+1). Trudno w to uwierzyć co?

Najbardziej powszechna implementacja funkcji strlen wygląda następująco (w uproszczeniu):

int strlen(const char* str) {
	int len = 0;
	while (*str++) len++;
	return len;
}

Nasz wcześniejszy kod zamiast jednej iteracji wykonuje ich miliony! Niepotrzebnie!1.

A można było tylko trochę się wysilić, pomyśleć i zastanowić, a wszystko osiągnęlibyśmy w jednej iteracji ciągu znakowego. Nawet bez narzutu wywoływania różnych “zewnętrznych” funkcji. Przecież, na litość boską, wystarczyłaby jedna pętelka i nawet nie trzeba znać rozmiaru stringu:

while (*buf) {
	...
	++buf;
}

No tak, ale żeby na to wpaść trzeba myśleć! A to przecież tak bardzo ciężko ludziom ostatnio przychodzi.

Nie optymalizować nie oznacza nie myśleć, a niestety dla wielu “programistów” tak właśnie jest.

I tutaj pojawia się mały problem, czy to jest rzeczywiście błąd? A może brak optymalizacji? Ciężko jednoznacznie określić, a zdania są podzielone. Jednak ja bardziej skłaniam się ku stwierdzeniu, że to podchodzi już pod optymalizację. Może nie taką stricte wielką optymalizację, ani przedwczesną, ale w jakimś małym kawałku jest to taka mikro-optymalizacja, która jednak powinna być już automatycznie i podświadomie wdrażana w wielu podobnych przypadkach.

Jak widać, w programowaniu nie można “wyłączyć” optymalizacji i myślenia kosztem tworzenia paskudnego kodu. Wszystko musi być wyważone w odpowiednich proporcjach, inaczej popadamy z jednej skrajności w drugą. Co nie oznacza, że mamy o wszystkim zapomnieć, bo jest to złe.

Dużo osób argumentuje to tym, że przecież mamy takie “wypasione” kompilatory, które za nas wszystko zrobią. Zrobią wiele, ale na pewno nie wszystko. Nie będą za Ciebie myśleć!

Ktoś inny może powiedzieć, że po co optymalizować, skoro koszt procesora/pamięci/dysku maleje w szybkim tempie, a koszt pracy programisty drożeje. Prawda, ale jakież było wielkie oburzenie po opublikowaniu wymagań nowego systemu Microsoftu - Visty, który niby wymagał “kosmicznych” zasobów. Ale, chwileczkę, przecież koszt tych zasobów jest niski, więc o co chodzi?!

Wszystko jest potrzebne, ale w rozsądnych ilościach i granicach. Optymalizacja w minimalnym wydaniu nie jest złem, ale olanie całkowite optymalizacji i złożoności tworzonego systemu jest wielkim błędem.

Zapamiętaj! Brak optymalizacji nie zwalnia z myślenia!


Przypisy

  1. W rzeczywistości nie będzie tak bardzo źle, bo wielce prawdopodobne jest to, że kompilator dokona tutaj pewnych optymalizacji. ↩︎

Komentarze (2)

Gadzio avatar
Gadzio
20100708-223010-gadzio
int strlen(const char* str)
{
	int len = 0;
	while (*str) len++;
	return len;
}

Kolego, masz tutaj błąd - to nie ma prawa zadziałać, bo należy jeszcze dodać 1 do wskaźnika, sam operator wyłuskania nie zrobi tego. Poprawny kod:

int strlen(const char* str)
{
	int len = 0;
	while (*str++) len++;
	return len;
}

Pozdrawiam.

Malcom avatar
Malcom
20100708-225942-malcom

Musiało gdzieś wsiąknąć inkrementację przy formatowaniu, dzięki za uwagę kolego ;)

Dodaj komentarz

/dozwolony markdown/

/nie zostanie opublikowany/