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 :
SecureRandombecause the call tonextBytes(...)is synchronized and it could become a bottleneck for a WebApp / a multi-threaded app -
Group 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
If you give 100 threads access to a shared
MessageDigestand 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
MessageDigesteach 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.