2012-12-17 3 views
9

Lösung

  • Message => erstellen neue Instanzen so oft zum Pool nach Bedarf
  • KeyFactory =>
  • um eine Shared-Instanz verwenden
  • Secure => verwenden, um ein StackObjectPool
  • Cipher => verwenden, um eine StackObjectPool

Frage

ich vor einer regelmäßige dilemna während Sicherheitscodierung Frameworks: „zu bündeln oder nicht zu bündeln“zum Pool oder nicht Java-Crypto Service Provider

Grundsätzlich ist diese Frage auf zwei „Gruppen“ unterteilt:

  1. Gruppe 1: SecureRandom weil der Aufruf an nextBytes(...) ist synchronisiert und es könnte ein Engpass für eine WebApp/eine Multithread-App werden

  2. Gruppe 2: Crypto-Dienstanbieter wie MessageDigest, Signature, Cipher, KeyFactory ... (wegen der Kosten für die getInstance()?)

Was ist Ihre Meinung?

Was sind Gewohnheiten bei solchen Fragen?

bearbeiten 09/07/2013

Ich habe endlich Zeit @Qwerky Share Klasse selbst zu testen, und ich finde das Ergebnis sehr ... überraschend.

Der Klasse fehlte meine Hauptsorge: Pools wie GenericObjectPool oder StackObjectPool.

Also habe ich die Klasse überarbeitet alle 4 Alternativen zu testen:

  • Single Shared-Instanz mit Synchronisation gist
  • neuen Instanzen innerhalb jeder Schleife (I im Fall interessiert mich nicht, wenn Sie ziehen der Digest Schaffung außerhalb der Schleife) gist
  • GenericObjectPool: gist
  • StackObjectPool: gist

Ich musste die Anzahl der Schleifen auf 100000 senken, da 1M zu viel Zeit mit Pools nahm.

Ich fügte auch eine Thread.yield() am Ende jeder Schleife hinzu, um der Last eine schönere Form zu geben.

Ergebnisse (kumulative Laufzeit):

  • Message
    • neue Instanzen: 420 s
    • Single Instance:
    • StackObjectPool s 550: 800 s
    • GenericObjectPool: 1900 s
  • KeyFactory
    • neue Instanzen: 400s
    • Single Instance: 350 s
    • StackObjectPool: 2900 s
    • GenericObjectPool: 3500 s
  • Secure
    • StackObjectPool: 1600 s
    • neue Instanzen : 2300 s
    • GenericObjectPool: 2300S
    • Single Instance: 2800 s
  • Cipher
    • StackObjectPool: 2800 s
    • GenericObjectPool: 3500 s
    • Single Instance: 5100 s
    • neue Instanzen: 8000 s

Fazit

Für Message und KeyFactory, Pools sind perf Killer und sind sogar noch schlimmer als eine einzelne Instanz mit einem Synchronisations Engpass, während sie wirklich nützlich, wenn es um Secure und Cipher kommt

+0

Die Wiederverwendung führt zu einem Kompromiss zwischen dem Aufwand für die teure Erstellung und dem synchronisierten Zugriff. Nur Sie können wählen und es hängt von Ihrem Nutzungsprofil ab. Beachten Sie, dass zwischen den beiden Gruppen kein Unterschied besteht. Sie müssen den Zugriff auf "MessageDigest" usw. synchronisieren, wenn Sie eine Instanz zwischen Threads teilen. – Qwerky

+0

Natürlich @Qwerky jede Option erfordert Synchronisation zwischen Threads/Verwendungsblock. Die Frage ist vielmehr, wie gehen Sie mit diesen Klassen in Ihren Apps um? – Cerber

+1

Das ist toll --- Ich wünschte, Informationen wie diese wären leichter für alle Arten von Klassen verfügbar. Wirklich, ich wünschte, es wäre im Standard-Javadoc. –

Antwort

6

Wenn Sie 100 Threads Zugriff auf eine freigegebene MessageDigest geben und sie dazu bringen, 1.000.000 Hashes zu berechnen, dann endet auf meinem Computer der erste Thread in 70.160ms und der letzte in 98.748ms.

Wenn die Threads jedes Mal eine neue Instanz von MessageDigest erstellen, endet der erste Thread in 43.392ms und die letzten 58.691ms.

Edit:
In der Tat mit diesem Beispiel mit nur zwei Threads das Beispiel neue Instanzen zu schaffen schneller läuft.

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

Diese Test scheint für Caching

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); 

zu sein, wenn md ist außerhalb der Schleife es 579 druckt, wenn innen - 953.

+0

Alles, was zeigt, ist "MessageDigest" hat einen Overhead für die Erstellung. In einer einzelnen Thread-Situation wird die Wiederverwendung immer schneller sein. – Qwerky

+0

Das habe ich mir vorgestellt – Cerber