2015-10-22 8 views
6

Normalnie wolę kontrolę zerową. Ale w bieżącym scenariuszu wiem, że przez większość czasu mój warunek if przejdzie i jest kilka uzasadnionych scenariuszy, w których obiekt może być pusty.Kontrola zerowa vs próba/przechwytywanie, gdy 99% obiektu czasu nie jest zerowe

Również obciążenie jest ogromny (około 5 milionów połączeń/godzina)

Teraz próbuje znaleźć sposób, który jest lepszy z punktu widzenia wydajności. Już sprawdziłem: try/catch vs null check in java, ale moja sprawa jest wyjątkowa.

Sprawdzono również pod numerem Which is faster, try catch or if-else in java (WRT performance), ale zarówno ten, jak i jeden, występują w ogólnym kontekście, w którym znajomość współczynnika pass/fail nie jest dostępna.

public void process(Job job) { 
    //... some code which processes job 

    SubJob subJob = job.getSubJob(); 
    if(subJob != null) { // 99% of the time this will pass 
     //.. do something 
    }    
} 

try/catch wersja

public void process(Job job) { 
    //... some code which processes job 

    SubJob subJob = job.getSubJob(); 
    try { 
     //.. do something 
    }catch(NullPointerException e) { //This may occure only 1% of the time. 
     //... 
    }    
} 

Aktualizacja:

Zwycięzca jest null check. W Try/catch, wewnętrznie JVM zrobi NULL check i wyrzuci NPE i tak dalej, poza tym obsługa wyjątków w JVM (tworzenie stosu itp.) Będzie narzutem. Podobnie jak w przypadku innej odpowiedzi, współczesne procesory są wystarczająco inteligentne, aby obsłużyć ten scenariusz z dobrymi prognozami, które w moim unikalnym przypadku zawsze będą działały na korzyść.

Napisałem również program (opublikowany poniżej pod moim imieniem), a wyniki wyraźnie wskazują, że zerowa kontrola jest znacznie lepsza na moim procesorze AMD.

Dziękuję ludziom za kierowanie mną.

+0

5 milionów połączeń na godzinę wynosi mniej niż 1400 połączeń na po drugie, a kontrola zerowa prawdopodobnie zajmuje tylko kilka dziesiątek mikrosekund. Jaki występ widziałeś, gdy porównałeś te dwa? –

+0

Dlaczego nie możesz tego przetestować i dowiedzieć się ... – brso05

+0

Powinieneś być w stanie ustawić test, aby porównać dwie metody i zobaczyć, który z nich jest bardziej wydajny. – brso05

Odpowiedz

7

TL; DR: Jeśli nie rób czek wartości null w kodzie, środowisko wykonawcze będzie wstawić jeden dla Ciebie tak. Kontrole zerowe prawie zawsze mają zerowy koszt.


Musisz zobaczyć ten problem z perspektywy HotSpot lub kompilatora JIT:

Kiedy wywołać metodę na zmiennej obiektu

someObject.callMethod() 

wówczas środowisko wykonawcze musi rzucać a NPE, jeśli zmienna ma wartość zerową (Pseudo ASM):

check someObject ref not null else throw NPE 
invoke 'callMethod' on 'someObject' 

Teraz czasami ru ntime może być pewnym, że zmienna nie ma wartości NULL. Ta analiza nosi nazwę eliminacja zerowego czeku.

-- no need: check someObject ref not null else throw NPE 
invoke 'callMethod' on 'someObject' 

Kluczem jest to, że czek w kodzie źródłowym Javy

if (someObject != null) 

jest wystarczająco dobry, aby udowodnić runtime, że zmienna nie jest null.

Uzasadnieniem:

zawsze wolą null kontrolę nad złapanie NPE. Jeśli nie wykonasz sprawdzania zerowego, środowisko wykonawcze wstawi je za Ciebie.

+1

dobry wpis na blogu o eliminacji czeku zerowego: http://jpbempel.blogspot.de/2013/09/null-check-elimination.html – wero

+0

wow. Tęskniłem za tym całkowicie. Tak, masz rację, tak czy inaczej zostanie wykonana zerowa kontrola. – Jags

0

Jak o odwrócenie swoje oświadczenie if

if(subJob == null){ 
//do something to fix it; 
} 

ten sposób (jak twierdzisz) ten kawałek kodu zostanie wywołana tylko 1% pozostałej Timeand czasu program nie będzie dbać o niego.

+1

lub nawet 'else' off z' instrukcji if ', aby to naprawić –

+4

