I was profiling Erlang on a slow embedded system and came across the atom_hash C function. This function gets called a lot even though it’s pretty fast. It probably shows up higher on profiles than it really should.
Having said that, there are a few things I don’t understand:
Is the Latin1 clutch for r16 still needed? A quick test shows a tiny, but measurable improvement if it could be deleted.
I replaced the whole atom_hash function with return 1;. My test didn’t slow down perceptibly which really surprised me. I expected things to be unusable if all ~30K atoms hashed to the same value. Does this make sense? I assume that I’ve somehow avoided the critical paths that make use of the hash.
Any reasons to stick with hashpjw algorithm? It looks like any decent hash function should work.
I’m happy to make PRs (or not) based on what I learn.
Tangentially related. I ran into the same problem when I attempted to use NIFs (e.g. esqlite). Turned out most if not all calls to atom_hash were caused by excessive (and unnecessary) usage of enif_make_atom routine. Hence in my implementation I followed the best practice - creating atoms upfront, and then just referencing.
Is there a chance you’re using some NIFs that are causing excessive calls to atom_hash?
The “best practice” is unfortunately not documented, even though I have thought many times to do it.
It would be explained as an exception to the rule that all ERL_NIF_TERM belong to an ErlNifEnv. Atoms created during loading (by callbacks load or upgrade) can be referred as a term in anyErlNifEnv. That is, the best practice is to create all your atoms during loading and store them in static/global variables.
I’ve had to clean up a few nifs where this was happening recently. I’ll submit a doc PR soon. I figured it would go along with the function and code example to go along with it, but maybe you see it as something that should be at the top? Can hash out in the PR if I don’t hear back first.