Sterowanie przy pomocy wyjątków

0

Czy zawsze jest złe? Mam bardzo duży plik tekstowy, chce odczytać wszystkie wiersze i z każdym wierszem spełniającym pewien warunek coś zrobić.
Kod

        try
        {
            sc = new Scanner(new File("test.txt"));
            while(true)
            {
                String s = sc.nextLine();
                if(...)
                {
                    //zrób coś
                }
            }
        }
        catch(Exception e)
        {
            System.out.println(e);
        }

który na pewno wygeneruje wyjątek NoSuchElementException, jest o około 20% szybszy od kodu

        try
        {
            sc = new Scanner(new File("test.txt"));
            while(sc.hasNext())
            {
                String s = sc.nextLine();
                if(...)
                {
                    //zrób coś
                }
            }
            sc.close();
        }
        catch(Exception e)
        {
            System.out.println(e);
        }

Czy istnieją kryteria wyboru inne niż czas wykonania?

0

jest o około 20% szybszy od kodu

Wydaje mi się, że powinieneś sparować sc.nextLine() z sc.hasNextLine().

Czy istnieją kryteria wyboru inne niż czas wykonania?

Tak - między innymi czytelność.

0

Nadal pierwszy jest szybszy, choć różnica jest mniejsza.
Dla mnie oba kody są równie czytelne ;). Jakieś inne kryteria?

0

Po skompilowaniu kodu przez HotSpota różnica powinna być minimalna, no chyba, że w środku ifa robisz podobne babole.

Przejrzałem kod Scannera i metody hasXxxx zapisują tokeny do wewnętrznej pamięci podręcznej i jeśli następne wywołanie będzie postaci xxxx() (tzn będą takie same pary, np hasNextLine() i nextLine(), albo hasNext() i next()) to wynik będzie wyciągany z tej pamięci podręcznej.

Poza tym w Javie7 możesz zrobić coś takiego: http://ideone.com/lPG7k

import java.util.Scanner;
 
public class Main {
 
    public static void main(final String[] args) {
        try (final Scanner sc = new Scanner(System.in)) {
            while (sc.hasNextLine()) {
                final String line = sc.nextLine();
                System.out.println(line); // lub cokolwiek innego
            }
        }
    }
}

Dla mnie oba kody są równie czytelne

A ja nie chciałbym z tym pierwszym pracować.

0

Się podrapałem w ucho.

Być może są sytuacje, gdzie sterowanie przy pomocy wyjątków ma sens. Taką sytuacją nie jest micro-benchmark. W powyższym przykładzie możliwe, że nie uwzględniasz tego, że JVM się rozkręca. A najbardziej możliwe, że pętlę masz tak absurdalnie ciasną, że nawet jeden if, robi różnicę. No każda instrukcja coś tam waży - tylko, że jeśli w pętli masz dwie instrukcje na krzyż, to nawet jeden if będzie stanowił znaczny % czasu.

A w praktyce wystarczy, że z tymi danymi coś sensownego będziesz robić. I wyjdzie na to, że głupie list.add() ci dowali kolejne 500% czasu... A żeby było śmieszniej - koniec końców podczas profilowania wyjdzie, że odczyt danych z zewnątrz (bo przecież używasz danych skądś) będzie zajmował 100ms, a twoja optymalizacja Scannera da Ci caaałee 2ms. Szał ;)

0

Myślę, że w kodzie w którym hasNext jest ułamkiem kosztu next nie dostaniesz żadnego zysku. Szczególnie w kodzie, w którym hasNext cache'uje dostęp do danych dla najbliższego next - wtedy możesz dostać nawet stratę.
W prostym kodzie możesz mieć pozorny zysk ponieważ wyrzut przez wyjątek to nic innego jak zwykłe goto skaczące po stosie. Jednocześnie szybki kod przeplatasz z kilkuset razy wolniejszym jakim jest dostęp do pliku, co bardzo zafałszowuje wszelkie benchmarki. Metoda hasNext dla pliku jest niezwykle powolna, więc wyeliminowanie jej z pętli daje pozorny zysk na szybkości, nawet mimo tego, że kod w bloku try wykonuje się ~dwa razy wolniej. Spróbuj najpierw wczytać cały strumień do ram'u (lub nie używaj operacji dyskowych/sieciowych) i wtedy porównaj wydajność.

Krótko mówiąc moim zdaniem zasugerowałeś się szczególnym przypadkiem zdegenerowanego kodu.
Pomijam już takie drobiazgi jak to, że w jednym try połączyłeś obsługę niedeterministycznego wyjątku jakim jest błąd operacji dyskowej z deterministycznym jakim jest NoSuchElementException. A gdyby była taka sytuacja, że w tej samej pętli wykonujesz jeszcze inny kod, który też może wyrzucić wyjątek NoSuchelementException, to dostaniesz klasyczne WTF. :)

No i na koniec jest to temat nr 57 w książce Blocha, który pisze coś takiego: "Jest to nieudolna próba poprawy wydajności oparta o nieprawidłowe założenie, że JVM kontroluje dostęp do iterowanych elementów". I co ciekawe 3 pełne strony poświęca na wykazanie dlaczego jest to wyjątkowo zły pomysł - posługując się dokładnie tym wyjątkiem i jego błędnym zastosowaniem do iterowania. Nie będę więc powielał - sam możesz sprawdzić (Joshua Bloch, Effective Java ed.2).

1 użytkowników online, w tym zalogowanych: 0, gości: 1