-
-
Save scoroberts/a60d61a2cc3afba1e8813b338ecd1501 to your computer and use it in GitHub Desktop.
@Grab(group='com.google.guava', module='guava', version='19.0') | |
@Grab(group='commons-codec', module='commons-codec', version='1.13') | |
@Grab(group='bouncycastle', module='bcprov-jdk15', version='140') | |
import groovy.transform.CompileStatic | |
import java.security.MessageDigest | |
import java.nio.charset.StandardCharsets | |
import com.google.common.hash.Hashing | |
import org.apache.commons.codec.digest.DigestUtils | |
import org.bouncycastle.crypto.digests.SHA256Digest | |
//test string for all hashing implementations | |
String test = "lwkjt23uy45pojsdf;lnwo45y23po5i;lknwe;lknasdflnqw3uo5" | |
String hashJava(String str){ | |
MessageDigest digest = MessageDigest.getInstance("SHA-256"); | |
byte[] encodedhash = digest.digest(str.getBytes(StandardCharsets.UTF_8)); | |
return bytesToHex(encodedhash) | |
} | |
@CompileStatic | |
String bytesToHex(byte[] hash) { | |
StringBuffer hexString = new StringBuffer(); | |
for (int i = 0; i < hash.length; i++) { | |
String hex = Integer.toHexString(0xff & hash[i]); | |
if(hex.length() == 1) hexString.append('0'); | |
hexString.append(hex); | |
} | |
return hexString.toString(); | |
} | |
@CompileStatic | |
String groovyHash(String str){ | |
str.digest('SHA-256') | |
} | |
@CompileStatic | |
String guavaHash(String str){ | |
Hashing.sha256().hashString(str, StandardCharsets.UTF_8).toString(); | |
} | |
@CompileStatic | |
String apacheHash(String str){ | |
DigestUtils.sha256Hex(str) | |
} | |
@CompileStatic | |
String bouncyHash(String str){ | |
SHA256Digest digest = new SHA256Digest(); | |
byte[] keyByteArray = str.getBytes(StandardCharsets.UTF_8) | |
byte[] output = new byte[digest.getDigestSize()]; | |
digest.update(keyByteArray, 0, keyByteArray.length); | |
digest.doFinal(output, 0); | |
bytesToHex(output) | |
} | |
long iterations = 100_0000 | |
println "Hashing ${iterations} iterations of SHA-256" | |
//warm up, throw away | |
for (int x=0; x<1000; x++) hashJava(test) | |
long start = System.currentTimeMillis() | |
for (int x=0; x<iterations; x++) hashJava(test) | |
long end = System.currentTimeMillis() | |
println ("time java: ${end-start}\t\t${iterations*1000/(end-start)} hashes/sec") | |
for (int x=0; x<1000; x++) groovyHash(test) | |
start = System.currentTimeMillis() | |
for (int x=0; x<iterations; x++) groovyHash(test) | |
end = System.currentTimeMillis() | |
println ("time groovy: ${end-start}\t${iterations*1000/(end-start)} hashes/sec") | |
for (int x=0; x<1000; x++) apacheHash(test) | |
start = System.currentTimeMillis() | |
for (int x=0; x<iterations; x++) apacheHash(test) | |
end = System.currentTimeMillis() | |
println ("time apache: ${end-start}\t\t${iterations*1000/(end-start)} hashes/sec") | |
for (int x=0; x<1000; x++) guavaHash(test) | |
start = System.currentTimeMillis() | |
for (int x=0; x<iterations; x++) guavaHash(test) | |
end = System.currentTimeMillis() | |
println ("time guava: ${end-start}\t\t${iterations*1000/(end-start)} hashes/sec") | |
for (int x=0; x<1000; x++) bouncyHash(test) | |
start = System.currentTimeMillis() | |
for (int x=0; x<iterations; x++) bouncyHash(test) | |
end = System.currentTimeMillis() | |
println ("time bouncy: ${end-start}\t\t${iterations*1000/(end-start)} hashes/sec") |
Apache commons-codec does not implement its own MessageDigest algorithms, but uses the standard java implementation. Apache beeing more than twice as fast as java would not make any sense. Whatever your benchmark is measuring, it is not SHA256 performance.
Your warm-up phase is probably way too short. Switching around the order in which the tests are run will significantly change the results.
I thought the same thing and moved them around. I ended up getting the same results -- and consistently the same results. I have also heard that Guava wraps standard, but there too I get better results. If you can find where they get similar results, that would be helpful.
I think bytesToHex
function makes the performance for MessageDigest and commons-codec different. If make both hashJava
and apacheHash
return byte[]
to avoid transforming to hex string. Then they can get similar results.
Took advice to remove the String conversion portions here: https://gist.github.com/scoroberts/77cc2d5c28fa2faeb1626d8eb39ec8c8
Test runs on Intel i5 8th gen laptop:
C:\dev\scripts>groovy hash_comp.groovy
Hashing 1000000 iterations of SHA-256
time java: 2968 336927.2237196765 hashes/sec
time groovy: 2451 407996.7360261118 hashes/sec
time apache: 1025 975609.7560975610 hashes/sec
time guava: 901 1109877.9134295228 hashes/sec
time guava: 1969 507872.0162519045 hashes/sec
C:\dev\scripts>groovy hash_comp.groovy
Hashing 1000000 iterations of SHA-256
time java: 2688 372023.8095238095 hashes/sec
time groovy: 1948 513347.0225872690 hashes/sec
time apache: 867 1153402.5374855825 hashes/sec
time guava: 953 1049317.9433368311 hashes/sec
time bouncy: 1890 529100.5291005291 hashes/sec
C:\dev\scripts>groovy hash_comp.groovy
Hashing 1000000 iterations of SHA-256
time java: 2371 421762.9692113032 hashes/sec
time groovy: 1969 507872.0162519045 hashes/sec
time apache: 916 1091703.0567685590 hashes/sec
time guava: 982 1018329.9389002037 hashes/sec
time bouncy: 2256 443262.4113475177 hashes/sec
C:\dev\scripts>groovy hash_comp.groovy
Hashing 1000000 iterations of SHA-256
time java: 2362 423370.0254022015 hashes/sec
time groovy: 2004 499001.9960079840 hashes/sec
time apache: 841 1189060.6420927467 hashes/sec
time guava: 890 1123595.5056179775 hashes/sec
time bouncy: 1802 554938.9567147614 hashes/sec
C:\dev\scripts>groovy hash_comp.groovy
Hashing 1000000 iterations of SHA-256
time java: 2351 425350.9145044662 hashes/sec
time groovy: 2118 472143.5316336166 hashes/sec
time apache: 999 1001001.0010010010 hashes/sec
time guava: 1135 881057.2687224670 hashes/sec
time bouncy: 2038 490677.1344455348 hashes/sec