"Piękne to nie jest, ale kogóż to obchodzi, skoro jest bardziej efektywne." Cóż, nie za bardzo. Jeżeli Czytelnik rozumie kod, zapisany w asemblerze x86, powinien przyjrzeć się bliżej wynikowi pracy 16-bitowego kompilatora optymalizującego, którym skompilowano obie implementacje funkcji StrLen
Wersja tablicowa | Wersja wskaźnikowa |
---|---|
?StrLen@@YAHPAD@Z PROC NEAR push bp mov bp,sp push di ; pStr = 4 ; register bx = i mov di,WORD PTR [bp+4] xor bx,bx cmp BYTE PTR [di],bl je $FB1596 $F1594: inc bx cmp BYTE PTR [bx][di],0 jne $F1594 $FB1596: mov ax,bx pop di mov sp,bp pop bp ret ?StrLen@@YAHPAD@Z ENDP |
?StrLen@@YAHPAD@Z PROC NEAR push bp mov bp,sp ; register bx = p ; pStr = 4 mov dx,WORD PTR [bp+4] mov bx,dx $FC1603: inc bx cmp BYTE PTR [bx-1],0 jne $FC1603 mov ax,bx sub ax,dx dec ax mov sp,bp pop bp ret ?StrLen@@YAHPAD@Z ENDP |
W pierwszej implementacji kompilator umieścił w rejestrach procesora aż dwie zmienne, stąd dodatkowe instrukcje push i pop. W obu przypadkach mamy do czynienia z zasadniczo taką samą pętlą -- róznica polega jedynie na zastosowanym trybie adresowania. Bliższa analiza obu programów wykazuje, że użyta w drugiej pętli instrukcja jest o jeden bajt dłuższa od pierwszej.
80 39 00 cmp BYTE PTR [bx][di],0 ; pierwsza pętla
80 7f ff 00 cmp BYTE PTR [bx-1],0 ; druga pętla
Dlatego w przypadku bardzo długich napisów implementacja tablicowa powinna być lepsza, od implementacji wskaźnikowej. Ale czy tak jest naprawdę? Wiele zależy od sposobu wyrównywania instrukcji (ang. instruction alignment). Na moim starym komputerze (z procesorem i80486) druga instrukcja była lepiej wyrównana i dlatego była szybsza.
W reprezentacji wskaźnikowej dodatkowe instrukcje wykonywane są na końcu funkcji, a w wersji tablicowej -- na jej początku, przed pętlą, jednak sama pętla wykonywana jest o jeden raz mniej. Na moim komputerze narzut, związany z zastosowaniem reprezentacji tablicowej jest mniejszy, niż w przypadku reprezentacji wskaźnikowej. Dlatego w przypadku krótkich napisów (liczących do trzech znaków) reprezentacja tablicowa jest szybsza.
Prawdę mówiąc, ewentualne różnice nie mają większego znaczenia. Czy dla w sumie niewielkich i niepewnych oszczędności warto tak komplikować program? Czy czytelnik dokonał porównania instrukcji asemblera i prędkości wykonywania kodu dla wszystkich swoich ulubionych sztuczek? Może nadszedł już czas, by porzucić wszystkie te idiomy pionierów języka C. Może należy zastąpić je nowymi idiomami, których celem jest ułatwienie zrozumienia i konserwacji kodu. Chyba nie chcemy być jak ten chłopek-roztropek, który tak chciał zaoszczędzić grosik, że nawet nie spostrzegł się, gdy stracił złotówkę.
Nie używaj wskaźników tam, gdzie wystarczy operator [ ] |