packages feed

hashabler-1.3.0: benchmarks/Main.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric,StandaloneDeriving #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE RankNTypes #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Main ( 
    main
   ) where

import Criterion.Main
import Data.Word
import Data.Int
import Data.Ratio
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Char8 as B8
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.Text.Lazy as TL
import qualified Data.Primitive as P
import Control.DeepSeq

import Data.Hashabler
import qualified Data.Hashable as Their


instance NFData FNV32 where rnf = rnf . fnv32 
instance NFData (Hash32 a) where rnf = rnf . hashWord32
instance NFData (Hash64 a) where rnf = rnf . hashWord64

-- BASELINE for list instances: 
-- a fused foldl' equivalent -- NOTE ~ 2x faster than Unfolded on 7.10
hashLeftNoList :: FNV32 -> Word8 -> Word32  -- NOTE: tested w/ this monomorphic sig
{-# INLINE hashLeftNoList #-}
hashLeftNoList = go
    where go !h 0 = fnv32 h
          go !h !b = go (h `hash` b) (b-1)

-- raise hash benchmarks above criterion noise floor from function call and nf.
hash32Times :: Hashable a=> Int -> a -> Word32
{-# INLINE hash32Times #-}
hash32Times iters =
  \a->  let go !h !0 = h
            go !h !n = go (h `hash` a) (n-1)
         in fnv32 $ go fnvOffsetBasis32 iters 

main :: IO ()
main = do
#  ifdef ASSERTIONS_ON
    error "Sorry, please reconfigure without -finstrumented so that we turn off assertions in library code."
#  endif

    let bs50 = B.pack $ replicate 48 1
        bs1000 = B.pack $ replicate 1000 1
        t50 = T.pack $ replicate 25 'a' -- TODO verify this is 50 bytes
        t1000 = T.pack $ replicate 500 'a' -- TODO verify this is 1000 bytes
        --
        bs1 = B.pack $ replicate 1 1
        bs2 = B.pack $ replicate 2 1
        bs3 = B.pack $ replicate 3 1
        bs4 = B.pack $ replicate 4 1
        bs5 = B.pack $ replicate 5 1
        bs6 = B.pack $ replicate 6 1
        bs7 = B.pack $ replicate 7 1
        bs8 = B.pack $ replicate 8 1
        bs9 = B.pack $ replicate 9 1
        bs11 = B.pack $ replicate 11 1
        bs16 = B.pack $ replicate 16 1
        bs32 = B.pack $ replicate 32 1
        bs64 = B.pack $ replicate 64 1
        bs128 = B.pack $ replicate 128 1
        bs256 = B.pack $ replicate 256 1
        bs512 = B.pack $ replicate 512 1
        bs1024 = B.pack $ replicate 1024 1

    ba50 <- P.newByteArray 50 >>= \ba'-> P.fillByteArray ba' 0 50 1 >> P.unsafeFreezeByteArray ba'
    ba50Aligned <- P.newAlignedPinnedByteArray 50 (P.alignment (undefined::Word64)) >>= \ba'-> P.fillByteArray ba' 0 50 1 >> P.unsafeFreezeByteArray ba'
    ba50AlignedBadly <- P.newAlignedPinnedByteArray 50 (7) >>= \ba'-> P.fillByteArray ba' 0 50 1 >> P.unsafeFreezeByteArray ba'
    ba1000 <- P.newByteArray 1000 >>= \ba'-> P.fillByteArray ba' 0 1000 1 >> P.unsafeFreezeByteArray ba'
    -- lazy Text and ByteString:
    let bs50LazyTrivial = BL.fromStrict bs50
        bs1000Lazy_by20Chunks = BL.fromChunks $ replicate 20 bs50
    let t50LazyTrivial = TL.fromStrict t50
        t1000Lazy_by20Chunks = TL.fromChunks $ replicate 20 t50


    let {-# INLINE byt #-}
        byt = 0xFF :: Word8

    -- TODO new set of list benchmarks:
    --        - different ways of constructing as "good producers" (and not)
    --        - different element types (e.g. Enum of Int vs Word32)
    --        - different syntactic variations, w/ eta expansion etc.
    let listBgroup nm sz = bgroup nm [
            -- ideal code, for baseline:
              bench "hashLeftNoList" $ nf (hashLeftNoList fnvOffsetBasis32) sz
            -- ...and this only  a few % faster:
          --, bench "hashLeftUnfoldedNoList" $ nf (hashLeftUnfoldedNoList fnvOffsetBasis32) sz
            -- Slow:
         -- , bench "hashFoldr" $ nf (hashFoldr fnvOffsetBasis32) [1..250]
         -- , bench "hashFoldr trying to fuse" $ nf (\i-> hashFoldr fnvOffsetBasis32 (take (fromIntegral sz) $ iterate (+1) i)) 1
            -- Slow:
            , bench "hashFoldl'" $ nf (hashFoldl' fnvOffsetBasis32) [1..sz]
            -- !!! As fast as hashLeftNoList -- TODO is there really a rule firing here?
            , bench "hashFoldl' trying to fuse" $ nf (\i-> hashFoldl' fnvOffsetBasis32 (take (fromIntegral sz) $ iterate (+1) i)) (1::Word8)
            -- adding extra multiply adds ~ 65% overhead!:
          --, bench "hashFoldl'Extra trying to fuse" $ nf (\i-> hashFoldl'Extra fnvOffsetBasis32 (take (fromIntegral sz) $ iterate (+1) i)) 1
            -- Faster than un-fused (I think that's what's happening) fold-based code:
            , bench "hashLeftUnfolded" $ nf (hashLeftUnfolded fnvOffsetBasis32) [1..sz]
          --, bench "hashLeftUnfolded trying to fuse" $ nf (\i-> hashLeftUnfolded fnvOffsetBasis32 (take (fromIntegral sz) $ iterate (+1) i)) 1
            ]

    allWordsText <- T.readFile "/usr/share/dict/words"
    let allWordsListText = T.lines allWordsText

    defaultMain [ 
     bgroup "compare" [
        bench "long Text, hashable" $ nf Their.hash allWordsText
      , bench "[Text], hashable" $  nf Their.hash allWordsListText
      , bench "long Text, hashabler" $ nf (hashWord32 . hashFNV32) allWordsText
      , bench "[Text], hashabler" $  nf (hashWord32 . hashFNV32) allWordsListText
      -- TODO ByteString
      ],
     bgroup "compare with hashable" [
        hashableBenchmarkTheir "hashable"
      , hashableBenchmarkFNV64
      , hashableBenchmarkSiphash64
     ],
     bgroup "dev" [
        -- We can more or less subtract this from benchmarks producing a Word32 hash:

        bench "baseline Word32" $ nf (\x-> x) (777::Word32)
      , bgroup "hashFNV32 on small primitive values x100" [
          -- platform-dependent:
            bench "Int" $ nf (hash32Times 100) (9999::Int)
          , bench "Word" $ nf (hash32Times 100) (9999::Word)
          , bench "Int8" $ nf (hash32Times 100) (maxBound :: Int8)
          , bench "Word8" $ nf (hash32Times 100) (maxBound :: Word8)
          , bench "Int16" $ nf (hash32Times 100) (maxBound :: Int16)
          , bench "Word16" $ nf (hash32Times 100) (maxBound :: Word16)
          , bench "Int32" $ nf (hash32Times 100) (maxBound :: Int32)
          , bench "Word32" $ nf (hash32Times 100) (maxBound :: Word32)
          , bench "Int64, in 32-bit range" $ nf (hash32Times 100) (maxBound :: Int64)
          , bench "Int64, out of 32-bit range" $ nf (hash32Times 100) ((fromIntegral (maxBound :: Int32) *2) :: Int64)
          , bench "Word64" $ nf (hash32Times 100) (maxBound :: Word64)
          , bench "Char, small" $ nf (hash32Times 100) 'a'
          , bench "Char, big"   $ nf (hash32Times 100) '\65537'
          , bench "Float" $ nf (hash32Times 100) (1.11111 :: Float)
          , bench "Double" $ nf (hash32Times 100) (1.111111111111 :: Double)
          , bench "Integer, in Int range" $ nf (hash32Times 100) (fromIntegral (maxBound :: Int) :: Integer)
          , bench "Integer, just out of Int range" $ nf (hash32Times 100) ((fromIntegral (maxBound :: Int) * 2) :: Integer)
          ]
      , bgroup "Sum types (of Word8, where relevant) x100" [
            bench "Bool (True)" $ nf (hash32Times 100) True
          , bench "Bool (False)" $ nf (hash32Times 100) False
          , bench "Ordering (LT)" $ nf (hash32Times 100) LT
          , bench "Ordering (EQ)" $ nf (hash32Times 100) EQ
          , bench "Ordering (GT)" $ nf (hash32Times 100) GT
          , bench "Maybe (Nothing)" $ nf (hash32Times 100) (Nothing :: Maybe Word8)
          , bench "Maybe (Just)" $ nf (hash32Times 100) (Just byt)
          , bench "Either (Left)" $ nf (hash32Times 100) (Left byt :: Either Word8 Word8)
          , bench "Either (Right)" $ nf (hash32Times 100) (Right byt :: Either Word8 Word8)
          ]
      , bgroup "Product types of Word8 x100" [
            bench "Ratio" $ nf (hash32Times 100) (0xFE % byt)
          , bench "(,)" $ nf (hash32Times 100) (byt,byt)
          , bench "(,,)" $ nf (hash32Times 100) (byt,byt,byt)
          , bench "(,,,)" $ nf (hash32Times 100) (byt,byt,byt,byt)
          , bench "(,,,,)" $ nf (hash32Times 100) (byt,byt,byt,byt,byt)
          , bench "(,,,,,)" $ nf (hash32Times 100) (byt,byt,byt,byt,byt,byt)
          , bench "(,,,,,,)" $ nf (hash32Times 100) (byt,byt,byt,byt,byt,byt,byt)
          , bench "(,,,,,,,)" $ nf (hash32Times 100) (byt,byt,byt,byt,byt,byt,byt,byt)
          ]

      , bgroup "on array types, hashFNV64" [
            bench "strict ByteString x50" $ nf hashFNV64 bs50
          -- ought to be same as above:
          , bench "trivial lazy ByteString x50" $ nf hashFNV64 bs50LazyTrivial
          , bench "Text x50" $ nf hashFNV64 t50
          -- ought to be same as above:
          , bench "trivial lazy Text x50" $ nf hashFNV64 t50LazyTrivial
          , bench "ByteArray x50" $ nf hashFNV64 ba50
          , bench "ByteArray x50 (pinned, aligned)" $ nf hashFNV64 ba50Aligned  -- (maybe non-aligned creation is aligned to word size)
          , bench "ByteArray x50 (pinned, incorrectly-aligned)" $ nf hashFNV64 ba50AlignedBadly  -- (no difference here either)

          , bench "ByteArray x1000" $ nf hashFNV64 ba1000
          , bench "strict ByteString x1000" $ nf hashFNV64 bs1000
          , bench "lazy ByteString x1000, in 20 chunks" $ nf hashFNV64 bs1000Lazy_by20Chunks
          , bench "Text x1000" $ nf hashFNV64 t1000
          , bench "lazy Text x1000, in 20 chunks" $ nf hashFNV64 t1000Lazy_by20Chunks
          -- TODO Integer of comparable size to above
          -- TODO BigNat on GHC 7.10
          -- TODO Natural on GHC 7.10
          ]

      , bgroup "on ByteStrings of various sizes, siphash64" [
          -- try to fool branch prediction in hashByteString and
          let ls = [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
           in bench "(baseline for fooling branch prediction)" $ nf (map (Hash64 :: Word64 -> Hash64 B.ByteString)) ls
          , bench "100 (fooling branch prediction)" $ nf
              (map (hashWord64 . siphash64 (SipKey 1 2)))
              [ bs1, bs9, bs8, bs32, bs11, bs9, bs3, bs16, bs11 ]
          , bench "96 (#1 more predictable branches)" $ nf
              (map (hashWord64 . siphash64 (SipKey 1 2)))
              [ bs16, bs16, bs16, bs16, bs16, bs16 ]
          , bench "96 (#2 more predictable branches)" $ nf
              (map (hashWord64 . siphash64 (SipKey 1 2)))
              [ bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8 ]

          , bench "1" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs1
          , bench "2" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs2
          , bench "3" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs3
          , bench "4" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs4
          , bench "5" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs5
          , bench "6" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs6
          , bench "7" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs7
          , bench "8" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs8
          , bench "16" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs16
          , bench "32" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs32
          , bench "64" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs64
          , bench "128" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs128
          , bench "256" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs256
          , bench "512" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs512
          , bench "1024" $ nf (hashWord64 . siphash64 (SipKey 1 2)) bs1024
      ]
      , bgroup "on ByteStrings of various sizes, hashable" [
          -- try to fool branch prediction in hashByteString and
          let ls = [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
           in bench "(baseline for fooling branch prediction)" $ nf (map (Hash64 :: Word64 -> Hash64 B.ByteString)) ls
          , bench "100 (fooling branch prediction)" $ nf
              (map Their.hash)
              [ bs1, bs9, bs8, bs32, bs11, bs9, bs3, bs16, bs11 ]
          , bench "96 (#1 more predictable branches)" $ nf
              (map Their.hash)
              [ bs16, bs16, bs16, bs16, bs16, bs16 ]
          , bench "96 (#2 more predictable branches)" $ nf
              (map Their.hash)
              [ bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8, bs8 ]

          , bench "1" $ nf Their.hash bs1
          , bench "2" $ nf Their.hash bs2
          , bench "3" $ nf Their.hash bs3
          , bench "4" $ nf Their.hash bs4
          , bench "5" $ nf Their.hash bs5
          , bench "6" $ nf Their.hash bs6
          , bench "7" $ nf Their.hash bs7
          , bench "8" $ nf Their.hash bs8
          , bench "16" $ nf Their.hash bs16
          , bench "32" $ nf Their.hash bs32
          , bench "64" $ nf Their.hash bs64
          , bench "128" $ nf Their.hash bs128
          , bench "256" $ nf Their.hash bs256
          , bench "512" $ nf Their.hash bs512
          , bench "1024" $ nf Their.hash bs1024
      ]
      , bgroup "on array types, siphash64" [
            bench "strict ByteString x50" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) bs50
          -- ought to be same as above:
          , bench "trivial lazy ByteString x50" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) bs50LazyTrivial
          , bench "Text x50" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) t50
          -- ought to be same as above:
          , bench "trivial lazy Text x50" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) t50LazyTrivial
          , bench "ByteArray x50" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) ba50
          , bench "ByteArray x50 (pinned, aligned)" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) ba50Aligned  -- (maybe non-aligned creation is aligned to word size)
          , bench "ByteArray x50 (pinned, incorrectly-aligned)" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) ba50AlignedBadly  -- (no difference here either)

          , bench "ByteArray x1000" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) ba1000
          , bench "strict ByteString x1000" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) bs1000
          , bench "lazy ByteString x1000, in 20 chunks" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) bs1000Lazy_by20Chunks
          , bench "Text x1000" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) t1000
          , bench "lazy Text x1000, in 20 chunks" $ nf (hashWord64 . siphash64 (SipKey 0x0706050403020100 0x0F0E0D0C0B0A0908)) t1000Lazy_by20Chunks
          -- TODO Integer of comparable size to above
          -- TODO BigNat on GHC 7.10
          -- TODO Natural on GHC 7.10
          ]

        
      , listBgroup "medium-size lists" 250
      -- In line with above, although NoList variants win out by a greater margin:
      --, listBgroup "small lists" 5

      ]
     ]



