2012-12-17 95 views
9

  • 消息摘要=>每當需要
  • 的KeyFactory =>使用單個共享實例
  • 的SecureRandom =>使用StackObjectPool創建新實例
  • 密碼=>使用StackObjectPool

問題

我面對一個普通的dilemna而編碼安全框架:「到池或不池」泳池或不游泳池的Java加密服務提供商

基本上這個問題是在兩個「團」分爲:

  1. 組1:SecureRandom,因爲對nextBytes(...)的呼叫被同步,並且它可能成爲WebApp /多線程應用的瓶頸

  2. 組2:加密服務提供商,如MessageDigest,SignatureCipherKeyFactory,...

(因爲getInstance()的成本是多少?)你是什麼看法?

你在這些問題上的習慣是什麼?

編輯2013年9月7日

我終於抽出時間由我自己來測試@Qwerky Share類,我覺得結果很令人吃驚......。

該課程缺乏我主要關心的問題:池類似GenericObjectPoolStackObjectPool

所以我已經修改了類來測試所有4個備選方案:

  • 單與同步gist
  • 新實例每個環內共享實例(我不感興趣的情況下,當你可以拉外循環摘要創建)gist
  • GenericObjectPool:gist
  • StackObjectPool:gist

我不得不將循環次數降低到100000次,因爲1M會花費太多時間處理池。

我還在每個循環的末尾添加了一個Thread.yield()以使負載具有更好的形狀。

結果(累積性運行時):

  • 消息摘要
    • 新的實例:420秒
    • 單實例:550章第
    • StackObjectPool:800小號
    • GenericObjectPool:1900小號
  • 個的KeyFactory
    • 新的實例:400S
    • 單實例:350小號
    • StackObjectPool:2900小號
    • GenericObjectPool:3500小號
  • SecureRandom的
    • StackObjectPool:1600小號
    • 新實例:2300小號
    • GenericObjectPool:2300S
    • 單實例:2800
  • 密碼
    • StackObjectPool:2800
    • GenericObjectPool:3500小號
    • 單實例:5100小號
    • 新實例:8000 s

結論

對於消息摘要和的KeyFactory,池PERF殺手,而且比用同步的瓶頸單個實例更糟糕,而他們當它涉及到的SecureRandom和密碼

+0

重複使用歸結爲昂貴創建和同步訪問的開銷之間的折衷。只有您可以選擇,並取決於您的使用情況。請注意,兩組之間沒有區別,如果共享線程之間的實例,則必須同步訪問「MessageDigest」等。 – Qwerky

+0

當然@Qwerky任何選項都需要線程/使用塊之間的同步。問題在於你如何在應用程序中處理thoses類? – Cerber

+1

這真是太棒了---我希望這樣的信息更容易適用於各種課程。真的,我希望它是在標準的Javadoc。 –

回答

6

如果您允許100線程訪問共享MessageDigest並讓它們計算每個100,000個散列,那麼在我的機器上,第一個線程以70,160ms結束,最後結束於98,748ms。

如果線程每次都創建一個MessageDigest的新實例,則第一個線程在4392ms和58691ms結束。

編輯:
其實這個例子中,只有兩個線程的例子中創建新實例運行更快。

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

這真的有用測試似乎是贊成的緩存

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

當MD是它打印579迴路,當境內關外 - 953

+0

所有顯示的是'MessageDigest'都有創建的開銷。在單線程情況下,重用總是會更快。 – Qwerky

+0

這就是我想象的 – Cerber