Próbuję uniknąć, jeśli sam stan, aby zapisać cykl procesora. – Jags

+1

@austinwernli Ale są to dodatkowe naciśnięcia klawiszy i wszyscy wiemy, że każdy programista ma skończoną liczbę naciśnięć klawiszy, dopóki nie wygaśnie. –

3

Idź z kontroli zerowej, procesor potokuje go i powinien zawsze "zignorować" jeśli i przejść do wyciągu.

podobne, mówi o potoku: Why is it faster to process a sorted array than an unsorted array?

+0

Nice read. Więc jeśli założę, że obecna tablica procesorów jest znacznie lepsza niż przeszłość, mój warunek if nie będzie miał wpływu na całkowity czas. Ale czy i tak nadal będzie trwało miseczkowanie? – Jags

+1

Tworzenie potoków ma na celu nie spowolnienie procesora, należy założyć, że nie ma ono wartości NULL i kontynuować przetwarzanie tak, jakby było tworzone. Następnie procesor spróbuje sprawdzić, czy jest w porządku, jeśli chodzi o brak wartości zerowej. – phflack

1

Istnieją dwa aspekty, jeden to wykonanie, a drugi to projekt. Możesz powiedzieć, co jest lepsze, nie jest to pytanie, które powinieneś zadać, czy wystarczająco dobre jest to, czego potrzebujesz. Jeśli 99% czasu nie ma wartości null, branch prediction zapisze Twój dzień, a JIT może zoptymalizować więcej. Ale cokolwiek wybierzesz, musisz zrobić coś w tej specjalnej walizce. Co robisz? Możesz rzucić exception lub złapać NPE i ponownie rzucić go ponownie? Następnie, exceptions może być wydajność bottleneck, a nie mechanizm.

Napisałbym to sam.

SubJob subJob = job.getSubJob(); 
Objects.requireNotNull(subJob); 
1

Chciałbym mocno oprzeć się na istniejącej preferencji dla zerowej kontroli. W przypadku sytuacji, która ma uzasadniony warunek zerowy, powiedziałbym, że należy należy sprawdzić pod kątem wartości NULL i zapisać wyjątek wskaźnik zerowy dla coś, co "nigdy się nie dzieje". Powinienem wspomnieć, że jest to raczej preferencja paradygmatu niż oparta na wynikach. Jednakże, dla mnie, musiałaby być ogromna korzyść w działaniu i skrajna potrzeba uzasadnienia takiego podejścia.

Zgodnie z powyższym, poprzedni komentarz stwierdzający, że narzut wyjątku zostałby poniesiony tylko 1% czasu, w przypadku wyjątku, zakłada, że ​​"konfiguracja" bloku try nie zajmuje żadnego obciążenia. Nie jestem pewien, czy tak jest.

0

Dla ludzi, którzy chcą zobaczyć kod wpadłem do testowania ...

public class TestMainResuse { 

    static int load = 1000000; 
    public static void main(String[] args) throws Exception { 
     Object[] objects = new Object[load]; 

     for(int i = 0; i < load; i++) { 
      if(i % 100 == 0) 
       objects[i] = null; 
      else 
       objects[i] = new Object(); 
     } 


     withTry(objects); 
     withIf(objects); 
     withTry(objects); 
     withIf(objects); 
     withTry(objects); 
     withIf(objects); 

     withIf(objects); 
     withIf(objects); 

     withTry(objects); 
     withTry(objects); 
    } 

    public static void withTry(Object[] objects){ 
     long startTime = System.nanoTime(); 
     for(int i = 0; i < load; i++) { 
      try { 
       objects[i].getClass(); 
      } catch (NullPointerException e) { 
       //System.out.println("this"); 
      } 
     } 
     System.out.println(" try took "+ (System.nanoTime() - startTime)); 

    } 

    public static void withIf(Object[] objects){ 
     long startTime = System.nanoTime(); 
     for(int i = 0; i < load; i++) { 
      if(objects[i] != null) { 
       objects[i].getClass(); 
      } 
     } 
     System.out.println("null took "+ (System.nanoTime() - startTime)); 

    } 
} 

wyjściowa (diff w nano):

try took 6906539 
null took 3801656 
try took 13380219 
null took 87684 
try took 1036815 
null took 79558 
try took 1021416 
null took 10693 
try took 1020989 
null took 1711 
null took 1284 
null took 1283 
null took 1283 
null took 1283 
null took 1711 
try took 1038954 
try took 1106107 
try took 1040237 
try took 1020134 
try took 1028261 
Powiązane problemy