/blasha1

Primary LanguageCThe UnlicenseUnlicense

blasha1

A portable pure C implementation of SHA1 and (partially) the sha1sum program.

Not verified to always be correct and thus not suitable for any serious use.

Done for fun, to learn and to comapre to other implementations, look at how the compiler optimizes it or not, try optimize some parts out, etc. and to maybe later use it in my other small utils like FRex/pixelsum and others.

In test.sh there is a bit of bash and sed code to run a few different SHA1 implementations (openssl sha1 from OpenSSL 1.1.1d 10 Sep 2019, GNU coreutils sha1sum, busybox sha1sum 64-bit Windows build from frippery.org, hashlib.sha1 in Python 3.7.3, and this implementation compiled with realgcc.exe (Rev1, Built by MSYS2 project) 7.2.0 with -O2) on a given file, and make sure their output is same format and collapsed into one line, and display how long they each took.

It also runs rampin -q0 first to make sure the file is in the cache so first command isn't disavantaged by having to load the file coldly, and to be upper limit for how fast anything can work (rampin -q0 touches all 4K pages of a memory mapped file so any hash that touches all bytes is at least slower than it, plus it has to do extra work of hashing itself).

rampin is my Windows specific CLI utility to ensure files get into and stay in disk cache: FRex/rampin.

In bench.py there is a Python script that does a test similar to one done by test.sh but prints the sorted hashing speeds (file size divided by time taken, so it favors big files where startup) too. It also runs rampin first.

In newbench.py there is another Python script that prints min, max, average and median of the commands too.

File tester.c is a program that tests various edge cases and block sizes.

Example run of bench.py and newbench.py on an already cached (hence rampin running at RAM speed) big file (with two different sha1sum.exe files):

$ python bench.py
hehe is 1.000 GiB
All hashes match: c6f8a50e6a3bb11fcc5695039362da3621f7c41b *hehe
 2406.069 MiB/s - rampin -0q
  600.912 MiB/s - openssl sha1
  511.919 MiB/s - ./native.exe
  472.199 MiB/s - ./mysha1sum.exe
  434.433 MiB/s - C:/Program Files/Haskell Platform/8.6.5/msys/usr/bin/sha1sum.exe
  431.084 MiB/s - C:/Program Files/Git/usr/bin/sha1sum.exe
  363.862 MiB/s - C:/Program Files/Git/usr/bin/perl.exe C:/Program Files/Git/usr/bin/core_perl/shasum
  360.976 MiB/s - python sha1.py
  169.359 MiB/s - busybox sha1sum
$ python newbench.py
file: hehe, size: 1024.0 MiB, 7 repetitions, sorted by med
            cmd|          min|          max|          avg|          med
---------------|-------------|-------------|-------------|-------------
   openssl sha1|623.534 MiB/s|611.294 MiB/s|617.858 MiB/s|618.576 MiB/s
   ./native.exe|545.441 MiB/s|535.286 MiB/s|540.261 MiB/s|540.515 MiB/s
./mysha1sum.exe|498.638 MiB/s|489.805 MiB/s|494.812 MiB/s|495.960 MiB/s
        sha1sum|448.375 MiB/s|433.114 MiB/s|442.559 MiB/s|444.187 MiB/s
 python sha1.py|381.382 MiB/s|366.942 MiB/s|375.433 MiB/s|378.010 MiB/s

On a VitualBox Linux VM (Fedora 31) where sha1sum and Python's hashlib.sha1 both use OpenSSL's libcrypto.so:

[ff@localhost blasha1]$ python bench.py
hehe is 1.000 GiB
All hashes match: 65b448bb43478646c0072b62e420b2cc8b46e014 *hehe
  699.686 MiB/s - sha1sum
  649.414 MiB/s - openssl sha1
  544.726 MiB/s - python sha1.py
  453.495 MiB/s - ./native.exe
  429.585 MiB/s - ./mysha1sum.exe
  201.637 MiB/s - busybox sha1sum

NOTE: BusyBox has an option to optimize for space instead of speed and until recently the option to enable performant SHA1 was missing.