Open-source project
rurban/smhasher avatar
rurban/smhasher

SMHasher: a bench for hash functions that shows its table

Hash function quality and speed tests

2,180 stars190 forksC++NOASSERTION

At a glance

What is it?
SMHasher is a C++ test bench that scores hash functions on speed, avalanche behaviour and collisions, and publishes the numbers next to the failures. It is a measurement tool, not a library, and the README is one large results table.
Who is it for?
SMHasher earns its place when you are choosing between hash functions and want failure evidence rather than marketing copy, and it is worth more for the Quality problems column than for the MiB/sec column. It gives no runtime library to adopt, so the question it answers is which function to call, not how to build a product around one.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A results table rather than a library to link against

SMHasher is a measurement harness. There is no API to call, no package to install and no release to upgrade to. The repository has no GitHub releases at all, and the last push was on 2026-09-19. What you get when you open the README is a single wide table whose columns are Hash function, MiB/sec, cycl./hash, cycl./map, size and Quality problems, with one row per candidate function.

The first rows are the reason the table is trustworthy at a glance. donothing32, donothing64 and donothing128 sit at roughly eleven to twelve million MiB/sec and are marked bad seed 0, test NOP. They are do-nothing placeholders, deliberately included so every other speed figure has an upper bound to be measured against. Below them, NOP_OAAT_read64 and BadHash act as the opposite reference: a slow, broken function that fails most tests. Only after those anchors do the real candidates appear, starting with crc32.

Each function name in the table is a link to a per-function note under doc/, so the README is an index into a larger set of notes rather than the whole documentation. The homepage listed for the project, rurban.github.io/smhasher/, is where the long form explanation lives.

What the test programs in the tree are actually checking

The top level of the tree is where the design becomes clear. Each check is its own translation unit with a matching header: AvalancheTest.cpp for single bit change propagation, KeysetTest.cpp for whole key sets, HashMapTest.cpp for using the function as a hash map, DifferentialTest.cpp for comparing behaviour across alignment and offset variants, and FarmTest.cc for structured and pseudorandom key families.

The candidate implementations sit beside them rather than inside them: MurmurHash1.cpp, MurmurHash2.cpp, MurmurHash3.cpp, PMurHash.c, City.cpp, HighwayHash.cpp, Bitslice.cpp, Bitvec.cpp and the PMP_Multilinear family, which has parallel files for the naive path, the multilinear path, the 32 bit output variant and a speed alternative. Pippip.h is present as a header only. Platform.cpp and Platform.h handle the machine dependent bits, which is exactly where the machine specific flags in the results come from.

HashMapTest.cpp deserves a note. Running a function through a real hash map is a different exercise from hashing buffers, and the table reports cycles per map alongside cycles per hash. A function that looks cheap on bulk input can still be expensive once the map pays for probing and resizing.

The Quality problems column records statistics, not cryptography

The easiest mistake with this table is reading an empty Quality problems cell as an endorsement. md5_32, md5_64, md5-128 and the sha2 rows have nothing in that column, while crc32 is flagged insecure, 8590x collisions, distrib, PerlinNoise and sha1ni is flagged insecure, sanity, Permutation, Zeroes, machine-specific. What the column lists is the failure of the statistical tests above: Cyclic low32, Cyclic hi32, Zeroes, collisions, distrib, bad seeds, bad seed 0.

So a blank cell means the function did not trip the test suite, which is a statement about avalanche quality and collision resistance under those specific tests, not a statement about cryptographic safety. MD5 and SHA-2 pass because they are statistically good hash functions. Whether they are safe for your use is a question this bench does not ask.

The second thing to notice is how often machine specific appears. sha1ni, sha1ni_32, sha2ni-256, sha2ni-256_64, crc32_hw and multiply_shift all carry it, and crc32_hw is annotated with the SSE4.2 instruction set it depends on. For those rows the MiB/sec figure describes one machine rather than the algorithm. blake3_c is flagged no 32bit portability, which is a different warning: the measured code path is not the one a 32 bit build would take.

Building it with CMake, the cross files and the bundled Dockerfile

Building means compiling a C++ test program, and the tree carries the whole toolchain description. CMakeLists.txt sits at the root, alongside three separate cross compilation files: CMakeARM64Cross.txt, CMakeARMCross.txt and CMakeMINGW64Cross.txt. Those are toolchain files for building the bench elsewhere rather than only on the developer machine. .clang-format fixes the style, and Platform.cpp isolates the parts that differ per processor.