-- --------------------------------------------------------
-- Sorry, couldn't figure out how to turn these into CPP macros:


-- Taken from 'hashable' Benchmarks.hs @47eaf9f:
hashableBenchmarkFNV64 :: Benchmark
{-# INLINE hashableBenchmarkFNV64 #-}
hashableBenchmarkFNV64 =
    let
        !mb = (2::Int)^(20 :: Int)  -- 1 Mb
        s5 = ['\0'..'\4'];   s8 = ['\0'..'\7'];     s11 = ['\0'..'\10']
        s40 = ['\0'..'\39']; s128 = ['\0'..'\127']; s512 = ['\0'..'\511']
        s1Mb = ['\0'..'\999999']

        !bs5 = B8.pack s5;   !bs8 = B8.pack s8;     !bs11 = B8.pack s11
        !bs40 = B8.pack s40; !bs128 = B8.pack s128; !bs512 = B8.pack s512
        !bs1Mb = B8.pack s1Mb

        blmeg = BL.take (fromIntegral mb) . BL.fromChunks . repeat
        bl5 = BL.fromChunks [bs5];     bl8 = BL.fromChunks [bs8]
        bl11 = BL.fromChunks [bs11];   bl40 = BL.fromChunks [bs40]
        bl128 = BL.fromChunks [bs128]; bl512 = BL.fromChunks [bs512]
        bl1Mb_40 = blmeg bs40;         bl1Mb_128 = blmeg bs128
        bl1Mb_64k = blmeg (B8.take 65536 bs1Mb)

        !t5 = T.pack s5;   !t8 = T.pack s8;     !t11 = T.pack s11
        !t40 = T.pack s40; !t128 = T.pack s128; !t512 = T.pack s512
        !t1Mb = T.pack s1Mb

        tlmeg = TL.take (fromIntegral mb) . TL.fromChunks . repeat
        tl5 = TL.fromStrict t5;     tl8 = TL.fromStrict t8
        tl11 = TL.fromStrict t11;   tl40 = TL.fromStrict t40
        tl128 = TL.fromStrict t128; tl512 = TL.fromChunks (replicate 4 t128)
        tl1Mb_40 = tlmeg t40;       tl1Mb_128 = tlmeg t128
        tl1Mb_64k = tlmeg (T.take 65536 t1Mb)
     in bgroup "hashabler hashFNV64"
          [ bgroup "ByteString"
            [ bgroup "strict"
              [ bench "5" $ whnf (hashWord64 . hashFNV64) bs5
              , bench "8" $ whnf (hashWord64 . hashFNV64) bs8
              , bench "11" $ whnf (hashWord64 . hashFNV64) bs11
              , bench "40" $ whnf (hashWord64 . hashFNV64) bs40
              , bench "128" $ whnf (hashWord64 . hashFNV64) bs128
              , bench "512" $ whnf (hashWord64 . hashFNV64) bs512
              , bench "2^20" $ whnf (hashWord64 . hashFNV64) bs1Mb
              ]
            , bgroup "lazy"
                [ bench "5" $ whnf (hashWord64 . hashFNV64) bl5
                , bench "8" $ whnf (hashWord64 . hashFNV64) bl8
                , bench "11" $ whnf (hashWord64 . hashFNV64) bl11
                , bench "40" $ whnf (hashWord64 . hashFNV64) bl40
                , bench "128" $ whnf (hashWord64 . hashFNV64) bl128
                , bench "512" $ whnf (hashWord64 . hashFNV64) bl512
                , bench "2^20_40" $ whnf (hashWord64 . hashFNV64) bl1Mb_40
                , bench "2^20_128" $ whnf (hashWord64 . hashFNV64) bl1Mb_128
                , bench "2^20_64k" $ whnf (hashWord64 . hashFNV64) bl1Mb_64k
                ]
            ]
          , bgroup "String"
            [ bench "5" $ whnf (hashWord64 . hashFNV64) s5
            , bench "8" $ whnf (hashWord64 . hashFNV64) s8
            , bench "11" $ whnf (hashWord64 . hashFNV64) s11
            , bench "40" $ whnf (hashWord64 . hashFNV64) s40
            , bench "128" $ whnf (hashWord64 . hashFNV64) s128
            , bench "512" $ whnf (hashWord64 . hashFNV64) s512
            , bench "2^20" $ whnf (hashWord64 . hashFNV64) s1Mb
            ]
          , bgroup "Text"
            [ bgroup "strict"
              [ bench "5" $ whnf (hashWord64 . hashFNV64) t5
              , bench "8" $ whnf (hashWord64 . hashFNV64) t8
              , bench "11" $ whnf (hashWord64 . hashFNV64) t11
              , bench "40" $ whnf (hashWord64 . hashFNV64) t40
              , bench "128" $ whnf (hashWord64 . hashFNV64) t128
              , bench "512" $ whnf (hashWord64 . hashFNV64) t512
              , bench "2^20" $ whnf (hashWord64 . hashFNV64) t1Mb
              ]
            , bgroup "lazy"
              [ bench "5" $ whnf (hashWord64 . hashFNV64) tl5
              , bench "8" $ whnf (hashWord64 . hashFNV64) tl8
              , bench "11" $ whnf (hashWord64 . hashFNV64) tl11
              , bench "40" $ whnf (hashWord64 . hashFNV64) tl40
              , bench "128" $ whnf (hashWord64 . hashFNV64) tl128
              , bench "512" $ whnf (hashWord64 . hashFNV64) tl512
              , bench "2^20_40" $ whnf (hashWord64 . hashFNV64) tl1Mb_40
              , bench "2^20_128" $ whnf (hashWord64 . hashFNV64) tl1Mb_128
              , bench "2^20_64k" $ whnf (hashWord64 . hashFNV64) tl1Mb_64k
              ]
            ]
          , bench "Int8" $ whnf (hashWord64 . hashFNV64) (127 :: Int8)
          , bench "Int16" $ whnf (hashWord64 . hashFNV64) (0x7eef :: Int16)
          , bench "Int32" $ whnf (hashWord64 . hashFNV64) (0x7eadbeef :: Int32)
          , bench "Int" $ whnf (hashWord64 . hashFNV64) (0x7eadbeefdeadbeef :: Int)
          , bench "Int64" $ whnf (hashWord64 . hashFNV64) (0x7eadbeefdeadbeef :: Int64)
          , bench "Double" $ whnf (hashWord64 . hashFNV64) (0.3780675796601578 :: Double)
          ]

-- Taken from 'hashable' Benchmarks.hs @47eaf9f:
hashableBenchmarkSiphash64 :: Benchmark
{-# INLINE hashableBenchmarkSiphash64 #-}
hashableBenchmarkSiphash64 =
    let
        !mb = (2::Int)^(20 :: Int)  -- 1 Mb
        s5 = ['\0'..'\4'];   s8 = ['\0'..'\7'];     s11 = ['\0'..'\10']
        s40 = ['\0'..'\39']; s128 = ['\0'..'\127']; s512 = ['\0'..'\511']
        s1Mb = ['\0'..'\999999']

        !bs5 = B8.pack s5;   !bs8 = B8.pack s8;     !bs11 = B8.pack s11
        !bs40 = B8.pack s40; !bs128 = B8.pack s128; !bs512 = B8.pack s512
        !bs1Mb = B8.pack s1Mb

        blmeg = BL.take (fromIntegral mb) . BL.fromChunks . repeat
        bl5 = BL.fromChunks [bs5];     bl8 = BL.fromChunks [bs8]
        bl11 = BL.fromChunks [bs11];   bl40 = BL.fromChunks [bs40]
        bl128 = BL.fromChunks [bs128]; bl512 = BL.fromChunks [bs512]
        bl1Mb_40 = blmeg bs40;         bl1Mb_128 = blmeg bs128
        bl1Mb_64k = blmeg (B8.take 65536 bs1Mb)

        !t5 = T.pack s5;   !t8 = T.pack s8;     !t11 = T.pack s11
        !t40 = T.pack s40; !t128 = T.pack s128; !t512 = T.pack s512
        !t1Mb = T.pack s1Mb

        tlmeg = TL.take (fromIntegral mb) . TL.fromChunks . repeat
        tl5 = TL.fromStrict t5;     tl8 = TL.fromStrict t8
        tl11 = TL.fromStrict t11;   tl40 = TL.fromStrict t40
        tl128 = TL.fromStrict t128; tl512 = TL.fromChunks (replicate 4 t128)
        tl1Mb_40 = tlmeg t40;       tl1Mb_128 = tlmeg t128
        tl1Mb_64k = tlmeg (T.take 65536 t1Mb)
     in bgroup "hashabler siphash64"
          [ bgroup "ByteString"
            [ bgroup "strict"
              [ bench "5" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bs5
              , bench "8" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bs8
              , bench "11" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bs11
              , bench "40" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bs40
              , bench "128" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bs128
              , bench "512" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bs512
              , bench "2^20" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bs1Mb
              ]
            , bgroup "strict (with siphash-1-3)" -- TODO make proper benchmark set for siphash64_1_3
              [ bench "5" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) bs5
              , bench "8" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) bs8
              , bench "11" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) bs11
              , bench "40" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) bs40
              , bench "128" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) bs128
              , bench "512" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) bs512
              , bench "2^20" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) bs1Mb
              ]
            , bgroup "lazy"
                [ bench "5" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl5
                , bench "8" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl8
                , bench "11" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl11
                , bench "40" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl40
                , bench "128" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl128
                , bench "512" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl512
                , bench "2^20_40" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl1Mb_40
                , bench "2^20_128" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl1Mb_128
                , bench "2^20_64k" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) bl1Mb_64k
                ]
            ]
          , bgroup "String"
            [ bench "5" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) s5
            , bench "8" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) s8
            , bench "11" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) s11
            , bench "40" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) s40
            , bench "128" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) s128
            , bench "512" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) s512
            , bench "2^20" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) s1Mb
            ]
          , bgroup "Text"
            [ bgroup "strict"
              [ bench "5" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) t5
              , bench "8" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) t8
              , bench "11" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) t11
              , bench "40" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) t40
              , bench "128" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) t128
              , bench "512" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) t512
              , bench "2^20" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) t1Mb
              ]
            , bgroup "strict (with siphash-1-3)" -- TODO make proper benchmark set for siphash64_1_3
              [ bench "5" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) t5
              , bench "8" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) t8
              , bench "11" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) t11
              , bench "40" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) t40
              , bench "128" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) t128
              , bench "512" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) t512
              , bench "2^20" $ whnf (hashWord64 . siphash64_1_3 (SipKey 1 2)) t1Mb
              ]
            , bgroup "lazy"
              [ bench "5" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl5
              , bench "8" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl8
              , bench "11" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl11
              , bench "40" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl40
              , bench "128" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl128
              , bench "512" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl512
              , bench "2^20_40" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl1Mb_40
              , bench "2^20_128" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl1Mb_128
              , bench "2^20_64k" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) tl1Mb_64k
              ]
            ]
          , bench "Int8" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) (127 :: Int8)
          , bench "Int16" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) (0x7eef :: Int16)
          , bench "Int32" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) (0x7eadbeef :: Int32)
          , bench "Int" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) (0x7eadbeefdeadbeef :: Int)
          , bench "Int64" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) (0x7eadbeefdeadbeef :: Int64)
          , bench "Double" $ whnf (hashWord64 . siphash64 (SipKey 1 2)) (0.3780675796601578 :: Double)
          ]


