2012-12-17 8 views
9

Rozwiązanie

  • MessageDigest => tworzenie nowych instancji tak często jak potrzeba
  • KeyFactory => stosowanie jednolitej wspólnej wystąpienie
  • SecureRandom => użyć StackObjectPool
  • Cipher => użyć StackObjectPool

Pytanie

I twarz regularny dilemna podczas kodowania do bezpieczeństwa ram „na basenie lub nie połączyć”basenu czy nie połączyć usługodawców java kryptograficznych

Zasadniczo kwestia ta jest podzielona na dwie „grupy”:

  1. Grupa 1: SecureRandom ponieważ połączenie z numerem nextBytes(...) jest zsynchronizowane i może stać się wąskim gardłem aplikacji WebApp/a wielowątkowej

  2. Grupa 2: Dostawcy usług kryptograficznych, tacy jak MessageDigest, Signature, Cipher, KeyFactory ... (ze względu na koszt getInstance()?)

Jaka jest Twoja opinia?

Jakie są twoje przyzwyczajenia w takich pytaniach?

Edit 09/07/2013

I wreszcie znalazł czas, aby przetestować @Qwerky Share klasę sam i uważam wynik dość ... zaskakujące.

W klasie brakowało mojej głównej uwagi: Baseny takie jak GenericObjectPool lub StackObjectPool.

więc mam przerobione klasa przetestować wszystkie 4 warianty:

  • jednolitej wspólnej wystąpienie z synchronizacją gist
  • nowe instancje wewnątrz każdej pętli (nie jestem zainteresowany w przypadku, gdy można wyciągnąć tworzenie trawienie poza pętlą) gist
  • GenericObjectPool: gist
  • StackObjectPool: gist

Musiałem zmniejszyć liczbę pętli do 100000, ponieważ 1M zabierało zbyt dużo czasu z pulami.

Dodałem również Thread.yield() na końcu każdej pętli, aby ładunek był ładniejszy.

Wyniki (narastająco wykonania):

  • MessageDigest
    • nowych przypadków: 420 s
    • pojedynczy przykład 550 s
    • StackObjectPool: 800 s
    • GenericObjectPool 1900 s
  • KeyFactory
    • nowych przypadków: 400S
    • jednej instancji 350 s
    • StackObjectPool: 2900 s
    • GenericObjectPool: 3500 s
  • SecureRandom
    • StackObjectPool 1600 s
    • nowe instancje : 2300 s
    • GenericObjectPool: 2300s
    • jednej instancji: 2800 s
  • szyfrowania
    • StackObjectPool: 2800 s
    • GenericObjectPool: 3500 s
    • jednej instancji: 5100 s
    • nowe wystąpienia: 8000 s

Wnioski

Dla MessageDigest i KeyFactory, baseny są perf zabójcy i są jeszcze gorsze niż jednej instancji z gardłem synchronizacji, podczas gdy są one naprawdę przydatny, jeśli chodzi o SecureRandom i Cipher

+0

Ponowne użycie sprowadza się do kompromisu między kosztami związanymi z kosztownym tworzeniem i zsynchronizowanym dostępem. Tylko Ty możesz wybrać i zależy to od Twojego profilu użytkowania. Zauważ, że nie ma różnicy między tymi dwiema grupami, musisz zsynchronizować dostęp do 'MessageDigest' itp, jeśli udostępniasz instancję między wątkami. – Qwerky

+0

Oczywiście @Qwerky jakakolwiek opcja wymaga synchronizacji pomiędzy wątkami/blokiem użytkowania. Pytanie brzmi raczej, jak sobie z nimi radzić w swoich aplikacjach? – Cerber

+1

To jest świetne - chciałbym, aby takie informacje były łatwiej dostępne dla wszystkich rodzajów zajęć. Naprawdę, chciałbym, żeby było w standardowym Javadoc. –

Odpowiedz

6

Jeśli dasz 100 wątkom dostęp do współużytkowanej MessageDigest i zmusisz ich do obliczenia 1 000 000 haszy, to na moim komputerze pierwszy wątek kończy się na 70,160 ms, a ostatni na 98 748 ms.

Jeśli za każdym razem wątki tworzą nowe wystąpienie MessageDigest, to pierwszy wątek kończy się 43,392 ms, a ostatni 58 691ms.

Edit:
W rzeczywistości z tego przykładu, tylko z dwóch nitek przykład tworzenia nowych instancji biegnie szybciej.

import java.security.MessageDigest; 
import java.security.NoSuchAlgorithmException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class Share { 

    final byte[] bytes = new byte[100]; 
    final MessageDigest sharedDigest; 
    final ExecutorService pool; 
    int threads = 100; 

    Share() throws NoSuchAlgorithmException { 
    sharedDigest = MessageDigest.getInstance("MD5"); 
    pool = Executors.newFixedThreadPool(threads); 
    } 

    void go() { 

    for (int i=0; i<threads; i++) { 
     pool.execute(new Runnable() { 
     public void run() { 
      long start = System.currentTimeMillis(); 
      for (int i=0; i<1000000; i++) { 
      /* 
      synchronized (sharedDigest) { 
       sharedDigest.reset(); 
       sharedDigest.update(bytes); 
       sharedDigest.digest(); 
      }*/ 
      try { 
       MessageDigest digest = MessageDigest.getInstance("MD5"); 
       digest.reset(); 
       digest.update(bytes); 
       digest.digest(); 
      } catch (Exception ex) { 
       ex.printStackTrace(); 
      } 
      } 
      long end = System.currentTimeMillis(); 
      System.out.println(end-start); 
      pool.shutdown(); 
     } 
     }); 
    } 

    } 

    public static void main(String[] args) throws Exception { 
    Share share = new Share(); 
    share.go(); 
    } 

} 
0

ten badanie wydaje się być za buforowanie

long t0 = System.currentTimeMillis(); 
byte[] bytes = new byte[100]; 
MessageDigest md = MessageDigest.getInstance("MD5"); 
for(int i = 0; i < 1000000; i++) { 
    //MessageDigest md = MessageDigest.getInstance("MD5"); 
    md.reset(); 
    md.update(bytes); 
    md.digest(); 
} 
System.out.println(System.currentTimeMillis() - t0); 

gdy MD jest na zewnątrz pętli drukuje 579, gdy wewnątrz - 953.

+0

Wszystko, co pokazuje, to "MessageDigest" ma narzut do tworzenia. W przypadku pojedynczego wątku ponowne użycie będzie zawsze szybsze. – Qwerky

+0

Tak właśnie sobie wyobrażałem – Cerber

Powiązane problemy