Continuous integration is described by three config files, .travis.yml for Linux, .appveyor.yml for Windows and .cirrus.yml for FreeBSD, and the README badges point at exactly those three services. .whitesource is also in the tree, which is the config for automated dependency scanning.

The Dockerfile is short and does one thing: it starts from a Fedora base, creates a non root user and runs the tests as that user rather than as root.

dockerfile
ARG BASE_IMAGE=fedora
FROM ${BASE_IMAGE}
ENV TEST_USER tester
WORKDIR "${WORK_DIR}"
RUN useradd "${TEST_USER}"
USER "${TEST_USER}"

The ARG on the first line is the interesting part, since it means the base image can be swapped from the command line for a specific distribution.

Where the README stops and the notes under doc take over

The README gives you numbers and it does not give you method. It does not name the processor the table was measured on, it does not list the tuning parameters passed to each function, and it does not explain which test produced which entry in the Quality problems column. Rows also vary in how many values are reported: several have a cycles per map figure in parentheses while others show a dash, which usually means the function was not measured in a hash map.

That detail is why the per function doc/ notes matter. Each row links to its own page, and those pages are where the parameters, the version and the known caveats for one function live. The homepage at rurban.github.io/smhasher/ covers the suite itself.

For a reader choosing a function, the practical split is this. Use the README table to narrow twenty candidates down to three, then open the doc/ page for those three, then run the bench on your own hardware for speed. Taking the MiB/sec column at face value across machines is the mistake the machine specific flags warn about.

The cases where this bench is the wrong instrument

SMHasher measures throughput on its own generated key sets with its own allocator and its own map implementation. It does not model your key distribution, your key lengths, your memory layout or your cache pressure. Two functions that tie in cycl./hash can diverge badly in a real workload, and the bench cannot tell you which way it will go.

There is also a licensing question worth naming. GitHub reports the repository license as NOASSERTION, which means the licence identifier could not be resolved, yet the tree contains a LICENSE file. The README does not settle this, and the two facts sit side by side. If you plan to copy implementation code out of the bench or its doc/ pages into a product, read that LICENSE file yourself before you do. The distinction matters more here than in most repositories because SMHasher is a place people go looking for hash function source.

Finally, with no releases published, there is no version to pin. Building from master means the source can change under you, so record the commit you build and keep the output from that run.

Editorial conclusion

SMHasher earns its place when you are choosing between hash functions and want failure evidence rather than marketing copy, and it is worth more for the Quality problems column than for the MiB/sec column. It gives no runtime library to adopt, so the question it answers is which function to call, not how to build a product around one. Two things to verify first: the LICENSE file, since GitHub reports the repository license as NOASSERTION while the tree contains a LICENSE file, and the machine the numbers came from, since several rows are explicitly flagged machine specific. Start from the doc/ page for the function you are considering, then run the bench on your own hardware before trusting the speed figures.

Frequently asked questions

How do I build SMHasher and run its test suite?

The root of the repository holds a CMakeLists.txt, and separate cross compilation files for ARM64, ARM and MinGW64 targets. A Dockerfile is also included, which starts from a Fedora base image, creates a tester user and copies the tree into a work directory so the tests run as a non root user. The README documents the build through CI badges rather than a command list.

What do the MiB/sec and cycl./hash columns in the SMHasher table mean?

MiB/sec is throughput on the bench's generated input, while cycl./hash is the cost of a single hash and cycl./map is the cost of using the function in a hash map, shown in parentheses where measured. Several rows are flagged machine specific, so for those functions the figures describe the machine used rather than the algorithm.

Does SMHasher consider MD5 and SHA-2 broken hash functions?

The Quality problems column is empty for the md5 and sha2 rows, while crc32 and sha1ni are flagged insecure alongside statistical failures such as collisions, Cyclic low32 and Zeroes. The column records the outcome of the statistical tests in the bench, so a blank cell means the function did not trip those tests rather than that it is safe for cryptographic use.

How does SMHasher test for collisions and poor avalanche behaviour?

Each check is a separate pair of files in the repository root: AvalancheTest.cpp for bit change propagation, KeysetTest.cpp for full key sets, HashMapTest.cpp for use inside a hash map, DifferentialTest.cpp for comparison across variants, and FarmTest.cc for structured key families. The failures these produce are what appear in the Quality problems column.

Official sources

  1. Issues
  2. Project website
  3. README
  4. rurban/smhasher on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rurban-smhasher.svg)](https://hysenlabs.com/projects/rurban-smhasher)