-- Same as above; just couldn't quite figure out how to get this to work
hashableBenchmarkTheir :: String -> Benchmark
{-# INLINE hashableBenchmarkTheir #-}
hashableBenchmarkTheir nm =
    let
        !mb = (2::Int)^(20 :: Int)  -- 1 Mb
        s5 = ['\0'..'\4'];   s8 = ['\0'..'\7'];     s11 = ['\0'..'\10']
        s40 = ['\0'..'\39']; s128 = ['\0'..'\127']; s512 = ['\0'..'\511']
        s1Mb = ['\0'..'\999999']

        !bs5 = B8.pack s5;   !bs8 = B8.pack s8;     !bs11 = B8.pack s11
        !bs40 = B8.pack s40; !bs128 = B8.pack s128; !bs512 = B8.pack s512
        !bs1Mb = B8.pack s1Mb

        blmeg = BL.take (fromIntegral mb) . BL.fromChunks . repeat
        bl5 = BL.fromChunks [bs5];     bl8 = BL.fromChunks [bs8]
        bl11 = BL.fromChunks [bs11];   bl40 = BL.fromChunks [bs40]
        bl128 = BL.fromChunks [bs128]; bl512 = BL.fromChunks [bs512]
        bl1Mb_40 = blmeg bs40;         bl1Mb_128 = blmeg bs128
        bl1Mb_64k = blmeg (B8.take 65536 bs1Mb)

        !t5 = T.pack s5;   !t8 = T.pack s8;     !t11 = T.pack s11
        !t40 = T.pack s40; !t128 = T.pack s128; !t512 = T.pack s512
        !t1Mb = T.pack s1Mb

        tlmeg = TL.take (fromIntegral mb) . TL.fromChunks . repeat
        tl5 = TL.fromStrict t5;     tl8 = TL.fromStrict t8
        tl11 = TL.fromStrict t11;   tl40 = TL.fromStrict t40
        tl128 = TL.fromStrict t128; tl512 = TL.fromChunks (replicate 4 t128)
        tl1Mb_40 = tlmeg t40;       tl1Mb_128 = tlmeg t128
        tl1Mb_64k = tlmeg (T.take 65536 t1Mb)
     in bgroup nm
          [ bgroup "ByteString"
            [ bgroup "strict"
              [ bench "5" $ whnf Their.hash bs5
              , bench "8" $ whnf Their.hash bs8
              , bench "11" $ whnf Their.hash bs11
              , bench "40" $ whnf Their.hash bs40
              , bench "128" $ whnf Their.hash bs128
              , bench "512" $ whnf Their.hash bs512
              , bench "2^20" $ whnf Their.hash bs1Mb
              ]
            , bgroup "lazy"
                [ bench "5" $ whnf Their.hash bl5
                , bench "8" $ whnf Their.hash bl8
                , bench "11" $ whnf Their.hash bl11
                , bench "40" $ whnf Their.hash bl40
                , bench "128" $ whnf Their.hash bl128
                , bench "512" $ whnf Their.hash bl512
                , bench "2^20_40" $ whnf Their.hash bl1Mb_40
                , bench "2^20_128" $ whnf Their.hash bl1Mb_128
                , bench "2^20_64k" $ whnf Their.hash bl1Mb_64k
                ]
            ]
          , bgroup "String"
            [ bench "5" $ whnf Their.hash s5
            , bench "8" $ whnf Their.hash s8
            , bench "11" $ whnf Their.hash s11
            , bench "40" $ whnf Their.hash s40
            , bench "128" $ whnf Their.hash s128
            , bench "512" $ whnf Their.hash s512
            , bench "2^20" $ whnf Their.hash s1Mb
            ]
          , bgroup "Text"
            [ bgroup "strict"
              [ bench "5" $ whnf Their.hash t5
              , bench "8" $ whnf Their.hash t8
              , bench "11" $ whnf Their.hash t11
              , bench "40" $ whnf Their.hash t40
              , bench "128" $ whnf Their.hash t128
              , bench "512" $ whnf Their.hash t512
              , bench "2^20" $ whnf Their.hash t1Mb
              ]
            , bgroup "lazy"
              [ bench "5" $ whnf Their.hash tl5
              , bench "8" $ whnf Their.hash tl8
              , bench "11" $ whnf Their.hash tl11
              , bench "40" $ whnf Their.hash tl40
              , bench "128" $ whnf Their.hash tl128
              , bench "512" $ whnf Their.hash tl512
              , bench "2^20_40" $ whnf Their.hash tl1Mb_40
              , bench "2^20_128" $ whnf Their.hash tl1Mb_128
              , bench "2^20_64k" $ whnf Their.hash tl1Mb_64k
              ]
            ]
          , bench "Int8" $ whnf Their.hash (127 :: Int8)
          , bench "Int16" $ whnf Their.hash (0x7eef :: Int16)
          , bench "Int32" $ whnf Their.hash (0x7eadbeef :: Int32)
          , bench "Int" $ whnf Their.hash (0x7eadbeefdeadbeef :: Int)
          , bench "Int64" $ whnf Their.hash (0x7eadbeefdeadbeef :: Int64)
          , bench "Double" $ whnf Their.hash (0.3780675796601578 :: Double)
          ]