Solution
- MessageDigest => create new instances as often as needed
- KeyFactory => use a single shared instance
- SecureRandom => use a StackObjectPool
- Cipher => use a StackObjectPool
Question
I face a regular dilemna while coding security frameworks : "to pool or not to pool"
Basically this question is divided on two "groups" :
Group 1 :
SecureRandom
because the call tonextBytes(...)
is synchronized and it could become a bottleneck for a WebApp / a multi-threaded appGroup 2 : Crypto service providers like
MessageDigest
,Signature
,Cipher
,KeyFactory
, ... (because of the cost of thegetInstance()
?)
What is your opinion ?
What are you habits on such questions?
Edit 09/07/2013
I finally took time to test @Qwerky Share
class by myself and I find the result quite ... surprising.
The class was lacking my main concern : Pools like GenericObjectPool or StackObjectPool.
So I've reworked the class to test all 4 alternatives :
- Single shared instance with synchronisation gist
- new instances inside each loops (I'm not interested in the case when you can pull the digest creation outside the loop) gist
- GenericObjectPool : gist
- StackObjectPool : gist
I had to lower the number of loops to 100000 since 1M was taking too much time with pools.
I also added a Thread.yield()
at the end of each loop to give the load a nicer shape.
Results (cummulative runtime):
- MessageDigest
- new instances : 420 s
- Single instance : 550 s
- StackObjectPool : 800 s
- GenericObjectPool : 1900 s
- KeyFactory
- new instances : 400s
- Single instance : 350 s
- StackObjectPool : 2900 s
- GenericObjectPool : 3500 s
- SecureRandom
- StackObjectPool : 1600 s
- new instances : 2300 s
- GenericObjectPool : 2300s
- Single instance : 2800 s
- Cipher
- StackObjectPool : 2800 s
- GenericObjectPool : 3500 s
- Single instance : 5100 s
- new instances : 8000 s
Conclusion
For MessageDigest and KeyFactory, pools are perf killers and are even worse than a single instance with a synchronisation bottleneck, whereas they are really usefull when it comes to SecureRandom and Cipher
This test seems to be in favour of caching
When md is outside the loop it prints 579, when inside - 953.
If you give 100 threads access to a shared
MessageDigest
and get them to calculate 1,000,000 hashes each then on my machine the first thread finishes in 70,160ms and the last finishes in 98,748ms.If the threads create a new instance of
MessageDigest
each time, then the first thread finishes in 43,392ms and the last 58,691ms.Edit:
In fact with this example, with only two threads the example creating new instances runs quicker.