2011-01-10

Są błędy, których nie należy poprawiać

There are routes which must not be followed,
Armies which must not me attacked,
Towns which must not be desieged,
Positions which must not be contested.

Sun Tzu - The Art of War / Sabaton - Cliffs of Gallipoli



Są również błędy, których poprawiać nie należy. Ostatnio, zajmując się obsługą plików w grze (rozwinięcie/uproszczenie pomysłu SpaceFight), natrafiłem - zupełnie przypadkiem - na dziwny błąd. Otóż, kiedy ilość wolnego miejsca na dysku wynosiła dokładnie 3 135 121 562 bajtów, aplikacja twierdziła, że dysk nie jest podłączony. Jaka jest tego przyczyna? Ta ilość wolnego miejsca w zapisie szesnastkowym to 0xBADDDA7A - co u mnie oznacza błąd dostępu do dysku (niewłaściwa ścieżka, brak dostępu, błędne/nieistniejące archiwum ZIP).

Co oczywiście robi programista w pierwszym odruchu? W kodzie korzystającym z dostępu do dysku dodaje warunek if(freeSize == 0xBADDDA7a) --freeSize; (jeden bajt wielkiej różnicy nie zrobi). Efekt? Profiler pokazał prawie 4% skok czasu wykonywania operacji dyskowych względem poprzedniej implementacji. Oczywiście - błąd nie pojawił się ponownie, ponieważ ilość miejsca wolnego była inna (inne aplikacje zapisują swoje dane, log się zapisuje). Jednak zmiana wydajności okazała się być odczuwalna w trakcie ładowania (zapis sporej ilości małych plików - przy każdym zapisie sprawdzana jest ilość wolnego miejsca).

Co więc należy zrobić? Zignorować taki błąd. Szansa, że ilość wolnego miejsca będzie dokładnie taka, jak jedna zdefiniowana stała jest nikła (użytkownik musiałby mieć około 2,92GB wolnego miejsca, z dokładnością do bajta), sama reprodukcja błędu wiedząc na czym on polega zajęła mi prawie 4 godziny. Wydajność ucierpi w sposób śladowy, jeśli sprawdzimy, czy na dysku jest ilość danych odpowiadająca (w przybliżeniu do 1kB) tej wartości i utworzymy 2kB plik tymczasowy celem "ominięcia" błędu przy inicjalizacji gry, jednocześnie pilnując by żadne archiwum nie miało takiego rozmiaru.

Zazwyczaj błędy należy poprawiać. Każda jednak poprawka (lub ich grupa) powinny być przetestowane pod kątem wymagań i założeń - jeśli więcej szkodzą, niż poprawiają, należy zastanowić się nad obejściem lub po prostu zostawić błąd jak jest, jeśli ma znikomą szkodliwość lub znikomą szansę zaistnienia. Podobnie robi się (dość często) z błędami zaokrągleń - różnica o 10-7 jednostki (przy obliczeniach na wartościach rzędu 102) jest na tyle mało istotna, że można ją pominąć.

2 komentarze:

  1. To wszystko prawda. Mnie natomiast interesuje, dlaczego u ciebie ilość wolnego miejsca mogła w ogóle mieć jakąś "wartość specjalną"? Ktoś kiedyś te 0xBADDDA7A musiał wprowadzić do kodu - nie skojarzyło mu się wtedy, że jest to jedna z możliwych poprawnych wartości?... Bardzo nierozsądnie ;P

    OdpowiedzUsuń
  2. @Xion: nie używam wyjątków, staram się mieścić w optymalnych wywołaniach dla fastcall, jakoś sprawdzanie błędów musiało być zrobione. Są trzy wartości specjalne: 0 (całkowity brak miejsca na dysku), 0xffffffff (to lub więcej miejsca na dysku) oraz właśnie wspomniana stała (uniwersalnie dla VFS, jej zwrócenie gdziekolwiek oznacza błąd). Jestem w pełni świadom, że to jedna z możliwych wartości, jednak dodanie kolejnego parametru (na stan błędu) zaczyna wymagać użycia stosu zamiast rejestrów, a to jest wydajnościowo be.

    OdpowiedzUsuń