2012-12-17 43 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

相关问题