Hash-Bench provides a JMH (Java microbenchmark harness) and published results for 114 Java implementations of major hash, CRC and checksum algorithms. These include:
- Adler32
- BSD Checksum
- CityHash
- cksum
- crc16
- crc24
- crc32
- crc64
- Ed2k Hash
- ElfHash
- FarmHash
- Fast Frame Check Sequence (FCS-16)
- Good Fast Hash
- GOST
- HAS-160
- HAVAL
- MD2
- MD4
- MD5
- MurmurHash
- RIPEMD (128, 160, 256 and 320)
- SHA-1 (including SHA-0)
- SHA-2 (SHA-224, 256, 384, 512, 512/t)
- SHA-3
- SipHash
- Skein (256, 512, 1024)
- SM3
- Sum
- SYSV Checksum
- Tiger (including Tiger 2)
- Whirlpool
- xor8
- xxHash (both XXH32 and XXH64)
Implementations tested:
- Bouncy Castle
- Forward Engineering SipHash_2_4
- Google Guava
- Inline siphash-java-inline
- Johann Löfflmann Jacksum
- JRE Adler32
- JRE CRC32
- Adrien Grand (@jpountz) xxHash for Java
- OpenHFT Zero Allocation Hashing
A wide variety of plots are generated, including by byte slice length,
by hash specification, and by implementation. This should let you determine the
lowest latency hash for your target byte size, compare the different
implementations of that hash, and evaluate how well an implementation responds
to different input types (eg byte[]
vs ByteBuffer
) and lengths.
An example plot is below, but there are many more:
Date | Processor | JVM | Results Link |
---|---|---|---|
2015-09-24 | Xeon E5-2667 | OpenJDK 1.8.0_60 | 1 |
2015-09-26 | Xeon E5-2667 | OpenJDK 1.8.0_60 | 2 |
2015-09-30 | Xeon E5-2667 | OpenJDK 1.8.0_60 | 3 |
2015-10-01 | Xeon E5-2667 | OpenJDK 1.8.0_60 | 4 |
2015-12-04 | Xeon E5-2667 | OpenJDK 1.8.0_66 | 5 |
This project is focused on JVM performance.
It does not test the accuracy of the implementations.
Please remember that latency is only one consideration. There is considerable variation between hash algorithms. Some common variations include:
- Overall hash quality (eg see SMHasher)
- Lack of cryptographic hash support
- Whether a hash remains consistent across process restarts or not
- Guarantees around machine-dependence (eg byte order)
- Output length (and associated storage and/or transmission costs)
Implementations vary considerably with respect to the inputs they are able to
hash. The most basic support is hashing an entire byte[]
. Most
implementations permit an offset and length to be nominated for a byte[]
.
Some implementations offer
ByteBuffer
awareness, others use Unsafe
, and some delegate to native code via JNI.
My motivating use case for developing this benchmark required hashing variable-length messages from a proprietary framed IO stream. As such this benchmark populates a 64 KB buffer with random bytes and then requires each implementation to hash a particular slice of that buffer. In order to provide each implementation with the best opportunity to efficiently hash such IO-sourced slices, the following three scenarios are benchmarked:
byte[]
from a given offset for a given length- Array-backed
ByteBuffer
from a given offset for a given length - Direct (native)
ByteBuffer
from a given offset for a given length
The adapter pattern is used to
abstract each implementation. This ensures each implementation is tested in the
same manner and by the same harness. Each adapter implementation contains the
minimal logic required to support the above three scenarios. For some of the
simpler implementations it was necessary to copy bytes into a dedicated
byte[]
or prepare a ByteBuffer
view.
Until xxHash for Java 1.4 is released,
please clone and build it locally to access the latest buffer fixes. Then
edit the hash-bench/pom.xml
to reflect the locally-installed snapshot.
Hash-Bench also requires Jacksum. Jacksum is not in any Maven repository, so download it, unzip, then
mvn install:install-file -Dfile=jacksum.jar -DgroupId=jonelo.jacksum -DartifactId=jacksum -Dversion=1.7 -Dpackaging=jar
mv jacksum-src.zip jacksum-sources.jar
mvn deploy:deploy-file -Dfile=jacksum-sources.jar -DgroupId=jonelo.jacksum -DartifactId=jacksum -Dversion=1.7 -Dpackaging=jar -Dclassifier=sources -Durl=file:https://$HOME/.m2/repository/
You'll need at least Java 8 and Maven 3 installed. Then:
cd hash-bench
mvn clean package
java -jar target/benchmarks.jar
This will run in default mode, testing all known libraries and input lengths. This takes roughly 15 hours with server-grade (Xeon E5-2667) hardware.
You can append -h
to the java -jar
line for JMH help. For example, use:
-wi 0
to run zero warm-ups (not recommended)-i 1
to run one iteration only (not recommended)-f 1
to run one fork only (not recommended)-p length=8,1024
to test input lengths of 8 and 1024 only-p algo=xxh64-zah,xxh64-jpountz-unsafe
to test two XXH64 implementations-lp
to list all available parameter (-p
keys and values)-rf csv
to emit CSV output (for use with theplot
command)-foe true
to stop on any error (recommended)
Algorithm names (such as xxh64-jpountz-unsafe
) are used in reports and
optionally for the -p algo
option. The naming convention is:
hash-implementation[-qualifier]
The hash
portion denotes the underlying hash specification (and potential
size disambiguation). The implementation
is a short abbreviation that
identifes the implementation from those listed at the top of this document. A
qualifier
is used if the implementation has been tested in a specific mode.
MIT License, as per LICENSE.txt.
This project uses Jacksum, which is GPLv2 licensed. Hash-Bench is not derived from Jacksum and is not including or redistributing any Jacksum files (you must manually download and install Jacksum yourself, as described above).
Two hash implementations (SipHash_2_4, Siphash-java-inline) are not available
from any known Maven repository. As each implementation is a single file, they
have been placed in the thirdparty
directory. Their licenses are shown
in those files.
Please send a pull request if you'd like to improve the project (eg use a particular hash library in a more efficient manner, add new libraries, update to new library versions etc).