diff --git a/benchmarks/PPoPP2019/build.sh b/benchmarks/PPoPP2019/build.sh new file mode 100755 index 0000000..e802daf --- /dev/null +++ b/benchmarks/PPoPP2019/build.sh @@ -0,0 +1,141 @@ +#!/bin/bash + +set -e + +if [[ -z $GHC_COMPILERS ]]; then + GHC_COMPILERS=$HOME/ghc-8 +fi + +# Figure, Label , benchmark, compiler , code , htm options +# ------------------------------------------------------------------------------- +# 1(a) , mut-hybrid , rbtree , hybrid , mut , (2,t) +# 1(a) , tvar-hybrid , rbtree , hybrid , tvar , (2,t) +# 1(a) , tstruct-hybrid , rbtree , hybrid , tstruct , (2,t) +# 1(a) , mut-fine-hybrid , rbtree , fine , mut , (t,t) +# 1(a) , tvar-fine , rbtree , fine , tvar , (t,t) +# 1(a) , tstruct-fine-hybrid , rbtree , fine , tstruct , (t,t) +# 1(a) , mut-fine , rbtree , fine , mut , (0,0) +# 1(a) , tvar-fine , rbtree , fine , tvar , (0,0) +# 1(a) , tstruct-fine , rbtree , fine , tstruct , (0,0) + +# 1(b) , mut-hybrid , hamt , hybrid , mut , (2,t) +# 1(b) , tvar-hybrid , hamt , hybrid , tvar , (2,t) +# 1(b) , tstruct-hybrid , hamt , hybrid , tstruct , (2,t) +# 1(b) , mut-fine-hybrid , hamt , fine , mut , (t,t) +# 1(b) , tvar-fine , hamt , fine , tvar , (t,t) +# 1(b) , tstruct-fine-hybrid , hamt , fine , tstruct , (t,t) +# 1(b) , mut-fine , hamt , fine , mut , (0,0) +# 1(b) , tvar-fine , hamt , fine , tvar , (0,0) +# 1(b) , tstruct-fine , hamt , fine , tstruct , (0,0) + +# 1(c) , mut-hybrid , treap , hybrid , mut , (2,t) +# 1(c) , tvar-hybrid , treap , hybrid , tvar , (2,t) +# 1(c) , mut-fine-hybrid , treap , fine , mut , (t,t) +# 1(c) , tvar-fine , treap , fine , tvar , (t,t) +# 1(c) , mut-fine , treap , fine , mut , (0,0) +# 1(c) , tvar-fine , treap , fine , tvar , (0,0) + +# 2(a) , mut-hard , treap , hybrid , mut , (2,t) +# 2(a) , tvar-hard , treap , hybrid , tvar , (2,t) +# 2(a) , mut-fall , treap , hybrid , mut , (2,t) +# 2(a) , tvar-fall , treap , hybrid , tvar , (2,t) +# 2(a) , mut-lock , treap , hybrid , mut , (2,t) +# 2(a) , tvar-lock , treap , hybrid , tvar , (2,t) + +# 2(b) , hamt-late , hamt , hybrid , mut , (2,t) +# 2(b) , hamt-early , hamt , hybrid-early , mut , (2,t) +# 2(b) , rbtree-late , rbtree , hybrid , mut , (2,t) +# 2(b) , rbtree-early , rbtree , hybrid-early , mut , (2,t) +# 2(b) , treap-late , treap , hybrid , mut , (2,t) +# 2(b) , treap-early , treap , hybrid-early , mut , (2,t) + +# 2(c) , (2,t) , treap , hybrid , mut , (2,t) +# 2(c) , (t,t) , treap , hybrid , mut , (t,t) +# 2(c) , (2,2) , treap , hybrid , mut , (2,2) +# 2(c) , (2,0) , treap , hybrid , mut , (2,0) + +# Compiler versions: +# hybrid -- supports: +# - full hybrid (2,t), (t,t), (2,2) +# - lock-elision on fallback lock (0,t), (0,2) +# - coarse-grain STM (0,0) +# - late lock subscription +# hybrid-lock -- Same as hybrid, but with early lock subscription. +# fine -- supports: +# - fine hybrid (t,t) (htm used for the commit of the fine-grain TM) +# - fine-grain STM (0,0) (equivalant to main branch of GHC when used with TVar code) +# +# All versions support mutable-fields extensions. + +# Code variations: +# mut -- Mutable fields. +# tvar -- TVar-based (equivalant to main branch of GHC). +# tstruct -- TStruct-based code. + +deps="-isrc/focus-0.1.5.2/library" +deps="$deps -isrc/hashable-1.2.6.1" +deps="$deps -isrc/base-prelude-1.2.0.1/library" +deps="$deps -isrc/loch-th-0.2.1" +deps="$deps -isrc/primitive-0.6.3.0" +deps="$deps -isrc/transformers-base-0.4.4/src" +deps="$deps -isrc/transformers-compat-0.5.1.4/src" +deps="$deps -isrc/entropy-0.4" + +flags="" +flags="$flags -XBangPatterns" +flags="$flags -XConstraintKinds" +flags="$flags -XDataKinds" +flags="$flags -XDefaultSignatures" +flags="$flags -XDeriveDataTypeable" +flags="$flags -XDeriveFunctor" +flags="$flags -XDeriveGeneric" +flags="$flags -XEmptyDataDecls" +flags="$flags -XFlexibleContexts" +flags="$flags -XFlexibleInstances" +flags="$flags -XFunctionalDependencies" +flags="$flags -XGADTs" +flags="$flags -XGeneralizedNewtypeDeriving" +flags="$flags -XLambdaCase" +flags="$flags -XMultiParamTypeClasses" +flags="$flags -XMultiWayIf" +flags="$flags -XNoMonomorphismRestriction" +flags="$flags -XPatternGuards" +flags="$flags -XPolyKinds" +flags="$flags -XQuasiQuotes" +flags="$flags -XRankNTypes" +flags="$flags -XStandaloneDeriving" +flags="$flags -XTemplateHaskell" +flags="$flags -XTupleSections" +flags="$flags -XTypeFamilies" +flags="$flags -XTypeOperators" + +cbits="src/cbits/gettime.c" +cbits="$cbits src/hashable-1.2.6.1/cbits/fnv.c" +cbits="$cbits src/entropy-0.4/cbits/rdrand.c" +cbits="$cbits src/primitive-0.6.3.0/cbits/primitive-memops.c" + +for compiler in hybrid hybrid-early fine; do + ghc=$GHC_COMPILERS/$compiler/bin/ghc + for bench in RBTREE HAMT TREAP; do + for code in MUT TVAR TSTRUCT; do + if [ "$bench" == "RBTREE" ]; then + a="" + elif [ "$bench" == "HAMT" ]; then + a="" + elif [ "$bench" == "TREAP" ]; then + if [ "$code" == "TSTRUCT" ]; then + continue + fi + fi + + n=$bench-$code-$compiler + opts="-O2 -rtsopts -threaded -fno-omit-yields -msse4.2 $flags" + buildopts="-no-user-package-db -outputdir .build-$n -isrc $deps" + cppflags="-DBYTECOUNTER -D$bench -D$code" + ccflags="--optc='-O2'" + + echo $ghc src/Main.hs -o bin/$n $cppflags + $ghc src/Main.hs $cbits $opts $buildopts -o bin/$n $cppflags + done + done +done diff --git a/benchmarks/PPoPP2019/clean.sh b/benchmarks/PPoPP2019/clean.sh new file mode 100755 index 0000000..6031218 --- /dev/null +++ b/benchmarks/PPoPP2019/clean.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +rm -rf .build-* diff --git a/benchmarks/PPoPP2019/fig-1.sh b/benchmarks/PPoPP2019/fig-1.sh new file mode 100755 index 0000000..84f15fb --- /dev/null +++ b/benchmarks/PPoPP2019/fig-1.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +set -e + +if [[ -z $GHC_COMPILERS ]]; then + GHC_COMPILERS=$HOME/ghc-8 +fi + +run=$GHC_COMPILERS/hybrid/bin/runhaskell + +if [[ ! -x $run ]]; then + echo "Did not find ghc here: $run" + echo "GHC_COMPILERS=$GHC_COMPILERS" + echo "Check that GHC_COMPILERS points to the right place." + exit 1 +fi + +N=`nproc` + +n=100000 +m=90 +q=-qatopo-cores-sockets-threads-$N +s=1000 + +function threads() +{ + if [[ $1 == "t" ]]; then + if [[ $2 -le 10 ]]; then + count=10 + else + count=$2 + fi + else + count=$1 + fi +} + +# mut-hybrid RBTREE HYBRID MUT 2 t +function doBenchmark() +{ + label=$1 + bench=$2 + compiler=$3 + code=$4 + htm=$5 + hle=$6 + + name=$bench-$code-$compiler + exe=./bin/$name + log="logs/$bench-$label.log" + logs="$logs $log" + echo "Benchmarking $name." + rm -f "$log" &> /dev/null + + for t in `seq 1 $N`; do + threads $htm $t + thtm=$count + threads $hle $t + thle=$count + retry="--htm-retry=$thtm --hle-retry=$thle" + cmd="$exe -e $n -t $t -m $m -s $s +RTS --stm-stats $q -N$t -ki4k -kc64k -kb4k -A1m $retry" + echo $cmd + echo "command: $cmd" &>> $log + $cmd &>> $log + done +} + +for p in a b c; do + logs="" + input="fig-1$p.txt" + while IFS= read -r var + do + doBenchmark $var + done < "$input" + + $run plot.hs "Figure 1($p)" "output/fig-1$p.html" $logs +done + diff --git a/benchmarks/PPoPP2019/fig-2a.sh b/benchmarks/PPoPP2019/fig-2a.sh new file mode 100755 index 0000000..f7197c5 --- /dev/null +++ b/benchmarks/PPoPP2019/fig-2a.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +set -e + +if [[ -z $GHC_COMPILERS ]]; then + GHC_COMPILERS=$HOME/ghc-8 +fi + +run=$GHC_COMPILERS/hybrid/bin/runhaskell + +N=`nproc` + +n=100000 +m=90 +q=-qatopo-cores-sockets-threads-$N +s=1000 + +function threads() +{ + if [[ $1 == "t" ]]; then + if [[ $2 -le 10 ]]; then + count=10 + else + count=$2 + fi + else + count=$1 + fi +} + +# mut-hybrid RBTREE HYBRID MUT 2 t +function doBenchmark() +{ + label=$1 + bench=$2 + compiler=$3 + code=$4 + htm=$5 + hle=$6 + + name=$bench-$code-$compiler + exe=./bin/$name + log="logs/$bench-$label.log" + logs="$logs $log" + echo "Benchmarking $name." + rm -f "$log" &> /dev/null + + for t in `seq 1 $N`; do + threads $htm $t + thtm=$count + threads $hle $t + thle=$count + retry="--htm-retry=$thtm --hle-retry=$thle" + cmd="$exe -e $n -t $t -m $m -s $s +RTS --stm-stats $q -N$t -ki4k -kc64k -kb4k -A1m $retry" + echo $cmd + $cmd &>> $log + echo "command: $cmd" &>> $log + done +} + +for p in a; do + logs="" + input="fig-2$p.txt" + while IFS= read -r var + do + doBenchmark $var + done < "$input" + + $run plot-stats.hs "Figure 2($p)" "output/fig-2$p.html" $logs +done + diff --git a/benchmarks/PPoPP2019/fig-2bc.sh b/benchmarks/PPoPP2019/fig-2bc.sh new file mode 100755 index 0000000..8044f15 --- /dev/null +++ b/benchmarks/PPoPP2019/fig-2bc.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +set -e + +if [[ -z $GHC_COMPILERS ]]; then + GHC_COMPILERS=$HOME/ghc-8 +fi + +run=$GHC_COMPILERS/hybrid/bin/runhaskell + +N=`nproc` + +n=100000 +m=90 +q=-qatopo-cores-sockets-threads-$N +s=1000 + +function threads() +{ + if [[ $1 == "t" ]]; then + if [[ $2 -le 10 ]]; then + count=10 + else + count=$2 + fi + else + count=$1 + fi +} + +# mut-hybrid RBTREE HYBRID MUT 2 t +function doBenchmark() +{ + label=$1 + bench=$2 + compiler=$3 + code=$4 + htm=$5 + hle=$6 + + name=$bench-$code-$compiler + exe=./bin/$name + log="logs/$bench-$label.log" + logs="$logs $log" + echo "Benchmarking $name." + rm -f "$log" &> /dev/null + + for t in `seq 1 $N`; do + threads $htm $t + thtm=$count + threads $hle $t + thle=$count + retry="--htm-retry=$thtm --hle-retry=$thle" + cmd="$exe -e $n -t $t -m $m -s $s +RTS --stm-stats $q -N$t -ki4k -kc64k -kb4k -A1m $retry" + echo $cmd + $cmd &>> $log + echo "command: $cmd" &>> $log + done +} + +for p in b c; do + logs="" + input="fig-2$p.txt" + while IFS= read -r var + do + doBenchmark $var + done < "$input" + + $run plot.hs "Figure 2($p)" "output/fig-2$p.html" $logs +done + diff --git a/benchmarks/PPoPP2019/mktopo.hs b/benchmarks/PPoPP2019/mktopo.hs new file mode 100644 index 0000000..bbefe9f --- /dev/null +++ b/benchmarks/PPoPP2019/mktopo.hs @@ -0,0 +1,45 @@ +import System.Environment +import Data.List + +asT vs = "(" ++ intercalate "," vs ++ ")" + +tcs nt nc ns = [ s + | c <- map transpose ts + , t <- c + , s <- t + ] + where + n = nt * nc * ns + ts = chunksOf nc . chunksOf nt $ [0..n-1] + +chunksOf :: Int -> [a] -> [[a]] +chunksOf n [] = [] +chunksOf n vs = take n vs : chunksOf n (drop n vs) + + +main = do + as <- getArgs + case as of + [a,b,c] | [((t,c,s), [])] <- reads (asT [a,b,c]) -> mapM_ print (tcs t c s) + _ -> mapM_ putStrLn $ + [ "Unknown input: " ++ show as + , "Usage: mktopo THREADS CORES SOCKETS" + , " Print thread assignment filling cores on each socket first, than" + , " hyperthreads on each core." + , " The arguments are threads per core, cores per socket, and total sockets." + , " This assumes a particular ordering that may not be correct for" + , " a particular OS and architecture." + ] + +-- The existing files are produced with the following arguments: +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 4 1 > topo-cores-sockets-threads-8 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 6 1 > topo-cores-sockets-threads-12 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 10 1 > topo-cores-sockets-threads-20 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 12 1 > topo-cores-sockets-threads-24 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 16 1 > topo-cores-sockets-threads-32 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 18 1 > topo-cores-sockets-threads-34 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 10 2 > topo-cores-sockets-threads-40 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 12 2 > topo-cores-sockets-threads-48 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 16 2 > topo-cores-sockets-threads-64 +-- $GHC_COMPILERS/hybrid/bin/runhaskell mktopo.hs 2 18 2 > topo-cores-sockets-threads-72 + diff --git a/benchmarks/PPoPP2019/plot-stats.hs b/benchmarks/PPoPP2019/plot-stats.hs new file mode 100644 index 0000000..2e79755 --- /dev/null +++ b/benchmarks/PPoPP2019/plot-stats.hs @@ -0,0 +1,131 @@ +import System.Environment +import Data.List +import Control.Monad +import qualified Data.Map as M + +-- In line before "HTM stats:" +-- Starts, column 0 +-- HTM-commit, column 6 +-- STM-commit, column 5 +-- +-- In line before "Heap stats:" +-- HTM-fallback, column 0 +-- HLE-fallback, column 5 + +readLog :: Bool -> FilePath -> IO [[String]] +readLog threads log = do + ss <- findStats . lines <$> readFile log + let rs = map rates ss + let hs = map (log++) ["-full", "-fall", "-lock"] + return $ (if threads then "Threads":hs else hs) + : [ if threads then show t:vs else vs + | (t, R full fall lock) <- zip [1..] rs + , let vs = map show [full, fall, lock] + ] + +data Stats = S + { starts :: Int + , stmCommit :: Int + , htmCommit :: Int + , htmFallback :: Int + , hleFallback :: Int + } deriving (Show) + +read' s = case reads (filter (/= ',') s) of + [(v, [])] -> v + _ -> error $ "Can't read: " ++ s + +findStats :: [String] -> [Stats] +findStats ls = go ls empty + where + empty = S 0 0 0 0 0 + go [] _ = [] + go (l:"HTM stats:": ls) s = + let ws = words l + in go ls s { starts = read' (ws !! 0) + , stmCommit = read' (ws !! 5) + , htmCommit = read' (ws !! 6) + } + go (l:"Heap stats:":ls) s = + let ws = words l + in s { htmFallback = read' (ws !! 0) + , hleFallback = read' (ws !! 5) + } : go ls empty + go (_:ls) s = go ls s + +data Rates = + R { full :: Double + , fall :: Double + , lock :: Double + } deriving (Show) + +hwFullCommit s = + let a = fromIntegral $ starts s + b = fromIntegral $ htmFallback s + in a - b + +hwFallbackCommit s = + let a = fromIntegral $ htmCommit s + b = hwFullCommit s + in a - b + +rates s = + let rate a b = if b == 0 then 0 else a / b + aFull = hwFullCommit s + bFull = fromIntegral $ starts s + aFall = hwFallbackCommit s + bFall = fromIntegral $ htmFallback s + aLock = fromIntegral $ stmCommit s + bLock = fromIntegral $ hleFallback s + in R { full = rate aFull bFull + , fall = rate aFall bFall + , lock = rate aLock bLock + } + +getTicks :: [[String]] -> [Int] +getTicks (_:rs) = ticks + where + (ts:_) = transpose rs + m = maximum $ map read ts + + ticks + | m <= 16 = [1..m] + | otherwise = [1, m `div` 4, m `div` 2, 3*(m `div` 4), m] + +colPlus :: [[a]] -> [[a]] -> [[a]] +colPlus as bs = transpose (transpose as ++ transpose bs) + +formatColumns :: [[String]] -> String +formatColumns rs = unlines rs' + where + cs = transpose rs + ls = map (succ . maximum . map length) cs + + pad s l = go s (l - length s) + + go s 0 = s + go s l = ' ' : go s (l - 1) + + rs' = map concat . transpose . zipWith (\l c -> map (flip pad l) c) ls $ cs + +applyTemplate :: FilePath -> FilePath -> M.Map String String -> IO () +applyTemplate input output m = do + ls <- lines <$> readFile input + let os = map apply ls + writeFile output (unlines os) + where + apply l + | "$$$" `isPrefixOf` l + , [_, n] <- words l + , Just v <- n `M.lookup` m = v + | otherwise = l + +main :: IO () +main = do + (t:out:as) <- getArgs + cs <- foldl1 colPlus <$> zipWithM readLog (True:repeat False) as + let d = formatColumns cs + ticks = "var ticks = " ++ show (getTicks cs) + + applyTemplate "plot/plot-stats.html.template" out + (M.fromList [("title", t), ("data", d), ("ticks", ticks)]) diff --git a/benchmarks/PPoPP2019/plot.hs b/benchmarks/PPoPP2019/plot.hs new file mode 100644 index 0000000..3f55628 --- /dev/null +++ b/benchmarks/PPoPP2019/plot.hs @@ -0,0 +1,70 @@ +import System.Environment +import Data.List +import Control.Monad +import qualified Data.Map as M + +-- benchdata: run-time 1.0047227869945345 no-kill-time 1.0047204369911924 transactions 1727020 prog RBTREE-MUT-hybrid threads 1 entries 100000 code RBTreeMutUSTM +readLog :: Bool -> FilePath -> IO [[String]] +readLog threads log = do + ls <- map words . filter ("benchdata:" `isPrefixOf`) . lines <$> readFile log + return $ (if threads then ["Threads", log] else [log]) + : [ if threads then [t, show v] else [show v] + | [_,_,s,_,_,_,n,_,_,_,t,_,_,_,_] <- ls + , let v = read n / read s + ] + +getMaxY :: [[String]] -> Double +getMaxY (_:rs) = maximum . map read . concat $ rs + +getY0 :: [[String]] -> Double +getY0 (_:r1:rs) = maximum . map read $ r1 + +getTicks :: [[String]] -> [Int] +getTicks (_:rs) = ticks + where + (ts:_) = transpose rs + m = maximum $ map read ts + + ticks + | m <= 16 = [1..m] + | otherwise = [1, m `div` 4, m `div` 2, 3*(m `div` 4), m] + +colPlus :: [[a]] -> [[a]] -> [[a]] +colPlus as bs = transpose (transpose as ++ transpose bs) + +formatColumns :: [[String]] -> String +formatColumns rs = unlines rs' + where + cs = transpose rs + ls = map (succ . maximum . map length) cs + + pad s l = go s (l - length s) + + go s 0 = s + go s l = ' ' : go s (l - 1) + + rs' = map concat . transpose . zipWith (\l c -> map (flip pad l) c) ls $ cs + +applyTemplate :: FilePath -> FilePath -> M.Map String String -> IO () +applyTemplate input output m = do + ls <- lines <$> readFile input + let os = map apply ls + writeFile output (unlines os) + where + apply l + | "$$$" `isPrefixOf` l + , [_, n] <- words l + , Just v <- n `M.lookup` m = v + | otherwise = l + +main :: IO () +main = do + (t:out:as) <- getArgs + cs <- foldl1 colPlus <$> zipWithM readLog (True:repeat False) as + let d = formatColumns cs + maxY = "var maxY = " ++ show (getMaxY cs) + y0 = "var y0 = " ++ show (getY0 cs) + ticks = "var ticks = " ++ show (getTicks cs) + + applyTemplate "plot/plot.html.template" out + (M.fromList [("title", t), ("data", d), ("maxY", maxY), ("y0", y0), ("ticks", ticks)]) diff --git a/benchmarks/PPoPP2019/plot/plot-stats.html.template b/benchmarks/PPoPP2019/plot/plot-stats.html.template new file mode 100644 index 0000000..e32643a --- /dev/null +++ b/benchmarks/PPoPP2019/plot/plot-stats.html.template @@ -0,0 +1,303 @@ + + + + + Data Explorer + + + + + +
+

+$$$ title +

+ +
+
+
+ + + + diff --git a/benchmarks/PPoPP2019/plot/plot.html.template b/benchmarks/PPoPP2019/plot/plot.html.template new file mode 100644 index 0000000..df9b454 --- /dev/null +++ b/benchmarks/PPoPP2019/plot/plot.html.template @@ -0,0 +1,310 @@ + + + + + Data Explorer + + + + + +
+

+$$$ title +

+ +
+
+
+ + + + diff --git a/benchmarks/PPoPP2019/src/Hamt.hs b/benchmarks/PPoPP2019/src/Hamt.hs new file mode 100644 index 0000000..464b15d --- /dev/null +++ b/benchmarks/PPoPP2019/src/Hamt.hs @@ -0,0 +1,117 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE CPP #-} +module Hamt + ( Tree + , mkTree + + , benchCode + + , insert + , delete + , update + , get + , contains + + , insertTest + , deleteTest + ) where + +#ifdef TSTRUCT +import qualified HamtTStruct as M +#elif defined(MUT) +import qualified HamtTRef as M +#elif defined(TVAR) +import qualified STMContainers.Map as M +#else +#error Unsupported HAMT code variation +#endif +import GHC.Conc.Sync +import Control.Applicative +import Control.Monad (forM_) +import Data.Maybe +import Data.Word +import Data.List (inits,tails) + +#ifdef TSTRUCT +benchCode :: String +benchCode = "HAMTTStruct" +#elif defined(MUT) +benchCode :: String +benchCode = "HAMTTRef" +#else +benchCode :: String +benchCode = "STMTrieTVar" +#endif + +type Tree = M.Map Word Word +---------------------------------- +-- Public API +-- +mkTree :: STM Tree +mkTree = M.new + +insert :: Tree -> Word -> Word -> STM Bool +#if defined(TSTRUCT) || defined(MUT) +insert t k v = M.insert k v t >> return False +#else +insert t k v = M.insert v k t >> return False +#endif + +delete :: Tree -> Word -> STM Bool +delete t k = M.delete k t >> return False + +update :: Tree -> Word -> Word -> STM Bool +update t k v = insert t k v + +get :: Tree -> Word -> STM (Maybe Word) +get t k = M.lookup k t + +contains :: Tree -> Word -> STM Bool +contains t k = isJust <$> get t k + +insertTest :: [Word] -> IO () +insertTest as = do + r <- atomically $ mkTree + forM_ (zip3 as (tail (inits as)) (tail (tails as))) $ \(a,is,ts) -> do + atomically $ insert r a a + + forM_ is $ \i -> do + m <- atomically $ get r i + case m of + Nothing -> print (i, "not found.") + Just i' -> if i == i' + then return () + else print (i, i', "Unmatched!") + + forM_ ts $ \i -> do + m <- atomically $ get r i + case m of + Nothing -> return () + Just i' -> print (i, i', "Unexpected!") + + + +deleteTest :: [Word] -> IO () +deleteTest as = do + r <- atomically $ mkTree + forM_ as $ \a -> do + atomically $ insert r a a + + forM_ (zip3 as (tail (inits as)) (tail (tails as))) $ \(a,is,ts) -> do + atomically $ delete r a + putStrLn $ "Deleting " ++ show a + -- putStrLn $ " out " ++ show is ++ " in " ++ show ts + + forM_ is $ \i -> do + m <- atomically $ get r i + case m of + Nothing -> return () + Just i' -> print (i, i', "Unexpected!") + + forM_ ts $ \i -> do + m <- atomically $ get r i + case m of + Nothing -> print (i, "not found.") + Just i' -> if i == i' + then return () + else print (i, i', "Unmatched!") diff --git a/benchmarks/PPoPP2019/src/HamtTRef.hs b/benchmarks/PPoPP2019/src/HamtTRef.hs new file mode 100644 index 0000000..8d923e5 --- /dev/null +++ b/benchmarks/PPoPP2019/src/HamtTRef.hs @@ -0,0 +1,540 @@ +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE MutableFields #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE UnboxedTuples #-} +module HamtTRef where + +import Prelude hiding (lookup) + +import Numeric + +import GHC.Num +import GHC.ST +import GHC.Base hiding (assert) +import GHC.Conc +import GHC.Word +import GHC.Prim + +import System.IO.Unsafe (unsafePerformIO) + +import Data.List (nub) + +-- import Prelude (Maybe(..), pred, succ, (=<<), ($!), snd, unlines) + +import qualified STMContainers.WordArray.Indices as Indices +import qualified STMContainers.HAMT.Level as Level + +import Data.Hashable +import Control.Monad (forM_, forM) +import Control.Applicative ((<$>)) + +-- In original HAMT the Node type has three constructors: +-- +-- data Node = Nodes (WordArray (Node e)) +-- | Leaf Hash e +-- | Leaves Hash (SizedArray e) +-- +-- If we collapse leaf and leaves: +-- +-- data Node = Nodes (WordArray (Node e)) +-- | Leaves Hash (SizedArray e) +-- +-- Now represent both of these with an STMMutableArray# version of the following: +-- +-- data Nodes = Nodes Indices [Nodes e] +-- data Leaves = Leaves Hash [e] +-- +-- In the monomorphic unboxed case Leaves stores everything in words while Nodes +-- always has pointers. We can use a word for the tag bit and represent both with +-- a STMMutableArray#. The tag bit would be read-only so we can cheat in STM +-- and do a non-transactional read: +-- +-- struct Nodes +-- { +-- halfword sizeWords; +-- halfword sizePtrs; +-- +-- word tag; +-- word indices; +-- Nodes* nodes[]; +-- } +-- +-- struct Leaves +-- { +-- halfword sizeWords; +-- halfword sizePtrs; +-- +-- word tag; +-- word Hash; +-- word words[]; +-- } +-- +-- We will assume word sized keys and heap object values are being stored (to match the +-- original). +-- + +newtype Map k v = Map { unMap :: TVar (Node (k,v)) } + +type Key a = (Eq a, Hashable a) +type Hash = Int + +class (Eq (ElementKey e)) => Element e where + type ElementKey e + elementKey :: e -> ElementKey e + +instance (Eq k) => Element (k, v) where + type ElementKey (k, v) = k + elementKey (k, v) = k + +data Node e where +-- TODO: fix this. Right now the wrapper gets the type Int# -> Int# -> ... + Nodes :: {-# UNPACK #-} !Indices -> mutableArray (Node e) -> STM (Node e) + Leaves :: {-# UNPACK #-} !Hash -> mutableArray e -> STM (Node e) +-- Nodes :: Indices -> mutableArray (Node e) -> STM (Node e) +-- Leaves :: Hash -> mutableArray e -> STM (Node e) + Deleted :: Node e + +instance Eq (Node e) where + Deleted == Deleted = True + _ == Deleted = False + Deleted == _ = False + a == a' = + case sameSTMMutableArray# (unsafeCoerce# a) (unsafeCoerce# a') of + 0# -> False + _ -> True + +type Index = Int +type Indices = Int + +--------------------------------------------------- +-- Array operations + +{-# INLINE refArraySize #-} +refArraySize :: RefArray# RealWorld e -> Int +refArraySize r = + case refArraySize# r of + i# -> I# i# + +type DataArray e = RefArray# RealWorld e +type NodeArray e = RefArray# RealWorld (Node e) + +{-# INLINE readDataP #-} +readDataP :: DataArray e -> Int -> STM e +readDataP arr (I# i#) = STM $ \s1# -> readRefArrayExt# arr i# s1# + +{-# INLINE writeDataP #-} +writeDataP :: DataArray e -> Int -> e -> STM () +writeDataP arr (I# i#) e = STM $ \s1# -> + case writeRefArrayExt# arr i# e s1# of + s2# -> (# s2#, () #) + +{-# INLINE writeNodesDataP #-} +writeNodesDataP :: NodeArray e -> Int -> Node e -> STM () +writeNodesDataP arr (I# i#) e = STM $ \s1# -> + case writeRefArrayExt# arr i# e s1# of + s2# -> (# s2#, () #) + +{-# INLINE readNodesData #-} +readNodesData :: NodeArray e -> Int -> STM (Node e) +readNodesData arr (I# i#) = STM $ \s1# -> readTRefArray# arr i# s1# + +{-# INLINE readData #-} +readData :: DataArray e -> Int -> STM e +readData arr (I# i#) = STM $ \s# -> readTRefArray# arr i# s# + +{-# INLINE writeData #-} +writeData :: DataArray e -> Int -> e -> STM () +writeData arr (I# i#) e = STM $ \s1# -> + case writeTRefArray# arr i# e s1# of + s2# -> (# s2#, () #) + +{-# INLINE writeNodesData #-} +writeNodesData :: NodeArray e -> Int -> Node e -> STM () +writeNodesData arr (I# i#) e = STM $ \s1# -> + case writeTRefArray# arr i# e s1# of + s2# -> (# s2#, () #) + +{-# INLINE mkNodes #-} +mkNodes :: Indices -> Int -> STM (Node e) +mkNodes i (I# sz#) = Nodes i sz# + +{-# INLINE mkLeaves #-} +mkLeaves :: Hash -> Int -> STM (Node e) +mkLeaves h (I# sz#) = Leaves h sz# + +--------------------------------------------------- +-- Operations +-- + +lookupLevel :: Element e => Hash -> ElementKey e -> Level.Level -> Node e -> STM (Maybe e) +lookupLevel h k l (Nodes b ns) = do + let i = Level.hashIndex l h + if Indices.elem i b + then readNodesData ns (Indices.position i b) >>= lookupLevel h k (Level.succ l) + else return Nothing +lookupLevel h k l (Leaves h' vs) + | h == h' = do + let sz = refArraySize vs + find i + | i >= sz = return Nothing + | otherwise = do + e <- readData vs i + if elementKey e == k + then return $! Just e + else find (i+1) + find 0 + | otherwise = return Nothing +lookupLevel _ _ _ Deleted = return Nothing + +{-# INLINE set #-} +set :: Index -> Node e -> Node e -> STM (Node e) +set i e a@(Nodes b ns) = do + let sparseIndex = Indices.position i b + size = Indices.size b + + if Indices.elem i b + then writeNodesData ns sparseIndex e >> return a + else do + a'@(Nodes _ ns') <- mkNodes (Indices.insert i b) (size+1) + forM_ [0..sparseIndex-1] $ \i -> readNodesData ns i >>= writeNodesDataP ns' i + writeNodesDataP ns' sparseIndex e + forM_ [sparseIndex..(size-1)] $ \i -> readNodesData ns i >>= writeNodesDataP ns' (i+1) + return a' +set _ _ _ = error "set on non Nodes" + +{-# INLINE unset #-} +unset :: Index -> Node e -> STM (Node e) +unset i a@(Nodes b ns) = do + -- TODO: handling a Nodes that goes to one element. + if Indices.elem i b + then do + let b' = Indices.invert i b + size = Indices.size b + sparseIndex = Indices.position i b + + if size == 1 + then return Deleted + else do + a'@(Nodes _ ns') <- mkNodes b' (pred size) + forM_ [0..pred sparseIndex] $ \i -> readNodesData ns i >>= writeNodesDataP ns' i + forM_ [succ sparseIndex..pred size] + $ \i -> readNodesData ns i >>= writeNodesDataP ns' (pred i) + return a' + else return a +unset _ _ = error "unset on non Nodes" + +{-# INLINE unset' #-} +unset' :: Index -> Node e -> STM (Maybe (Node e)) +unset' i a@(Nodes b ns) = do + -- TODO: handling a Nodes that goes to one element. + if Indices.elem i b + then do + let b' = Indices.invert i b + size = Indices.size b + sparseIndex = Indices.position i b + + if size == 1 + then return Nothing + else do + a'@(Nodes _ ns') <- mkNodes b' (pred size) + forM_ [0..pred sparseIndex] $ \i -> readNodesData ns i >>= writeNodesDataP ns' i + forM_ [succ sparseIndex..pred size] + $ \i -> readNodesData ns i >>= writeNodesDataP ns' (pred i) + return (Just a') + else return (Just a) +unset' _ _ = error "unset' on non Nodes" + + +{-# INLINE singleton #-} +singleton :: Index -> Node e -> STM (Node e) +singleton i e = do + a@(Nodes _ ns) <- mkNodes (Indices.singleton i) 1 + writeNodesDataP ns 0 e + return a + +{-# INLINE pairNodes #-} +pairNodes :: Index -> Node e -> Index -> Node e -> STM (Node e) +pairNodes i e i' e' = do + if | i < i' -> do + a@(Nodes _ ns) <- mkNodes is 2 + writeNodesDataP ns 0 e + writeNodesDataP ns 1 e' + return a + | i > i' -> do + a@(Nodes _ ns) <- mkNodes is 2 + writeNodesDataP ns 0 e' + writeNodesDataP ns 1 e + return a + | otherwise -> singleton is e' + where + is = Indices.fromList [i, i'] + +pair :: Hash -> Node e -> Hash -> Node e -> Level.Level -> STM (Node e) +pair h1 n1 h2 n2 l = + -- Even if the whole hashes do not match, the next chunk of it might, so check + -- if the indexes at this level match. If they do, make a new level and recurse + -- until we find the difference. + + if i1 == i2 + then singleton i1 =<< pair h1 n1 h2 n2 (Level.succ l) + else pairNodes i1 n1 i2 n2 + where + hashIndex = Level.hashIndex l + i1 = hashIndex h1 + i2 = hashIndex h2 + + +insertLevel :: Element e => Hash -> e -> Level.Level -> Node e -> STM (Node e) +insertLevel h e l a@(Nodes b ns) + | Indices.elem i b = do + -- If there is already an entry for the hash at this level, follow + -- it down. + let !sparseIndex = Indices.position i b + a' <- readNodesData ns sparseIndex + insertLevel h e (Level.succ l) a' >>= update sparseIndex a' + return a + | otherwise = do + -- There is not an entry for the hash at this level: + -- - Make a new leaf entry + ls@(Leaves _ vs) <- mkLeaves h 1 + writeDataP vs 0 e + -- - Expand this level to include a new entry + -- returning the new nodes level. + set i ls a + where + update i old new + | old /= new = writeNodesData ns i new + | otherwise = return () + !i = Level.hashIndex l h + +insertLevel h e l a@(Leaves h' vs) + | h == h' = do + -- Hashes match. Check to see if the match is due to a + -- matching key, if so, replace the value otherwise, expand to + -- include this new entry as a collision. + let !sz = refArraySize vs + k = elementKey e + find i + | i >= sz = do + -- Expand and add the new value. + ls@(Leaves _ vs') <- mkLeaves h (sz+1) + forM_ [0..sz-1] $ \j -> do + readData vs j >>= writeDataP vs' j + writeDataP vs' sz e + return ls + | otherwise = do + e' <- readData vs i + if elementKey e' == k + then do + -- TODO: Do not replace! This is something + -- that could be specalized in a monomorphic + -- version where value comparisons could happen. + -- -- Found entry with same key, replace. + -- writeData a i e + return a + else find (i+1) + find 0 + | otherwise = do + -- Hashes do not match, we need to turn this into a nodes level + -- with a pair of leaf levels below. + -- - Make a new single leaf for the new entry. + ls@(Leaves _ vs) <- mkLeaves h 1 + writeDataP vs 0 e + + -- - Make a new nodes level, Link the new leaf (ls), and the old leaves (a). + -- Return this pair to replace the old level (may be a chain of new levels + -- down to the difference in the hash). + pair h' a h ls l + +insertLevel h e _ Deleted = do + ls@(Leaves _ vs) <- mkLeaves h 1 + writeDataP vs 0 e + return ls + +-- Delete from a level +-- +-- Cases we must handle. +-- +-- - Deleting form a collision leaf +-- +-- Shrink the leaf, updated parent pointer. +-- +-- - Deleting from a singlton leaf when the parent nodes has multiple entries +-- +-- Shrink the parent, update grandparent pointer. +-- +-- - Deleting from a singlton leaf when the parent nodes is also a singlton +-- +-- Shrink grandparent, update above. +-- +-- Recursive call can dictate the following actions when it returns: +-- +-- - Update pointer to new entry +-- - Shrink removing the entry +-- - Signal above to shrink +-- +deleteLevel + :: (Element e) + => Hash + -> ElementKey e + -> Level.Level + -> Node e + -> STM (Node e) + +deleteLevel h k l a@(Nodes b ns) + | Indices.elem i b = do + -- If there is already an entry for the hash at this level, follow + -- it down. + let !sparseIndex = Indices.position i b + a' <- readNodesData ns sparseIndex + deleteLevel h k (Level.succ l) a' >>= update i sparseIndex a' + | otherwise = return a + where + !i = Level.hashIndex l h + update i _ _ Deleted = unset i a + update i sparseIndex old new + | old /= new = writeNodesData ns sparseIndex new >> return a + | otherwise = return a + +deleteLevel h k l a@(Leaves h' vs) + | h == h' = do + -- Hashes match. Check to see if the match is due to a + -- matching key, if so shrink the leaf. + let !sz = refArraySize vs + find i + | i >= sz = return a -- Key not found, do nothing. + | otherwise = do + e' <- readData vs i + if elementKey e' == k + then do + -- Found entry with same key, shrink. + if sz == 1 + then return Deleted + else do + ls@(Leaves _ vs') <- mkLeaves h (sz-1) + forM_ [0..i-1] $ \j -> + readData vs j >>= writeDataP vs' j + forM_ [i+1..sz-1] $ \j -> + readData vs j >>= writeDataP vs' (j-1) + return $ ls + else find (i+1) + find 0 + | otherwise = return a -- Hashes do not match, we are done. + +deleteLevel _ _ _ Deleted = return Deleted + +--------------------------------------------------- +-- API +-- + +-- lookup :: (Show k, Show v, Key k) => k -> Map k v -> STM (Maybe v) +lookup :: (Key k) => k -> Map k v -> STM (Maybe v) +lookup k (Map m) = do + e <- readTVar m >>= lookupLevel (hash k) k 0 + return $ fmap snd e + +-- insert :: (Show k, Show v, Key k) => k -> v -> Map k v -> STM () +insert :: (Key k) => k -> v -> Map k v -> STM () +insert k v (Map m) = do + a <- readTVar m + a' <- insertLevel (hash k) (k,v) 0 a + if a == a' + then return () -- >> validate (Map m) + else writeTVar m a' -- >> validate (Map m) +{- + assertM ("VVV Key not present after insert!") $ do + lookup k (Map m) >>= \case + Just _ -> return True + Nothing -> return False +-} + +-- delete :: (Show k, Show v, Key k) => k -> Map k v -> STM () +delete :: (Key k) => k -> Map k v -> STM () +delete k (Map m) = do + a <- readTVar m + a' <- deleteLevel (hash k) k 0 a + + if a == a' + then return () -- >> validate (Map m) + else writeTVar m a' -- >> validate (Map m) +{- + assertM ("VVV Key still present after delete!") $ do + lookup k (Map m) >>= \case + Just _ -> return False + Nothing -> return True +-} + +new :: STM (Map k v) +new = do + v <- newTVar Deleted + return (Map v) + + +showLevel :: (Show e, Element e) => Node e -> STM [String] +showLevel (Nodes b ns) = do + let sz = Indices.size b + ls <- forM [0..sz-1] $ \i -> readNodesData ns i >>= showLevel + return $ ("Nodes " ++ showBin b) : map (" "++) (concat ls) +showLevel (Leaves h vs) = do + let sz = refArraySize vs + ls <- forM [0..sz-1] $ \i -> show <$> readData vs i + return $ ("Leaf " ++ showBin h) : map (" "++) ls +showLevel Deleted = return ["Deleted"] + +showBin x = showIntAtBase 2 ("01"!!) x "" + +assertM :: Show e => e -> STM Bool -> STM () +assertM m a = do + b <- a + if b + then return () + else unsafeIOToSTM (print m) + +assert :: Show e => e -> Bool -> STM () +assert m b = do + if b + then return () + else unsafeIOToSTM (print m) + +-- Find the first hash available. +findHash :: Node e -> STM Hash +findHash (Nodes _ ns) = do + assert ("VVV Empty non-root nodes") $ refArraySize ns > 0 + -- Recurse to find the first hash. Really we want all + -- hashes to match, but this aproximates. + readNodesData ns 0 >>= findHash +findHash (Leaves h _) = return h +findHash _ = error "Deleted in findHash" + +validateLevel :: (Show e, Element e, Eq (ElementKey e), Key (ElementKey e)) => Level.Level -> Node e -> STM () +validateLevel l a@(Nodes b ns) = do + let sz = Indices.size b + forM_ [0..sz-1] $ \i -> do + a' <- readNodesData ns i + h <- findHash a' + + let h' = Level.hashIndex l h + i' = Indices.position h' b + + assert ("VVV First hash does not match.", i, i', h', h, b, l) $ i == i' + + validateLevel (Level.succ l) a' +validateLevel l a@(Leaves h vs) = do + let sz = refArraySize vs + assert ("VVV Empty Leaf!", h) $ sz > 0 + + -- check that keys and hashes match, keys do not match. + es <- forM [0..sz-1] $ readData vs + let ks = map elementKey es + + assert ("VVV Keys to not all hash same", es, map hash ks) $ nub (map hash ks) == [h] + assert ("VVV Some keys match!", es) $ nub ks == ks + + +validate :: (Show k, Show v, Key k) => Map k v -> STM () +validate (Map m) = readTVar m >>= validateLevel 0 + +showSTM :: (Show k, Show v, Key k) => Map k v -> STM String +showSTM (Map m) = do + a <- readTVar m + unlines <$> showLevel a diff --git a/benchmarks/PPoPP2019/src/HamtTStruct.hs b/benchmarks/PPoPP2019/src/HamtTStruct.hs new file mode 100644 index 0000000..c0cfe96 --- /dev/null +++ b/benchmarks/PPoPP2019/src/HamtTStruct.hs @@ -0,0 +1,726 @@ +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} +module HamtTStruct where + +import Prelude hiding (lookup) + +import Numeric + +import GHC.Num +import GHC.ST +import GHC.Base hiding (assert) +import GHC.Conc +import GHC.Word +import GHC.Prim + +import System.IO.Unsafe (unsafePerformIO) + +import Data.List (nub) + +-- import Prelude (Maybe(..), pred, succ, (=<<), ($!), snd, unlines) + +import qualified STMContainers.WordArray.Indices as Indices +import qualified STMContainers.HAMT.Level as Level + +import Data.Hashable +import Control.Monad (forM_, forM) +import Control.Applicative ((<$>)) + +-- In original HAMT the Node type has three constructors: +-- +-- data Node = Nodes (WordArray (Node e)) +-- | Leaf Hash e +-- | Leaves Hash (SizedArray e) +-- +-- If we collapse leaf and leaves: +-- +-- data Node = Nodes (WordArray (Node e)) +-- | Leaves Hash (SizedArray e) +-- +-- Now represent both of these with an STMMutableArray# version of the following: +-- +-- data Nodes = Nodes Indices [Nodes e] +-- data Leaves = Leaves Hash [e] +-- +-- In the monomorphic unboxed case Leaves stores everything in words while Nodes +-- always has pointers. We can use a word for the tag bit and represent both with +-- a STMMutableArray#. The tag bit would be read-only so we can cheat in STM +-- and do a non-transactional read: +-- +-- struct Nodes +-- { +-- halfword sizeWords; +-- halfword sizePtrs; +-- +-- word tag; +-- word indices; +-- Nodes* nodes[]; +-- } +-- +-- struct Leaves +-- { +-- halfword sizeWords; +-- halfword sizePtrs; +-- +-- word tag; +-- word Hash; +-- word words[]; +-- } +-- +-- We will assume word sized keys and heap object values are being stored (to match the +-- original). +-- + +newtype Map k v = Map { unMap :: TVar (WordArray (k,v)) } + +type Key a = (Eq a, Hashable a) +type Hash = Int + +class (Eq (ElementKey e)) => Element e where + type ElementKey e + elementKey :: e -> ElementKey e + +instance (Eq k) => Element (k, v) where + type ElementKey (k, v) = k + elementKey (k, v) = k + +data WordArray e = WordArray { unWordArray :: STMMutableArray# RealWorld e } + +instance Eq (WordArray e) where + (WordArray a) == (WordArray a') = + case sameSTMMutableArray# a a' of + 0# -> False + _ -> True + +type Index = Int +type Indices = Int + +-- Private API for building a new private WordArray that we can then use transactionally. +{-# INLINE newWordArrayP #-} +newWordArrayP :: Int -> STM (WordArray e) +newWordArrayP (I# i#) = STM $ \s1# -> + case newSTMArray# i# 1# undefined s1# of + (# s2#, marr# #) -> (# s2#, WordArray marr# #) + +{-# INLINE newWordArrayIO #-} +newWordArrayIO :: Int -> IO (WordArray e) +newWordArrayIO (I# i#) = IO $ \s1# -> + case newSTMArray# i# 1# undefined s1# of + (# s2#, marr# #) -> (# s2#, WordArray marr# #) + +{-# INLINE newWordArrayInitP #-} +newWordArrayInitP :: Int -> e -> STM (WordArray e) +newWordArrayInitP (I# i#) e = STM $ \s1# -> + case newSTMArray# i# 1# e s1# of + (# s2#, marr# #) -> (# s2#, WordArray marr# #) + +{-# INLINE readTagP #-} +readTagP :: WordArray e -> STM Word +readTagP (WordArray arr#) = STM $ \s1# -> + case readSTMArrayWord# arr# 0# s1# of + (# s2#, w# #) -> (# s2#, W# w# #) + +--------------------------------------------------- +-- Nodes +{-# INLINE roundUp64# #-} +roundUp64# :: Int# -> Int# +roundUp64# i# = (i# +# 63#) `andI#` (-64#) + +{-# INLINE newNodesInitP #-} +newNodesInitP :: Int -> WordArray e -> STM (WordArray e) +newNodesInitP (I# i#) (WordArray e#) = STM $ \s1# -> +-- case newSTMArray# (roundUp64# i#) 2# (unsafeCoerce# e#) s1# of + case newSTMArray# i# 2# (unsafeCoerce# e#) s1# of + (# s2#, arr# #) -> + case writeSTMArrayWord# arr# 0# 0## s2# of -- Set the tag to zero + s3# -> (# s3#, WordArray arr# #) + +{-# INLINE newNodesP #-} +newNodesP :: Int -> STM (WordArray e) +newNodesP (I# i#) = STM $ \s1# -> +-- case newSTMArray# (roundUp64# i#) 2# undefined s1# of -- round up pointer count so words are on next cacheline + case newSTMArray# i# 2# undefined s1# of -- Minimal allocation + (# s2#, arr# #) -> + case writeSTMArrayWord# arr# 0# 0## s2# of -- Set the tag to zero + s3# -> (# s3#, WordArray arr# #) + +{-# INLINE modifyIndicesP #-} +modifyIndicesP :: WordArray e -> (Int -> Int) -> STM () +modifyIndicesP arr f = do + i <- readIndicesP arr + writeIndicesP arr (f i) + +{-# INLINE readIndicesP #-} +readIndicesP :: WordArray e -> STM Int +readIndicesP (WordArray arr#) = STM $ \s1# -> + case readSTMArrayWord# arr# 1# s1# of + (# s2#, w# #) -> (# s2#, I# (word2Int# w#) #) + +{-# INLINE writeIndicesP #-} +writeIndicesP :: WordArray e -> Int -> STM () +writeIndicesP (WordArray arr#) (I# i#) = STM $ \s1# -> + case writeSTMArrayWord# arr# 1# (int2Word# i#) s1# of + s2# -> (# s2#, () #) + +--------------------------------------------------- +-- Leaves + +{-# INLINE newLeavesP #-} +newLeavesP :: Int -> STM (WordArray e) +newLeavesP (I# i#) = STM $ \s1# -> + case newSTMArray# i# 2# undefined s1# of + (# s2#, arr# #) -> + case writeSTMArrayWord# arr# 0# 1## s2# of -- Set the tag to one + s3# -> (# s3#, WordArray arr# #) + +{-# INLINE modifyHashP #-} +modifyHashP :: WordArray e -> (Hash -> Hash) -> STM () +modifyHashP arr f = do + i <- readHashP arr + writeHashP arr (f i) + +{-# INLINE readHashP #-} +readHashP :: WordArray e -> STM Hash +readHashP (WordArray arr#) = STM $ \s1# -> + case readSTMArrayWord# arr# 1# s1# of + (# s2#, w# #) -> (# s2#, I# (word2Int# w#) #) + +{-# INLINE writeHashP #-} +writeHashP :: WordArray e -> Hash -> STM () +writeHashP (WordArray arr#) (I# i#) = STM $ \s1# -> + case writeSTMArrayWord# arr# 1# (int2Word# i#) s1# of + s2# -> (# s2#, () #) + +--------------------------------------------------- +-- Pointers +-- + +{-# INLINE dataSize #-} +dataSize :: WordArray e -> Int +dataSize (WordArray arr#) = + case sizeofSTMMutableArray# arr# of + i# -> I# i# + +{-# INLINE readDataP #-} +readDataP :: WordArray e -> Int -> STM e +readDataP arr (I# i#) = STM $ \s1# -> readSTMArray# (unWordArray arr) i# s1# + +{-# INLINE writeDataP #-} +writeDataP :: WordArray e -> Int -> e -> STM () +writeDataP arr (I# i#) e = STM $ \s1# -> + case writeSTMArray# (unWordArray arr) i# e s1# of + s2# -> (# s2#, () #) + +{-# INLINE writeNodesDataP #-} +writeNodesDataP :: WordArray e -> Int -> WordArray e -> STM () +writeNodesDataP arr (I# i#) (WordArray e#) = STM $ \s1# -> + case writeSTMArray# (unWordArray arr) i# (unsafeCoerce# e#) s1# of + s2# -> (# s2#, () #) + +{-# INLINE readNodesData #-} +readNodesData :: WordArray e -> Int -> STM (WordArray e) +readNodesData a i = do + e <- readData a i + return (WordArray (unsafeCoerce# e)) + +{-# INLINE readData #-} +readData :: WordArray e -> Int -> STM e +readData arr (I# i#) = STM $ \s# -> readTArray# (unWordArray arr) (int2Word# i#) s# + +{-# INLINE writeData #-} +writeData :: WordArray e -> Int -> e -> STM () +writeData arr (I# i#) e = STM $ \s1# -> + case writeTArray# (unWordArray arr) (int2Word# i#) e s1# of + s2# -> (# s2#, () #) + +{-# INLINE writeNodesData #-} +writeNodesData :: WordArray e -> Int -> WordArray e -> STM () +writeNodesData arr (I# i#) (WordArray e#) = STM $ \s1# -> + case writeTArray# (unWordArray arr) (int2Word# i#) (unsafeCoerce# e#) s1# of + s2# -> (# s2#, () #) + +--------------------------------------------------- +-- Operations +-- + +lookupLevel :: Element e => Hash -> ElementKey e -> Level.Level -> WordArray e -> STM (Maybe e) +lookupLevel h k l arr = do + t <- readTagP arr + case t of + 0 -> do -- A nodes level. + let i = Level.hashIndex l h + b <- readIndicesP arr -- TODO: if we can grow this must be transactional + if Indices.elem i b + then readNodesData arr (Indices.position i b) >>= lookupLevel h k (Level.succ l) + else return Nothing + 1 -> do -- A leaf level. + h' <- readHashP arr + if h == h' + then do + let sz = dataSize arr + find i + | i >= sz = return Nothing + | otherwise = do + e <- readData arr i + if elementKey e == k + then return $! Just e + else find (i+1) + find 0 + else return Nothing + +{-# INLINE set #-} +set :: Index -> WordArray e -> WordArray e -> STM (WordArray e) +set i e a = do + b <- readIndicesP a + let sparseIndex = Indices.position i b + size = Indices.size b + + if Indices.elem i b + then writeNodesData a sparseIndex e >> return a + else do + a' <- newNodesP (size+1) + forM_ [0..sparseIndex-1] $ \i -> readNodesData a i >>= writeNodesDataP a' i + writeNodesDataP a' sparseIndex e + forM_ [sparseIndex..(size-1)] $ \i -> readNodesData a i >>= writeNodesDataP a' (i+1) + writeIndicesP a' (Indices.insert i b) + return a' + +{-# NOINLINE deleteMarker #-} +deleteMarker :: WordArray e +deleteMarker = unsafePerformIO $ newWordArrayIO 0 + +{-# INLINE unset #-} +unset :: Index -> WordArray e -> STM (WordArray e) +unset i a = do + -- TODO: handling a Nodes that goes to one element. + b <- readIndicesP a + if Indices.elem i b + then do + let b' = Indices.invert i b + size = Indices.size b + sparseIndex = Indices.position i b + + if size == 1 + then return deleteMarker + else do + a' <- newNodesP (pred size) + forM_ [0..pred sparseIndex] $ \i -> readNodesData a i >>= writeNodesDataP a' i + forM_ [succ sparseIndex..pred size] + $ \i -> readNodesData a i >>= writeNodesDataP a' (pred i) + writeIndicesP a' b' + return a' + else return a + +{-# INLINE unset' #-} +unset' :: Index -> WordArray e -> STM (Maybe (WordArray e)) +unset' i a = do + -- TODO: handling a Nodes that goes to one element. + b <- readIndicesP a + if Indices.elem i b + then do + let b' = Indices.invert i b + size = Indices.size b + sparseIndex = Indices.position i b + + if size == 1 + then return Nothing + else do + a' <- newNodesP (pred size) + forM_ [0..pred sparseIndex] $ \i -> readNodesData a i >>= writeNodesDataP a' i + forM_ [succ sparseIndex..pred size] + $ \i -> readNodesData a i >>= writeNodesDataP a' (pred i) + writeIndicesP a' b' + return (Just a') + else return (Just a) + +{-# INLINE singleton #-} +singleton :: Index -> WordArray e -> STM (WordArray e) +singleton i a = do + ns <- newNodesInitP 1 a + writeIndicesP ns (Indices.singleton i) + return ns + +{-# INLINE pairNodes #-} +pairNodes :: Index -> WordArray e -> Index -> WordArray e -> STM (WordArray e) +pairNodes i e i' e' = do + if | i < i' -> do + a <- newNodesInitP 2 e + writeNodesDataP a 1 e' + writeIndicesP a is + return a + | i > i' -> do + a <- newNodesInitP 2 e + writeNodesDataP a 0 e' + writeIndicesP a is + return a + | i == i' -> do + a <- newNodesInitP 1 e' + writeIndicesP a is + return a + where + is = Indices.fromList [i, i'] + +pair :: Hash -> WordArray e -> Hash -> WordArray e -> Level.Level -> STM (WordArray e) +pair h1 n1 h2 n2 l = + -- Even if the whole hashes do not match, the next chunk of it might, so check + -- if the indexes at this level match. If they do, make a new level and recurse + -- until we find the difference. + + if i1 == i2 + then singleton i1 =<< pair h1 n1 h2 n2 (Level.succ l) + else pairNodes i1 n1 i2 n2 + where + hashIndex = Level.hashIndex l + i1 = hashIndex h1 + i2 = hashIndex h2 + + +insertLevel :: Element e => Hash -> e -> Level.Level -> WordArray e -> STM (WordArray e) +insertLevel h e l a = do + let update i old new + | old /= new = writeNodesData a i new + | otherwise = return () + k = elementKey e + t <- readTagP a + case t of + 0 -> do + -- We have a nodes level + let !i = Level.hashIndex l h + b <- readIndicesP a + if Indices.elem i b + then do + -- If there is already an entry for the hash at this level, follow + -- it down. + let !sparseIndex = Indices.position i b + a' <- readNodesData a sparseIndex + insertLevel h e (Level.succ l) a' >>= update sparseIndex a' + return a + else do + -- There is not an entry for the hash at this level: + -- - Make a new leaf entry + ls <- newLeavesP 1 + writeHashP ls h + writeDataP ls 0 e + -- - Expand this level to include a new entry + -- returning the new nodes level. + set i ls a + + 1 -> do + -- We have a leaf level do the hashes match? + h' <- readHashP a + if h == h' + then do + -- Hashes match. Check to see if the match is due to a + -- matching key, if so, replace the value otherwise, expand to + -- include this new entry as a collision. + let !sz = dataSize a + find i + | i >= sz = do + -- Expand and add the new value. + ls <- newLeavesP (sz+1) + writeHashP ls h + forM_ [0..sz-1] $ \j -> do + readData a j >>= writeDataP ls j + writeDataP ls sz e + return ls + | otherwise = do + e' <- readData a i + if elementKey e' == k + then do + -- TODO: Do not replace! This is something + -- that could be specalized in a monomorphic + -- version where value comparisons could happen. + -- -- Found entry with same key, replace. + -- writeData a i e + return a + else find (i+1) + find 0 + else do + -- Hashes do not match, we need to turn this into a nodes level + -- with a pair of leaf levels below. + -- - Make a new single leaf for the new entry. + ls <- newLeavesP 1 + writeHashP ls h + writeDataP ls 0 e + + -- - Make a new nodes level, Link the new leaf (ls), and the old leaves (a). + -- Return this pair to replace the old level (may be a chain of new levels + -- down to the difference in the hash). + pair h' a h ls l + +-- Delete from a level +-- +-- Cases we must handle. +-- +-- - Deleting form a collision leaf +-- +-- Shrink the leaf, updated parent pointer. +-- +-- - Deleting from a singlton leaf when the parent nodes has multiple entries +-- +-- Shrink the parent, update grandparent pointer. +-- +-- - Deleting from a singlton leaf when the parent nodes is also a singlton +-- +-- Shrink grandparent, update above. +-- +-- Recursive call can dictate the following actions when it returns: +-- +-- - Update pointer to new entry +-- - Shrink removing the entry +-- - Signal above to shrink +-- +deleteLevel + :: (Element e) + => Hash + -> ElementKey e + -> Level.Level + -> WordArray e + -> STM (WordArray e) + +deleteLevel h k l a = do + let update i sparseIndex old new + | new == deleteMarker = unset i a -- remove entry, shrink + | old /= new = writeNodesData a sparseIndex new >> return a + | otherwise = return a + t <- readTagP a + case t of + 0 -> do + -- We have a nodes level + let !i = Level.hashIndex l h + b <- readIndicesP a + if Indices.elem i b + then do + -- If there is already an entry for the hash at this level, follow + -- it down. + let !sparseIndex = Indices.position i b + a' <- readNodesData a sparseIndex + deleteLevel h k (Level.succ l) a' >>= update i sparseIndex a' + else -- There is not an entry for the hash at this level, we are done. + return a + + 1 -> do + -- We have a leaf level do the hashes match? + h' <- readHashP a + if h == h' + then do + -- Hashes match. Check to see if the match is due to a + -- matching key, if so shrink the leaf. + let !sz = dataSize a + find i + | i >= sz = return a -- Key not found, do nothing. + | otherwise = do + e' <- readData a i + if elementKey e' == k + then do + -- Found entry with same key, shrink. + if sz == 1 + then return deleteMarker + else do + ls <- newLeavesP (sz-1) + writeHashP ls h + forM_ [0..i-1] $ \j -> + readData a j >>= writeDataP ls j + forM_ [i+1..sz-1] $ \j -> + readData a j >>= writeDataP ls (j-1) + return $ ls + else find (i+1) + find 0 + else return a -- Hashes do not match, we are done. + +deleteLevel' + :: (Element e) + => Hash + -> ElementKey e + -> Level.Level + -> WordArray e + -> STM (Maybe (WordArray e)) + +deleteLevel' h k l a = do + let update i sparseIndex old Nothing = unset' i a -- remove entry, shrink + update i sparseIndex old (Just new) -- Update pointer + | old /= new = writeNodesData a sparseIndex new >> return (Just a) + | otherwise = return (Just a) + t <- readTagP a + case t of + 0 -> do + -- We have a nodes level + let i = Level.hashIndex l h + b <- readIndicesP a + if Indices.elem i b + then do + -- If there is already an entry for the hash at this level, follow + -- it down. + let sparseIndex = Indices.position i b + a' <- readNodesData a sparseIndex + deleteLevel' h k (Level.succ l) a' >>= update i sparseIndex a' + else -- There is not an entry for the hash at this level, we are done. + return (Just a) + + 1 -> do + -- We have a leaf level do the hashes match? + h' <- readHashP a + if h == h' + then do + -- Hashes match. Check to see if the match is due to a + -- matching key, if so shrink the leaf. + let sz = dataSize a + find i + | i >= sz = return (Just a) -- Key not found, do nothing. + | otherwise = do + e' <- readData a i + if elementKey e' == k + then do + -- Found entry with same key, shrink. + if sz == 1 + then return Nothing + else do + ls <- newLeavesP (sz-1) + writeHashP ls h + forM_ [0..i-1] $ \j -> + readData a j >>= writeDataP ls j + forM_ [i+1..sz-1] $ \j -> + readData a j >>= writeDataP ls (j-1) + return $ Just ls + else find (i+1) + find 0 + else return (Just a) -- Hashes do not match, we are done. + +--------------------------------------------------- +-- API +-- + +-- lookup :: (Show k, Show v, Key k) => k -> Map k v -> STM (Maybe v) +lookup :: (Key k) => k -> Map k v -> STM (Maybe v) +lookup k (Map m) = do + e <- readTVar m >>= lookupLevel (hash k) k 0 + return $ fmap snd e + +-- insert :: (Show k, Show v, Key k) => k -> v -> Map k v -> STM () +insert :: (Key k) => k -> v -> Map k v -> STM () +insert k v (Map m) = do + a <- readTVar m + a' <- insertLevel (hash k) (k,v) 0 a + if a == a' + then return () -- validate (Map m) + else writeTVar m a' -- >> validate (Map m) +{- + assertM ("VVV Key not present after insert!") $ do + lookup k (Map m) >>= \case + Just _ -> return True + Nothing -> return False +-} +-- delete :: (Show k, Show v, Key k) => k -> Map k v -> STM () +delete :: (Key k) => k -> Map k v -> STM () +delete k (Map m) = do + a <- readTVar m + a' <- deleteLevel (hash k) k 0 a + + if a' == deleteMarker + then (newNodesP 0 >>= writeTVar m) -- >> validate (Map m) + else if a == a' + then return () -- validate (Map m) + else writeTVar m a' -- >> validate (Map m) +{- + assertM ("VVV Key still present after delete!") $ do + lookup k (Map m) >>= \case + Just _ -> return False + Nothing -> return True +-} +new :: STM (Map k v) +new = do + n <- newNodesP 0 + v <- newTVar n + return (Map v) + + +showLevel :: (Show e, Element e) => WordArray e -> STM [String] +showLevel a = do + t <- readTagP a + case t of + 0 -> do + -- Nodes + b <- readIndicesP a + let sz = Indices.size b + ls <- forM [0..sz-1] $ \i -> readNodesData a i >>= showLevel + return $ ("Nodes " ++ showBin b) : map (" "++) (concat ls) + 1 -> do + -- Leaf + h <- readHashP a + let sz = dataSize a + ls <- forM [0..sz-1] $ \i -> show <$> readData a i + return $ ("Leaf " ++ showBin h) : map (" "++) ls + where + showBin x = showIntAtBase 2 ("01"!!) x "" + +assertM :: Show e => e -> STM Bool -> STM () +assertM m a = do + b <- a + if b + then return () + else unsafeIOToSTM (print m) + +assert :: Show e => e -> Bool -> STM () +assert m b = do + if b + then return () + else unsafeIOToSTM (print m) + +-- Find the first hash available. +findHash :: WordArray e -> STM Hash +findHash a = do + t <- readTagP a + case t of + 0 -> do + -- Nodes + assert ("VVV Empty non-root nodes") $ dataSize a > 0 + -- Recurse to find the first hash. Really we want all + -- hashes to match, but this aproximates. + readNodesData a 0 >>= findHash + 1 -> readHashP a + +validateLevel :: (Show e, Element e, Eq (ElementKey e), Key (ElementKey e)) => Level.Level -> WordArray e -> STM () +validateLevel l a = do + t <- readTagP a + case t of + 0 -> do + -- Nodes + b <- readIndicesP a + let sz = Indices.size b + +-- We abuse this at the moment by over allocating for cache alignment +-- assert ("VVV PopCount dataSize mismatch", sz, dataSize a) $ sz == dataSize a + + forM_ [0..sz-1] $ \i -> do + a' <- readNodesData a i + h <- findHash a' + + let h' = Level.hashIndex l h + i' = Indices.position h' b + + assert ("VVV First hash does not match.", i, i', h', h, b, l) $ i == i' + + validateLevel (Level.succ l) a' + + 1 -> do + -- leaf + h <- readHashP a + + let sz = dataSize a + assert ("VVV Empty Leaf!", h) $ sz > 0 + + -- check that keys and hashes match, keys do not match. + es <- forM [0..sz-1] $ readData a + let ks = map elementKey es + + assert ("VVV Keys to not all hash same", es, map hash ks) $ nub (map hash ks) == [h] + assert ("VVV Some keys match!", es) $ nub ks == ks + + +validate :: (Show k, Show v, Key k) => Map k v -> STM () +validate (Map m) = readTVar m >>= validateLevel 0 + +showSTM :: (Show k, Show v, Key k) => Map k v -> STM String +showSTM (Map m) = do + a <- readTVar m + unlines <$> showLevel a diff --git a/benchmarks/PPoPP2019/src/Main.hs b/benchmarks/PPoPP2019/src/Main.hs new file mode 100644 index 0000000..7bc6587 --- /dev/null +++ b/benchmarks/PPoPP2019/src/Main.hs @@ -0,0 +1,212 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} +module Main where + +import Control.Applicative +import Control.Monad + +import GHC.Conc.Sync +import Control.Concurrent + +import Data.Maybe +import Data.Word + +#ifdef RBTREE +import RBTree +#elif HAMT +import Hamt +#elif TREAP +import Treap +#else +#error Unknown Variant for Import +#endif + +import Throughput +import System.Random.PCG.Fast.Pure + +import System.Console.GetOpt +import System.Environment + +import GHC.Prim (resetSTMStats#) +import GHC.Types + +type RGen = GenIO + +initGens :: Int -> IO [RGen] +initGens threads = mapM initialize (map fromIntegral [1..threads]) + +samples :: Word -> Word -> RGen -> IO ((Word,Word), RGen) +samples sampleMax total g = do + r <- uniformB sampleMax g + v <- uniformB (total - 2) g + return ((r, v+1), g) + +type BenchTree = Tree +#define VALUE 0 +#define ATOMIC atomically + +data BenchOpts = BenchOpts + { _entries :: Word + , _threads :: Int + , _initOnly :: Bool + , _withoutTM :: Bool + , _mix :: Double + , _throughput :: Int + } deriving (Show) + +benchDefs :: BenchOpts +benchDefs = BenchOpts + { _entries = 800 + , _threads = 8 + , _initOnly = False + , _withoutTM = False + , _mix = 90 + , _throughput = 1000 + } + +benchOpts :: [OptDescr (BenchOpts -> BenchOpts)] +benchOpts = + [ Option ['e'] ["entries"] + (ReqArg (\v o -> o { _entries = read v}) "ENTRIES") "Number of values in the tree" + , Option ['t'] ["threads"] + (ReqArg (\v o -> o { _threads = read v}) "THREADS") "Number of threads" + , Option ['i'] ["initOnly"] + (NoArg (\o -> o { _initOnly = True })) "Initialize only" + , Option ['w'] ["withoutTM"] + (NoArg (\o -> o { _withoutTM = True })) "No transactions" + , Option ['m'] ["mix"] + (ReqArg (\v o -> o { _mix = read v}) "MIX") "Read mix percent" + , Option ['s'] ["throughput"] + (ReqArg (\v o -> o { _throughput = read v}) "TIME") + "Throughput runtime in milliseconds" + ] + +getBenchOpts = do + prog <- getProgName + let header = "Usage: " ++ prog + argv <- getArgs + case getOpt Permute benchOpts argv of + (o, n, []) -> return (foldl (flip id) benchDefs o) + (_, _, es) -> ioError (userError (concat es ++ usageInfo header benchOpts)) + +runRSTMEmpty :: CountIO -> RGen -> BenchTree -> Word -> Double -> IO () +runRSTMEmpty count g t total readRate = go g + where + insertRate = ((100 - readRate) / 2) + readRate + sampleMax = 100000 :: Word + + toPercent :: Word -> Double + toPercent r = fromIntegral r * 100 / fromIntegral sampleMax + + go g = do + (!(toPercent -> !r,!v),!g') <- samples sampleMax total g + case () of + () | r <= readRate -> ATOMIC (doNothing t v) + | r <= insertRate -> ATOMIC (doNothing t v) + | otherwise -> ATOMIC (doNothing t v) + incCount count + go g' + {-# NOINLINE go #-} + + doNothing t 0 = return () + doNothing t _ = return () + + +runRSTMSingle :: CountIO -> RGen -> BenchTree -> Word -> Double -> IO () +runRSTMSingle count g t total readRate = go g + where + insertRate = ((100 - readRate) / 2) + readRate + sampleMax = 100000 :: Word + + readLevel :: Word + !readLevel = floor $ readRate / 100.0 * fromIntegral sampleMax + insertLevel :: Word + !insertLevel = floor $ insertRate / 100.0 * fromIntegral sampleMax + + toPercent :: Word -> Double + toPercent r = fromIntegral r * 100 / fromIntegral sampleMax + +#ifndef TESTCODE + go g = do + (!(!r,!v),!g') <- samples sampleMax total g + case () of + () | r <= readLevel -> ATOMIC (get t v >> return ()) + | r <= insertLevel -> ATOMIC (insert t v VALUE >> return ()) + | otherwise -> ATOMIC (delete t v >> return ()) + incCount count + go g' + {-# NOINLINE go #-} + +#else + go g = do + (!(!r,!v),!g') <- samples sampleMax total g + case () of + () | r <= readLevel -> ATOMIC (get t v >> return ()) + | r <= insertLevel -> ATOMIC (insert t v VALUE >> return ()) >> hasValue t v -- single-threaded sanity check + | otherwise -> ATOMIC (delete t v >> return ()) >> noValue t v + incCount count + go g' + {-# NOINLINE go #-} + +hasValue t v = do + x <- ATOMIC (get t v) + case x of + Just _ -> return () + Nothing -> print (v, "missing") + +noValue t v = do + x <- ATOMIC (get t v) + case x of + Nothing -> return () + _ -> print (v, "found") +#endif + +resetSTMStats :: IO () +resetSTMStats = IO $ \s# -> case resetSTMStats# s# of s'# -> (# s'#, () #) + +main :: IO () +main = do + prog <- getProgName + opts <- getBenchOpts + + setNumCapabilities (_threads opts) + + let !e = _entries opts + !m = _mix opts + !s = _throughput opts + + (g':gs) <- initGens (_threads opts + 1) + + ((_,v'), g'') <- samples 100000 e g' + ((_,v''),_ ) <- samples 100000 e g'' + + when (v' == v'') $ putStrLn $ "v' == v'' (" ++ show v' ++ ")" + + t <- ATOMIC mkTree + forM_ [0,2..e] $ \a -> ATOMIC $ insert t a VALUE + + unless (_initOnly opts) $ resetSTMStats + + cs <- replicateM (_threads opts) $ newCount 0 + + unless (_initOnly opts) $ do + -- loop forever, stopping after s milliseconds. + (t,ta) <- case () of + () | _withoutTM opts -> + throughputMain (s * 1000) (zipWith (\c g -> runRSTMEmpty c g t e m) cs gs) + | otherwise -> + throughputMain (s * 1000) (zipWith (\c g -> runRSTMSingle c g t e m) cs gs) + trans <- sum <$> forM cs readCount + putStrLn $ unwords [ "benchdata:" + , "run-time" , show t + , "no-kill-time", show ta + , "transactions", show trans + , "prog" , prog + , "threads" , show (_threads opts) + , "entries" , show e + , "code" , benchCode + ] diff --git a/benchmarks/PPoPP2019/src/Opts.hs b/benchmarks/PPoPP2019/src/Opts.hs new file mode 100644 index 0000000..6a0fddc --- /dev/null +++ b/benchmarks/PPoPP2019/src/Opts.hs @@ -0,0 +1,54 @@ +import System.Console.GetOpt +import System.Environment +import Data.Maybe (fromMaybe) + +data BenchOpts = BenchOpts + { _entries :: Word + , _threads :: Int + , _initOnly :: Bool + , _withoutTM :: Bool + , _mix :: Double + , _throughput :: Int + } deriving (Show) + +benchDefs :: BenchOpts +benchDefs = BenchOpts + { _entries = 800 + , _threads = 8 + , _initOnly = False + , _withoutTM = False + , _mix = 90 + , _throughput = 1000 + } + +benchOpts :: [OptDescr (BenchOpts -> BenchOpts)] +benchOpts = + [ Option ['e'] ["entries"] + (ReqArg (\v o -> o { _entries = read v}) "ENTRIES") "Number of values in the tree" + , Option ['t'] ["threads"] + (ReqArg (\v o -> o { _threads = read v}) "THREADS") "Number of threads" + , Option ['i'] ["initOnly"] + (NoArg (\o -> o { _initOnly = True })) "Initialize only" + , Option ['w'] ["withoutTM"] + (NoArg (\o -> o { _withoutTM = True })) "No transactions" + , Option ['m'] ["mix"] + (ReqArg (\v o -> o { _mix = read v}) "MIX") "Read mix percent" + , Option ['s'] ["throughput"] + (ReqArg (\v o -> o { _throughput = read v}) "TIME") + "Throughput runtime in milliseconds" + ] + +readOpt f v = read v + +getBenchOpts = do + prog <- getProgName + let header = "Usage: " ++ prog + argv <- getArgs + case getOpt Permute benchOpts argv of + (o, n, []) -> return (foldl (flip id) benchDefs o, n) + (_, _, es) -> ioError (userError (concat es ++ usageInfo header benchOpts)) + +main = do + as <- getBenchOpts + + print as diff --git a/benchmarks/PPoPP2019/src/RBTree.hs b/benchmarks/PPoPP2019/src/RBTree.hs new file mode 100644 index 0000000..11600d7 --- /dev/null +++ b/benchmarks/PPoPP2019/src/RBTree.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE CPP #-} +module RBTree + ( Tree + , mkTree + + , insert + , delete + , update + , get + , contains + + , benchCode + ) where + +import GHC.Conc.Sync +import Data.Word + +#if defined(TSTRUCT) +import RBTreeTStruct +type Tree = RBTree +#elif defined(MUT) +import RBTreeMutUSTM +type Tree = RBTree Word Word +#elif defined(TVAR) +import RBTreeTVar +type Tree = RBTree Word Word +#else +#error Unknown RBTree Variant +#endif + + +mkTree :: STM Tree +mkTree = mkRBTree diff --git a/benchmarks/PPoPP2019/src/RBTreeMutUSTM.hs b/benchmarks/PPoPP2019/src/RBTreeMutUSTM.hs new file mode 100644 index 0000000..b9d875a --- /dev/null +++ b/benchmarks/PPoPP2019/src/RBTreeMutUSTM.hs @@ -0,0 +1,668 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE MutableFields #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} +module RBTreeMutUSTM + ( RBTree + , mkRBTree + + , insert + , delete + , update + , get + , contains + + , benchCode +#ifdef TESTCODE + , testMain + + , verify +#endif + ) where + +import Prelude hiding (lookup) + +import Control.Applicative +import Control.Monad +import Control.Exception + +import Data.List (sort,inits) +import Debug.Trace + +import GHC.Types +import GHC.Prim +import GHC.IO +import GHC.Int +import GHC.Conc + +benchCode :: String +benchCode = "RBTreeMutUSTM" + +-- data Node k v where +-- Node :: { key :: !k +-- , value :: !v +-- , parent :: mutable (Node k v) +-- , left :: mutable (Node k v) +-- , right :: mutable (Node k v) +-- , color :: mutable Color +-- } :: STM (Node k v) +-- Nil :: Node k v +-- TODO: record syntax. + +type TRef a = Ref# RealWorld a +type TRefInt = RefU# RealWorld Int# + +readTRef :: TRef a -> STM a +readTRef r = STM (\s# -> readTRef# r s#) + +writeTRef :: TRef a -> a -> STM () +writeTRef r l = STM (\s1# -> case writeTRef# r l s1# of + s2# -> (# s2#, () #)) + +readTRefInt :: TRefInt -> STM Int +readTRefInt r = STM (\s# -> case readTRefInt# r s# of + (# s2#, i# #) -> (# s2#, I# i# #)) + +writeTRefInt :: TRefInt -> Int -> STM () +writeTRefInt r (I# i#) = STM (\s1# -> case writeTRefInt# r i# s1# of + s2# -> (# s2#, () #)) + + +data Node k v where + Node :: !k + -> !v + -> mutable (Node k v) + -> mutable (Node k v) + -> mutable (Node k v) + -> mutable Int# + -> STM (Node k v) + Nil :: Node k v + + +key :: Node k v -> k +key (Node k _ _ _ _ _) = k + +value :: Node k v -> v +value (Node _ v _ _ _ _) = v + +parent :: Node k v -> TRef (Node k v) +parent (Node _ _ p _ _ _) = p + +left :: Node k v -> TRef (Node k v) +left (Node _ _ _ l _ _) = l + +right :: Node k v -> TRef (Node k v) +right (Node _ _ _ _ r _) = r + +color :: Node k v -> TRefInt +color (Node _ _ _ _ _ c) = c + +rED :: Int +rED = 0 + +bLACK :: Int +bLACK = 1 + +instance (Show k, Show v) => Show (Node k v) where + show Nil = "Nil" + show (Node k v _ _ _ _) = show ("Node " ++ show k ++ " " ++ show v) + +instance Eq k => Eq (Node k v) where + Nil == Nil = True + Nil == _ = False + _ == Nil = False + +-- x == y = case reallyUnsafePtrEquality# x y of +-- 0# -> False +-- _ -> True +-- TODO: This caused a problem in unarize where it found a mutable +-- UnaryRep in the alt. + (Node k _ _ _ _ _) == (Node k' _ _ _ _ _) = k == k' + +isNil :: Node k v -> Bool +isNil Nil = True +isNil _ = False + +isNode = not . isNil + +-- data RBTree k v where +-- { root :: mutable (Node k v) } :: STM (RBTree k v) + +data RBTree k v where + RBTree :: mutable (Node k v) -> STM (RBTree k v) + +root :: RBTree k v -> TRef (Node k v) +root (RBTree n) = n + +lookupNode :: Ord k => k -> Node k v -> STM (Node k v) +lookupNode _ Nil = return Nil +lookupNode s n@(Node k v _ tl tr _) + = case compare s k of + EQ -> return n + LT -> readTRef tl >>= lookupNode s + GT -> readTRef tr >>= lookupNode s + +lookup :: Ord k => k -> RBTree k v -> STM (Node k v) +lookup k t = readTRef (root t) >>= lookupNode k + +rotateLeft :: (Eq v, Eq k) => RBTree k v -> Node k v -> STM () +rotateLeft s x = do + r <- readTRef (right x) + rl <- readTRef (left r) + writeTRef (right x) rl + when (isNode rl) $ writeTRef (parent rl) x + + xp <- readTRef (parent x) + writeTRef (parent r) xp + + if isNil xp + then writeTRef (root s) r + else do + xpl <- readTRef (left xp) + if xpl == x + then writeTRef (left xp) r + else writeTRef (right xp) r + + writeTRef (left r) x + writeTRef (parent x) r + + +rotateRight :: (Eq v, Eq k) => RBTree k v -> Node k v -> STM () +rotateRight s x = do + l <- readTRef (left x) + lr <- readTRef (right l) + writeTRef (left x) lr + when (isNode lr) $ writeTRef (parent lr) x + + xp <- readTRef (parent x) + writeTRef (parent l) xp + + if isNil xp + then writeTRef (root s) l + else do + xpr <- readTRef (right xp) + if xpr == x + then writeTRef (right xp) l + else writeTRef (left xp) l + + writeTRef (right l) x + writeTRef (parent x) l + +setField :: (Node k v -> TRef a) -> a -> Node k v -> STM () +setField _ _ Nil = return () +setField f v x = writeTRef (f x) v + +setColor :: Int -> Node k v -> STM () +setColor _ Nil = return () +setColor c n = writeTRefInt (color n) c + +-- TODO: This complains about matching kinds... +-- node _ Nil = return Nil +-- node f x = f x +-- +-- parentOf, leftOf, rightOf :: Node k v -> STM (Node k v) +-- parentOf = node (readTRef . parent) +-- leftOf = node (readTRef . left) +-- rightOf = node (readTRef . right) + + +readIfNode :: (Node k v -> TRef (Node k v)) -> Node k v -> STM (Node k v) +readIfNode _ Nil = return Nil +readIfNode f n = readTRef (f n) + +parentOf, leftOf, rightOf :: Node k v -> STM (Node k v) +parentOf = readIfNode parent +leftOf = readIfNode left +rightOf = readIfNode right + +colorOf Nil = return bLACK +colorOf x = readTRefInt (color x) + +isLeftBranch :: (Eq k, Eq v) => Node k v -> STM Bool +isLeftBranch x = do + x' <- parentOf x >>= leftOf + return $ x == x' + +fixAfterInsertion :: (Eq k, Eq v) => RBTree k v -> Node k v -> STM () +fixAfterInsertion _ Nil = return () +fixAfterInsertion s x = do + setColor rED x + + loop x + + ro <- readTRef (root s) + c <- readTRefInt (color ro) + when (c /= bLACK ) $ writeTRefInt (color ro) bLACK + where + loop Nil = return () + loop x = do + sr <- readTRef (root s) + if sr == x + then return () + else body x >>= loop + + body x = do + xp <- readTRef (parent x) + if isNil xp + then return Nil + else do + xpp <- parentOf xp + xppl <- leftOf xpp + + c <- readTRefInt (color xp) + + if c /= rED + then return Nil + else do + if xp == xppl + then handle x xp xpp rightOf rotateLeft rotateRight + else handle x xp xpp leftOf rotateRight rotateLeft + + handle x xp xpp f ra rb = do + y <- f xpp + c <- colorOf y + if c == rED + then do + setColor bLACK xp + setColor bLACK y + setColor rED xpp + return xpp + else do + z <- f xp + (x',xp',xpp') <- if x == z + then do + ra s xp + xp' <- parentOf xp + xpp' <- parentOf xp' + return (xp,xp',xpp') + else return (x,xp,xpp) + setColor bLACK xp' + setColor rED xpp' + when (isNode xpp') $ rb s xpp' + return x' + +-- Note: we differ here in not taking a node argument of an exiting +-- allocated node. The behavior when that argument is NULL in the original +-- code is to have insert act like find. +insert' :: (Eq v, Ord k) => RBTree k v -> k -> v -> STM (Node k v) +insert' s k v = do + t <- readTRef (root s) + if isNil t + then do + n <- Node k v Nil Nil Nil 1# {- bLACK -} + writeTRef (root s) n + return Nil + else loop t + where + loop t = case compare k (key t) of + EQ -> return t + LT -> handle t left + GT -> handle t right + + handle t f = do + tc <- readTRef (f t) + if isNode tc + then loop tc + else do + n <- Node k v t Nil Nil 1# {- bLACK -} + writeTRef (f t) n + fixAfterInsertion s n + return Nil + +successor :: (Eq k, Eq v) => Node k v -> STM (Node k v) +successor Nil = return Nil +successor t = do + r <- readTRef (right t) + if isNode r + then leftMost r + else readTRef (parent t) >>= rightParent t + where + leftMost p = do + l <- readTRef (left p) + case l of + Nil -> return p + _ -> leftMost l + + rightParent _ Nil = return Nil + rightParent c p = do -- Find the first parent further right + r <- readTRef (right p) + if r == c + then readTRef (parent p) >>= rightParent p + else return p + + +fixAfterDeletion :: (Eq k, Eq v) => RBTree k v -> Node k v -> STM () +fixAfterDeletion tree x = do + x' <- loop x + when (isNode x') $ do + c <- readTRefInt (color x') + when (c /= bLACK ) $ writeTRefInt (color x') bLACK + where + loop x = do + r <- readTRef (root tree) + case () of + () | x == r -> return x + | otherwise -> do + c <- colorOf x + if c /= bLACK + then return x + else body x >>= loop + + body x = do + b <- isLeftBranch x + if b + then handle x rightOf leftOf rotateRight rotateLeft + else handle x leftOf rightOf rotateLeft rotateRight + + handle x fR fL rotateR rotateL = do + s <- parentOf x >>= fR + c <- colorOf s + s' <- if c == rED + then do + setColor bLACK s + parentOf x >>= setColor rED + parentOf x >>= rotateL tree + parentOf x >>= fR + else return s + cl <- fL s' >>= colorOf + cr <- fR s' >>= colorOf + if cl == bLACK && cr == bLACK + then setColor rED s' >> parentOf x + else do + s'' <- if cr == bLACK + then do + fL s' >>= setColor bLACK + setColor rED s' + rotateR tree s' + parentOf x >>= fR + else return s' + p <- parentOf x + colorOf p >>= (`setColor` s'') + setColor bLACK p + fR s'' >>= setColor bLACK + rotateL tree p + readTRef (root tree) + +-- link in a new node replacing the given node with new +-- key and value. +replace :: (Eq k, Eq v) => k -> v -> RBTree k v -> Node k v -> STM (Node k v) +replace k v t n@(Node _ _ rp rl rr rc) = do + p <- readTRef rp + l <- readTRef rl + r <- readTRef rr + (I# c) <- readTRefInt rc + n' <- Node k v p l r c + -- update the rest of the tree: + b <- isLeftBranch n + setField parent n' l -- Set left's parent to n' + setField parent n' r -- set right's parent to n' + if isNil p + then writeTRef (root t) n' + else setField (if b then left else right) n' p -- set parent's left or right to n' + return n' + +deleteNode :: (Eq k, Eq v) => RBTree k v -> Node k v -> STM (Node k v) +deleteNode s p = do + l <- readTRef (left p) + r <- readTRef (right p) + p' <- if isNode l && isNode r + then do + suc <- successor p + replace (key suc) (value suc) s p + return suc + else return p + + l' <- readTRef (left p') + rep <- if isNode l' + then return l' + else readTRef (right p') + pp <- readTRef (parent p') + if isNode rep + then do + writeTRef (parent rep) pp + if isNil pp + then writeTRef (root s) rep + else do + ppl <- readTRef (left pp) + if p' == ppl + then writeTRef (left pp) rep + else writeTRef (right pp) rep + writeTRef (left p') Nil + writeTRef (right p') Nil + writeTRef (parent p') Nil + c <- readTRefInt (color p') + when (c == bLACK ) $ fixAfterDeletion s rep + else if isNil pp + then writeTRef (root s) Nil + else do + c <- readTRefInt (color p') + when (c == bLACK ) $ fixAfterDeletion s p' + pp' <- readTRef (parent p') + when (isNode pp') $ do + ppl <- readTRef (left pp') + if p' == ppl + then writeTRef (left pp') Nil + else do + ppr <- readTRef (right pp') + when (p' == ppr) $ writeTRef (right pp') Nil + writeTRef (parent p') Nil + return p' + +---------------------------------- +-- Public API +-- +mkRBTree :: STM (RBTree k v) +mkRBTree = RBTree Nil + +-- insert :: (Eq v, Ord k) => RBTree k v -> k -> v -> STM Bool +insert t k v = isNil <$> insert' t k v <* postVerify t + +preVerify _ = return () +postVerify _ = return () +-- Comment out the type signatures for insert and delete for verify code. +-- preVerify = verify' +-- postVerify = verify' + +-- delete :: (Eq v, Ord k) => RBTree k v -> k -> STM Bool +delete t k = do + n <- lookup k t + if isNode n + then isNode <$> (preVerify t *> deleteNode t n <* postVerify t) + else return False + +update :: (Eq v, Ord k) => RBTree k v -> k -> v -> STM Bool +update t k v = do + n <- insert' t k v + case n of + Node _ v' _ _ _ _ -> do + when (v /= v') $ replace k v t n >> return () + return True + Nil -> return False + + +get :: Ord k => RBTree k v -> k -> STM (Maybe v) +get t k = do + n <- lookup k t + case n of + Node _ v _ _ _ _ -> return (Just v) + Nil -> return Nothing + +contains :: Ord k => RBTree k v -> k -> STM Bool +contains t k = isNode <$> lookup k t + +---------------------------------------------------- +-- Test code +-- +#ifdef TESTCODE + +unlessM bm a = do + b <- bm + unless b a + +verifyRedBlack :: (Show k, Show v, Eq k, Eq v) => Node k v -> Int -> STM Int +verifyRedBlack Nil _ = return 1 +verifyRedBlack n d = do + l <- readTRef (left n) + r <- readTRef (right n) + c <- readTRefInt (color n) + + hl <- verifyRedBlack l (d + 1) + hr <- verifyRedBlack r (d + 1) + if hl == 0 || hr == 0 + then return 0 + else do + when (hl /= hr) $ error ("Imbalance @depth=" ++ show d ++ " : " ++ show hl ++ " " ++ show hr) + lineage l + lineage r + + c <- readTRefInt (color n) + if c == rED + then do + unlessM (isBlack l) $ error ("Expected black left of " ++ show (key n)) + unlessM (isBlack r) $ error ("Expected black right of " ++ show (key n)) + return hl + else return (hl + 1) + where + lineage Nil = return () + lineage c = do + p <- readTRef (parent c) + when (p /= n) $ error ("lineage") + + isBlack Nil = return True + isBlack n = readTRefInt (color n) >>= \c -> return (c == bLACK ) + +assertEq s t v = readTRef t >>= \v' -> when (v /= v') $ error s +assertIntEq s t v = readTRefInt t >>= \v' -> when (v /= v') $ error s + +{- +inOrder :: Show k => Node k v -> STM [k] +inOrder Nil = trace "." $ return [] +inOrder n = do + l <- readTRef (left n) + ol <- trace "(" $ inOrder l + + c <- readTRefInt (color n) + r <- trace (if c == rED then "r" else "b") $ readTRef (right n) + -- r <- trace (show (key n)) $ readTRef (right n) + or <- inOrder r + trace ")" $ return $ ol ++ [key n] ++ or +-- -} +{--} +inOrder :: Show k => Node k v -> STM [k] +inOrder Nil = return [] +inOrder n = do + l <- readTRef (left n) + ol <- inOrder l + r <- readTRef (right n) + or <- inOrder r + return $ ol ++ [key n] ++ or +-- -} +verifyOrder :: (Ord k, Show k) => Node k v -> STM () +verifyOrder Nil = return () +verifyOrder n = do + es <- inOrder n + unless (sort es == es) $ error "Ordering." + +verifyLinks Nil Nil _ = return () +verifyLinks Nil n as = do + l <- readTRef (left n) + r <- readTRef (right n) + +-- case l of +-- Node k v _ _ _ _ -> print ("l: Node", k, v) +-- Nil -> print "l: Nil" + +-- case r of +-- Node k v _ _ _ _ -> print ("r: Node", k, v) +-- Nil -> print "r: Nil" + + assertM "left loop" $ return (l `notElem` as) + assertM "right loop" $ return (r `notElem` as) + + when (isNode l) $ verifyLinks n l (l:as) + when (isNode r) $ verifyLinks n r (r:as) +verifyLinks p Nil _ = return () +verifyLinks p n as = do + p' <- readTRef (parent n) + if p /= p' + then print (p, p') + else return () + assertM "parent mismatch" $ return (p == p') + verifyLinks Nil n as + +verify' t = do + r <- readTRef (root t) + verifyLinks Nil r [] + +verify :: (Eq k, Eq v, Ord k, Show v, Show k) => RBTree k v -> STM Int +verify t = do + r <- readTRef (root t) + if isNil r + then print ("root is Nil") >> return 1 + else do + +-- case r of +-- Node k v _ _ _ _ -> print ("Node", k, v) +-- Nil -> print "Nil" + + assertEq ("Root parent not Nil " ++ show (key r)) (parent r) Nil + assertIntEq ("Root color not Black " ++ show (key r)) (color r) bLACK + + verifyLinks Nil r ([]) + verifyOrder r + verifyRedBlack r 0 + +assertM s v = do + b <- v + unless b $ error s + +insertTest :: [Int] -> STM Int +insertTest as = do + r <- mkRBTree + forM_ as $ \a -> do + -- print ("insert", a) + assertM "Insert failed" $ insert r a a + verify r + verify r + +deleteTest :: [Int] -> [Int] -> STM () +deleteTest as bs = do + r <- mkRBTree + forM_ as $ \a -> do + assertM "insert failed" $ insert r a a + verify r + forM_ bs $ \b -> do + assertM "delete failed" $ delete r b + verify r + +testMain' = do + insertTest [4,6,9,1,8,7,2] + +testMain'' = do + forM_ (inits [4,6,9,1,8,7,2,3,0,5]) $ \as -> do + putStrLn $ "Inserting " ++ show as + insertTest as + +testMain''' = do + forM_ (zip (inits [4,6,9,1,8,7,2,3,0,5]) (map reverse (inits [4,6,9,1,8,7,2,3,0,5]))) $ \(as,bs) -> do + putStrLn $ "Inserting " ++ show as ++ " deleting " ++ show bs + insertTest as + +testMain = do + let as = [92,86,34,84, 5,64, 1, 87,11,39 + ,17,15,13,66,63,38,69, 67,88,16 + , 9,95,31,96,19,33,21, 27,65,10 + ,23,32,80,41,36,14,37, 54,98,51 + ,55,45,43,97,61,60, 2, 12,49,85 + , 8,76,46,78,48,56,35,100,29,90 + ,99,70,73,52,81,20, 3, 68,22,83 + ,71,72, 4,74,47,94,77, 89,59,91 + ,93,75,25,50, 6,58, 7, 62,53,40 + ,26,24,42,30,18,28,79, 82,44,57 + ] + forM_ (inits as) $ \bs -> do + putStrLn $ "Inserting " ++ show bs + insertTest bs +#endif diff --git a/benchmarks/PPoPP2019/src/RBTreeNode.hs b/benchmarks/PPoPP2019/src/RBTreeNode.hs new file mode 100644 index 0000000..3daaf82 --- /dev/null +++ b/benchmarks/PPoPP2019/src/RBTreeNode.hs @@ -0,0 +1,283 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} +module RBTreeNode + ( Node(..) + , Key + , Value + , Color(..) + , writeKey + , writeColor + , writeValue + , writeParent + , writeLeft + , writeRight + + , writeKeyP + , writeColorP + , writeValueP + , writeParentP + , writeLeftP + , writeRightP + + , parent + , left + , right + , key + , value + , color + , mkNode + , nil + + , lookupNode' + ) where + +import GHC.Num +import GHC.ST +import GHC.Base +import GHC.Conc +import GHC.Word +import GHC.Prim + +import System.IO.Unsafe (unsafePerformIO) + +#define KEY 0 +#define VALUE 1 +#define COLOR 2 + +-- #define SEPARATE_POINTERS +#ifdef SEPARATE_POINTERS +#define PARENT 0 +#define LEFT 8 +#define RIGHT 16 +#define PTRS 24 +#define WORDS 3 +#define PTRSZH 24# +#define WORDSZH 3# +#else +#define PARENT 0 +#define LEFT 1 +#define RIGHT 2 +#define PTRS 3 +#define WORDS 3 +#define PTRSZH 3# +#define WORDSZH 3# +#endif + +type Key = Word +type Value = Word + +-- data Node = Node {-# UNPACK #-} !(STMMutableArray# RealWorld Node) | Nil +data Node = Node { unNode :: !(STMMutableArray# RealWorld Any) } + +{-# NOINLINE nil' #-} +nil' :: Node +nil' = unsafePerformIO $ do + !n <- rawNil + writePIO n LEFT n + writePIO n RIGHT n + writePIO n PARENT n + return n + where + writePIO marr (W# w#) (Node !a) = IO $ \s# -> + case writeSTMArray# (unNode marr) (word2Int# w#) (unsafeCoerce# a) s# of + s2# -> (# s2#, () #) + + rawNil = IO $ \s1# -> + case newSTMArray# PTRSZH WORDSZH undefined s1# of + (# s2#, marr# #) -> (# s2#, Node marr# #) + +{-# NOINLINE nil #-} +nil :: Node +nil = unsafePerformIO $ rawNil + where + rawNil = IO $ \s1# -> + case newSTMArray# PTRSZH WORDSZH undefined s1# of + (# s2#, marr# #) -> + case writeSTMArray# marr# 0# (unsafeCoerce# marr#) s2# of + s3# -> + case writeSTMArray# marr# 1# (unsafeCoerce# marr#) s3# of + s4# -> + case writeSTMArray# marr# 2# (unsafeCoerce# marr#) s4# of + s5# -> (# s5#, Node marr# #) + +lookupNode' :: Key -> Node -> STM Node +lookupNode' (W# k#) !(Node !a#) = STM $ \s# -> go a# s# + where + !(Node !nil#) = nil + + go a# s1# = + case sameSTMMutableArray# a# nil# of + 1# -> (# s1#, nil #) + _ -> case readTArrayWord# a# 0## s1# of + (# s2#, w# #) -> + case k# `eqWord#` w# of + 1# -> (# s2#, Node a# #) + _ -> case k# `ltWord#` w# of + 1# -> case readTArray# a# 1## s2# of + (# s3#, a #) -> go (unsafeCoerce# a) s3# + _ -> case readTArray# a# 2## s2# of + (# s3#, a #) -> go (unsafeCoerce# a) s3# + + +data Color = Red | Black + deriving (Eq, Show, Read) + +newNode :: Int -> Int -> Node -> STM Node +newNode (I# ptrs#) (I# words#) (Node !a) = STM $ \s1# -> + case newSTMArray# ptrs# words# (unsafeCoerce# a) s1# of + (# s2#, marr# #) -> (# s2#, Node marr# #) +{-# INLINE newNode #-} + +unsafeReadNode :: Node -> Word -> STM Node +unsafeReadNode marr (W# w#) = STM $ \s# -> + case readTArray# (unNode marr) w# s# of + (# s2#, a #) -> (# s2#, Node (unsafeCoerce# a) #) +{-# INLINE unsafeReadNode #-} + +unsafeWriteNode :: Node -> Word -> Node -> STM () +unsafeWriteNode marr (W# w#) (Node !a) = STM $ \s# -> + case writeTArray# (unNode marr) w# (unsafeCoerce# a) s# of + s2# -> (# s2#, () #) +{-# INLINE unsafeWriteNode #-} + +unsafeWriteNodeP :: Node -> Word -> Node -> STM () +unsafeWriteNodeP marr (W# w#) (Node !a) = STM $ \s# -> + case writeSTMArray# (unNode marr) (word2Int# w#) (unsafeCoerce# a) s# of + s2# -> (# s2#, () #) +{-# INLINE unsafeWriteNodeP #-} + +unsafeReadNodeWord :: Node -> Word -> STM Word +unsafeReadNodeWord marr (W# wi#) = STM $ \s# -> + case readTArrayWord# (unNode marr) wi# s# of + (# s2#, w# #) -> (# s2#, W# w# #) +{-# INLINE unsafeReadNodeWord #-} + +unsafeWriteNodeWord :: Node -> Word -> Word -> STM () +unsafeWriteNodeWord marr (W# wi#) (W# w#) = STM $ \s# -> + case writeTArrayWord# (unNode marr) wi# w# s# of + s2# -> (# s2#, () #) +{-# INLINE unsafeWriteNodeWord #-} + +unsafeWriteNodeWordP :: Node -> Word -> Word -> STM () +unsafeWriteNodeWordP marr (W# wi#) (W# w#) = STM $ \s# -> + case writeSTMArrayWord# (unNode marr) (word2Int# wi#) w# s# of + s2# -> (# s2#, () #) +{-# INLINE unsafeWriteNodeWordP #-} + +lengthNode :: Node -> Int +lengthNode marr = I# (sizeofSTMMutableArray# (unNode marr)) +{-# INLINE lengthNode #-} + +lengthNodeWords :: Node -> Int +lengthNodeWords marr = I# (sizeofSTMMutableArrayWords# (unNode marr)) +{-# INLINE lengthNodeWords #-} + +instance Eq Node where + (Node t) == (Node t') = + case sameSTMMutableArray# t t' of + 0# -> False + _ -> True + + +writeKey :: Node -> Word -> STM () +writeKey s x = unsafeWriteNodeWord s KEY x +{-# INLINE writeKey #-} + +writeKeyP :: Node -> Word -> STM () +writeKeyP s x = unsafeWriteNodeWordP s KEY x +{-# INLINE writeKeyP #-} + +writeValue :: Node -> Word -> STM () +writeValue s x = unsafeWriteNodeWord s VALUE x +{-# INLINE writeValue #-} + +writeValueP :: Node -> Word -> STM () +writeValueP s x = unsafeWriteNodeWordP s VALUE x +{-# INLINE writeValueP #-} + +writeColor :: Node -> Color -> STM () +writeColor s Black = unsafeWriteNodeWord s COLOR 0 +writeColor s Red = unsafeWriteNodeWord s COLOR 1 +{-# INLINE writeColor #-} + +writeColorP :: Node -> Color -> STM () +writeColorP s Black = unsafeWriteNodeWordP s COLOR 0 +writeColorP s Red = unsafeWriteNodeWordP s COLOR 1 +{-# INLINE writeColorP #-} + +writeParent :: Node -> Node -> STM () +writeParent s x = unsafeWriteNode s PARENT x +{-# INLINE writeParent #-} + +writeLeft :: Node -> Node -> STM () +writeLeft s x = unsafeWriteNode s LEFT x +{-# INLINE writeLeft #-} + +writeRight :: Node -> Node -> STM () +writeRight s x = unsafeWriteNode s RIGHT x +{-# INLINE writeRight #-} + +writeParentP :: Node -> Node -> STM () +writeParentP s x = unsafeWriteNodeP s PARENT x +{-# INLINE writeParentP #-} + +writeLeftP :: Node -> Node -> STM () +writeLeftP s x = unsafeWriteNodeP s LEFT x +{-# INLINE writeLeftP #-} + +writeRightP :: Node -> Node -> STM () +writeRightP s x = unsafeWriteNodeP s RIGHT x +{-# INLINE writeRightP #-} + +mkNode :: Key -> Value -> Color -> STM Node +mkNode k v c = do + s <- newNode PTRS WORDS nil + -- We just made the node so it is private and we can access it + -- non-transactionally. + writeKeyP s k + writeValueP s v + writeColorP s c + return s +{-# INLINE mkNode #-} + +key :: Node -> STM Key +key s = unsafeReadNodeWord s KEY +{- For non-monomorphic keys we need to do something else for nil (which + - shouldn't be compaired anyway! So this should just go away... +key s + | s == nil = return 0 + | otherwise = unsafeReadNodeWord s KEY +-} +{-# INLINE key #-} + +value :: Node -> STM Value +value s = unsafeReadNodeWord s VALUE +{- See key +value s + | s == nil = return 0 + | otherwise = unsafeReadNodeWord s VALUE +-} +{-# INLINE value #-} + +color :: Node -> STM Color +color s = do + w <- unsafeReadNodeWord s COLOR + case w == (0 :: Word) of + True -> return Black + _ -> return Red +{-# INLINE color #-} + +parent :: Node -> STM Node +parent s = unsafeReadNode s PARENT +{-# INLINE parent #-} + +left :: Node -> STM Node +left s = unsafeReadNode s LEFT +{-# INLINE left #-} + +right :: Node -> STM Node +right s = unsafeReadNode s RIGHT +{-# INLINE right #-} + diff --git a/benchmarks/PPoPP2019/src/RBTreeTStruct.hs b/benchmarks/PPoPP2019/src/RBTreeTStruct.hs new file mode 100644 index 0000000..7609dd6 --- /dev/null +++ b/benchmarks/PPoPP2019/src/RBTreeTStruct.hs @@ -0,0 +1,554 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE CPP #-} +module RBTreeTStruct + ( RBTree + , mkRBTree + + , insert + , delete + , update + , get + , contains + + , benchCode +#ifdef TESTCODE + , testMain + + , verify +#endif + ) where + +import Prelude hiding (lookup) + +import GHC.Conc.Sync +import Control.Applicative +import Control.Monad +import Control.Exception + +import Data.List (sort,inits) +import Data.Word +import Debug.Trace + +import System.IO.Unsafe +import RBTreeNode + +import GHC.Conc(unsafeIOToSTM) + +benchCode :: String +benchCode = "RBTreeTStruct" + +isNil :: Node -> Bool +isNil s = s == nil + +isNode = not . isNil + +newtype RBTree = RBTree { root :: TVar Node } + +lookupNode :: Key -> Node -> STM Node +lookupNode k n + | n == nil = return nil + | otherwise = do + k' <- key n + case compare k k' of + EQ -> return n + LT -> left n >>= lookupNode k + GT -> right n >>= lookupNode k + +lookup :: Word -> RBTree -> STM Node +lookup k t = readTVar (root t) >>= lookupNode' k + +rotateLeft :: RBTree -> Node -> STM () +rotateLeft s x = do + r <- right x + rl <- left r + writeRight x rl + when (isNode rl) $ writeParent rl x + + xp <- parent x + writeParent r xp + + if isNil xp + then writeTVar (root s) r + else do + xpl <- left xp + if xpl == x + then writeLeft xp r + else writeRight xp r + + writeLeft r x + writeParent x r + + +rotateRight :: RBTree -> Node -> STM () +rotateRight s x = do + l <- left x + lr <- right l + writeLeft x lr + when (isNode lr) $ writeParent lr x + + xp <- parent x + writeParent l xp + + if isNil xp + then writeTVar (root s) l + else do + xpr <- right xp + if xpr == x + then writeRight xp l + else writeLeft xp l + + writeRight l x + writeParent x l + +setColor :: Color -> Node -> STM () +setColor c n = writeColor n c + +node f x + | x == nil = return nil + | otherwise = f x + +parentOf, leftOf, rightOf :: Node -> STM Node +parentOf = node parent +leftOf = node left +rightOf = node right + +colorOf x + | x == nil = return Black + | otherwise = color x + +isLeftBranch :: Node -> STM Bool +isLeftBranch x = do + x' <- parentOf x >>= leftOf + return $ x == x' + +fixAfterInsertion :: RBTree -> Node -> STM () +fixAfterInsertion s x + | x == nil = return () + | otherwise = do + setColor Red x + + loop x + + ro <- readTVar (root s) + c <- color ro + when (c /= Black) $ writeColor ro Black + where + loop x + | x == nil = return () + | otherwise = do + sr <- readTVar (root s) + if sr == x + then return () + else body x >>= loop + + body x = do + xp <- parent x + if isNil xp + then return nil + else do + xpp <- parentOf xp + xppl <- leftOf xpp + + c <- color xp + + if c /= Red + then return nil + else do + if xp == xppl + then handle x xp xpp rightOf rotateLeft rotateRight + else handle x xp xpp leftOf rotateRight rotateLeft + + handle x xp xpp f ra rb = do + y <- f xpp + c <- colorOf y + if c == Red + then do + setColor Black xp + setColor Black y + setColor Red xpp + return xpp + else do + z <- f xp + (x',xp',xpp') <- if x == z + then do + ra s xp + xp' <- parentOf xp + xpp' <- parentOf xp' + return (xp,xp',xpp') + else return (x,xp,xpp) + setColor Black xp' + setColor Red xpp' + when (isNode xpp') $ rb s xpp' + return x' + + +-- Note: we differ here in not taking a node argument of an exiting +-- allocated node. The behavior when that argument is NULL in the original +-- code is to have insert act like find. +insert' :: RBTree -> Key -> Value -> STM Node +insert' s k v = do + t <- readTVar (root s) + if isNil t + then do + n <- mkNode k v Black + writeTVar (root s) n + return nil + else loop t + where + loop t = key t >>= \k' -> case compare k k' of + EQ -> return t + LT -> handle t left writeLeft + GT -> handle t right writeRight + + handle t get set = do + tc <- get t + if isNode tc + then loop tc + else do + n <- mkNode k v Black + writeParentP n t -- non-transactional access + set t n + fixAfterInsertion s n + return nil + +successor :: Node -> STM Node +successor t + | t == nil = return nil + | otherwise = do + r <- right t + if isNode r + then leftMost r + else parent t >>= rightParent t + where + leftMost p = do + l <- left p + if isNil l + then return p + else leftMost l + + rightParent c p + | p == nil = return nil + | otherwise = do -- Find the first parent further right + r <- right p + if r == c + then parent p >>= rightParent p + else return p + +fixAfterDeletion :: RBTree -> Node -> STM () +fixAfterDeletion tree x = do + x' <- loop x + when (isNode x') $ do + c <- color x' + when (c /= Black) $ writeColor x' Black + where + loop x = do + r <- readTVar (root tree) + case () of + () | x == r -> return x + | otherwise -> do + c <- colorOf x + if c /= Black + then return x + else body x >>= loop + + body x = do + b <- isLeftBranch x + if b + then handle x rightOf leftOf rotateRight rotateLeft + else handle x leftOf rightOf rotateLeft rotateRight + + handle x fR fL rotateR rotateL = do + s <- parentOf x >>= fR + c <- colorOf s + s' <- if c == Red + then do + setColor Black s + parentOf x >>= setColor Red + parentOf x >>= rotateL tree + parentOf x >>= fR + else return s + cl <- fL s' >>= colorOf + cr <- fR s' >>= colorOf + if cl == Black && cr == Black + then setColor Red s' >> parentOf x + else do + s'' <- if cr == Black + then do + fL s' >>= setColor Black + setColor Red s' + rotateR tree s' + parentOf x >>= fR + else return s' + p <- parentOf x + colorOf p >>= (`setColor` s'') + setColor Black p + fR s'' >>= setColor Black + rotateL tree p + readTVar (root tree) + +-- Update key and value in a node in place. +updateKV :: Key -> Value -> Node -> STM () +updateKV k v n = do + writeKey n k + writeValue n v + +deleteNode :: RBTree -> Node -> STM Node +deleteNode s p = do + l <- left p + r <- right p + p' <- if isNode l && isNode r + then do + suc <- successor p + k <- key suc + v <- value suc + updateKV k v p + return suc + else return p + + l' <- left p' + rep <- if isNode l' + then return l' + else right p' + pp <- parent p' + if isNode rep + then do + writeParent rep pp + if isNil pp + then writeTVar (root s) rep + else do + ppl <- left pp + if p' == ppl + then writeLeft pp rep + else writeRight pp rep + writeLeft p' nil + writeRight p' nil + writeParent p' nil + c <- color p' + when (c == Black) $ fixAfterDeletion s rep + else if isNil pp + then writeTVar (root s) nil + else do + c <- color p' + when (c == Black) $ fixAfterDeletion s p' + pp' <- parent p' + when (isNode pp') $ do + ppl <- left pp' + if p' == ppl + then writeLeft pp' nil + else do + ppr <- right pp' + when (p' == ppr) $ writeRight pp' nil + writeParent p' nil + return p' + +---------------------------------- +-- Public API +-- +mkRBTree :: STM (RBTree) +mkRBTree = RBTree <$> newTVar nil + +insert :: RBTree -> Key -> Value -> STM Bool +insert t k v = do + isNil <$> insert' t k v <* postVerify t + +{- +verifyContains t k = do + b <- contains t k + if b + then return () + else error ("Expected key " ++ show k) + +verifyDelete t k = do + b <- contains t k + if not b + then return () + else error ("Found deleted key " ++ show k) +-} +preVerify _ = return () +postVerify _ = return () +-- preVerify = verify' +-- postVerify = verify' + +delete :: RBTree -> Key -> STM Bool +delete t k = do + n <- lookup k t + if isNode n + then isNode <$> (preVerify t *> deleteNode t n <* postVerify t) + else return False + +update :: RBTree -> Key -> Value -> STM Bool +update t k v = do + n <- insert' t k v + if isNil n + then return False + else do + v' <- value n + when (v /= v') $ updateKV k v n + return True + +get :: RBTree -> Key -> STM (Maybe Value) +get t k = do + n <- lookup k t + if isNil n + then return Nothing + else Just <$> value n + +contains :: RBTree -> Key -> STM Bool +contains t k = isNode <$> lookup k t + +---------------------------------------------------- +-- Test code +-- +-- #define TESTCODE +#ifdef TESTCODE + +unlessM bm a = do + b <- bm + unless b a + +verifyRedBlack :: Node -> Int -> STM Int +verifyRedBlack n d + | n == nil = return 1 + | otherwise = do + l <- left n + r <- right n + c <- color n + + hl <- verifyRedBlack l (d + 1) + hr <- verifyRedBlack r (d + 1) + if hl == 0 || hr == 0 + then return 0 + else do + when (hl /= hr) $ error ("Imbalance @depth=" ++ show d ++ " : " ++ show hl ++ " " ++ show hr) + lineage l + lineage r + + c <- color n + if c == Red + then do + unlessM (isBlack l) $ key n >>= \k -> error ("Expected black left of " ++ show k) + unlessM (isBlack r) $ key n >>= \k -> error ("Expected black right of " ++ show k) + return hl + else return (hl + 1) + where + lineage c + | c == nil = return () + | otherwise = do + p <- parent c + when (p /= n) $ error ("lineage") + + isBlack n + | n == nil = return True + | otherwise = color n >>= \c -> return (c == Black) + +assertEq s get v = get >>= \v' -> when (v /= v') $ error s + +inOrder :: Node -> STM [Key] +inOrder n + | n == nil = return [] + | otherwise = do + l <- left n + ol <- inOrder l + r <- right n + or <- inOrder r + k <- key n + return $ ol ++ [k] ++ or + +verifyOrder :: Node -> STM () +verifyOrder n + | n == nil = return () + | otherwise = do + es <- inOrder n + unless (sort es == es) $ error "Ordering." + +verifyLinks p n as + | p == nil && n == nil = return () + | p == nil = do + l <- left n + r <- right n + + assertM "left loop" $ return (l `notElem` as) + assertM "right loop" $ return (r `notElem` as) + + when (isNode l) $ verifyLinks n l (l:as) + when (isNode r) $ verifyLinks n r (r:as) + | n == nil = return () + | otherwise = do + p' <- parent n + assertM "parent mismatch" $ return (p == p') + verifyLinks nil n as + +verify' t = do + r <- readTVar (root t) + verifyLinks nil r [] + +debug :: String -> STM () +debug s = return $ unsafePerformIO $ putStrLn s + +verify :: RBTree -> STM Int +verify t = do + debug $ "Verifying." + r <- readTVar (root t) + if isNil r + then return 1 + else do + k <- key r + assertEq ("Root parent not Nil " ++ show k) (parent r) nil + assertEq ("Root color not Black " ++ show k) (color r) Black + + verifyLinks nil r ([]) + verifyOrder r + verifyRedBlack r 0 + +assertM s v = do + b <- v + unless b $ error s + +insertTest :: [Key] -> IO Int +insertTest as = atomically $ do + r <- mkRBTree + debug $ "Tree made." + forM_ as $ \a -> do + debug $ "Inserting " ++ show a + assertM "Insert failed" $ insert r a a + verify r + +deleteTest :: [Key] -> [Key] -> IO () +deleteTest as bs = atomically $ do + r <- mkRBTree + forM_ as $ \a -> do + assertM "insert failed" $ insert r a a + verify r + forM_ bs $ \b -> do + assertM "delete failed" $ delete r b + verify r + +testMain' = do + insertTest [4,6,9,1,8,7,2] + +testMain'' = do + forM_ (inits [4,6,9,1,8,7,2,3,0,5]) $ \as -> do + putStrLn $ "Inserting " ++ show as + insertTest as + +testMain''' = do + forM_ (zip (inits [4,6,9,1,8,7,2,3,0,5]) (map reverse (inits [4,6,9,1,8,7,2,3,0,5]))) $ \(as,bs) -> do + putStrLn $ "Inserting " ++ show as ++ " deleting " ++ show bs + insertTest as + +testMain = do + let as = [92,86,34,84, 5,64, 1, 87,11,39 + ,17,15,13,66,63,38,69, 67,88,16 + , 9,95,31,96,19,33,21, 27,65,10 + ,23,32,80,41,36,14,37, 54,98,51 + ,55,45,43,97,61,60, 2, 12,49,85 + , 8,76,46,78,48,56,35,100,29,90 + ,99,70,73,52,81,20, 3, 68,22,83 + ,71,72, 4,74,47,94,77, 89,59,91 + ,93,75,25,50, 6,58, 7, 62,53,40 + ,26,24,42,30,18,28,79, 82,44,57 + ] + forM_ (inits as) $ \bs -> do + putStrLn $ "Inserting " ++ show bs + insertTest bs +#endif diff --git a/benchmarks/PPoPP2019/src/RBTreeTVar.hs b/benchmarks/PPoPP2019/src/RBTreeTVar.hs new file mode 100644 index 0000000..d1f85b7 --- /dev/null +++ b/benchmarks/PPoPP2019/src/RBTreeTVar.hs @@ -0,0 +1,564 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE CPP #-} +module RBTreeTVar + ( RBTree + , mkRBTree + + , insert + , delete + , update + , get + , contains + + , benchCode +#ifdef TESTCODE + , testMain + + , verify +#endif + ) where + +import Prelude hiding (lookup) + +import GHC.Conc.Sync +import Control.Applicative +import Control.Monad +import Control.Exception + +import Data.List (sort,inits) +import Debug.Trace + +benchCode :: String +benchCode = "RBTreeTVar" + +data Node k v + = Node { key :: !k + , value :: !v + , parent :: TVar (Node k v) + , left :: TVar (Node k v) + , right :: TVar (Node k v) + , color :: TVar Color + } + | Nil + +instance Eq k => Eq (Node k v) where + Nil == Nil = True + Nil == _ = False + _ == Nil = False + + (Node k _ _ _ _ _) == (Node k' _ _ _ _ _) = k == k' + +isNil :: Node k v -> Bool +isNil Nil = True +isNil _ = False + +isNode = not . isNil + +data Color = Red | Black + deriving (Eq, Show, Read) + +newtype RBTree k v = RBTree { root :: TVar (Node k v) } + +lookupNode :: Ord k => k -> Node k v -> STM (Node k v) +lookupNode _ Nil = return Nil +lookupNode s n@(Node k v _ tl tr _) + = case compare s k of + EQ -> return n + LT -> readTVar tl >>= lookupNode s + GT -> readTVar tr >>= lookupNode s + +lookup :: Ord k => k -> RBTree k v -> STM (Node k v) +lookup k t = readTVar (root t) >>= lookupNode k + +rotateLeft :: (Eq v, Eq k) => RBTree k v -> Node k v -> STM () +rotateLeft s x = do + r <- readTVar (right x) + rl <- readTVar (left r) + writeTVar (right x) rl + when (isNode rl) $ writeTVar (parent rl) x + + xp <- readTVar (parent x) + writeTVar (parent r) xp + + if isNil xp + then writeTVar (root s) r + else do + xpl <- readTVar (left xp) + if xpl == x + then writeTVar (left xp) r + else writeTVar (right xp) r + + writeTVar (left r) x + writeTVar (parent x) r + + +rotateRight :: (Eq v, Eq k) => RBTree k v -> Node k v -> STM () +rotateRight s x = do + l <- readTVar (left x) + lr <- readTVar (right l) + writeTVar (left x) lr + when (isNode lr) $ writeTVar (parent lr) x + + xp <- readTVar (parent x) + writeTVar (parent l) xp + + if isNil xp + then writeTVar (root s) l + else do + xpr <- readTVar (right xp) + if xpr == x + then writeTVar (right xp) l + else writeTVar (left xp) l + + writeTVar (right l) x + writeTVar (parent x) l + +setField :: (Node k v -> TVar a) -> a -> Node k v -> STM () +setField _ _ Nil = return () +setField f v x = writeTVar (f x) v + +setColor :: Color -> Node k v -> STM () +setColor = setField color + +node _ Nil = return Nil +node f x = f x + +parentOf, leftOf, rightOf :: Node k v -> STM (Node k v) +parentOf = node (readTVar . parent) +leftOf = node (readTVar . left) +rightOf = node (readTVar . right) + +colorOf Nil = return Black +colorOf x = readTVar (color x) + +isLeftBranch :: (Eq k, Eq v) => Node k v -> STM Bool +isLeftBranch x = do + x' <- parentOf x >>= leftOf + return $ x == x' + +fixAfterInsertion :: (Eq k, Eq v) => RBTree k v -> Node k v -> STM () +fixAfterInsertion _ Nil = return () +fixAfterInsertion s x = do + setColor Red x + + loop x + + ro <- readTVar (root s) + c <- readTVar (color ro) + when (c /= Black) $ writeTVar (color ro) Black + where + loop Nil = return () + loop x = do + sr <- readTVar (root s) + if sr == x + then return () + else body x >>= loop + + body x = do + xp <- readTVar (parent x) + if isNil xp + then return Nil + else do + xpp <- parentOf xp + xppl <- leftOf xpp + + c <- readTVar (color xp) + + if c /= Red + then return Nil + else do + if xp == xppl + then handle x xp xpp rightOf rotateLeft rotateRight + else handle x xp xpp leftOf rotateRight rotateLeft + + handle x xp xpp f ra rb = do + y <- f xpp + c <- colorOf y + if c == Red + then do + setColor Black xp + setColor Black y + setColor Red xpp + return xpp + else do + z <- f xp + (x',xp',xpp') <- if x == z + then do + ra s xp + xp' <- parentOf xp + xpp' <- parentOf xp' + return (xp,xp',xpp') + else return (x,xp,xpp) + setColor Black xp' + setColor Red xpp' + when (isNode xpp') $ rb s xpp' + return x' + +-- Note: we differ here in not taking a node argument of an exiting +-- allocated node. The behavior when that argument is NULL in the original +-- code is to have insert act like find. +insert' :: (Eq v, Ord k) => RBTree k v -> k -> v -> STM (Node k v) +insert' s k v = do + t <- readTVar (root s) + if isNil t + then do + n <- Node k v + <$> newTVar Nil + <*> newTVar Nil + <*> newTVar Nil + <*> newTVar Black + writeTVar (root s) n + return Nil + else loop t + where + loop t = case compare k (key t) of + EQ -> return t + LT -> handle t left + GT -> handle t right + + handle t f = do + tc <- readTVar (f t) + if isNode tc + then loop tc + else do + n <- Node k v + <$> newTVar t + <*> newTVar Nil + <*> newTVar Nil + <*> newTVar Black + writeTVar (f t) n + fixAfterInsertion s n + return Nil + +successor :: (Eq k, Eq v) => Node k v -> STM (Node k v) +successor Nil = return Nil +successor t = do + r <- readTVar (right t) + if isNode r + then leftMost r + else readTVar (parent t) >>= rightParent t + where + leftMost p = do + l <- readTVar (left p) + case l of + Nil -> return p + _ -> leftMost l + + rightParent _ Nil = return Nil + rightParent c p = do -- Find the first parent further right + r <- readTVar (right p) + if r == c + then readTVar (parent p) >>= rightParent p + else return p + + +fixAfterDeletion :: (Eq k, Eq v) => RBTree k v -> Node k v -> STM () +fixAfterDeletion tree x = do + x' <- loop x + when (isNode x') $ do + c <- readTVar (color x') + when (c /= Black) $ writeTVar (color x') Black + where + loop x = do + r <- readTVar (root tree) + case () of + () | x == r -> return x + | otherwise -> do + c <- colorOf x + if c /= Black + then return x + else body x >>= loop + + body x = do + b <- isLeftBranch x + if b + then handle x rightOf leftOf rotateRight rotateLeft + else handle x leftOf rightOf rotateLeft rotateRight + + handle x fR fL rotateR rotateL = do + s <- parentOf x >>= fR + c <- colorOf s + s' <- if c == Red + then do + setColor Black s + parentOf x >>= setColor Red + parentOf x >>= rotateL tree + parentOf x >>= fR + else return s + cl <- fL s' >>= colorOf + cr <- fR s' >>= colorOf + if cl == Black && cr == Black + then setColor Red s' >> parentOf x + else do + s'' <- if cr == Black + then do + fL s' >>= setColor Black + setColor Red s' + rotateR tree s' + parentOf x >>= fR + else return s' + p <- parentOf x + colorOf p >>= (`setColor` s'') + setColor Black p + fR s'' >>= setColor Black + rotateL tree p + readTVar (root tree) + +-- link in a new node replacing the given node with new +-- key and value. +replace :: (Eq k, Eq v) => k -> v -> RBTree k v -> Node k v -> STM (Node k v) +replace k v t n@(Node _ _ p l r c) = do + let n' = Node k v p l r c + -- update the rest of the tree: + b <- isLeftBranch n + readTVar l >>= setField parent n' + readTVar r >>= setField parent n' + np <- readTVar p + if isNil np + then writeTVar (root t) n' + else setField (if b then left else right) n' np + return n' + +deleteNode :: (Eq k, Eq v) => RBTree k v -> Node k v -> STM (Node k v) +deleteNode s p = do + l <- readTVar (left p) + r <- readTVar (right p) + p' <- if isNode l && isNode r + then do + suc <- successor p + replace (key suc) (value suc) s p + return suc + else return p + + l' <- readTVar (left p') + rep <- if isNode l' + then return l' + else readTVar (right p') + pp <- readTVar (parent p') + if isNode rep + then do + writeTVar (parent rep) pp + if isNil pp + then writeTVar (root s) rep + else do + ppl <- readTVar (left pp) + if p' == ppl + then writeTVar (left pp) rep + else writeTVar (right pp) rep + writeTVar (left p') Nil + writeTVar (right p') Nil + writeTVar (parent p') Nil + c <- readTVar (color p') + when (c == Black) $ fixAfterDeletion s rep + else if isNil pp + then writeTVar (root s) Nil + else do + c <- readTVar (color p') + when (c == Black) $ fixAfterDeletion s p' + pp' <- readTVar (parent p') + when (isNode pp') $ do + ppl <- readTVar (left pp') + if p' == ppl + then writeTVar (left pp') Nil + else do + ppr <- readTVar (right pp') + when (p' == ppr) $ writeTVar (right pp') Nil + writeTVar (parent p') Nil + return p' + +---------------------------------- +-- Public API +-- +mkRBTree :: STM (RBTree k v) +mkRBTree = RBTree <$> newTVar Nil + +insert :: (Eq v, Ord k) => RBTree k v -> k -> v -> STM Bool +insert t k v = isNil <$> insert' t k v <* postVerify t + +preVerify _ = return () +postVerify _ = return () +-- preVerify = verify' +-- postVerify = verify' + +delete :: (Eq v, Ord k) => RBTree k v -> k -> STM Bool +delete t k = do + n <- lookup k t + if isNode n + then isNode <$> (preVerify t *> deleteNode t n <* postVerify t) + else return False + +update :: (Eq v, Ord k) => RBTree k v -> k -> v -> STM Bool +update t k v = do + n <- insert' t k v + case n of + Node _ v' _ _ _ _ -> do + when (v /= v') $ replace k v t n >> return () + return True + Nil -> return False + + +get :: Ord k => RBTree k v -> k -> STM (Maybe v) +get t k = do + n <- lookup k t + case n of + Node _ v _ _ _ _ -> return (Just v) + Nil -> return Nothing + +contains :: Ord k => RBTree k v -> k -> STM Bool +contains t k = isNode <$> lookup k t + +---------------------------------------------------- +-- Test code +-- +#ifdef TESTCODE + +unlessM bm a = do + b <- bm + unless b a + +verifyRedBlack :: (Show k, Show v, Eq k, Eq v) => Node k v -> Int -> STM Int +verifyRedBlack Nil _ = return 1 +verifyRedBlack n d = do + l <- readTVar (left n) + r <- readTVar (right n) + c <- readTVar (color n) + + hl <- verifyRedBlack l (d + 1) + hr <- verifyRedBlack r (d + 1) + if hl == 0 || hr == 0 + then return 0 + else do + when (hl /= hr) $ error ("Imbalance @depth=" ++ show d ++ " : " ++ show hl ++ " " ++ show hr) + lineage l + lineage r + + c <- readTVar (color n) + if c == Red + then do + unlessM (isBlack l) $ error ("Expected black left of " ++ show (key n)) + unlessM (isBlack r) $ error ("Expected black right of " ++ show (key n)) + return hl + else return (hl + 1) + where + lineage Nil = return () + lineage c = do + p <- readTVar (parent c) + when (p /= n) $ error ("lineage") + + isBlack Nil = return True + isBlack n = readTVar (color n) >>= \c -> return (c == Black) + +assertEq s t v = readTVar t >>= \v' -> when (v /= v') $ error s + +{- +inOrder :: Show k => Node k v -> STM [k] +inOrder Nil = trace "." $ return [] +inOrder n = do + l <- readTVar (left n) + ol <- trace "(" $ inOrder l + + c <- readTVar (color n) + r <- trace (if c == Red then "r" else "b") $ readTVar (right n) + -- r <- trace (show (key n)) $ readTVar (right n) + or <- inOrder r + trace ")" $ return $ ol ++ [key n] ++ or +-- -} +{--} +inOrder :: Show k => Node k v -> STM [k] +inOrder Nil = return [] +inOrder n = do + l <- readTVar (left n) + ol <- inOrder l + r <- readTVar (right n) + or <- inOrder r + return $ ol ++ [key n] ++ or +-- -} +verifyOrder :: (Ord k, Show k) => Node k v -> STM () +verifyOrder Nil = return () +verifyOrder n = do + es <- inOrder n + unless (sort es == es) $ error "Ordering." + +verifyLinks Nil Nil _ = return () +verifyLinks Nil n as = do + l <- readTVar (left n) + r <- readTVar (right n) + + assertM "left loop" $ return (l `notElem` as) + assertM "right loop" $ return (r `notElem` as) + + when (isNode l) $ verifyLinks n l (l:as) + when (isNode r) $ verifyLinks n r (r:as) +verifyLinks p Nil _ = return () +verifyLinks p n as = do + p' <- readTVar (parent n) + assertM "parent mismatch" $ return (p == p') + verifyLinks Nil n as + +verify' t = do + r <- readTVar (root t) + verifyLinks Nil r [] + +verify :: (Eq k, Eq v, Ord k, Show v, Show k) => RBTree k v -> STM Int +verify t = do + r <- readTVar (root t) + if isNil r + then return 1 + else do + assertEq ("Root parent not Nil " ++ show (key r)) (parent r) Nil + assertEq ("Root color not Black " ++ show (key r)) (color r) Black + + verifyLinks Nil r ([]) + verifyOrder r + verifyRedBlack r 0 + +assertM s v = do + b <- v + unless b $ error s + +insertTest :: [Int] -> IO Int +insertTest as = atomically $ do + r <- mkRBTree + forM_ as $ \a -> do + assertM "Insert failed" $ insert r a a + verify r + +deleteTest :: [Int] -> [Int] -> IO () +deleteTest as bs = atomically $ do + r <- mkRBTree + forM_ as $ \a -> do + assertM "insert failed" $ insert r a a + verify r + forM_ bs $ \b -> do + assertM "delete failed" $ delete r b + verify r + +testMain' = do + insertTest [4,6,9,1,8,7,2] + +testMain'' = do + forM_ (inits [4,6,9,1,8,7,2,3,0,5]) $ \as -> do + putStrLn $ "Inserting " ++ show as + insertTest as + +testMain''' = do + forM_ (zip (inits [4,6,9,1,8,7,2,3,0,5]) (map reverse (inits [4,6,9,1,8,7,2,3,0,5]))) $ \(as,bs) -> do + putStrLn $ "Inserting " ++ show as ++ " deleting " ++ show bs + insertTest as + +testMain = do + let as = [92,86,34,84, 5,64, 1, 87,11,39 + ,17,15,13,66,63,38,69, 67,88,16 + , 9,95,31,96,19,33,21, 27,65,10 + ,23,32,80,41,36,14,37, 54,98,51 + ,55,45,43,97,61,60, 2, 12,49,85 + , 8,76,46,78,48,56,35,100,29,90 + ,99,70,73,52,81,20, 3, 68,22,83 + ,71,72, 4,74,47,94,77, 89,59,91 + ,93,75,25,50, 6,58, 7, 62,53,40 + ,26,24,42,30,18,28,79, 82,44,57 + ] + forM_ (inits as) $ \bs -> do + putStrLn $ "Inserting " ++ show bs + insertTest as +#endif diff --git a/benchmarks/PPoPP2019/src/STMContainers/Bimap.hs b/benchmarks/PPoPP2019/src/STMContainers/Bimap.hs new file mode 100644 index 0000000..d2fe30e --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/Bimap.hs @@ -0,0 +1,142 @@ +module STMContainers.Bimap +( + Bimap, + Association, + Key, + new, + newIO, + insert1, + insert2, + delete1, + delete2, + lookup1, + lookup2, + focus1, + focus2, + null, +) +where + +import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null) +import qualified Focus +import qualified STMContainers.Map as Map + + +-- | +-- A bidirectional map. +-- Essentially a bijection between subsets of its two argument types. +-- +-- For one value of a left-hand type this map contains one value +-- of the right-hand type and vice versa. +data Bimap a b = + Bimap {m1 :: !(Map.Map a b), m2 :: !(Map.Map b a)} + deriving (Typeable) + +-- | +-- A constraint for associations. +type Association a b = (Key a, Key b) + +-- | +-- A constraint for keys. +type Key k = Map.Key k + +-- | +-- Construct a new bimap. +{-# INLINABLE new #-} +new :: STM (Bimap a b) +new = Bimap <$> Map.new <*> Map.new + +-- | +-- Construct a new bimap in IO. +-- +-- This is useful for creating it on a top-level using 'unsafePerformIO', +-- because using 'atomically' inside 'unsafePerformIO' isn't possible. +{-# INLINABLE newIO #-} +newIO :: IO (Bimap a b) +newIO = Bimap <$> Map.newIO <*> Map.newIO + +-- | +-- Check on being empty. +{-# INLINABLE null #-} +null :: Bimap a b -> STM Bool +null = Map.null . m1 + +-- | +-- Look up a right value by a left value. +{-# INLINABLE lookup1 #-} +lookup1 :: (Association a b) => a -> Bimap a b -> STM (Maybe b) +lookup1 k = Map.lookup k . m1 + +-- | +-- Look up a left value by a right value. +{-# INLINABLE lookup2 #-} +lookup2 :: (Association a b) => b -> Bimap a b -> STM (Maybe a) +lookup2 k = Map.lookup k . m2 + +-- | +-- Insert an association by a left value. +{-# INLINABLE insert1 #-} +insert1 :: (Association a b) => b -> a -> Bimap a b -> STM () +insert1 b a (Bimap m1 m2) = + do + Map.insert b a m1 + Map.insert a b m2 + +-- | +-- Insert an association by a right value. +{-# INLINABLE insert2 #-} +insert2 :: (Association a b) => a -> b -> Bimap a b -> STM () +insert2 b a (Bimap m1 m2) = (inline insert1) b a (Bimap m2 m1) + +-- | +-- Delete an association by a left value. +{-# INLINABLE delete1 #-} +delete1 :: (Association a b) => a -> Bimap a b -> STM () +delete1 k (Bimap m1 m2) = + Map.focus lookupAndDeleteStrategy k m1 >>= + mapM_ (\k' -> Map.delete k' m2) + where + lookupAndDeleteStrategy r = + return (r, Focus.Remove) + +-- | +-- Delete an association by a right value. +{-# INLINABLE delete2 #-} +delete2 :: (Association a b) => b -> Bimap a b -> STM () +delete2 k (Bimap m1 m2) = (inline delete1) k (Bimap m2 m1) + +-- | +-- Focus on a right value by a left value with a strategy. +-- +-- This function allows to perform composite operations in a single access +-- to a map item. +-- E.g., you can look up an item and delete it at the same time, +-- or update it and return the new value. +{-# INLINABLE focus1 #-} +focus1 :: (Association a b) => Focus.StrategyM STM b r -> a -> Bimap a b -> STM r +focus1 s a (Bimap m1 m2) = + do + (r, d, mb) <- Map.focus s' a m1 + case d of + Focus.Keep -> + return () + Focus.Remove -> + forM_ mb $ \b -> Map.delete b m2 + Focus.Replace b' -> + do + forM_ mb $ \b -> Map.delete b m2 + Map.insert a b' m2 + return r + where + s' = \k -> s k >>= \(r, d) -> return ((r, d, k), d) + +-- | +-- Focus on a left value by a right value with a strategy. +-- +-- This function allows to perform composite operations in a single access +-- to a map item. +-- E.g., you can look up an item and delete it at the same time, +-- or update it and return the new value. +{-# INLINABLE focus2 #-} +focus2 :: (Association a b) => Focus.StrategyM STM a r -> b -> Bimap a b -> STM r +focus2 s b (Bimap m1 m2) = (inline focus1) s b (Bimap m2 m1) diff --git a/benchmarks/PPoPP2019/src/STMContainers/HAMT.hs b/benchmarks/PPoPP2019/src/STMContainers/HAMT.hs new file mode 100644 index 0000000..54f7d1a --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/HAMT.hs @@ -0,0 +1,34 @@ +module STMContainers.HAMT where + +import STMContainers.Prelude hiding (insert, lookup, delete, foldM) +import qualified STMContainers.HAMT.Nodes as Nodes +import qualified Focus + + +type HAMT e = Nodes.Nodes e + +type Element e = (Nodes.Element e, Hashable (Nodes.ElementKey e)) + +{-# INLINE insert #-} +insert :: (Element e) => e -> HAMT e -> STM () +insert e = Nodes.insert e (hash (Nodes.elementKey e)) (Nodes.elementKey e) 0 + +{-# INLINE focus #-} +focus :: (Element e) => Focus.StrategyM STM e r -> Nodes.ElementKey e -> HAMT e -> STM r +focus s k = Nodes.focus s (hash k) k 0 + +{-# INLINE foldM #-} +foldM :: (a -> e -> STM a) -> a -> HAMT e -> STM a +foldM step acc = Nodes.foldM step acc 0 + +{-# INLINE new #-} +new :: STM (HAMT e) +new = Nodes.new + +{-# INLINE newIO #-} +newIO :: IO (HAMT e) +newIO = Nodes.newIO + +{-# INLINE null #-} +null :: HAMT e -> STM Bool +null = Nodes.null diff --git a/benchmarks/PPoPP2019/src/STMContainers/HAMT/Level.hs b/benchmarks/PPoPP2019/src/STMContainers/HAMT/Level.hs new file mode 100644 index 0000000..ce5c382 --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/HAMT/Level.hs @@ -0,0 +1,30 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module STMContainers.HAMT.Level where + +import STMContainers.Prelude hiding (mask) + + +-- | +-- A depth level of a node. +-- Must be a multiple of the 'step' value. +type Level = Int + +{-# INLINE hashIndex #-} +hashIndex :: Level -> (Int -> Int) +hashIndex l i = mask .&. unsafeShiftR i l + +{-# INLINE mask #-} +mask :: Int +mask = bit step - 1 + +{-# INLINE step #-} +step :: Int +step = 5 + +{-# INLINE limit #-} +limit :: Int +limit = bitSize (undefined :: Int) + +{-# INLINE succ #-} +succ :: Level -> Level +succ = (+ step) diff --git a/benchmarks/PPoPP2019/src/STMContainers/HAMT/Nodes.hs b/benchmarks/PPoPP2019/src/STMContainers/HAMT/Nodes.hs new file mode 100644 index 0000000..da088bf --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/HAMT/Nodes.hs @@ -0,0 +1,154 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module STMContainers.HAMT.Nodes where + +import STMContainers.Prelude hiding (insert, lookup, delete, foldM, null) +import qualified STMContainers.Prelude as Prelude +import qualified STMContainers.WordArray as WordArray +import qualified STMContainers.SizedArray as SizedArray +import qualified STMContainers.HAMT.Level as Level +import qualified Focus + + +type Nodes e = TVar (WordArray.WordArray (Node e)) + +data Node e = + Nodes {-# UNPACK #-} !(Nodes e) | + Leaf {-# UNPACK #-} !Hash !e | + Leaves {-# UNPACK #-} !Hash {-# UNPACK #-} !(SizedArray.SizedArray e) + +type Hash = Int + +class (Eq (ElementKey e)) => Element e where + type ElementKey e + elementKey :: e -> ElementKey e + +{-# INLINE new #-} +new :: STM (Nodes e) +new = newTVar WordArray.empty + +{-# INLINE newIO #-} +newIO :: IO (Nodes e) +newIO = newTVarIO WordArray.empty + +insert :: (Element e) => e -> Hash -> ElementKey e -> Level.Level -> Nodes e -> STM () +insert e h k l ns = do + a <- readTVar ns + let write n = writeTVar ns $ WordArray.set i n a + case WordArray.lookup i a of + Nothing -> write (Leaf h e) + Just n -> case n of + Nodes ns' -> insert e h k (Level.succ l) ns' + Leaf h' e' -> + if h' == h + then if elementKey e' == k + then write (Leaf h e) + else write (Leaves h (SizedArray.pair e e')) + else do + nodes <- pair h (Leaf h e) h' (Leaf h' e') (Level.succ l) + write (Nodes nodes) + Leaves h' la -> + if h' == h + then case SizedArray.find ((== k) . elementKey) la of + Just (lai, _) -> + write (Leaves h' (SizedArray.insert lai e la)) + Nothing -> + write (Leaves h' (SizedArray.append e la)) + else + write . Nodes =<< pair h (Leaf h e) h' (Leaves h' la) (Level.succ l) + where + i = Level.hashIndex l h + +pair :: Hash -> Node e -> Hash -> Node e -> Level.Level -> STM (Nodes e) +pair h1 n1 h2 n2 l = + if i1 == i2 + then newTVar . WordArray.singleton i1 . Nodes =<< pair h1 n1 h2 n2 (Level.succ l) + else newTVar $ WordArray.pair i1 n1 i2 n2 + where + hashIndex = Level.hashIndex l + i1 = hashIndex h1 + i2 = hashIndex h2 + +focus :: (Element e) => Focus.StrategyM STM e r -> Hash -> ElementKey e -> Level.Level -> Nodes e -> STM r +focus s h k l ns = do + a <- readTVar ns + (r, a'm) <- WordArray.focusM s' ai a + maybe (return ()) (writeTVar ns) a'm + return r + where + ai = Level.hashIndex l h + s' = \case + Nothing -> traversePair (return . fmap (Leaf h)) =<< s Nothing + Just n -> case n of + Nodes ns' -> do + r <- focus s h k (Level.succ l) ns' + null ns' >>= \case + True -> return (r, Focus.Remove) + False -> return (r, Focus.Keep) + Leaf h' e' -> + case h' == h of + True -> + case elementKey e' == k of + True -> + traversePair (return . fmap (Leaf h)) =<< s (Just e') + False -> + traversePair processDecision =<< s Nothing + where + processDecision = \case + Focus.Replace e -> + return (Focus.Replace (Leaves h (SizedArray.pair e e'))) + _ -> + return Focus.Keep + False -> + traversePair processDecision =<< s Nothing + where + processDecision = \case + Focus.Replace e -> do + ns' <- pair h (Leaf h e) h' (Leaf h' e') (Level.succ l) + return (Focus.Replace (Nodes ns')) + _ -> return Focus.Keep + Leaves h' a' -> + case h' == h of + True -> + case SizedArray.find ((== k) . elementKey) a' of + Just (i', e') -> + s (Just e') >>= traversePair processDecision + where + processDecision = \case + Focus.Keep -> + return Focus.Keep + Focus.Remove -> + case SizedArray.delete i' a' of + a'' -> case SizedArray.null a'' of + False -> return (Focus.Replace (Leaves h' a'')) + True -> return Focus.Remove + Focus.Replace e -> + return (Focus.Replace (Leaves h' (SizedArray.insert i' e a'))) + Nothing -> + s Nothing >>= traversePair processDecision + where + processDecision = \case + Focus.Replace e -> + return (Focus.Replace (Leaves h' (SizedArray.append e a'))) + _ -> + return Focus.Keep + False -> + s Nothing >>= traversePair processDecision + where + processDecision = \case + Focus.Replace e -> do + ns' <- pair h (Leaf h e) h' (Leaves h' a') (Level.succ l) + return (Focus.Replace (Nodes ns')) + _ -> + return Focus.Keep + +null :: Nodes e -> STM Bool +null = fmap WordArray.null . readTVar + +foldM :: (a -> e -> STM a) -> a -> Level.Level -> Nodes e -> STM a +foldM step acc level = + readTVar >=> foldlM step' acc + where + step' acc' = \case + Nodes ns -> foldM step acc' (Level.succ level) ns + Leaf _ e -> step acc' e + Leaves _ a -> SizedArray.foldM step acc' a diff --git a/benchmarks/PPoPP2019/src/STMContainers/LICENSE b/benchmarks/PPoPP2019/src/STMContainers/LICENSE new file mode 100644 index 0000000..fe32f2b --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2014, Nikita Volkov + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/benchmarks/PPoPP2019/src/STMContainers/Map.hs b/benchmarks/PPoPP2019/src/STMContainers/Map.hs new file mode 100644 index 0000000..6dcc042 --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/Map.hs @@ -0,0 +1,89 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module STMContainers.Map +( + Map, + Key, + new, + newIO, + insert, + delete, + lookup, + focus, + null, +) +where + +import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null) +import qualified STMContainers.HAMT as HAMT +import qualified STMContainers.HAMT.Nodes as HAMTNodes +import qualified Focus + + +-- | +-- A hash table, based on an STM-specialized hash array mapped trie. +newtype Map k v = Map (HAMT.HAMT (k, v)) + deriving (Typeable) + +-- | +-- A constraint for keys. +type Key a = (Eq a, Hashable a) + +instance (Eq k) => HAMTNodes.Element (k, v) where + type ElementKey (k, v) = k + elementKey (k, v) = k + +{-# INLINE associationValue #-} +associationValue :: (k, v) -> v +associationValue (_, v) = v + +-- | +-- Look up an item. +{-# INLINE lookup #-} +lookup :: (Key k) => k -> Map k v -> STM (Maybe v) +lookup k = focus Focus.lookupM k + +-- | +-- Insert a value at a key. +{-# INLINE insert #-} +insert :: (Key k) => v -> k -> Map k v -> STM () +insert !v !k (Map h) = HAMT.insert (k, v) h + +-- | +-- Delete an item by a key. +{-# INLINE delete #-} +delete :: (Key k) => k -> Map k v -> STM () +delete k (Map h) = HAMT.focus Focus.deleteM k h + +-- | +-- Focus on an item by a key with a strategy. +-- +-- This function allows to perform composite operations in a single access +-- to a map item. +-- E.g., you can look up an item and delete it at the same time, +-- or update it and return the new value. +{-# INLINE focus #-} +focus :: (Key k) => Focus.StrategyM STM v r -> k -> Map k v -> STM r +focus f k (Map h) = HAMT.focus f' k h + where + f' = (fmap . fmap . fmap) (\v -> k `seq` v `seq` (k, v)) . f . fmap associationValue + +-- | +-- Construct a new map. +{-# INLINE new #-} +new :: STM (Map k v) +new = Map <$> HAMT.new + +-- | +-- Construct a new map in IO. +-- +-- This is useful for creating it on a top-level using 'unsafePerformIO', +-- because using 'atomically' inside 'unsafePerformIO' isn't possible. +{-# INLINE newIO #-} +newIO :: IO (Map k v) +newIO = Map <$> HAMT.newIO + +-- | +-- Check, whether the map is empty. +{-# INLINE null #-} +null :: Map k v -> STM Bool +null (Map h) = HAMT.null h diff --git a/benchmarks/PPoPP2019/src/STMContainers/Multimap.hs b/benchmarks/PPoPP2019/src/STMContainers/Multimap.hs new file mode 100644 index 0000000..1ec60c7 --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/Multimap.hs @@ -0,0 +1,147 @@ +module STMContainers.Multimap +( + Multimap, + Association, + Key, + Value, + new, + newIO, + insert, + delete, + deleteByKey, + lookup, + focus, + null, +) +where + +import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null) +import qualified Focus +import qualified STMContainers.Map as Map +import qualified STMContainers.Set as Set + + +-- | +-- A multimap, based on an STM-specialized hash array mapped trie. +-- +-- Basically it's just a wrapper API around @'Map.Map' k ('Set.Set' v)@. +newtype Multimap k v = Multimap (Map.Map k (Set.Set v)) + deriving (Typeable) + +-- | +-- A constraint for associations. +type Association k v = (Key k, Value v) + +-- | +-- A constraint for keys. +type Key k = Map.Key k + +-- | +-- A constraint for values. +type Value v = Set.Element v + +-- | +-- Look up an item by a value and a key. +{-# INLINE lookup #-} +lookup :: (Association k v) => v -> k -> Multimap k v -> STM Bool +lookup v k (Multimap m) = + maybe (return False) (Set.lookup v) =<< Map.lookup k m + +-- | +-- Insert an item. +{-# INLINABLE insert #-} +insert :: (Association k v) => v -> k -> Multimap k v -> STM () +insert v k (Multimap m) = + Map.focus ms k m + where + ms = + \case + Just s -> + do + Set.insert v s + return ((), Focus.Keep) + Nothing -> + do + s <- Set.new + Set.insert v s + return ((), Focus.Replace s) + +-- | +-- Delete an item by a value and a key. +{-# INLINABLE delete #-} +delete :: (Association k v) => v -> k -> Multimap k v -> STM () +delete v k (Multimap m) = + Map.focus ms k m + where + ms = + \case + Just s -> + do + Set.delete v s + Set.null s >>= returnDecision . bool Focus.Keep Focus.Remove + Nothing -> + returnDecision Focus.Keep + where + returnDecision c = return ((), c) + +-- | +-- Delete all values associated with a key. +{-# INLINEABLE deleteByKey #-} +deleteByKey :: Key k => k -> Multimap k v -> STM () +deleteByKey k (Multimap m) = + Map.delete k m + +-- | +-- Focus on an item with a strategy by a value and a key. +-- +-- This function allows to perform simultaneous lookup and modification. +-- +-- The strategy is over a unit since we already know, +-- which value we're focusing on and it doesn't make sense to replace it, +-- however we still can decide wether to keep or remove it. +{-# INLINE focus #-} +focus :: (Association k v) => Focus.StrategyM STM () r -> v -> k -> Multimap k v -> STM r +focus = + \s v k (Multimap m) -> Map.focus (liftSetItemStrategy v s) k m + where + liftSetItemStrategy :: + (Set.Element e) => e -> Focus.StrategyM STM () r -> Focus.StrategyM STM (Set.Set e) r + liftSetItemStrategy e s = + \case + Nothing -> + traversePair liftDecision =<< s Nothing + where + liftDecision = + \case + Focus.Replace b -> + do + s <- Set.new + Set.insert e s + return (Focus.Replace s) + _ -> + return Focus.Keep + Just set -> + do + r <- Set.focus s e set + (r,) . bool Focus.Keep Focus.Remove <$> Set.null set + +-- | +-- Construct a new multimap. +{-# INLINE new #-} +new :: STM (Multimap k v) +new = Multimap <$> Map.new + +-- | +-- Construct a new multimap in IO. +-- +-- This is useful for creating it on a top-level using 'unsafePerformIO', +-- because using 'atomically' inside 'unsafePerformIO' isn't possible. +{-# INLINE newIO #-} +newIO :: IO (Multimap k v) +newIO = Multimap <$> Map.newIO + +-- | +-- Check on being empty. +{-# INLINE null #-} +null :: Multimap k v -> STM Bool +null (Multimap m) = Map.null m diff --git a/benchmarks/PPoPP2019/src/STMContainers/Prelude.hs b/benchmarks/PPoPP2019/src/STMContainers/Prelude.hs new file mode 100644 index 0000000..788fdda --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/Prelude.hs @@ -0,0 +1,36 @@ +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE NoImplicitPrelude #-} +module STMContainers.Prelude +( + module Exports, + bug, + bottom, + traversePair, +) +where + +-- base +------------------------- +import BasePrelude as Exports + +-- hashable +------------------------- +import Data.Hashable as Exports (Hashable(..)) + +-- transformers +------------------------- +import Control.Monad.Trans.Class as Exports + +-- custom +------------------------- +import qualified Debug.Trace.LocationTH + +bug = [e| $(Debug.Trace.LocationTH.failure) . (msg <>) |] + where + msg = "A \"stm-containers\" package bug: " :: String + +bottom = [e| $bug "Bottom evaluated" |] + +-- | A replacement for the missing 'Traverse' instance of pair in base < 4.7. +traversePair :: Functor f => (a -> f b) -> (c, a) -> f (c, b) +traversePair f (x, y) = (,) x <$> f y diff --git a/benchmarks/PPoPP2019/src/STMContainers/Set.hs b/benchmarks/PPoPP2019/src/STMContainers/Set.hs new file mode 100644 index 0000000..52dceb8 --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/Set.hs @@ -0,0 +1,92 @@ +module STMContainers.Set +( + Set, + Element, + new, + newIO, + insert, + delete, + lookup, + focus, + null, +) +where + +import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null) +import qualified STMContainers.HAMT as HAMT +import qualified STMContainers.HAMT.Nodes as HAMTNodes +import qualified Focus + + +-- | +-- A hash set, based on an STM-specialized hash array mapped trie. +newtype Set e = Set {hamt :: HAMT.HAMT (HAMTElement e)} + deriving (Typeable) + +-- | +-- A constraint for elements. +type Element a = (Eq a, Hashable a) + +newtype HAMTElement e = HAMTElement e + +instance (Eq e) => HAMTNodes.Element (HAMTElement e) where + type ElementKey (HAMTElement e) = e + elementKey (HAMTElement e) = e + +{-# INLINE elementValue #-} +elementValue :: HAMTElement e -> e +elementValue (HAMTElement e) = e + +-- | +-- Insert a new element. +{-# INLINE insert #-} +insert :: (Element e) => e -> Set e -> STM () +insert e = HAMT.insert (HAMTElement e) . hamt + +-- | +-- Delete an element. +{-# INLINE delete #-} +delete :: (Element e) => e -> Set e -> STM () +delete e = HAMT.focus Focus.deleteM e . hamt + +-- | +-- Lookup an element. +{-# INLINE lookup #-} +lookup :: (Element e) => e -> Set e -> STM Bool +lookup e = fmap (maybe False (const True)) . HAMT.focus Focus.lookupM e . hamt + +-- | +-- Focus on an element with a strategy. +-- +-- This function allows to perform simultaneous lookup and modification. +-- +-- The strategy is over a unit since we already know, +-- which element we're focusing on and it doesn't make sense to replace it, +-- however we still can decide wether to keep or remove it. +{-# INLINE focus #-} +focus :: (Element e) => Focus.StrategyM STM () r -> e -> Set e -> STM r +focus s e = HAMT.focus elementStrategy e . hamt + where + elementStrategy = + (fmap . fmap . fmap) (const (HAMTElement e)) . s . fmap (const ()) + +-- | +-- Construct a new set. +{-# INLINE new #-} +new :: STM (Set e) +new = Set <$> HAMT.new + +-- | +-- Construct a new set in IO. +-- +-- This is useful for creating it on a top-level using 'unsafePerformIO', +-- because using 'atomically' inside 'unsafePerformIO' isn't possible. +{-# INLINE newIO #-} +newIO :: IO (Set e) +newIO = Set <$> HAMT.newIO + +-- | +-- Check, whether the set is empty. +{-# INLINE null #-} +null :: Set e -> STM Bool +null = HAMT.null . hamt diff --git a/benchmarks/PPoPP2019/src/STMContainers/SizedArray.hs b/benchmarks/PPoPP2019/src/STMContainers/SizedArray.hs new file mode 100644 index 0000000..f201592 --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/SizedArray.hs @@ -0,0 +1,88 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module STMContainers.SizedArray where + +import STMContainers.Prelude hiding (lookup, toList, foldM) +import Data.Primitive.Array +import qualified STMContainers.Prelude as Prelude +import qualified Focus + +-- | +-- An array, +-- which sacrifices the performance for space-efficiency and thread-safety. +data SizedArray a = + SizedArray {-# UNPACK #-} !Int {-# UNPACK #-} !(Array a) + +instance Foldable SizedArray where + {-# INLINE foldr #-} + foldr step r (SizedArray size array) = + foldr step r $ map (indexArray array) [0 .. pred size] + +-- | +-- An index of an element. +type Index = Int + +{-# INLINE pair #-} +pair :: a -> a -> SizedArray a +pair e e' = + runST $ do + a <- newArray 2 e + writeArray a 1 e' + SizedArray 2 <$> unsafeFreezeArray a + +-- | +-- Get the amount of elements. +{-# INLINE size #-} +size :: SizedArray a -> Int +size (SizedArray b _) = b + +-- | +-- Get the amount of elements. +{-# INLINE null #-} +null :: SizedArray a -> Bool +null = (== 0) . size + +{-# INLINE find #-} +find :: (a -> Bool) -> SizedArray a -> Maybe (Index, a) +find p (SizedArray s a) = loop 0 + where + loop i = if i < s + then let e = indexArray a i in if p e + then Just (i, e) + else loop (succ i) + else Nothing + +-- | +-- Unsafe. Doesn't check the index overflow. +{-# INLINE insert #-} +insert :: Index -> a -> SizedArray a -> SizedArray a +insert i e (SizedArray s a) = + runST $ do + m' <- newArray s undefined + forM_ [0 .. pred s] $ \i' -> indexArrayM a i' >>= writeArray m' i' + writeArray m' i e + SizedArray s <$> unsafeFreezeArray m' + +{-# INLINE delete #-} +delete :: Index -> SizedArray a -> SizedArray a +delete i (SizedArray s a) = + runST $ do + m' <- newArray (pred s) undefined + forM_ [0 .. pred i] $ \i' -> indexArrayM a i' >>= writeArray m' i' + forM_ [succ i .. pred s] $ \i' -> indexArrayM a i' >>= writeArray m' (pred i') + SizedArray (pred s) <$> unsafeFreezeArray m' + +{-# INLINE append #-} +append :: a -> SizedArray a -> SizedArray a +append e (SizedArray s a) = + runST $ do + m' <- newArray (succ s) undefined + forM_ [0 .. pred s] $ \i -> indexArrayM a i >>= writeArray m' i + writeArray m' s e + SizedArray (succ s) <$> unsafeFreezeArray m' + +{-# INLINE foldM #-} +foldM :: (Monad m) => (a -> b -> m a) -> a -> SizedArray b -> m a +foldM step acc (SizedArray size array) = + Prelude.foldM step' acc [0 .. pred size] + where + step' acc' i = indexArrayM array i >>= step acc' diff --git a/benchmarks/PPoPP2019/src/STMContainers/WordArray.hs b/benchmarks/PPoPP2019/src/STMContainers/WordArray.hs new file mode 100644 index 0000000..5fa34e0 --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/WordArray.hs @@ -0,0 +1,199 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module STMContainers.WordArray where + +import STMContainers.Prelude hiding (lookup, toList, traverse_) +import Data.Primitive.Array +import qualified STMContainers.Prelude as Prelude +import qualified STMContainers.WordArray.Indices as Indices +import qualified Focus + + +-- | +-- An immutable space-efficient sparse array, +-- which can store only as many elements as there are bits in the machine word. +data WordArray e = + WordArray {-# UNPACK #-} !Indices {-# UNPACK #-} !(Array e) + +instance Foldable WordArray where + {-# INLINE foldr #-} + foldr step r (WordArray indices array) = + foldr (step . indexArray array) r $ Indices.positions indices + +-- | +-- A bitmap of set elements. +type Indices = Indices.Indices + +-- | +-- An index of an element. +type Index = Int + +{-# INLINE indices #-} +indices :: WordArray e -> Indices +indices (WordArray b _) = b + +{-# INLINE maxSize #-} +maxSize :: Int +maxSize = Indices.maxSize + +{-# INLINE empty #-} +empty :: WordArray e +empty = WordArray 0 a + where + a = runST $ newArray 0 undefined >>= unsafeFreezeArray + +-- | +-- An array with a single element at the specified index. +{-# INLINE singleton #-} +singleton :: Index -> e -> WordArray e +singleton i e = + let b = Indices.insert i 0 + a = runST $ newArray 1 e >>= unsafeFreezeArray + in WordArray b a + +{-# INLINE pair #-} +pair :: Index -> e -> Index -> e -> WordArray e +pair i e i' e' = + WordArray is a + where + is = Indices.fromList [i, i'] + a = + runST $ if + | i < i' -> do + a <- newArray 2 e + writeArray a 1 e' + unsafeFreezeArray a + | i > i' -> do + a <- newArray 2 e + writeArray a 0 e' + unsafeFreezeArray a + | i == i' -> do + a <- newArray 1 e' + unsafeFreezeArray a + +-- | +-- Unsafe. +-- Assumes that the list is sorted and contains no duplicate indexes. +{-# INLINE fromList #-} +fromList :: [(Index, e)] -> WordArray e +fromList l = + runST $ do + indices <- newSTRef 0 + array <- newArray (length l) undefined + forM_ (zip l [0..]) $ \((i, e), ai) -> do + modifySTRef indices $ Indices.insert i + writeArray array ai e + WordArray <$> readSTRef indices <*> unsafeFreezeArray array + +{-# INLINE toList #-} +toList :: WordArray e -> [(Index, e)] +toList (WordArray is a) = do + i <- Indices.toList is + e <- indexArrayM a (Indices.position i is) + return (i, e) + +-- | +-- Convert into a list representation. +{-# INLINE toMaybeList #-} +toMaybeList :: WordArray e -> [Maybe e] +toMaybeList w = do + i <- [0 .. pred Indices.maxSize] + return $ lookup i w + +{-# INLINE elements #-} +elements :: WordArray e -> [e] +elements (WordArray indices array) = + map (\i -> indexArray array (Indices.position i indices)) . + Indices.toList $ + indices + +-- | +-- Set an element value at the index. +{-# INLINE set #-} +set :: Index -> e -> WordArray e -> WordArray e +set i e (WordArray b a) = + let + sparseIndex = Indices.position i b + size = Indices.size b + in if Indices.elem i b + then + let a' = runST $ do + ma' <- newArray size undefined + forM_ [0 .. (size - 1)] $ \i -> indexArrayM a i >>= writeArray ma' i + writeArray ma' sparseIndex e + unsafeFreezeArray ma' + in WordArray b a' + else + let a' = runST $ do + ma' <- newArray (size + 1) undefined + forM_ [0 .. (sparseIndex - 1)] $ \i -> indexArrayM a i >>= writeArray ma' i + writeArray ma' sparseIndex e + forM_ [sparseIndex .. (size - 1)] $ \i -> indexArrayM a i >>= writeArray ma' (i + 1) + unsafeFreezeArray ma' + b' = Indices.insert i b + in WordArray b' a' + +-- | +-- Remove an element. +{-# INLINE unset #-} +unset :: Index -> WordArray e -> WordArray e +unset i (WordArray b a) = + if Indices.elem i b + then + let + b' = Indices.invert i b + a' = runST $ do + ma' <- newArray (pred size) undefined + forM_ [0 .. pred sparseIndex] $ \i -> indexArrayM a i >>= writeArray ma' i + forM_ [succ sparseIndex .. pred size] $ \i -> indexArrayM a i >>= writeArray ma' (pred i) + unsafeFreezeArray ma' + sparseIndex = Indices.position i b + size = Indices.size b + in WordArray b' a' + else WordArray b a + +-- | +-- Lookup an item at the index. +{-# INLINE lookup #-} +lookup :: Index -> WordArray e -> Maybe e +lookup i (WordArray b a) = + if Indices.elem i b + then Just (indexArray a (Indices.position i b)) + else Nothing + +-- | +-- Lookup strictly, using 'indexArrayM'. +{-# INLINE lookupM #-} +lookupM :: Monad m => Index -> WordArray e -> m (Maybe e) +lookupM i (WordArray b a) = + if Indices.elem i b + then liftM Just (indexArrayM a (Indices.position i b)) + else return Nothing + +-- | +-- Check, whether there is an element at the index. +{-# INLINE isSet #-} +isSet :: Index -> WordArray e -> Bool +isSet i = Indices.elem i . indices + +-- | +-- Get the amount of elements. +{-# INLINE size #-} +size :: WordArray e -> Int +size = Indices.size . indices + +{-# INLINE null #-} +null :: WordArray e -> Bool +null = Indices.null . indices + +{-# INLINE focusM #-} +focusM :: Monad m => Focus.StrategyM m a r -> Index -> WordArray a -> m (r, Maybe (WordArray a)) +focusM f i w = do + let em = lookup i w + (r, c) <- f em + let w' = case c of + Focus.Keep -> Nothing + Focus.Remove -> case em of + Nothing -> Nothing + Just _ -> Just $ unset i w + Focus.Replace e' -> Just $ set i e' w + return (r, w') diff --git a/benchmarks/PPoPP2019/src/STMContainers/WordArray/Indices.hs b/benchmarks/PPoPP2019/src/STMContainers/WordArray/Indices.hs new file mode 100644 index 0000000..4dcb381 --- /dev/null +++ b/benchmarks/PPoPP2019/src/STMContainers/WordArray/Indices.hs @@ -0,0 +1,69 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module STMContainers.WordArray.Indices where + +import STMContainers.Prelude hiding (toList, traverse_) +import qualified STMContainers.Prelude as Prelude + + +-- | +-- A compact set of indices. +type Indices = Int + +type Index = Int + +type Position = Int + +-- | +-- A number of indexes, preceding this one. +{-# INLINE position #-} +position :: Index -> Indices -> Position +position i b = popCount (b .&. (bit i - 1)) + +{-# INLINE singleton #-} +singleton :: Index -> Indices +singleton = bit + +{-# INLINE insert #-} +insert :: Index -> Indices -> Indices +insert i = (bit i .|.) + +{-# INLINE invert #-} +invert :: Index -> Indices -> Indices +invert i = (bit i `xor`) + +{-# INLINE elem #-} +elem :: Index -> Indices -> Bool +elem = flip testBit + +{-# INLINE size #-} +size :: Indices -> Int +size = popCount + +{-# INLINE null #-} +null :: Indices -> Bool +null = (== 0) + +{-# INLINE maxSize #-} +maxSize :: Int +maxSize = bitSize (undefined :: Indices) + +{-# INLINE fromList #-} +fromList :: [Index] -> Indices +fromList = Prelude.foldr (.|.) 0 . map bit + +{-# INLINE toList #-} +toList :: Indices -> [Index] +toList w = filter (testBit w) allIndices + +{-# INLINE positions #-} +positions :: Indices -> [Position] +positions = enumFromTo 0 . pred . size + +{-# NOINLINE allIndices #-} +allIndices :: [Index] +allIndices = [0 .. pred maxSize] + +{-# INLINE foldr #-} +foldr :: (Index -> r -> r) -> r -> Indices -> r +foldr s r ix = + Prelude.foldr (\i r' -> if testBit ix i then s i r' else r') r allIndices diff --git a/benchmarks/PPoPP2019/src/System/Random/PCG/Class.hs b/benchmarks/PPoPP2019/src/System/Random/PCG/Class.hs new file mode 100644 index 0000000..ddabe5d --- /dev/null +++ b/benchmarks/PPoPP2019/src/System/Random/PCG/Class.hs @@ -0,0 +1,473 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UnboxedTuples #-} +-- | +-- Module : System.Random.PCG.Class +-- Copyright : Copyright (c) 2014-2015, Christopher Chalmers +-- License : BSD3 +-- Maintainer : Christopher Chalmers +-- Stability : experimental +-- Portability: CPP +-- +-- Classes for working with random numbers along with utility functions. +-- In a future release this module may disappear and use another module +-- for this functionality. +module System.Random.PCG.Class + ( -- * Classes + Generator (..) + , Variate (..) + + -- * Type restricted versions + -- ** uniform + , uniformW8, uniformW16, uniformW32, uniformW64 + , uniformI8, uniformI16, uniformI32, uniformI64 + , uniformF, uniformD, uniformBool + + -- ** uniformR + , uniformRW8, uniformRW16, uniformRW32, uniformRW64 + , uniformRI8, uniformRI16, uniformRI32, uniformRI64 + , uniformRF, uniformRD, uniformRBool + + -- ** uniformB + , uniformBW8, uniformBW16, uniformBW32, uniformBW64 + , uniformBI8, uniformBI16, uniformBI32, uniformBI64 + , uniformBF, uniformBD, uniformBBool + + -- * Utilities + , Unsigned + , wordsTo64Bit + , wordToBool + , wordToFloat + , wordsToDouble + , sysRandom + ) where + +#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__) +#include "MachDeps.h" +#endif + +import Control.Monad +import Data.Bits +import Data.ByteString (useAsCString) +import Data.Int +import Data.Word +import Foreign (castPtr, peek) +import System.Entropy + +class Monad m => Generator g m where + uniform1 :: (Word32 -> a) -> g -> m a + uniform2 :: (Word32 -> Word32 -> a) -> g -> m a + uniform1B :: Integral a => (Word32 -> a) -> Word32 -> g -> m a + +class Variate a where + -- | Generate a uniformly distributed random vairate. + -- + -- * Use entire range for integral types. + -- + -- * Use (0,1] range for floating types. + uniform :: Generator g m => g -> m a + + -- | Generate a uniformly distributed random vairate in the given + -- range. + -- + -- * Use inclusive range for integral types. + -- + -- * Use (a,b] range for floating types. + uniformR :: Generator g m => (a,a) -> g -> m a + + -- | Generate a uniformly distributed random vairate in the range + -- [0,b). For integral types the bound must be less than the max bound + -- of 'Word32' (4294967295). Behaviour is undefined for negative + -- bounds. + uniformB :: Generator g m => a -> g -> m a + +------------------------------------------------------------------------ +-- Variate instances +------------------------------------------------------------------------ + +instance Variate Int8 where + uniform = uniform1 fromIntegral + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Int16 where + uniform = uniform1 fromIntegral + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Int32 where + uniform = uniform1 fromIntegral + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Int64 where + uniform = uniform2 wordsTo64Bit + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Word8 where + uniform = uniform1 fromIntegral + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Word16 where + uniform = uniform1 fromIntegral + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Word32 where + uniform = uniform1 fromIntegral + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Word64 where + uniform = uniform2 wordsTo64Bit + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Bool where + uniform = uniform1 wordToBool + {-# INLINE uniform #-} + uniformR (False,True) g = uniform g + uniformR (False,False) _ = return False + uniformR (True,True) _ = return True + uniformR (True,False) g = uniform g + {-# INLINE uniformR #-} + uniformB False _ = return False + uniformB _ g = uniform g + {-# INLINE uniformB #-} + +instance Variate Float where + uniform = uniform1 wordToFloat + {-# INLINE uniform #-} + uniformR (x1,x2) = uniform1 (\w -> x1 + (x2-x1) * wordToFloat w) + {-# INLINE uniformR #-} + -- subtract 2**(-33) to go from (0,b] to [0,b) (I think) + uniformB b g = (subtract 1.16415321826934814453125e-10) `liftM` uniformR (0,b) g + {-# INLINE uniformB #-} + +instance Variate Double where + uniform = uniform2 wordsToDouble + {-# INLINE uniform #-} + uniformR (x1,x2) = uniform2 (\w1 w2 -> x1 + (x2-x1) * wordsToDouble w1 w2) + {-# INLINE uniformR #-} + -- subtract 2**(-53) to go from (0,b] to [0,b) (I think) + uniformB b g = (subtract 1.1102230246251565404236316680908203125e-16) `liftM` uniformR (0,b) g + {-# INLINE uniformB #-} + +instance Variate Word where +#if WORD_SIZE_IN_BITS < 64 + uniform = uniform1 fromIntegral +#else + uniform = uniform2 wordsTo64Bit +#endif + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance Variate Int where +#if WORD_SIZE_IN_BITS < 64 + uniform = uniform1 fromIntegral +#else + uniform = uniform2 wordsTo64Bit +#endif + {-# INLINE uniform #-} + uniformR a g = uniformRange a g + {-# INLINE uniformR #-} + uniformB b g = uniform1B fromIntegral (fromIntegral b) g + {-# INLINE uniformB #-} + +instance (Variate a, Variate b) => Variate (a,b) where + uniform g = (,) `liftM` uniform g `ap` uniform g + {-# INLINE uniform #-} + uniformR ((x1,y1),(x2,y2)) g = (,) `liftM` uniformR (x1,x2) g `ap` uniformR (y1,y2) g + {-# INLINE uniformR #-} + uniformB (b1,b2) g = (,) `liftM` uniformB b1 g `ap` uniformB b2 g + {-# INLINE uniformB #-} + +instance (Variate a, Variate b, Variate c) => Variate (a,b,c) where + uniform g = (,,) `liftM` uniform g `ap` uniform g `ap` uniform g + {-# INLINE uniform #-} + uniformR ((x1,y1,z1),(x2,y2,z2)) g = + (,,) `liftM` uniformR (x1,x2) g `ap` uniformR (y1,y2) g `ap` uniformR (z1,z2) g + {-# INLINE uniformR #-} + uniformB (b1,b2,b3) g = (,,) `liftM` uniformB b1 g `ap` uniformB b2 g `ap` uniformB b3 g + {-# INLINE uniformB #-} + +instance (Variate a, Variate b, Variate c, Variate d) => Variate (a,b,c,d) where + uniform g = (,,,) `liftM` uniform g `ap` uniform g `ap` uniform g `ap` uniform g + {-# INLINE uniform #-} + uniformR ((x1,y1,z1,t1),(x2,y2,z2,t2)) g = + (,,,) `liftM` uniformR (x1,x2) g `ap` uniformR (y1,y2) g `ap` + uniformR (z1,z2) g `ap` uniformR (t1,t2) g + {-# INLINE uniformR #-} + uniformB (b1,b2,b3,b4) g = (,,,) `liftM` uniformB b1 g `ap` uniformB b2 g `ap` uniformB b3 g `ap` uniformB b4 g + {-# INLINE uniformB #-} + +------------------------------------------------------------------------ +-- Type restricted versions +------------------------------------------------------------------------ + +-- uniform ------------------------------------------------------------- + +uniformI8 :: Generator g m => g -> m Int8 +uniformI8 = uniform +{-# INLINE uniformI8 #-} + +uniformI16 :: Generator g m => g -> m Int16 +uniformI16 = uniform +{-# INLINE uniformI16 #-} + +uniformI32 :: Generator g m => g -> m Int32 +uniformI32 = uniform +{-# INLINE uniformI32 #-} + +uniformI64 :: Generator g m => g -> m Int64 +uniformI64 = uniform +{-# INLINE uniformI64 #-} + +uniformW8 :: Generator g m => g -> m Word8 +uniformW8 = uniform +{-# INLINE uniformW8 #-} + +uniformW16 :: Generator g m => g -> m Word16 +uniformW16 = uniform +{-# INLINE uniformW16 #-} + +uniformW32 :: Generator g m => g -> m Word32 +uniformW32 = uniform +{-# INLINE uniformW32 #-} + +uniformW64 :: Generator g m => g -> m Word64 +uniformW64 = uniform +{-# INLINE uniformW64 #-} + +uniformBool :: Generator g m => g -> m Bool +uniformBool = uniform +{-# INLINE uniformBool #-} + +uniformF :: Generator g m => g -> m Float +uniformF = uniform +{-# INLINE uniformF #-} + +uniformD :: Generator g m => g -> m Double +uniformD = uniform +{-# INLINE uniformD #-} + +-- uniformR ------------------------------------------------------------ + +uniformRI8 :: Generator g m => (Int8, Int8) -> g -> m Int8 +uniformRI8 = uniformR +{-# INLINE uniformRI8 #-} + +uniformRI16 :: Generator g m => (Int16, Int16) -> g -> m Int16 +uniformRI16 = uniformR +{-# INLINE uniformRI16 #-} + +uniformRI32 :: Generator g m => (Int32, Int32) -> g -> m Int32 +uniformRI32 = uniformR +{-# INLINE uniformRI32 #-} + +uniformRI64 :: Generator g m => (Int64, Int64) -> g -> m Int64 +uniformRI64 = uniformR +{-# INLINE uniformRI64 #-} + +uniformRW8 :: Generator g m => (Word8, Word8) -> g -> m Word8 +uniformRW8 = uniformR +{-# INLINE uniformRW8 #-} + +uniformRW16 :: Generator g m => (Word16, Word16) -> g -> m Word16 +uniformRW16 = uniformR +{-# INLINE uniformRW16 #-} + +uniformRW32 :: Generator g m => (Word32, Word32) -> g -> m Word32 +uniformRW32 = uniformR +{-# INLINE uniformRW32 #-} + +uniformRW64 :: Generator g m => (Word64, Word64) -> g -> m Word64 +uniformRW64 = uniformR +{-# INLINE uniformRW64 #-} + +uniformRBool :: Generator g m => (Bool, Bool) -> g -> m Bool +uniformRBool = uniformR +{-# INLINE uniformRBool #-} + +uniformRF :: Generator g m => (Float, Float) -> g -> m Float +uniformRF = uniformR +{-# INLINE uniformRF #-} + +uniformRD :: Generator g m => (Double, Double) -> g -> m Double +uniformRD = uniformR +{-# INLINE uniformRD #-} + +-- uniformB ------------------------------------------------------------ + +uniformBI8 :: Generator g m => Int8 -> g -> m Int8 +uniformBI8 = uniformB +{-# INLINE uniformBI8 #-} + +uniformBI16 :: Generator g m => Int16 -> g -> m Int16 +uniformBI16 = uniformB +{-# INLINE uniformBI16 #-} + +uniformBI32 :: Generator g m => Int32 -> g -> m Int32 +uniformBI32 = uniformB +{-# INLINE uniformBI32 #-} + +uniformBI64 :: Generator g m => Int64 -> g -> m Int64 +uniformBI64 = uniformB +{-# INLINE uniformBI64 #-} + +uniformBW8 :: Generator g m => Word8 -> g -> m Word8 +uniformBW8 = uniformB +{-# INLINE uniformBW8 #-} + +uniformBW16 :: Generator g m => Word16 -> g -> m Word16 +uniformBW16 = uniformB +{-# INLINE uniformBW16 #-} + +uniformBW32 :: Generator g m => Word32 -> g -> m Word32 +uniformBW32 = uniformB +{-# INLINE uniformBW32 #-} + +uniformBW64 :: Generator g m => Word64 -> g -> m Word64 +uniformBW64 = uniformB +{-# INLINE uniformBW64 #-} + +uniformBBool :: Generator g m => Bool -> g -> m Bool +uniformBBool = uniformB +{-# INLINE uniformBBool #-} + +uniformBF :: Generator g m => Float -> g -> m Float +uniformBF = uniformB +{-# INLINE uniformBF #-} + +uniformBD :: Generator g m => Double -> g -> m Double +uniformBD = uniformB +{-# INLINE uniformBD #-} + +------------------------------------------------------------------------ +-- Utilities +------------------------------------------------------------------------ + +sub :: (Integral a, Integral (Unsigned a)) => a -> a -> Unsigned a +sub x y = fromIntegral x - fromIntegral y +{-# INLINE sub #-} + +add :: (Integral a, Integral (Unsigned a)) => a -> Unsigned a -> a +add m x = m + fromIntegral x +{-# INLINE add #-} + +wordsTo64Bit :: Integral a => Word32 -> Word32 -> a +wordsTo64Bit x y = + fromIntegral ((fromIntegral x `shiftL` 32) .|. fromIntegral y :: Word64) +{-# INLINE wordsTo64Bit #-} + +wordToBool :: Word32 -> Bool +wordToBool i = (i .&. 1) /= 0 +{-# INLINE wordToBool #-} + +wordToFloat :: Word32 -> Float +wordToFloat x = (fromIntegral i * m_inv_32) + 0.5 + m_inv_33 + where m_inv_33 = 1.16415321826934814453125e-10 + m_inv_32 = 2.3283064365386962890625e-10 + i = fromIntegral x :: Int32 +{-# INLINE wordToFloat #-} + +wordsToDouble :: Word32 -> Word32 -> Double +wordsToDouble x y = (fromIntegral u * m_inv_32 + (0.5 + m_inv_53) + + fromIntegral (v .&. 0xFFFFF) * m_inv_52) + where m_inv_52 = 2.220446049250313080847263336181640625e-16 + m_inv_53 = 1.1102230246251565404236316680908203125e-16 + m_inv_32 = 2.3283064365386962890625e-10 + u = fromIntegral x :: Int32 + v = fromIntegral y :: Int32 +{-# INLINE wordsToDouble #-} + +-- IO randoms + +-- | Generate a random number using "System.Entropy". +-- +-- Use RDRAND if available and XOR with @\/dev\/urandom@ on Unix and +-- CryptAPI on Windows. This entropy is considered cryptographically +-- secure but not true entropy. +sysRandom :: IO Word64 +sysRandom = do + bs <- getEntropy 8 + useAsCString bs $ peek . castPtr + +uniformRange + :: (Generator g m, Integral a, Variate a, Integral (Unsigned a), + Bounded (Unsigned a), Variate (Unsigned a)) + => (a,a) -> g -> m a +uniformRange (x1,x2) g + | n == 0 = uniform g -- Abuse overflow in unsigned types + | otherwise = loop + where + -- Allow ranges where x2= 707 +{-# LANGUAGE RoleAnnotations #-} +#endif +-- | +-- Module : System.Random.PCG.Fast.Pure +-- Copyright : Copyright (c) 2015, Christopher Chalmers +-- License : BSD3 +-- Maintainer : Christopher Chalmers +-- Stability : experimental +-- Portability: CPP +-- +-- Experimental pure haskell version of the fast variant of the PCG +-- random number generator. This module can perform faster than the c +-- bindings version, especially for parallel code. +-- +-- See for details. +-- +-- @ +-- import Control.Monad.ST +-- import System.Random.PCG.Fast.Pure +-- +-- three :: [Double] +-- three = runST $ do +-- g <- create +-- a <- uniform g +-- b <- uniform g +-- c <- uniform g +-- return [a,b,c] +-- @ +module System.Random.PCG.Fast.Pure + ( -- * Gen + Gen, GenIO + , create, initialize + + -- * Getting random numbers + , Variate (..) + , advance, retract + + -- * Seeds + , FrozenGen, save, restore, seed, initFrozen + + -- * Type restricted versions + -- ** uniform + , uniformW8, uniformW16, uniformW32, uniformW64 + , uniformI8, uniformI16, uniformI32, uniformI64 + , uniformF, uniformD, uniformBool + + -- ** uniformR + , uniformRW8, uniformRW16, uniformRW32, uniformRW64 + , uniformRI8, uniformRI16, uniformRI32, uniformRI64 + , uniformRF, uniformRD, uniformRBool + + -- ** uniformB + , uniformBW8, uniformBW16, uniformBW32, uniformBW64 + , uniformBI8, uniformBI16, uniformBI32, uniformBI64 + , uniformBF, uniformBD, uniformBBool + + , Pair(..), pair + ) where + +import Control.Monad.Primitive +import Data.Bits +import Data.Primitive.ByteArray +import GHC.Word + +import System.Random.PCG.Class + +newtype FrozenGen = F Word64 + deriving (Show, Eq, Ord) + +-- | State of the random number generator. +newtype Gen = G (MutableByteArray RealWorld) + +type GenIO = Gen + +-- $setup +-- >>> import System.Random.PCG.Fast.Pure +-- >>> import System.Random.PCG.Class +-- >>> import Control.Monad + +-- internals ----------------------------------------------------------- + +data Pair = P {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word32 + deriving Show + +fastMultiplier :: Word64 +fastMultiplier = 6364136223846793005 + +-- Compute the next state of the generator +state :: Word64 -> Word64 +state s = s * fastMultiplier + +-- Compute the output random number from the state of the generator. +output :: Word64 -> Word32 +output s = fromIntegral $ + ((s `shiftR` 22) `xor` s) `unsafeShiftR` (fromIntegral (s `shiftR` 61) + 22) + +-- Compute the next state and output in a strict pair. +pair :: Word64 -> Pair +pair s = P (state s) (output s) + +-- Given some bound and the generator, compute the new state and bounded +-- random number. +bounded :: Word32 -> Word64 -> Pair +bounded b s0 = go s0 + where + t = negate b `mod` b + go !s | r >= t = P s' (r `mod` b) + | otherwise = go s' + where P s' r = pair s +{-# INLINE bounded #-} + +advancing + :: Word64 -- amount to advance by + -> Word64 -- state + -> Word64 -- multiplier + -> Word64 -- increment + -> Word64 -- new state +advancing d0 s m0 p0 = go d0 m0 p0 1 0 + where + go d cm cp am ap + | d <= 0 = am * s + ap + | odd d = go d' cm' cp' (am * cm) (ap * cm + cp) + | otherwise = go d' cm' cp' am ap + where + cm' = cm * cm + cp' = (cm + 1) * cp + d' = d `div` 2 + +advanceFast :: Word64 -> FrozenGen -> FrozenGen +advanceFast d (F s) = F $ advancing d s fastMultiplier 0 + +------------------------------------------------------------------------ +-- Seed +------------------------------------------------------------------------ + +-- | Save the state of a 'Gen' in a 'Seed'. +save :: GenIO -> IO FrozenGen +save (G a) = F <$> readByteArray a 0 +{-# INLINE save #-} + +-- | Restore a 'Gen' from a 'Seed'. +restore :: FrozenGen -> IO GenIO +restore (F f) = do +-- a <- newByteArray 8 + a <- newByteArray 48 -- 8 Fill a whole cacheline + writeByteArray a 0 f + return $! G a +{-# INLINE restore #-} + +-- | Generate a new seed using single 'Word64'. +-- +-- >>> initFrozen 0 +-- FrozenGen 1 +initFrozen :: Word64 -> FrozenGen +initFrozen w = F (w .|. 1) + +-- | Standard initial seed. +seed :: FrozenGen +seed = F 0xcafef00dd15ea5e5 + +-- | Create a 'Gen' from a fixed initial seed. +create :: IO GenIO +create = restore seed + +-- | Initialize a generator a single word. +-- +-- >>> initialize 0 >>= save +-- FrozenGen 1 +initialize :: Word64 -> IO GenIO +initialize a = restore (initFrozen a) + +-- | Advance the given generator n steps in log(n) time. (Note that a +-- \"step\" is a single random 32-bit (or less) 'Variate'. Data types +-- such as 'Double' or 'Word64' require two \"steps\".) +-- +-- >>> create >>= \g -> replicateM_ 1000 (uniformW32 g) >> uniformW32 g +-- 3725702568 +-- >>> create >>= \g -> replicateM_ 500 (uniformD g) >> uniformW32 g +-- 3725702568 +-- >>> create >>= \g -> advance 1000 g >> uniformW32 g +-- 3725702568 +advance :: Word64 -> GenIO -> IO () +advance u (G a) = do + s <- readByteArray a 0 + let (F s') = advanceFast u (F s) + writeByteArray a 0 s' +{-# INLINE advance #-} + +-- | Retract the given generator n steps in log(2^64-n) time. This +-- is just @advance (-n)@. +-- +-- >>> create >>= \g -> replicateM 3 (uniformW32 g) +-- [2951688802,2698927131,361549788] +-- >>> create >>= \g -> retract 1 g >> replicateM 3 (uniformW32 g) +-- [954135925,2951688802,2698927131] +retract :: Word64 -> GenIO -> IO () +retract u g = advance (-u) g +{-# INLINE retract #-} + +------------------------------------------------------------------------ +-- Instances +------------------------------------------------------------------------ + +instance Generator GenIO IO where + uniform1 f (G a) = do + s <- readByteArray a 0 + let P s' r = pair s + writeByteArray a 0 s' + return $! f r + {-# INLINE uniform1 #-} + + uniform2 f (G a) = do + s <- readByteArray a 0 + let s' = state s + writeByteArray a 0 (state s') + return $! f (output s) (output s') + {-# INLINE uniform2 #-} + + uniform1B f b (G a) = do + s <- readByteArray a 0 + let P s' r = bounded b s + writeByteArray a 0 s' + return $! f r + {-# INLINE uniform1B #-} diff --git a/benchmarks/PPoPP2019/src/Throughput.hs b/benchmarks/PPoPP2019/src/Throughput.hs new file mode 100644 index 0000000..1167521 --- /dev/null +++ b/benchmarks/PPoPP2019/src/Throughput.hs @@ -0,0 +1,163 @@ +{-# LANGUAGE ForeignFunctionInterface #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE CPP #-} +module Throughput + ( throughputMain + , throughputMain' + , locallyCountingForever + , locallyCountingForever' + , locallyCountingIterate + , locallyCountingIterate' + , voidForever + , ThroughputAction + + , newCount + , readCount + , incCount + , Count(..) + , CountIO + ) where + +import GHC.Conc.Sync +import Control.Concurrent +import Control.Concurrent.MVar +import Control.Monad +import Control.Applicative +import Control.Exception (finally) + +import Data.IORef +#ifdef BYTECOUNTER +import Control.Monad.Primitive +import Data.Primitive.ByteArray +#endif + +import GHC.Word + +import System.IO + + +-- Here we provide a framework for running some worker threads for a given +-- amount of time. We may also want to collect data as these threads work in a +-- thread local way. To achieve this we will run the worker action forever and +-- kill all the worker threads after the desired time has elapsed. The +-- framework will wait for all the threads to complete after killing and then +-- record and result in the actual elapsed time. Each worker action given must +-- spawn its own looping thread. Wrappers for this looping and spawning are +-- given as 'locallyCountingForever' and 'voidForever'. The first one counts +-- every time the action runs in a thread local counter and gives access to +-- that counter with an 'IO' action. The second doesn't do any reporting, but +-- simply loops forever in a thread. + +type ThroughputAction a = IO (ThreadId, a) +#ifdef BYTECOUNTER +newtype Count = C (MutableByteArray RealWorld) + +type CountIO = Count + +newCount :: Word64 -> IO Count +newCount i = do + a <- newByteArray 48 -- 8 (fill a whole cacheline instead) + writeByteArray a 0 i + return $! C a +{-# INLINE newCount #-} + +readCount :: Count -> IO Word64 +readCount (C a) = readByteArray a 0 +{-# INLINE readCount #-} + +incCount :: Count -> IO () +incCount (C a) = do + c <- readByteArray a 0 + writeByteArray a 0 (c+1::Word64) +{-# INLINE incCount #-} +#else +newtype Count = C (IORef Word64) +type CountIO = Count + +newCount :: Word64 -> IO Count +newCount i = do + a <- newIORef i + return $! C a +{-# INLINE newCount #-} + +readCount :: Count -> IO Word64 +readCount (C a) = readIORef a +{-# INLINE readCount #-} + +incCount :: Count -> IO () +incCount (C a) = do + modifyIORef' a succ +{-# INLINE incCount #-} +#endif + + +locallyCountingIterate' :: Int -> (a -> IO a) -> a -> ThroughputAction (IO Word64) +locallyCountingIterate' i step initial = do + c <- newCount 0 + t <- forkOn i $ act c initial + return (t, readCount c) + where + act c x = do + x' <- step x + incCount c + act c x' + +locallyCountingIterate :: (a -> IO a) -> a -> ThroughputAction (IO Word64) +locallyCountingIterate step initial = do + c <- newCount 0 + t <- forkIO $ act c initial + return (t, readCount c) + where + act c x = do + x' <- step x + incCount c + act c x' + + +locallyCountingForever' :: Int -> IO () -> ThroughputAction (IO Word64) +locallyCountingForever' i act = do + c <- newCount 0 + t <- forkOn i . forever $ (incCount c >> act) + return (t, readCount c) + +locallyCountingForever :: IO () -> ThroughputAction (IO Word64) +locallyCountingForever act = do + c <- newCount 0 + t <- forkIO . forever $ (incCount c >> act) + return (t, readCount c) + +voidForever :: Int -> IO () -> ThroughputAction () +voidForever i act = do + t <- forkOn i . forever $ act + return (t, ()) + +throughputMain :: Int -> [IO ()] -> IO (Double,Double) +throughputMain timeout ws = fst <$> throughputMain' timeout (zipWith voidForever [0..length ws-1] ws) + +throughputMain' :: Int -> [ThroughputAction a] -> IO ((Double,Double), [a]) +throughputMain' timeout ws = do + -- Record start time. + start <- getTime + + -- Start threads + (unzip -> (ts,rs),vs) <- fmap unzip . forM ws $ \w -> do + v <- newEmptyMVar + r <- finally w (putMVar v ()) + return (r,v) + + -- Wait for time + threadDelay timeout + + endA <- getTime + + -- Kill threads + mapM_ killThread ts + mapM_ takeMVar vs -- Wait for the finish signal + + -- result in total time + end <- getTime + + return $! ((end - start, endA - start), rs) + + +foreign import ccall unsafe "throughput_gettime" getTime :: IO Double diff --git a/benchmarks/PPoPP2019/src/Treap.hs b/benchmarks/PPoPP2019/src/Treap.hs new file mode 100644 index 0000000..533397c --- /dev/null +++ b/benchmarks/PPoPP2019/src/Treap.hs @@ -0,0 +1,64 @@ +{-# LANGUAGE CPP #-} +module Treap + ( Tree + , mkTree + + , insert + , delete + , update + , get + , contains + + , benchCode + ) where + +import Control.Applicative +import Data.Word +import Data.Maybe (isJust) + +import GHC.Conc + +#if defined(MUT) +import qualified TreapMutSTM as T +#define M STM +#elif defined(TVAR) +import qualified TreapTVar as T +#define M STM +#else +#error Unknown Treap Variant +#endif + +benchCode :: String +benchCode = T.benchCode + +type Tree = T.Treap + +---------------------------------- +-- Public API +-- +mkTree :: M Tree +mkTree = T.mkTreap + +-- #define TESTCODE +#ifdef TESTCODE +insert :: Tree -> Word -> Word -> M Bool +insert = T.insertV + +delete :: Tree -> Word -> M Bool +delete = T.deleteV +#else +insert :: Tree -> Word -> Word -> M Bool +insert = T.insert + +delete :: Tree -> Word -> M Bool +delete = T.delete +#endif + +get :: Tree -> Word -> M (Maybe Word) +get = T.get + +update :: Tree -> Word -> Word -> M Bool +update t k v = T.insert t k v >> return False + +contains :: Tree -> Word -> M Bool +contains t k = T.contains t k diff --git a/benchmarks/PPoPP2019/src/TreapMut.hs b/benchmarks/PPoPP2019/src/TreapMut.hs new file mode 100644 index 0000000..f94bdf9 --- /dev/null +++ b/benchmarks/PPoPP2019/src/TreapMut.hs @@ -0,0 +1,305 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE MutableFields #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE BangPatterns #-} +{- LANGUAGE Strict -} +-- #define TESTCODE +module TreapMut + ( Treap + , mkTreap + , insert + , get + , contains + , delete + + , benchCode +#ifdef TESTCODE + , verifyTreap + , insertV + , deleteV +#endif + ) where + +import qualified System.Random.PCG.Fast.Pure as R +-- import qualified Data.Vector.Unboxed.Mutable as U +import Data.Primitive.ByteArray +import Control.Monad.Primitive +import System.Random.PCG.Class (sysRandom) +import Data.Word (Word64, Word32, Word) +import Control.Monad + +import GHC.Types +import GHC.Prim +import GHC.IO +import GHC.Int +import GHC.Conc + +benchCode :: String +benchCode = "TreapMut" + +------------------------------ +-- Random + +-- | Constant for aligning RNG seed to cache-line +-- which usually is 64 Kb long (while seed only is 'Data.Word64'). +cacheFactor :: Int +cacheFactor = 8 + +type RandomState = MutableByteArray + +-- Obtains PCG state, generate random value and store new state +gen :: RandomState -> Int -> IO Word32 +gen v !i = do + let i' = i * cacheFactor + st <- readByteArray v i' + let (R.P st' x) = R.pair st + writeByteArray v i' st' + return x + + +---------------------------------------------- +-- mutable + +type Mutable a = Ref# RealWorld a + +readRef :: Mutable a -> IO a +readRef r = IO (\s# -> readRef# r s#) + +writeRef :: Mutable a -> a -> IO () +writeRef r l = IO (\s1# -> case writeRef# r l s1# of + s2# -> (# s2#, () #)) + +--------------------------------------------- +-- Treap + +type Key = Word +type Value = Word +type Priority = Word32 -- native to our PRNG + +data Treap where + Treap :: RandomState + -> mutable Node + -> IO Treap + +data Node where + Node :: !Key -- Key + -> !Value -- Value + -> !Priority -- Priority + -> mutable Node -- Left + -> mutable Node -- Right + -> IO Node + Nil :: Node + +key :: Node -> Key +key (Node k _ _ _ _) = k + +value :: Node -> Value +value (Node _ v _ _ _) = v + +priority :: Node -> Priority +priority (Node _ _ p _ _) = p + + +left :: Node -> Mutable Node +left (Node _ _ _ l _) = l + +right :: Node -> Mutable Node +right (Node _ _ _ _ r) = r + +instance Show Node where + show Nil = "Nil" + show (Node k v _ _ _) = show ("Node " ++ show k ++ " " ++ show v) + +instance Eq Node where + Nil == Nil = True + Nil == _ = False + _ == Nil = False + +-- x == y = case reallyUnsafePtrEquality# x y of +-- 0# -> False +-- _ -> True + (Node k _ _ _ _) == (Node k' _ _ _ _) = k == k' + +isNil :: Node -> Bool +isNil Nil = True +isNil _ = False + +isNode = not . isNil + +mkTreap :: IO Treap +mkTreap = do + cn <- getNumCapabilities + statev <- newByteArray (cn * cacheFactor * 8) + forM_ [0..cn-1] $ \i -> do + seed <- sysRandom + writeByteArray statev (i * cacheFactor) seed + Treap statev Nil + +-- Implementation somewhat follows: +-- https://github.com/frol/completely-unscientific-benchmarks/blob/master/c%23/Program.cs +mkNode :: Treap -> Key -> Value -> IO Node +mkNode (Treap s _) k v = do + cn <- do + tid <- myThreadId + fst `fmap` threadCapability tid + p <- gen s cn + Node k v p Nil Nil + +merge :: Node -> Node -> IO Node +merge Nil g = return g +merge l Nil = return l +merge l@(Node _ _ lp _ lr) g@(Node _ _ gp gl _) + | lp < gp = do + lrn <- readRef lr + merge lrn g >>= writeRef lr + return l + | otherwise = do + gln <- readRef gl + merge l gln >>= writeRef gl + return g + +splitL :: Key -> Node -> IO (Node, Node) +splitL _ Nil = return (Nil, Nil) +splitL key n@(Node k _ _ l r) + | k < key = do + (f, s) <- readRef r >>= splitL key + writeRef r f + return (n, s) + | otherwise = do + (f, s) <- readRef l >>= splitL key + writeRef l s + return (f, n) + +splitLEq :: Key -> Node -> IO (Node, Node) +splitLEq _ Nil = return (Nil, Nil) +splitLEq key n@(Node k _ _ l r) + | k <= key = do + (f, s) <- readRef r >>= splitLEq key + writeRef r f + return (n, s) + | otherwise = do + (f, s) <- readRef l >>= splitLEq key + writeRef l s + return (f, n) + +merge3 :: Node -> Node -> Node -> IO Node +merge3 l e g = do + l' <- merge l e + merge l' g + +split :: Key -> Node -> IO (Node, Node, Node) +split k n = do + (lof, los) <- splitL k n + (egf, egs) <- splitLEq k los + return (lof, egf, egs) + +nodeContains :: Key -> Node -> IO Bool +nodeContains k n = do + let loop Nil = return False + loop (Node k' _ _ l r) + | k < k' = readRef l >>= loop + | k > k' = readRef r >>= loop + | otherwise = return True + loop n + +nodeGet :: Key -> Node -> IO (Maybe Value) +nodeGet k n = do + let loop Nil = return Nothing + loop (Node k' v _ l r) + | k < k' = readRef l >>= loop + | k > k' = readRef r >>= loop + | otherwise = return (Just v) + loop n + +get :: Treap -> Key -> IO (Maybe Value) +get (Treap _ n) k = readRef n >>= nodeGet k + +contains :: Treap -> Key -> IO Bool +contains (Treap _ n) k = readRef n >>= nodeContains k + +insert :: Treap -> Key -> Value -> IO Bool +insert t@(Treap s nr) k v = do + n <- readRef nr + b <- nodeContains k n + if b + then return False + else do + (l,e,g) <- split k n + e' <- mkNode t k v + merge3 l e' g >>= writeRef nr + return True + +delete :: Treap -> Key -> IO Bool +delete (Treap _ nr) k = do + n <- readRef nr + b <- nodeContains k n + if not b + then return False + else do + (l,e,g) <- split k n + merge l g >>= writeRef nr + return True + +#ifdef TESTCODE +assertIO :: Show s => String -> s -> Bool -> IO () +assertIO _ _ True = return () +assertIO s v False = error (s ++ "\n -- " ++ show v) + +verifyNode :: Node -> IO Int +verifyNode Nil = return 0 +verifyNode (Node k v p l r) = do + ln <- readRef l + rn <- readRef r + + when (isNode ln) $ do + assertIO "left <" (key ln, k) (key ln < k) + assertIO "prio >=" (priority ln, p) (priority ln >= p) + when (isNode rn) $ do + assertIO "right >" (key rn, k) (key rn > k) + assertIO "prio >=" (priority rn, p) (priority rn >= p) + + ls <- verifyNode ln + rs <- verifyNode rn + return (ls + rs + 1) + +verifyTreap :: Treap -> IO Int +verifyTreap (Treap _ n) = readRef n >>= verifyNode + +insertV :: Treap -> Key -> Value -> IO Bool +insertV t k v = do + s1 <- verifyTreap t + b1 <- contains t k + b2 <- insert t k v + b3 <- contains t k + s2 <- verifyTreap t + assertIO "insert check" (b1,b2,b3) + ( (b1 && not b2 && b3) -- already present, stays + || (not b1 && b2 && b3)) -- not present, is added. + mv <- get t k + assertIO "insert value check" (mv, v, b2) + (case mv of + Nothing -> False + Just v' -> v == v' || not b2) + assertIO "insert size" (s1,s2,b2) + ( (b2 && s1 == s2 - 1) + || (not b2 && s1 == s2)) + return b2 + +deleteV :: Treap -> Key -> IO Bool +deleteV t k = do + s1 <- verifyTreap t + b1 <- contains t k + b2 <- delete t k + b3 <- contains t k + s2 <- verifyTreap t + assertIO "delete check" (b1,b2,b3) + ( (not b1 && not b2 && not b3) -- already gone, stays gone. + || (b1 && b2 && not b3)) -- is present, goes away. + assertIO "delete size" (s1,s2,b2) + ( (b2 && s1 == s2 + 1) + || (not b2 && s1 == s2)) + return b2 +#endif diff --git a/benchmarks/PPoPP2019/src/TreapMutSTM.hs b/benchmarks/PPoPP2019/src/TreapMutSTM.hs new file mode 100644 index 0000000..59053c9 --- /dev/null +++ b/benchmarks/PPoPP2019/src/TreapMutSTM.hs @@ -0,0 +1,308 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE MutableFields #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE Strict #-} +-- #define TESTCODE +module TreapMutSTM + ( Treap + , mkTreap + , insert + , get + , contains + , delete + + , benchCode +#ifdef TESTCODE + , verifyTreap + , insertV + , deleteV +#endif + ) where + +import qualified System.Random.PCG.Fast.Pure as R +-- import qualified Data.Vector.Unboxed.Mutable as U +import Data.Primitive.ByteArray +import Control.Monad.Primitive +import System.Random.PCG.Class (sysRandom) +import Data.Word (Word64, Word32, Word) +import Control.Monad + +import GHC.Types +import GHC.Prim +import GHC.IO +import GHC.Int +import GHC.Conc + +benchCode :: String +benchCode = "TreapMutSTM" + +------------------------------ +-- Random + +-- | Constant for aligning RNG seed to cache-line +-- which usually is 64 Kb long (while seed only is 'Data.Word64'). +cacheFactor :: Int +cacheFactor = 8 + +type RandomState = MutableByteArray RealWorld + +-- Obtains PCG state, generate random value and store new state +gen :: RandomState -> Int -> IO Word32 +gen v !i = do + let i' = i * cacheFactor + st <- readByteArray v i' + let (R.P st' x) = R.pair st + writeByteArray v i' st' + return x + + +---------------------------------------------- +-- mutable + +type Mutable a = Ref# RealWorld a + +readRef :: Mutable a -> STM a +readRef r = STM (\s# -> readTRef# r s#) + +writeRef :: Mutable a -> a -> STM () +writeRef r l = STM (\s1# -> case writeTRef# r l s1# of + s2# -> (# s2#, () #)) + +--------------------------------------------- +-- Treap + +type Key = Word +type Value = Word +type Priority = Word32 -- native to our PRNG + +data Treap where + Treap :: RandomState + -> mutable Node + -> STM Treap + +data Node where + Node :: !Key -- Key + -> !Value -- Value + -> !Priority -- Priority + -> mutable Node -- Left + -> mutable Node -- Right + -> STM Node + Nil :: Node + +key :: Node -> Key +key (Node k _ _ _ _) = k + +value :: Node -> Value +value (Node _ v _ _ _) = v + +priority :: Node -> Priority +priority (Node _ _ p _ _) = p + + +left :: Node -> Mutable Node +left (Node _ _ _ l _) = l + +right :: Node -> Mutable Node +right (Node _ _ _ _ r) = r + +instance Show Node where + show Nil = "Nil" + show (Node k v _ _ _) = show ("Node " ++ show k ++ " " ++ show v) + +instance Eq Node where + Nil == Nil = True + Nil == _ = False + _ == Nil = False + +-- x == y = case reallyUnsafePtrEquality# x y of +-- 0# -> False +-- _ -> True + (Node k _ _ _ _) == (Node k' _ _ _ _) = k == k' + +isNil :: Node -> Bool +isNil Nil = True +isNil _ = False + +isNode = not . isNil + +mkTreap :: STM Treap +mkTreap = do + state <- unsafeIOToSTM $ do + cn <- getNumCapabilities + statev <- newByteArray (cn * cacheFactor * 8) + forM_ [0..cn-1] $ \i -> do + seed <- sysRandom + writeByteArray statev (i * cacheFactor) seed + return statev + Treap state Nil -- This isn't ideal... + +-- Implementation somewhat follows: +-- https://github.com/frol/completely-unscientific-benchmarks/blob/master/c%23/Program.cs +mkNode :: Treap -> Key -> Value -> STM Node +mkNode (Treap s _) k v = do + p <- unsafeIOToSTM $ do + cn <- do + tid <- myThreadId + fst `fmap` threadCapability tid + gen s cn + Node k v p Nil Nil + +merge :: Node -> Node -> STM Node +merge Nil g = return g +merge l Nil = return l +merge l@(Node _ _ lp _ lr) g@(Node _ _ gp gl _) + | lp < gp = do + lrn <- readRef lr + merge lrn g >>= writeRef lr + return l + | otherwise = do + gln <- readRef gl + merge l gln >>= writeRef gl + return g + +splitL :: Key -> Node -> STM (Node, Node) +splitL _ Nil = return (Nil, Nil) +splitL key n@(Node k _ _ l r) + | k < key = do + (f, s) <- readRef r >>= splitL key + writeRef r f + return (n, s) + | otherwise = do + (f, s) <- readRef l >>= splitL key + writeRef l s + return (f, n) + +splitLEq :: Key -> Node -> STM (Node, Node) +splitLEq _ Nil = return (Nil, Nil) +splitLEq key n@(Node k _ _ l r) + | k <= key = do + (f, s) <- readRef r >>= splitLEq key + writeRef r f + return (n, s) + | otherwise = do + (f, s) <- readRef l >>= splitLEq key + writeRef l s + return (f, n) + +merge3 :: Node -> Node -> Node -> STM Node +merge3 l e g = do + l' <- merge l e + merge l' g + +split :: Key -> Node -> STM (Node, Node, Node) +split k n = do + (lof, los) <- splitL k n + (egf, egs) <- splitLEq k los + return (lof, egf, egs) + +nodeContains :: Key -> Node -> STM Bool +nodeContains k n = do + let loop Nil = return False + loop (Node k' _ _ l r) + | k < k' = readRef l >>= loop + | k > k' = readRef r >>= loop + | otherwise = return True + loop n + +nodeGet :: Key -> Node -> STM (Maybe Value) +nodeGet k n = do + let loop Nil = return Nothing + loop (Node k' v _ l r) + | k < k' = readRef l >>= loop + | k > k' = readRef r >>= loop + | otherwise = return (Just v) + loop n + +get :: Treap -> Key -> STM (Maybe Value) +get (Treap _ n) k = readRef n >>= nodeGet k + +contains :: Treap -> Key -> STM Bool +contains (Treap _ n) k = readRef n >>= nodeContains k + +insert :: Treap -> Key -> Value -> STM Bool +insert t@(Treap s nr) k v = do + n <- readRef nr + b <- nodeContains k n + if b + then return False + else do + (l,e,g) <- split k n + e' <- mkNode t k v + merge3 l e' g >>= writeRef nr + return True + +delete :: Treap -> Key -> STM Bool +delete (Treap _ nr) k = do + n <- readRef nr + b <- nodeContains k n + if not b + then return False + else do + (l,e,g) <- split k n + merge l g >>= writeRef nr + return True + +#ifdef TESTCODE +assertSTM :: Show s => String -> s -> Bool -> STM () +assertSTM _ _ True = return () +assertSTM s v False = error (s ++ "\n -- " ++ show v) + +verifyNode :: Node -> STM Int +verifyNode Nil = return 0 +verifyNode (Node k v p l r) = do + ln <- readRef l + rn <- readRef r + + when (isNode ln) $ do + assertSTM "left <" (key ln, k) (key ln < k) + assertSTM "prio >=" (priority ln, p) (priority ln >= p) + when (isNode rn) $ do + assertSTM "right >" (key rn, k) (key rn > k) + assertSTM "prio >=" (priority rn, p) (priority rn >= p) + + ls <- verifyNode ln + rs <- verifyNode rn + return (ls + rs + 1) + +verifyTreap :: Treap -> STM Int +verifyTreap (Treap _ n) = readRef n >>= verifyNode + +insertV :: Treap -> Key -> Value -> STM Bool +insertV t k v = do + s1 <- verifyTreap t + b1 <- contains t k + b2 <- insert t k v + b3 <- contains t k + s2 <- verifyTreap t + assertSTM "insert check" (b1,b2,b3) + ( (b1 && not b2 && b3) -- already present, stays + || (not b1 && b2 && b3)) -- not present, is added. + mv <- get t k + assertSTM "insert value check" (mv, v, b2) + (case mv of + Nothing -> False + Just v' -> v == v' || not b2) + assertSTM "insert size" (s1,s2,b2) + ( (b2 && s1 == s2 - 1) + || (not b2 && s1 == s2)) + return b2 + +deleteV :: Treap -> Key -> STM Bool +deleteV t k = do + s1 <- verifyTreap t + b1 <- contains t k + b2 <- delete t k + b3 <- contains t k + s2 <- verifyTreap t + assertSTM "delete check" (b1,b2,b3) + ( (not b1 && not b2 && not b3) -- already gone, stays gone. + || (b1 && b2 && not b3)) -- is present, goes away. + assertSTM "delete size" (s1,s2,b2) + ( (b2 && s1 == s2 + 1) + || (not b2 && s1 == s2)) + return b2 +#endif diff --git a/benchmarks/PPoPP2019/src/TreapTVar.hs b/benchmarks/PPoPP2019/src/TreapTVar.hs new file mode 100644 index 0000000..da5dd03 --- /dev/null +++ b/benchmarks/PPoPP2019/src/TreapTVar.hs @@ -0,0 +1,317 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE BangPatterns #-} +{- LANGUAGE Strict -} +-- #define TESTCODE +module TreapTVar + ( Treap + , mkTreap + , insert + , get + , contains + , delete + + , benchCode +#ifdef TESTCODE + , verifyTreap + , insertV + , deleteV +#endif + ) where + +import qualified System.Random.PCG.Fast.Pure as R +-- import qualified Data.Vector.Unboxed.Mutable as U +import Data.Primitive.ByteArray +import Control.Monad.Primitive +import System.Random.PCG.Class (sysRandom) +import Data.Word (Word64, Word32, Word) +import Control.Monad + +import GHC.Types +import GHC.Prim +import GHC.IO +import GHC.Int +import GHC.Conc +import GHC.Conc.Sync + +benchCode :: String +benchCode = "TreapTVar" + +------------------------------ +-- Random + +-- | Constant for aligning RNG seed to cache-line +-- which usually is 64 Kb long (while seed only is 'Data.Word64'). +cacheFactor :: Int +cacheFactor = 8 + +type RandomState = MutableByteArray RealWorld + +-- Obtains PCG state, generate random value and store new state +gen :: RandomState -> Int -> IO Word32 +gen v !i = do + let i' = i * cacheFactor + st <- readByteArray v i' + let (R.P st' x) = R.pair st + writeByteArray v i' st' + return x + + +---------------------------------------------- +-- mutable + +type Mutable a = TVar a + +readRef :: Mutable a -> STM a +readRef = readTVar + +writeRef :: Mutable a -> a -> STM () +writeRef = writeTVar + +--------------------------------------------- +-- Treap + +type Key = Word +type Value = Word +type Priority = Word32 -- native to our PRNG + +data Treap where + Treap :: RandomState + -> TVar Node + -> Treap + +data Node where + Node :: !Key -- Key + -> !Value -- Value + -> !Priority -- Priority + -> TVar Node -- Left + -> TVar Node -- Right + -> Node + Nil :: Node + +key :: Node -> Key +key (Node k _ _ _ _) = k + +value :: Node -> Value +value (Node _ v _ _ _) = v + +priority :: Node -> Priority +priority (Node _ _ p _ _) = p + + +left :: Node -> Mutable Node +left (Node _ _ _ l _) = l + +right :: Node -> Mutable Node +right (Node _ _ _ _ r) = r + +instance Show Node where + show Nil = "Nil" + show (Node k v _ _ _) = show ("Node " ++ show k ++ " " ++ show v) + +instance Eq Node where + Nil == Nil = True + Nil == _ = False + _ == Nil = False + +-- x == y = case reallyUnsafePtrEquality# x y of +-- 0# -> False +-- _ -> True + (Node k _ _ _ _) == (Node k' _ _ _ _) = k == k' + +isNil :: Node -> Bool +isNil Nil = True +isNil _ = False + +isNode = not . isNil + +mkTreapIO :: IO Treap +mkTreapIO = do + cn <- getNumCapabilities + statev <- newByteArray (cn * cacheFactor * 8) + forM_ [0..cn-1] $ \i -> do + seed <- sysRandom + writeByteArray statev (i * cacheFactor) seed + Treap statev <$> newTVarIO Nil + +mkTreap :: STM Treap +mkTreap = do + state <- unsafeIOToSTM $ do + cn <- getNumCapabilities + statev <- newByteArray (cn * cacheFactor * 8) + forM_ [0..cn-1] $ \i -> do + seed <- sysRandom + writeByteArray statev (i * cacheFactor) seed + return statev + Treap state <$> newTVar Nil + + +-- Implementation somewhat follows: +-- https://github.com/frol/completely-unscientific-benchmarks/blob/master/c%23/Program.cs +mkNode :: Treap -> Key -> Value -> STM Node +mkNode (Treap s _) k v = do + p <- unsafeIOToSTM $ do + cn <- do + tid <- myThreadId + fst `fmap` threadCapability tid + gen s cn + Node k v p <$> newTVar Nil <*> newTVar Nil + +merge :: Node -> Node -> STM Node +merge Nil g = return g +merge l Nil = return l +merge l@(Node _ _ lp _ lr) g@(Node _ _ gp gl _) + | lp < gp = do + lrn <- readRef lr + merge lrn g >>= writeRef lr + return l + | otherwise = do + gln <- readRef gl + merge l gln >>= writeRef gl + return g + +splitL :: Key -> Node -> STM (Node, Node) +splitL _ Nil = return (Nil, Nil) +splitL key n@(Node k _ _ l r) + | k < key = do + (f, s) <- readRef r >>= splitL key + writeRef r f + return (n, s) + | otherwise = do + (f, s) <- readRef l >>= splitL key + writeRef l s + return (f, n) + +splitLEq :: Key -> Node -> STM (Node, Node) +splitLEq _ Nil = return (Nil, Nil) +splitLEq key n@(Node k _ _ l r) + | k <= key = do + (f, s) <- readRef r >>= splitLEq key + writeRef r f + return (n, s) + | otherwise = do + (f, s) <- readRef l >>= splitLEq key + writeRef l s + return (f, n) + +merge3 :: Node -> Node -> Node -> STM Node +merge3 l e g = do + l' <- merge l e + merge l' g + +split :: Key -> Node -> STM (Node, Node, Node) +split k n = do + (lof, los) <- splitL k n + (egf, egs) <- splitLEq k los + return (lof, egf, egs) + +nodeContains :: Key -> Node -> STM Bool +nodeContains k n = do + let loop Nil = return False + loop (Node k' _ _ l r) + | k < k' = readRef l >>= loop + | k > k' = readRef r >>= loop + | otherwise = return True + loop n + +nodeGet :: Key -> Node -> STM (Maybe Value) +nodeGet k n = do + let loop Nil = return Nothing + loop (Node k' v _ l r) + | k < k' = readRef l >>= loop + | k > k' = readRef r >>= loop + | otherwise = return (Just v) + loop n + +get :: Treap -> Key -> STM (Maybe Value) +get (Treap _ n) k = readRef n >>= nodeGet k + +contains :: Treap -> Key -> STM Bool +contains (Treap _ n) k = readRef n >>= nodeContains k + +insert :: Treap -> Key -> Value -> STM Bool +insert t@(Treap s nr) k v = do + n <- readRef nr + b <- nodeContains k n + if b + then return False + else do + (l,e,g) <- split k n + e' <- mkNode t k v + merge3 l e' g >>= writeRef nr + return True + +delete :: Treap -> Key -> STM Bool +delete (Treap _ nr) k = do + n <- readRef nr + b <- nodeContains k n + if not b + then return False + else do + (l,e,g) <- split k n + merge l g >>= writeRef nr + return True + +#ifdef TESTCODE +assertSTM :: Show s => String -> s -> Bool -> STM () +assertSTM _ _ True = return () +assertSTM s v False = error (s ++ "\n -- " ++ show v) + +verifyNode :: Node -> STM Int +verifyNode Nil = return 0 +verifyNode (Node k v p l r) = do + ln <- readRef l + rn <- readRef r + + when (isNode ln) $ do + assertSTM "left <" (key ln, k) (key ln < k) + assertSTM "prio >=" (priority ln, p) (priority ln >= p) + when (isNode rn) $ do + assertSTM "right >" (key rn, k) (key rn > k) + assertSTM "prio >=" (priority rn, p) (priority rn >= p) + + ls <- verifyNode ln + rs <- verifyNode rn + return (ls + rs + 1) + +verifyTreap :: Treap -> STM Int +verifyTreap (Treap _ n) = readRef n >>= verifyNode + +insertV :: Treap -> Key -> Value -> STM Bool +insertV t k v = do + s1 <- verifyTreap t + b1 <- contains t k + b2 <- insert t k v + b3 <- contains t k + s2 <- verifyTreap t + assertSTM "insert check" (b1,b2,b3) + ( (b1 && not b2 && b3) -- already present, stays + || (not b1 && b2 && b3)) -- not present, is added. + mv <- get t k + assertSTM "insert value check" (mv, v, b2) + (case mv of + Nothing -> False + Just v' -> v == v' || not b2) + assertSTM "insert size" (s1,s2,b2) + ( (b2 && s1 == s2 - 1) + || (not b2 && s1 == s2)) + return b2 + +deleteV :: Treap -> Key -> STM Bool +deleteV t k = do + s1 <- verifyTreap t + b1 <- contains t k + b2 <- delete t k + b3 <- contains t k + s2 <- verifyTreap t + assertSTM "delete check" (b1,b2,b3) + ( (not b1 && not b2 && not b3) -- already gone, stays gone. + || (b1 && b2 && not b3)) -- is present, goes away. + assertSTM "delete size" (s1,s2,b2) + ( (b2 && s1 == s2 + 1) + || (not b2 && s1 == s2)) + return b2 +#endif diff --git a/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/CHANGELOG.md b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/CHANGELOG.md new file mode 100644 index 0000000..1e6bd23 --- /dev/null +++ b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/CHANGELOG.md @@ -0,0 +1,41 @@ +# 1.2 + +* Replace the `Foreign` export with `Foreign.Storable`, `Foreign.Ptr`, `Foreign.ForeignPtr`, `Foreign.StablePtr`. It's more conservative and way less likely to cause name collisions. + +# 1.1 + +* Export `Foreign` + +# 1.0.1 + +* Relaxed the "base" dependency + +# 1 + +No changes. + +# 0.2 + +* Reexported `Data.Bifunctor`. + +* `first` and `second` are now (conditionally) exported from `Data.Bifunctor`, not `Control.Arrow`; note that if your version of base is lower than 4.8, `first` and `second` won't be available at all. + +# 0.1.21 + +* Reexported `printf` and `hPrintf` from `Text.Printf`. + +# 0.1.20 + +* Reexported `Numeric`. + +# 0.1.19 + +* Avoided the clash between `(&)` and `sortOn` defined in the package and versions of these functions imported from base. + +# 0.1.18 + +* Added implementations of `(&)` and `sortOn` (normally not available in older versions of base). + +# 0.1.17 + +* Reexported `Control.Monad.Fix`. diff --git a/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/LICENSE b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/LICENSE new file mode 100644 index 0000000..fe32f2b --- /dev/null +++ b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2014, Nikita Volkov + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/Setup.hs b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/base-prelude.cabal b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/base-prelude.cabal new file mode 100644 index 0000000..26a8982 --- /dev/null +++ b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/base-prelude.cabal @@ -0,0 +1,61 @@ +name: + base-prelude +version: + 1.2.0.1 +synopsis: + The most complete prelude formed solely from the "base" package +description: + A library which aims to reexport all the non-conflicting and + most general definitions from the \"base\" package. + This includes APIs for applicatives, arrows, monoids, foldables, traversables, + exceptions, generics, ST, MVars and STM. + . + This package will never have any dependencies other than \"base\". + . + /Versioning policy/ + . + The versioning policy of this package deviates from PVP in the sense + that its exports in part are transitively determined by the version of \"base\". + Therefore it's recommended for the users of \"base-prelude\" to specify + the bounds of \"base\" as well. +category: + Prelude +homepage: + https://github.com/nikita-volkov/base-prelude +bug-reports: + https://github.com/nikita-volkov/base-prelude/issues +author: + Nikita Volkov +maintainer: + Nikita Volkov +copyright: + (c) 2014, Nikita Volkov +license: + MIT +license-file: + LICENSE +build-type: + Simple +extra-source-files: + CHANGELOG.md +cabal-version: + >=1.10 + + +source-repository head + type: + git + location: + git://github.com/nikita-volkov/base-prelude.git + + +library + hs-source-dirs: + library + other-modules: + exposed-modules: + BasePrelude + build-depends: + base >= 4.6 && < 5 + default-language: + Haskell2010 diff --git a/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/library/BasePrelude.hs b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/library/BasePrelude.hs new file mode 100644 index 0000000..76d8f4b --- /dev/null +++ b/benchmarks/PPoPP2019/src/base-prelude-1.2.0.1/library/BasePrelude.hs @@ -0,0 +1,206 @@ +{-# LANGUAGE CPP #-} +-- | +-- This module reexports most of the definitions from the \"base\" package, +-- which are meant to be imported unqualified. +-- +-- For details check out the source. +module BasePrelude +( + module Exports, + -- * Reimplementations of functions presented in versions of \"base\" newer than 4.6 + -- ** Data.Bool + bool, + -- ** Data.Function + (&), + -- ** Data.Functor + ($>), + -- ** Data.List + isSubsequenceOf, + sortOn, + uncons, + -- ** Debug.Trace + traceShowId, + traceM, + traceShowM, +) +where + +-- Reexports +------------------------- + +import Control.Applicative as Exports +import Control.Arrow as Exports hiding (first, second) +import Control.Category as Exports +import Control.Concurrent as Exports +import Control.Exception as Exports +import Control.Monad as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM) +import Control.Monad.Fix as Exports hiding (fix) +import Control.Monad.ST as Exports +#if MIN_VERSION_base(4,8,0) +import Data.Bifunctor as Exports +#endif +import Data.Bits as Exports +import Data.Bool as Exports hiding (bool) +import Data.Char as Exports +import Data.Complex as Exports +import Data.Data as Exports +import Data.Dynamic as Exports +import Data.Either as Exports +import Data.Fixed as Exports +import Data.Foldable as Exports +import Data.Functor as Exports hiding (($>)) +import Data.Function as Exports hiding ((.), id, (&)) +import Data.Int as Exports +import Data.IORef as Exports +import Data.Ix as Exports +import Data.List as Exports hiding (sortOn, isSubsequenceOf, uncons, concat, foldr, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, find, maximumBy, minimumBy, mapAccumL, mapAccumR, foldl') +import Data.Maybe as Exports +import Data.Monoid as Exports +import Data.Ord as Exports +import Data.Ratio as Exports +import Data.STRef as Exports +import Data.String as Exports +import Data.Traversable as Exports +import Data.Tuple as Exports +import Data.Unique as Exports +import Data.Version as Exports +import Data.Word as Exports +import Debug.Trace as Exports hiding (traceShowId, traceM, traceShowM) +import Foreign.Storable as Exports +import Foreign.Ptr as Exports +import Foreign.ForeignPtr as Exports +import Foreign.StablePtr as Exports +import GHC.Conc as Exports hiding (withMVar, threadWaitWriteSTM, threadWaitWrite, threadWaitReadSTM, threadWaitRead) +import GHC.Exts as Exports (lazy, inline, sortWith, groupWith) +import GHC.Generics as Exports (Generic) +import GHC.IO.Exception as Exports +import Numeric as Exports +import Prelude as Exports hiding (concat, foldr, mapM_, sequence_, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, mapM, sequence, id, (.)) +import System.Environment as Exports +import System.Exit as Exports +import System.IO as Exports (Handle, hClose) +import System.IO.Error as Exports +import System.IO.Unsafe as Exports +import System.Mem as Exports +import System.Mem.StableName as Exports +import System.Timeout as Exports +import Text.ParserCombinators.ReadP as Exports (ReadP, ReadS, readP_to_S, readS_to_P) +import Text.ParserCombinators.ReadPrec as Exports (ReadPrec, readPrec_to_P, readP_to_Prec, readPrec_to_S, readS_to_Prec) +import Text.Printf as Exports (printf, hPrintf) +import Text.Read as Exports (Read(..), readMaybe, readEither) +import Unsafe.Coerce as Exports + +-- Conditional imports for reimplementations +#if MIN_VERSION_base(4,7,0) +import Data.Bool (bool) +import Debug.Trace (traceShowId, traceM, traceShowM) +import Data.Functor (($>)) +#endif +#if MIN_VERSION_base(4,8,0) +import Data.Function ((&)) +import Data.List (isSubsequenceOf, sortOn, uncons) +#endif + + +--------------------------------- +-- Reimplementations for base-4.7 +--------------------------------- + +#if !MIN_VERSION_base(4,7,0) + +-- | Case analysis for the 'Bool' type. +-- @bool a b p@ evaluates to @a@ when @p@ is @False@, and evaluates to @b@ +-- when @p@ is @True@. +bool :: a -> a -> Bool -> a +bool f t b = if b then t else f + +{-| +Like 'traceShow' but returns the shown value instead of a third value. +-} +traceShowId :: (Show a) => a -> a +traceShowId a = trace (show a) a + +{-| +Like 'trace' but returning unit in an arbitrary monad. Allows for convenient +use in do-notation. Note that the application of 'trace' is not an action in the +monad, as 'traceIO' is in the 'IO' monad. + +> ... = do +> x <- ... +> traceM $ "x: " ++ show x +> y <- ... +> traceM $ "y: " ++ show y +-} +traceM :: (Monad m) => String -> m () +traceM string = trace string $ return () + +{-| +Like 'traceM', but uses 'show' on the argument to convert it to a 'String'. + +> ... = do +> x <- ... +> traceShowM $ x +> y <- ... +> traceShowM $ x + y +-} +traceShowM :: (Show a, Monad m) => a -> m () +traceShowM = traceM . show + +infixl 4 $> + +-- | Flipped version of '<$'. +($>) :: Functor f => f a -> b -> f b +($>) = flip (<$) + +#endif + +--------------------------------- +-- Reimplementations for base-4.8 +--------------------------------- + +#if !MIN_VERSION_base(4,8,0) + +infixl 1 & + +-- | '&' is a reverse application operator. This provides notational +-- convenience. Its precedence is one higher than that of the forward +-- application operator '$', which allows '&' to be nested in '$'. +(&) :: a -> (a -> b) -> b +x & f = f x + +-- | The 'isSubsequenceOf' function takes two lists and returns 'True' if the +-- first list is a subsequence of the second list. +-- +-- @'isSubsequenceOf' x y@ is equivalent to @'elem' x ('subsequences' y)@. +-- +-- ==== __Examples__ +-- +-- >>> isSubsequenceOf "GHC" "The Glorious Haskell Compiler" +-- True +-- >>> isSubsequenceOf ['a','d'..'z'] ['a'..'z'] +-- True +-- >>> isSubsequenceOf [1..10] [10,9..0] +-- False +isSubsequenceOf :: (Eq a) => [a] -> [a] -> Bool +isSubsequenceOf [] _ = True +isSubsequenceOf _ [] = False +isSubsequenceOf a@(x:a') (y:b) | x == y = isSubsequenceOf a' b + | otherwise = isSubsequenceOf a b + +-- | Decompose a list into its head and tail. If the list is empty, +-- returns 'Nothing'. If the list is non-empty, returns @'Just' (x, xs)@, +-- where @x@ is the head of the list and @xs@ its tail. +uncons :: [a] -> Maybe (a, [a]) +uncons [] = Nothing +uncons (x:xs) = Just (x, xs) + +-- | Sort a list by comparing the results of a key function applied to each +-- element. @sortOn f@ is equivalent to @sortBy . comparing f@, but has the +-- performance advantage of only evaluating @f@ once for each element in the +-- input list. This is called the decorate-sort-undecorate paradigm, or +-- Schwartzian transform. +sortOn :: Ord b => (a -> b) -> [a] -> [a] +sortOn f = + map snd . sortBy (comparing fst) . map (\x -> let y = f x in y `seq` (y, x)) + +#endif diff --git a/benchmarks/PPoPP2019/src/cbits/gettime.c b/benchmarks/PPoPP2019/src/cbits/gettime.c new file mode 100644 index 0000000..a122e5c --- /dev/null +++ b/benchmarks/PPoPP2019/src/cbits/gettime.c @@ -0,0 +1,13 @@ +// Modified from criterion: github.com/bos/criterion/ +// Copyright (c) 2009, 2010 Bryan O'Sullivan + +#include + +double throughput_gettime(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return ts.tv_sec + ts.tv_nsec * 1e-9; +} diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/LICENSE b/benchmarks/PPoPP2019/src/entropy-0.4/LICENSE new file mode 100644 index 0000000..6d24e89 --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) Thomas DuBuisson + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the author nor the names of his contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/README.md b/benchmarks/PPoPP2019/src/entropy-0.4/README.md new file mode 100644 index 0000000..7a5d865 --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/README.md @@ -0,0 +1,25 @@ +# Introduction + +This package allows Haskell users to easily acquire entropy for use in critical +security applications by calling out to either windows crypto api, unix/linux's +`/dev/urandom`. Hardware RNGs (currently RDRAND, patches welcome) are supported +via the `hardwareRNG` function. + +If you wish to obtain an XOR of the hardware and system RNG consider: + +``` +import Data.Bits (xor) +import qualified Data.ByteString as B +import qualified Control.Exception as X + +xorRNG sz = do hw <- hardwareRNG sz + h <- openHandle + sys <- hGetEntropy h `X.finally` closeHandle h + pure $ B.pack $ B.zipWith xor hw sys +``` + +This package supports Windows, {li,u}nix, QNX, and has preliminary support for HaLVM. + +Typically tested on Linux and OSX - testers are as welcome as patches. + +[![Build Status](https://travis-ci.org/TomMD/entropy.svg?branch=master)](https://travis-ci.org/TomMD/entropy) diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/Setup.hs b/benchmarks/PPoPP2019/src/entropy-0.4/Setup.hs new file mode 100644 index 0000000..49b693f --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/Setup.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE CPP #-} +import Control.Monad +import Distribution.Simple +import Distribution.Simple.LocalBuildInfo +import Distribution.Simple.Setup +import Distribution.PackageDescription +import Distribution.Simple.Utils +import Distribution.Simple.Program +import Distribution.Verbosity +import System.Process +import System.Directory +import System.FilePath +import System.Exit +import System.IO + +main = defaultMainWithHooks hk + where + hk = simpleUserHooks { buildHook = \pd lbi uh bf -> do + -- let ccProg = Program "gcc" undefined undefined undefined + let hcProg = Program "ghc" undefined undefined undefined + mConf = lookupProgram hcProg (withPrograms lbi) + err = error "Could not determine C compiler" + cc = locationPath . programLocation . maybe err id $ mConf + b <- canUseRDRAND cc + let newWithPrograms1 = userSpecifyArgs "gcc" cArgs (withPrograms lbi) + newWithPrograms = userSpecifyArgs "ghc" cArgsHC newWithPrograms1 + lbiNew = if b then (lbi {withPrograms = newWithPrograms }) else lbi + buildHook simpleUserHooks pd lbiNew uh bf + } + +cArgs :: [String] +cArgs = ["-DHAVE_RDRAND"] + +cArgsHC :: [String] +cArgsHC = cArgs ++ map ("-optc" ++) cArgs + +canUseRDRAND :: FilePath -> IO Bool +canUseRDRAND cc = do + withTempDirectory normal "" "testRDRAND" $ \tmpDir -> do + writeFile (tmpDir ++ "/testRDRAND.c") + (unlines [ "#include " + , "int main() {" + , " uint64_t therand;" + , " unsigned char err;" + , " asm volatile(\"rdrand %0 ; setc %1\"" + , " : \"=r\" (therand), \"=qm\" (err));" + , " return (!err);" + , "}" + ]) + ec <- myRawSystemExitCode normal cc [tmpDir "testRDRAND.c", "-o", tmpDir ++ "/a.o","-c"] + notice normal $ "Result of RDRAND Test: " ++ show (ec == ExitSuccess) + return (ec == ExitSuccess) + +myRawSystemExitCode :: Verbosity -> FilePath -> [String] -> IO ExitCode +#if __GLASGOW_HASKELL__ >= 704 +-- We know for sure, that if GHC >= 7.4 implies Cabal >= 1.14 +myRawSystemExitCode = rawSystemExitCode +#else +-- Legacy branch: +-- We implement our own 'rawSystemExitCode', this will even work if +-- the user happens to have Cabal >= 1.14 installed with GHC 7.0 or +-- 7.2 +myRawSystemExitCode verbosity path args = do + printRawCommandAndArgs verbosity path args + hFlush stdout + exitcode <- rawSystem path args + unless (exitcode == ExitSuccess) $ do + debug verbosity $ path ++ " returned " ++ show exitcode + return exitcode + where + printRawCommandAndArgs :: Verbosity -> FilePath -> [String] -> IO () + printRawCommandAndArgs verbosity path args + | verbosity >= deafening = print (path, args) + | verbosity >= verbose = putStrLn $ unwords (path : args) + | otherwise = return () +#endif diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/System/Entropy.hs b/benchmarks/PPoPP2019/src/entropy-0.4/System/Entropy.hs new file mode 100644 index 0000000..db809f0 --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/System/Entropy.hs @@ -0,0 +1,77 @@ +{-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables #-} +{-| + Maintainer: Thomas.DuBuisson@gmail.com + Stability: beta + Portability: portable + + Obtain entropy from system sources or x86 RDRAND when available. + + Currently supporting: + + - Windows via CryptoAPI + - *nix systems via @\/dev\/urandom@ + - Includes QNX + - Xen (only when RDRAND is available) +-} + +module System.Entropy + ( getEntropy, + getHardwareEntropy, + CryptHandle, + openHandle, + hGetEntropy, + closeHandle + ) where +#if defined(isWindows) +import System.EntropyWindows +#else +#ifdef XEN +import System.EntropyXen +#else +import System.EntropyNix +#endif +#endif + +import qualified Data.ByteString as B +import Control.Exception (bracket) + +-- |Get a specific number of bytes of cryptographically +-- secure random data using the *system-specific* sources. +-- (As of 0.4. Verions <0.4 mixed system and hardware sources) +-- +-- The returned random value is considered cryptographically secure but not true entropy. +-- +-- On some platforms this requies a file handle which can lead to resource +-- exhaustion in some situations. +getEntropy :: Int -- ^ Number of bytes + -> IO B.ByteString +getEntropy = bracket openHandle closeHandle . flip hGetEntropy + +-- |Get a specific number of bytes of cryptographically +-- secure random data using a supported *hardware* random bit generator. +-- +-- If there is no hardware random number generator then @Nothing@ is returned. +-- If any call returns non-Nothing then it should never be @Nothing@ unless +-- there has been a hardware failure. +-- +-- If trust of the CPU allows it and no context switching is important, +-- a bias to the hardware rng with system rng as fall back is trivial: +-- +-- @ +-- let fastRandom nr = maybe (getEntropy nr) pure =<< getHardwareEntropy nr +-- @ +-- +-- The old, @<0.4@, behavior is possible using @xor@ from 'Data.Bits': +-- +-- @ +-- let oldRandom nr = +-- do hwRnd <- maybe (replicate nr 0) BS.unpack <$> getHardwareEntropy nr +-- sysRnd <- BS.unpack <$> getEntropy nr +-- pure $ BS.pack $ zipWith xor sysRnd hwRnd +-- @ +-- +-- A less maliable mixing can be accomplished by replacing `xor` with a +-- composition of concat and cryptographic hash. +getHardwareEntropy :: Int -- ^ Number of bytes + -> IO (Maybe B.ByteString) +getHardwareEntropy = hardwareRandom diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyNix.hs b/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyNix.hs new file mode 100644 index 0000000..01a7bc4 --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyNix.hs @@ -0,0 +1,98 @@ +{-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables #-} +{-| + Maintainer: Thomas.DuBuisson@gmail.com + Stability: beta + Portability: portable + + Obtain entropy from system sources or x86 RDRAND when available. + +-} + +module System.EntropyNix + ( CryptHandle + , openHandle + , hGetEntropy + , closeHandle + , hardwareRandom + ) where + +import Control.Monad (liftM, when) +import Data.ByteString as B +import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString) +import Data.Bits (xor) + +import Foreign (allocaBytes) +import Foreign.Ptr +import Foreign.C.Types +import Data.ByteString.Internal as B + +#ifdef arch_i386 +-- See .cabal wrt GCC 4.8.2 asm compilation bug +#undef HAVE_RDRAND +#endif + +import System.Posix (openFd, closeFd, fdReadBuf, OpenMode(..), defaultFileFlags, Fd) + +source :: FilePath +source = "/dev/urandom" + +-- |Handle for manual resource management +data CryptHandle + = CH Fd + +-- | Get random values from the hardward RNG or return Nothing if no +-- supported hardware RNG is available. +-- +-- Supported hardware: +-- * RDRAND +-- * Patches welcome +hardwareRandom :: Int -> IO (Maybe B.ByteString) +#ifdef HAVE_RDRAND +hardwareRandom n = + do b <- cpuHasRdRand + if b + then Just <$> B.create n (\ptr -> + do r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n) + when (r /= 0) (fail "RDRand failed to gather entropy")) + else pure Nothing +#else +hardwareRandom _ = pure Nothing +#endif + +-- |Open a `CryptHandle` +openHandle :: IO CryptHandle +openHandle = do CH `fmap` nonRDRandHandle + where + nonRDRandHandle :: IO Fd + nonRDRandHandle = openFd source ReadOnly Nothing defaultFileFlags + +-- |Close the `CryptHandle` +closeHandle :: CryptHandle -> IO () +closeHandle (CH h) = closeFd h + +-- |Read random data from a `CryptHandle` +hGetEntropy :: CryptHandle -> Int -> IO B.ByteString +hGetEntropy (CH h) = fdReadBS h + +fdReadBS :: Fd -> Int -> IO B.ByteString +fdReadBS fd n = + allocaBytes n $ \buf -> go buf n + where + go buf 0 = B.packCStringLen (castPtr buf, fromIntegral n) + go buf cnt | cnt <= n = do + rc <- fdReadBuf fd (plusPtr buf (n - cnt)) (fromIntegral cnt) + case rc of + 0 -> ioError (ioeSetErrorString (mkIOError eofErrorType "fdRead" Nothing Nothing) "EOF") + n' -> go buf (cnt - fromIntegral n') + go _ _ = error "Impossible! The count of bytes left to read is greater than the request or less than zero!" + +#ifdef HAVE_RDRAND +foreign import ccall unsafe "cpu_has_rdrand" + c_cpu_has_rdrand :: IO CInt + +foreign import ccall unsafe "get_rand_bytes" + c_get_rand_bytes :: Ptr CUChar -> CSize -> IO CInt + +cpuHasRdRand :: IO Bool +cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand +#endif diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyWindows.hs b/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyWindows.hs new file mode 100644 index 0000000..3659ffe --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyWindows.hs @@ -0,0 +1,139 @@ +{-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables #-} +{-| + Maintainer: Thomas.DuBuisson@gmail.com + Stability: beta + Portability: portable + + Obtain entropy from system sources. +-} + +module System.EntropyWindows + ( CryptHandle + , openHandle + , hGetEntropy + , closeHandle + , hardwareRandom + ) where + +import Control.Monad (liftM, when) +import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString) +import Foreign (allocaBytes) +import Data.ByteString as B +import Data.ByteString.Internal as BI +import Data.Int (Int32) +import Data.Bits (xor) +import Data.Word (Word32, Word8) +import Foreign.C.String (CString, withCString) +import Foreign.C.Types +import Foreign.Ptr (Ptr, nullPtr, castPtr) +import Foreign.Marshal.Alloc (alloca) +import Foreign.Marshal.Utils (toBool) +import Foreign.Storable (peek) + +{- C example for windows rng - taken from a blog, can't recall which one but thank you! + #include + #include + ... + // + // DISCLAIMER: Don't forget to check your error codes!! + // I am not checking as to make the example simple... + // + HCRYPTPROV hCryptCtx = NULL; + BYTE randomArray[128]; + + CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + CryptGenRandom(hCryptCtx, 128, randomArray); + CryptReleaseContext(hCryptCtx, 0); +-} + + +#ifdef arch_i386 +-- See .cabal wrt GCC 4.8.2 asm compilation bug +#undef HAVE_RDRAND +#endif + +#ifdef HAVE_RDRAND +foreign import ccall unsafe "cpu_has_rdrand" + c_cpu_has_rdrand :: IO CInt + +foreign import ccall unsafe "get_rand_bytes" + c_get_rand_bytes :: Ptr CUChar -> CSize -> IO CInt + +cpuHasRdRand :: IO Bool +cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand +#endif + +data CryptHandle + = CH Word32 + + +-- | Get random values from the hardward RNG or return Nothing if no +-- supported hardware RNG is available. +-- +-- Supported hardware: +-- * RDRAND +-- * Patches welcome +hardwareRandom :: Int -> IO (Maybe B.ByteString) +#ifdef HAVE_RDRAND +hardwareRandom n = + do b <- cpuHasRdRand + if b + then Just <$> BI.create n (\ptr -> + do r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n) + when (r /= 0) (fail "RDRand failed to gather entropy")) + else pure Nothing +#else +hardwareRandom _ = pure Nothing +#endif + +-- Define the constants we need from WinCrypt.h +msDefProv :: String +msDefProv = "Microsoft Base Cryptographic Provider v1.0" +provRSAFull :: Word32 +provRSAFull = 1 +cryptVerifyContext :: Word32 +cryptVerifyContext = fromIntegral 0xF0000000 + +-- Declare the required CryptoAPI imports +foreign import stdcall unsafe "CryptAcquireContextA" + c_cryptAcquireCtx :: Ptr Word32 -> CString -> CString -> Word32 -> Word32 -> IO Int32 +foreign import stdcall unsafe "CryptGenRandom" + c_cryptGenRandom :: Word32 -> Word32 -> Ptr Word8 -> IO Int32 +foreign import stdcall unsafe "CryptReleaseContext" + c_cryptReleaseCtx :: Word32 -> Word32 -> IO Int32 + +cryptAcquireCtx :: IO Word32 +cryptAcquireCtx = + alloca $ \handlePtr -> + withCString msDefProv $ \provName -> do + stat <- c_cryptAcquireCtx handlePtr nullPtr provName provRSAFull cryptVerifyContext + if (toBool stat) + then peek handlePtr + else fail "c_cryptAcquireCtx" + +cryptGenRandom :: Word32 -> Int -> IO B.ByteString +cryptGenRandom h i = + BI.create i $ \c_buffer -> do + stat <- c_cryptGenRandom h (fromIntegral i) c_buffer + if (toBool stat) + then return () + else fail "c_cryptGenRandom" + +cryptReleaseCtx :: Word32 -> IO () +cryptReleaseCtx h = do + stat <- c_cryptReleaseCtx h 0 + if (toBool stat) + then return () + else fail "c_cryptReleaseCtx" + +-- |Open a handle from which random data can be read +openHandle :: IO CryptHandle +openHandle = CH `fmap` cryptAcquireCtx + +-- |Close the `CryptHandle` +closeHandle :: CryptHandle -> IO () +closeHandle (CH h) = cryptReleaseCtx h + +-- |Read from `CryptHandle` +hGetEntropy :: CryptHandle -> Int -> IO B.ByteString +hGetEntropy (CH h) n = cryptGenRandom h n diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyXen.hs b/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyXen.hs new file mode 100644 index 0000000..c1799cc --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/System/EntropyXen.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables #-} +{-| + Maintainer: Thomas.DuBuisson@gmail.com + Stability: beta + Portability: portable + + Obtain entropy from RDRAND when available. + +-} + +module System.EntropyXen + ( CryptHandle + , openHandle + , hGetEntropy + , closeHandle + , hardwardRNG + ) where + +import Control.Monad (liftM, when) +import Data.ByteString as B +import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString) + +import Foreign (allocaBytes) +import Foreign.Ptr +import Foreign.C.Types +import Data.ByteString.Internal as B + +#ifdef arch_i386 +-- See .cabal wrt GCC 4.8.2 asm compilation bug +#undef HAVE_RDRAND +#endif + +#ifndef HAVE_RDRAND +#error "The entropy package requires RDRAND support when using the halvm/Xen" +#endif +data CryptHandle = UseRdRand -- or die trying + +-- |Open a `CryptHandle` +openHandle :: IO CryptHandle +openHandle = do + b <- cpuHasRdRand + if b then return UseRdRand + else nonRDRandHandle + where + nonRDRandHandle :: IO CryptHandle + nonRDRandHandle = error "entropy: On halvm there is no entropy other than RDRAND." + +-- |Close the `CryptHandle` +closeHandle :: CryptHandle -> IO () +closeHandle UseRdRand = return () + +-- | Get random values from the hardward RNG or return Nothing if no +-- supported hardware RNG is available. +-- +-- Supported hardware: +-- * RDRAND +-- * Patches welcome +hardwareRandom :: Int -> IO (Maybe B.ByteString) +hardwareRandom sz = Just <$> hGetEntropy UseRdRand sz + +-- |Read random data from a `CryptHandle`, which uses RDRAND (when on Xen) +hGetEntropy :: CryptHandle -> Int -> IO B.ByteString +hGetEntropy UseRdRand = \n -> do + B.create n $ \ptr -> do + r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n) + when (r /= 0) + (fail "RDRand failed to gather entropy") + +foreign import ccall unsafe "cpu_has_rdrand" + c_cpu_has_rdrand :: IO CInt + +foreign import ccall unsafe "get_rand_bytes" + c_get_rand_bytes :: Ptr CUChar -> CSize -> IO CInt + +cpuHasRdRand :: IO Bool +cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/cbits/rdrand.c b/benchmarks/PPoPP2019/src/entropy-0.4/cbits/rdrand.c new file mode 100644 index 0000000..4fa8b1b --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/cbits/rdrand.c @@ -0,0 +1,99 @@ +#ifdef HAVE_RDRAND + +#include +#include + +int cpu_has_rdrand() +{ + uint32_t ax,bx,cx,dx,func=1; + __asm__ volatile ("cpuid":\ + "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func)); + return (cx & 0x40000000); +} + +#ifdef arch_x86_64 +// Returns 1 on success +static inline int _rdrand64_step(uint64_t *therand) +{ + unsigned char err; + asm volatile("rdrand %0 ; setc %1" + : "=r" (*therand), "=qm" (err)); + return (int) err; +} + +// Returns 0 on success, non-zero on failure. +int get_rand_bytes(uint8_t *therand, size_t len) +{ + int cnt; + int fail=0; + uint8_t *p = therand; + uint8_t *end = therand + len; + if((uint64_t)p%8 != 0) { + uint64_t tmp; + fail |= !_rdrand64_step(&tmp); + while((uint64_t)p%8 != 0 && p != end) { + *p = (uint8_t)(tmp & 0xFF); + tmp = tmp >> 8; + p++; + } + } + for(; p <= end - sizeof(uint64_t); p+=sizeof(uint64_t)) { + fail |= !_rdrand64_step((uint64_t *)p); + } + if(p != end) { + uint64_t tmp; + int cnt; + fail |= !_rdrand64_step(&tmp); + while(p != end) { + *p = (uint8_t)(tmp & 0xFF); + tmp = tmp >> 8; + p++; + } + } + return fail; +} +#endif /* x86-64 */ + +#ifdef arch_i386 +// Returns 1 on success +static inline int _rdrand32_step(uint32_t *therand) +{ + unsigned char err; + asm volatile("rdrand %0 ; setc %1" + : "=r" (*therand), "=qm" (err)); + return (int) err; +} + +int get_rand_bytes(uint8_t *therand, size_t len) +{ + int cnt; + int fail=0; + uint8_t *p = therand; + uint8_t *end = therand + len; + if((uint32_t)p % sizeof(uint32_t) != 0) { + uint32_t tmp; + fail |= !_rdrand32_step(&tmp); + while((uint32_t)p % sizeof(uint32_t) != 0 && p != end) { + *p = (uint8_t)(tmp & 0xFF); + tmp = tmp >> 8; + p++; + } + } + for(; p <= end - sizeof(uint32_t); p+=sizeof(uint32_t)) { + fail |= !_rdrand32_step((uint32_t *)p); + } + if(p != end) { + uint32_t tmp; + int cnt; + fail |= !_rdrand32_step(&tmp); + while(p != end) { + *p = (uint8_t)(tmp & 0xFF); + tmp = tmp >> 8; + p++; + } + } + return fail; +} +#endif /* i386 */ + +#endif // RDRAND diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/cbits/rdrand.h b/benchmarks/PPoPP2019/src/entropy-0.4/cbits/rdrand.h new file mode 100644 index 0000000..9f43a72 --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/cbits/rdrand.h @@ -0,0 +1,10 @@ +#ifndef rdrand_h +#ifdef HAVE_RDRAND +#include + +int cpu_has_rdrand() + +// Returns 0 on success, non-zero on failure. +int get_rand_bytes(uint8_t *therand, size_t len) +#endif // HAVE_RDRAND +#endif // rdrand_h diff --git a/benchmarks/PPoPP2019/src/entropy-0.4/entropy.cabal b/benchmarks/PPoPP2019/src/entropy-0.4/entropy.cabal new file mode 100644 index 0000000..2dc104b --- /dev/null +++ b/benchmarks/PPoPP2019/src/entropy-0.4/entropy.cabal @@ -0,0 +1,79 @@ +name: entropy +version: 0.4 +x-revision: 1 +description: A platform independent method to obtain cryptographically strong entropy + (RDRAND, urandom, CryptAPI, and patches welcome) + Users looking for cryptographically strong (number-theoretically + sound) PRNGs should see the 'DRBG' package too. +synopsis: A platform independent entropy source +license: BSD3 +license-file: LICENSE +copyright: Thomas DuBuisson +author: Thomas DuBuisson +maintainer: Thomas DuBuisson +category: Data, Cryptography +homepage: https://github.com/TomMD/entropy +bug-reports: https://github.com/TomMD/entropy/issues +stability: stable +-- build-type: Simple +-- ^^ Used for HaLVM +build-type: Custom +-- ^^ Test for RDRAND support using 'ghc' +cabal-version: >=1.10 +tested-with: GHC == 8.2.2 +-- data-files: +extra-source-files: ./cbits/rdrand.c, ./cbits/rdrand.h, README.md + +-- Notice to compile with HaLVM the above 'build-type' must be changed +-- to 'Simple' instead of 'Custom'. The current build system naively +-- runs GHC to determine if the compiler supports RDRAND before proceeding. +flag halvm + description: Build for the HaLVM + default: False + + +custom-setup + setup-depends: Cabal >= 1.10 && < 2.2 + , base < 5 + , filepath < 1.5 + , directory < 1.4 + , process < 1.7 + +library + ghc-options: -O2 + exposed-modules: System.Entropy + if os(windows) + other-modules: System.EntropyWindows + else { + if os(halvm) + other-modules: System.EntropyXen + else + other-modules: System.EntropyNix + } + other-extensions: CPP, ForeignFunctionInterface, BangPatterns, + ScopedTypeVariables + build-depends: base >= 4.8 && < 5, bytestring + default-language: Haskell2010 + if(os(halvm)) + cpp-options: -DXEN -DHAVE_RDRAND + cc-options: -DXEN -DHAVE_RDRAND + if arch(x86_64) + cpp-options: -Darch_x86_64 + cc-options: -Darch_x86_64 -O2 + -- gcc 4.8.2 on i386 fails to compile rdrand.c when using -fPIC! + c-sources: cbits/rdrand.c + include-dirs: cbits + if arch(i386) + cpp-options: -Darch_i386 + cc-options: -Darch_i386 -O2 + if os(windows) + cpp-options: -DisWindows + cc-options: -DisWindows + extra-libraries: advapi32 + else + if !os(halvm) + Build-Depends: unix + +source-repository head + type: git + location: https://github.com/TomMD/entropy diff --git a/benchmarks/PPoPP2019/src/focus-0.1.5.2/LICENSE b/benchmarks/PPoPP2019/src/focus-0.1.5.2/LICENSE new file mode 100644 index 0000000..fe32f2b --- /dev/null +++ b/benchmarks/PPoPP2019/src/focus-0.1.5.2/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2014, Nikita Volkov + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/benchmarks/PPoPP2019/src/focus-0.1.5.2/Setup.hs b/benchmarks/PPoPP2019/src/focus-0.1.5.2/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/benchmarks/PPoPP2019/src/focus-0.1.5.2/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/benchmarks/PPoPP2019/src/focus-0.1.5.2/focus.cabal b/benchmarks/PPoPP2019/src/focus-0.1.5.2/focus.cabal new file mode 100644 index 0000000..bcf3309 --- /dev/null +++ b/benchmarks/PPoPP2019/src/focus-0.1.5.2/focus.cabal @@ -0,0 +1,65 @@ +name: + focus +version: + 0.1.5.2 +synopsis: + A general abstraction for manipulating elements of container data structures +description: + An API for construction of free-form strategies of access and manipulation of + elements of arbitrary data structures. + It allows to implement efficient composite patterns, e.g., + a simultaneous update and lookup of an element, + and even more complex things. + . + Strategies are meant to be interpreted by the host data structure libraries. + Thus they allow to implement all access and modification patterns of + a data structure with just a single function, + which interprets strategies. + . + This library provides pure and monadic interfaces, + so it supports both immutable and mutable data structures. +category: + Containers, Data +homepage: + https://github.com/nikita-volkov/focus +bug-reports: + https://github.com/nikita-volkov/focus/issues +author: + Nikita Volkov +maintainer: + Nikita Volkov +copyright: + (c) 2014, Nikita Volkov +license: + MIT +license-file: + LICENSE +build-type: + Simple +cabal-version: + >=1.10 +tested-with: + GHC==7.6.3, + GHC==7.8.4, + GHC==7.10.3, + GHC==8.0.1 + GHC==8.2.1 + +source-repository head + type: + git + location: + git://github.com/nikita-volkov/focus.git + + +library + hs-source-dirs: + library + exposed-modules: + Focus + build-depends: + base >= 4.6 && < 5 + default-extensions: + DeriveFunctor, NoImplicitPrelude, TupleSections + default-language: + Haskell2010 diff --git a/benchmarks/PPoPP2019/src/focus-0.1.5.2/library/Focus.hs b/benchmarks/PPoPP2019/src/focus-0.1.5.2/library/Focus.hs new file mode 100644 index 0000000..468a3ce --- /dev/null +++ b/benchmarks/PPoPP2019/src/focus-0.1.5.2/library/Focus.hs @@ -0,0 +1,115 @@ +module Focus where + +import Prelude hiding (adjust, update, alter, insert, delete, lookup) +import Control.Monad + + +-- | +-- A general modification function for some match. +-- By processing a 'Maybe' value it produces some value to emit and +-- a 'Decision' to perform on the match. +-- +-- The interpretation of this function is up to the context APIs. +type Strategy a r = Maybe a -> (r, Decision a) + +-- | +-- A monadic version of 'Strategy'. +type StrategyM m a r = Maybe a -> m (r, Decision a) + +-- | +-- What to do with the focused value. +-- +-- The interpretation of the commands is up to the context APIs. +data Decision a = + Keep | + Remove | + Replace a + deriving (Functor) + + +-- * Constructors for common pure patterns +------------------------- + +-- | +-- Reproduces the behaviour of +-- @Data.Map.@. +{-# INLINE adjust #-} +adjust :: (a -> a) -> Strategy a () +adjust f = maybe ((), Keep) (\a -> ((), Replace (f a))) + +-- | +-- Reproduces the behaviour of +-- @Data.Map.@. +{-# INLINE update #-} +update :: (a -> Maybe a) -> Strategy a () +update f = maybe ((), Keep) (\a -> ((), maybe Remove Replace (f a))) + +-- | +-- Reproduces the behaviour of +-- @Data.Map.@. +{-# INLINE alter #-} +alter :: (Maybe a -> Maybe a) -> Strategy a () +alter f = ((),) . maybe Remove Replace . f + +-- | +-- Reproduces the behaviour of +-- @Data.Map.@. +{-# INLINE insert #-} +insert :: a -> Strategy a () +insert a = const ((), Replace a) + +-- | +-- Reproduces the behaviour of +-- @Data.Map.@. +{-# INLINE delete #-} +delete :: Strategy a () +delete = const ((), Remove) + +-- | +-- Reproduces the behaviour of +-- @Data.Map.@. +{-# INLINE lookup #-} +lookup :: Strategy a (Maybe a) +lookup r = (r, Keep) + + +-- * Constructors for monadic patterns +------------------------- + +-- | +-- A monadic version of 'adjust'. +{-# INLINE adjustM #-} +adjustM :: (Monad m) => (a -> m a) -> StrategyM m a () +adjustM f = maybe (return ((), Keep)) (liftM (((),) . Replace) . f) + +-- | +-- A monadic version of 'update'. +{-# INLINE updateM #-} +updateM :: (Monad m) => (a -> m (Maybe a)) -> StrategyM m a () +updateM f = maybe (return ((), Keep)) (liftM (((),) . maybe Remove Replace) . f) + +-- | +-- A monadic version of 'alter'. +{-# INLINE alterM #-} +alterM :: (Monad m) => (Maybe a -> m (Maybe a)) -> StrategyM m a () +alterM f = liftM (((),) . maybe Remove Replace) . f + +-- | +-- A monadic version of 'insert'. +{-# INLINE insertM #-} +insertM :: (Monad m) => a -> StrategyM m a () +insertM = fmap return . insert + +-- | +-- A monadic version of 'delete'. +{-# INLINE deleteM #-} +deleteM :: (Monad m) => StrategyM m a () +deleteM = fmap return delete + +-- | +-- A monadic version of 'lookup'. +{-# INLINE lookupM #-} +lookupM :: (Monad m) => StrategyM m a (Maybe a) +lookupM = fmap return lookup + + diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/CHANGES.md b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/CHANGES.md new file mode 100644 index 0000000..b938239 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/CHANGES.md @@ -0,0 +1,225 @@ +## Version 1.2.6.1 + + * Use typeRepFingerprint from Type.Reflection.Unsafe + + * Bump minimum version of base to 4.4. + +## Version 1.2.6.0 + + * Add support for type-indexed `Typeable`. + + * Rework the `Generic` hashable for sums. + +## Version 1.2.5.0 + + * Add `Hashable1` and `Hashable2` + + * Add instances for: `Eq1`, `Ord1`, `Show1`, `Ptr`, `FunPtr`, `IntPtr`, `WordPtr` + + * Add `Hashed` type for caching the `hash` function result. + +## Version 1.2.4.0 + + * Add instances for: Unique, Version, Fixed, NonEmpty, Min, Max, Arg, + First, Last, WrappedMonoid, Option + + * Support GHC 8.0 + +## Version 1.2.3.3 + + * Support integer-simple. + +## Version 1.2.3.2 + + * Add support for GHC 7.10 typeRepFingerprint + +## Version 1.2.3.1 + + * Added support for random 1.1.*. + +## Version 1.2.3.0 + + * Silence integer literal overflow warning + + * Add support for GHC 7.10 `integer-gmp2` & `Natural` + + * Add instance for Data.Void + + * Make the SSE .cabal flags manual + + * Add an upper bound on bytestring + +## Version 1.2.2.0 + + * Add instances for `Data.ByteString.Short` + + * Use a 32-bit default salt on 32-bit archs. + +## Version 1.2.1.0 + + * Revert instances to their 1.1 implementations to regain the + performance we had then. + + * Remove use of random salt altogether. Without using SipHash the + benefit is unclear (i.e. collision attacks still work) and the + complexity is no longer worth it. + + * Documentation improvements. + +## Version 1.2.0.10 + + * Fix for GHC 7.0. + +## Version 1.2.0.9 + + * Stop using SipHash. The current implementation still has segfault + causing bugs that we won't be able to fix soon. + + * Stop using Wang hash. It degrades performance of fixed-size integer + hashing too much. + +## Version 1.2.0.8 + + * Fix linking issue when SSE was disabled. + + * Hash small signed Integers correctly. + +## Version 1.2.0.7 + + * Add flags to control usage of SSE. + +## Version 1.2.0.6 + + * Fix another segfault caused by SSE2 code. + +## Version 1.2.0.5 + + * More portability fixes. + + * Force stack alignment to 16 bytes everywhere. Fixes a segfault. + + * Fix bug where code relied on rewrite rules firing for correctness. + +## Version1.2.0.4 + + * Update docs to match code. + + * Work around bug in GHCi runtime linker, which never call static + initializers. + +## Version1.2.0.3 + + * Make building of SSE 4.1 code conditional, as it doesn't work on all + platforms. + + * Use a fixed salt, but allow random salting. Random salting by + default broke people's code. + +## Version1.2.0.2 + + * Work around ghci linker bug on Windows. + +## Version1.2.0.1 + + * Fix performance bug in SSE implementation of SipHash. + + * Fix segfault due to incorrect stack alignment on Windows. + +## Version1.2.0.0 + + * Switch string hashing from FNV-1 to SipHash, in an effort to + prevent collision attacks. + + * Switch fixed-size integer hashing to Wang hash. + + * The default salt now switched on every program run, in an effort to + prevent collision attacks. + + * Move hash method out of Hashable type class. + + * Add support for generic instance deriving. + + * Add instance for Ordering. + +## Version1.1.2.5 + + * Bug fix for bytestring < 0.10.0. + +## Version1.1.2.4 + + * Switch string hashing from Bernstein to FNV-1 + + * Faster instance for Integer. + + * Update dependency on base, ghc-prim + + * Now works with GHC 7.6. + +## Version1.1.2.3 + + * Add instance for TypeRep. + + * Update dependency on test-framework. + +## Version1.1.2.2 + + * Bug fix for GHC 7.4 + +## Version1.1.2.1 + + * Update dependency on test-framework. + + * Improve documentation of combine. + +## Version1.1.2.0 + + * Add instances for Interger, Ratio, Float, Double, and StableName. + + * Fix hash collision issues for lists and tuples when using a + user-specified salt. + +## Version1.1.1.0 + + * Improved instances for tuples and lists. + + * Add instances for StableName, Float, Double, Integer, and Ratio. + +## Version1.1.1.0 + + * Add hashWithSalt, which allows the user to create different hash + values for the same input by providing different seeds. This is + useful for application like Cuckoo hashing which need a family of + hash functions. + + * Fix a bug in the Hashable instance for Int64/Word64 on 32-bit + platforms. + + * Improved resilience to leading zero in the input being hashed. + +## Version1.1.0.0 + + * Add instance for: strict and lazy Texts, ThreadId + + * Add hashPtrWithSalt and hashByteArrayWithSalt. + + * Faster ByteArray# hashing. + + * Fix a signedness bug that affected ByteString. + + * Fix ByteString hashing to work correctly on both 32 and 64-bit + platforms. + +## Version1.0.1.1 + + * Fix bug in Hashable instance for lazy ByteStrings where differences + in the internal structure of the ByteString could cause different + hash values for ByteStrings that are equal according to ==. + +## Version1.0.1.0 + + * Add two helpers for creating Hashable instances: hashPtr and + hashByteArray. + +## Version1.0.0 + + * Separate Hashable class to its own package from hashmap 1.0.0.3. diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable.hs new file mode 100644 index 0000000..6c393d1 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable.hs @@ -0,0 +1,213 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveDataTypeable #-} +#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Trustworthy #-} +#endif + +------------------------------------------------------------------------ +-- | +-- Module : Data.Hashable +-- Copyright : (c) Milan Straka 2010 +-- (c) Johan Tibell 2011 +-- (c) Bryan O'Sullivan 2011, 2012 +-- License : BSD-style +-- Maintainer : johan.tibell@gmail.com +-- Stability : provisional +-- Portability : portable +-- +-- This module defines a class, 'Hashable', for types that can be +-- converted to a hash value. This class exists for the benefit of +-- hashing-based data structures. The module provides instances for +-- most standard types. Efficient instances for other types can be +-- generated automatically and effortlessly using the generics support +-- in GHC 7.2 and above. +-- +-- The easiest way to get started is to use the 'hash' function. Here +-- is an example session with @ghci@. +-- +-- > ghci> import Data.Hashable +-- > ghci> hash "foo" +-- > 60853164 + +module Data.Hashable + ( + -- * Hashing and security + -- $security + + -- * Computing hash values + Hashable(..) + + -- * Creating new instances + -- | There are two ways to create new instances: by deriving + -- instances automatically using GHC's generic programming + -- support or by writing instances manually. + + -- ** Generic instances + -- $generics + + -- *** Understanding a compiler error + -- $generic_err + + -- ** Writing instances by hand + -- $blocks + + -- *** Hashing contructors with multiple fields + -- $multiple-fields + + -- *** Hashing types with multiple constructors + -- $multiple-ctors + + , hashUsing + , hashPtr + , hashPtrWithSalt +#if defined(__GLASGOW_HASKELL__) + , hashByteArray + , hashByteArrayWithSalt +#endif + -- * Caching hashes + , Hashed + , hashed + , unhashed + , mapHashed + , traverseHashed + ) where + +import Data.Hashable.Class + +#ifdef GENERICS +import Data.Hashable.Generic () +#endif + +-- $security +-- #security# +-- +-- Applications that use hash-based data structures to store input +-- from untrusted users can be susceptible to \"hash DoS\", a class of +-- denial-of-service attack that uses deliberately chosen colliding +-- inputs to force an application into unexpectedly behaving with +-- quadratic time complexity. +-- +-- At this time, the string hashing functions used in this library are +-- susceptible to such attacks and users are recommended to either use +-- a 'Data.Map' to store keys derived from untrusted input or to use a +-- hash function (e.g. SipHash) that's resistant to such attacks. A +-- future version of this library might ship with such hash functions. + +-- $generics +-- +-- Beginning with GHC 7.2, the recommended way to make instances of +-- 'Hashable' for most types is to use the compiler's support for +-- automatically generating default instances. +-- +-- > {-# LANGUAGE DeriveGeneric #-} +-- > +-- > import GHC.Generics (Generic) +-- > import Data.Hashable +-- > +-- > data Foo a = Foo a String +-- > deriving (Eq, Generic) +-- > +-- > instance Hashable a => Hashable (Foo a) +-- > +-- > data Colour = Red | Green | Blue +-- > deriving Generic +-- > +-- > instance Hashable Colour +-- +-- If you omit a body for the instance declaration, GHC will generate +-- a default instance that correctly and efficiently hashes every +-- constructor and parameter. + +-- $generic_err +-- +-- Suppose you intend to use the generic machinery to automatically +-- generate a 'Hashable' instance. +-- +-- > data Oops = Oops +-- > -- forgot to add "deriving Generic" here! +-- > +-- > instance Hashable Oops +-- +-- And imagine that, as in the example above, you forget to add a +-- \"@deriving 'Generic'@\" clause to your data type. At compile time, +-- you will get an error message from GHC that begins roughly as +-- follows: +-- +-- > No instance for (GHashable (Rep Oops)) +-- +-- This error can be confusing, as 'GHashable' is not exported (it is +-- an internal typeclass used by this library's generics machinery). +-- The correct fix is simply to add the missing \"@deriving +-- 'Generic'@\". + +-- $blocks +-- +-- To maintain high quality hashes, new 'Hashable' instances should be +-- built using existing 'Hashable' instances, combinators, and hash +-- functions. +-- +-- The functions below can be used when creating new instances of +-- 'Hashable'. For example, for many string-like types the +-- 'hashWithSalt' method can be defined in terms of either +-- 'hashPtrWithSalt' or 'hashByteArrayWithSalt'. Here's how you could +-- implement an instance for the 'B.ByteString' data type, from the +-- @bytestring@ package: +-- +-- > import qualified Data.ByteString as B +-- > import qualified Data.ByteString.Internal as B +-- > import qualified Data.ByteString.Unsafe as B +-- > import Data.Hashable +-- > import Foreign.Ptr (castPtr) +-- > +-- > instance Hashable B.ByteString where +-- > hashWithSalt salt bs = B.inlinePerformIO $ +-- > B.unsafeUseAsCStringLen bs $ \(p, len) -> +-- > hashPtrWithSalt p (fromIntegral len) salt + +-- $multiple-fields +-- +-- Hash constructors with multiple fields by chaining 'hashWithSalt': +-- +-- > data Date = Date Int Int Int +-- > +-- > instance Hashable Date where +-- > hashWithSalt s (Date yr mo dy) = +-- > s `hashWithSalt` +-- > yr `hashWithSalt` +-- > mo `hashWithSalt` dy +-- +-- If you need to chain hashes together, use 'hashWithSalt' and follow +-- this recipe: +-- +-- > combineTwo h1 h2 = h1 `hashWithSalt` h2 + +-- $multiple-ctors +-- +-- For a type with several value constructors, there are a few +-- possible approaches to writing a 'Hashable' instance. +-- +-- If the type is an instance of 'Enum', the easiest path is to +-- convert it to an 'Int', and use the existing 'Hashable' instance +-- for 'Int'. +-- +-- > data Color = Red | Green | Blue +-- > deriving Enum +-- > +-- > instance Hashable Color where +-- > hashWithSalt = hashUsing fromEnum +-- +-- If the type's constructors accept parameters, it is important to +-- distinguish the constructors. To distinguish the constructors, add +-- a different integer to the hash computation of each constructor: +-- +-- > data Time = Days Int +-- > | Weeks Int +-- > | Months Int +-- > +-- > instance Hashable Time where +-- > hashWithSalt s (Days n) = s `hashWithSalt` +-- > (0::Int) `hashWithSalt` n +-- > hashWithSalt s (Weeks n) = s `hashWithSalt` +-- > (1::Int) `hashWithSalt` n +-- > hashWithSalt s (Months n) = s `hashWithSalt` +-- > (2::Int) `hashWithSalt` n diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Class.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Class.hs new file mode 100644 index 0000000..d10472b --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Class.hs @@ -0,0 +1,874 @@ +{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, MagicHash, + ScopedTypeVariables, UnliftedFFITypes #-} + +#if __GLASGOW_HASKELL__ < 710 +{-# LANGUAGE DeriveDataTypeable #-} +#endif + +#if __GLASGOW_HASKELL__ >= 801 +{-# LANGUAGE PolyKinds #-} -- For TypeRep instances +#endif + +#ifdef GENERICS +{-# LANGUAGE DefaultSignatures, FlexibleContexts, GADTs, + MultiParamTypeClasses, EmptyDataDecls #-} +#endif + +------------------------------------------------------------------------ +-- | +-- Module : Data.Hashable.Class +-- Copyright : (c) Milan Straka 2010 +-- (c) Johan Tibell 2011 +-- (c) Bryan O'Sullivan 2011, 2012 +-- License : BSD-style +-- Maintainer : johan.tibell@gmail.com +-- Stability : provisional +-- Portability : portable +-- +-- This module defines a class, 'Hashable', for types that can be +-- converted to a hash value. This class exists for the benefit of +-- hashing-based data structures. The module provides instances for +-- most standard types. + +module Data.Hashable.Class + ( + -- * Computing hash values + Hashable(..) + , Hashable1(..) + , Hashable2(..) +#ifdef GENERICS + -- ** Support for generics + , GHashable(..) + , HashArgs(..) + , Zero + , One +#endif + + -- * Creating new instances + , hashUsing + , hashPtr + , hashPtrWithSalt + , hashByteArray + , hashByteArrayWithSalt + , defaultHashWithSalt + -- * Higher Rank Functions + , hashWithSalt1 + , hashWithSalt2 + , defaultLiftHashWithSalt + -- * Caching hashes + , Hashed + , hashed + , unhashed + , mapHashed + , traverseHashed + ) where + +import Control.Applicative (Const(..)) +import Control.Exception (assert) +import Data.Bits (shiftL, shiftR, xor) +import Data.Int (Int8, Int16, Int32, Int64) +import Data.List (foldl') +import Data.Ratio (Ratio, denominator, numerator) +import Data.Version (Version(..)) +import Data.Word (Word8, Word16, Word32, Word64) +import Foreign.C (CString) +import Foreign.Marshal.Utils (with) +import Foreign.Ptr (Ptr, FunPtr, IntPtr, WordPtr, castPtr, castFunPtrToPtr, ptrToIntPtr) +import Foreign.Storable (alignment, peek, sizeOf) +import GHC.Base (ByteArray#) +import GHC.Conc (ThreadId(..)) +import GHC.Prim (ThreadId#) +import System.IO.Unsafe (unsafeDupablePerformIO) +import System.Mem.StableName +import Data.Unique (Unique, hashUnique) + +-- As we use qualified F.Foldable, we don't get warnings with newer base +import qualified Data.Foldable as F + +#if MIN_VERSION_base(4,7,0) +import Data.Proxy (Proxy) +#endif + +#if MIN_VERSION_base(4,7,0) +import Data.Fixed (Fixed(..)) +#endif + +#if MIN_VERSION_base(4,8,0) +import Data.Functor.Identity (Identity(..)) +#endif + +#ifdef GENERICS +import GHC.Generics +#endif + +#if __GLASGOW_HASKELL__ >= 801 +import Type.Reflection (Typeable, TypeRep, SomeTypeRep(..)) +import Type.Reflection.Unsafe (typeRepFingerprint) +import GHC.Fingerprint.Type(Fingerprint(..)) +#elif __GLASGOW_HASKELL__ >= 710 +import Data.Typeable (typeRepFingerprint, Typeable, TypeRep) +import GHC.Fingerprint.Type(Fingerprint(..)) +#elif __GLASGOW_HASKELL__ >= 702 +import Data.Typeable.Internal (Typeable, TypeRep (..)) +import GHC.Fingerprint.Type(Fingerprint(..)) +#elif __GLASGOW_HASKELL__ >= 606 +import Data.Typeable (typeRepKey, Typeable, TypeRep) +#endif + +#if __GLASGOW_HASKELL__ >= 703 +import Foreign.C (CLong(..)) +import Foreign.C.Types (CInt(..)) +#else +import Foreign.C (CLong) +import Foreign.C.Types (CInt) +#endif + +#if !(MIN_VERSION_base(4,8,0)) +import Data.Word (Word) +#endif + +#if MIN_VERSION_base(4,7,0) +import Data.Bits (finiteBitSize) +#else +import Data.Bits (bitSize) +#endif + +#if !(MIN_VERSION_bytestring(0,10,0)) +import qualified Data.ByteString.Lazy.Internal as BL -- foldlChunks +#endif + +#if MIN_VERSION_bytestring(0,10,4) +import qualified Data.ByteString.Short.Internal as BSI +#endif + +#ifdef VERSION_integer_gmp + +# if MIN_VERSION_integer_gmp(1,0,0) +# define MIN_VERSION_integer_gmp_1_0_0 +# endif + +import GHC.Exts (Int(..)) +import GHC.Integer.GMP.Internals (Integer(..)) +# if defined(MIN_VERSION_integer_gmp_1_0_0) +import GHC.Exts (sizeofByteArray#) +import GHC.Integer.GMP.Internals (BigNat(BN#)) +# endif +#endif + +#if MIN_VERSION_base(4,8,0) +import Data.Void (Void, absurd) +import GHC.Natural (Natural(..)) +import GHC.Exts (Word(..)) +#endif + +#if MIN_VERSION_base(4,9,0) +import qualified Data.List.NonEmpty as NE +import Data.Semigroup +import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..),showsUnaryWith) + +import Data.Functor.Compose (Compose(..)) +import qualified Data.Functor.Product as FP +import qualified Data.Functor.Sum as FS +#endif + +import Data.String (IsString(..)) + +#include "MachDeps.h" + +infixl 0 `hashWithSalt` + +------------------------------------------------------------------------ +-- * Computing hash values + +-- | A default salt used in the implementation of 'hash'. +defaultSalt :: Int +#if WORD_SIZE_IN_BITS == 64 +defaultSalt = -2578643520546668380 -- 0xdc36d1615b7400a4 +#else +defaultSalt = 0x087fc72c +#endif +{-# INLINE defaultSalt #-} + +-- | The class of types that can be converted to a hash value. +-- +-- Minimal implementation: 'hashWithSalt'. +class Hashable a where + -- | Return a hash value for the argument, using the given salt. + -- + -- The general contract of 'hashWithSalt' is: + -- + -- * If two values are equal according to the '==' method, then + -- applying the 'hashWithSalt' method on each of the two values + -- /must/ produce the same integer result if the same salt is + -- used in each case. + -- + -- * It is /not/ required that if two values are unequal + -- according to the '==' method, then applying the + -- 'hashWithSalt' method on each of the two values must produce + -- distinct integer results. However, the programmer should be + -- aware that producing distinct integer results for unequal + -- values may improve the performance of hashing-based data + -- structures. + -- + -- * This method can be used to compute different hash values for + -- the same input by providing a different salt in each + -- application of the method. This implies that any instance + -- that defines 'hashWithSalt' /must/ make use of the salt in + -- its implementation. + hashWithSalt :: Int -> a -> Int + + -- | Like 'hashWithSalt', but no salt is used. The default + -- implementation uses 'hashWithSalt' with some default salt. + -- Instances might want to implement this method to provide a more + -- efficient implementation than the default implementation. + hash :: a -> Int + hash = hashWithSalt defaultSalt + +#ifdef GENERICS + default hashWithSalt :: (Generic a, GHashable Zero (Rep a)) => Int -> a -> Int + hashWithSalt salt = ghashWithSalt HashArgs0 salt . from + +data Zero +data One + +data HashArgs arity a where + HashArgs0 :: HashArgs Zero a + HashArgs1 :: (Int -> a -> Int) -> HashArgs One a + +-- | The class of types that can be generically hashed. +class GHashable arity f where + ghashWithSalt :: HashArgs arity a -> Int -> f a -> Int + +#endif + +class Hashable1 t where + -- | Lift a hashing function through the type constructor. + liftHashWithSalt :: (Int -> a -> Int) -> Int -> t a -> Int +#ifdef GENERICS + default liftHashWithSalt :: (Generic1 t, GHashable One (Rep1 t)) => (Int -> a -> Int) -> Int -> t a -> Int + liftHashWithSalt h salt = ghashWithSalt (HashArgs1 h) salt . from1 +#endif + +class Hashable2 t where + -- | Lift a hashing function through the binary type constructor. + liftHashWithSalt2 :: (Int -> a -> Int) -> (Int -> b -> Int) -> Int -> t a b -> Int + +-- | Lift the 'hashWithSalt' function through the type constructor. +-- +-- > hashWithSalt1 = liftHashWithSalt hashWithSalt +hashWithSalt1 :: (Hashable1 f, Hashable a) => Int -> f a -> Int +hashWithSalt1 = liftHashWithSalt hashWithSalt + +-- | Lift the 'hashWithSalt' function through the type constructor. +-- +-- > hashWithSalt2 = liftHashWithSalt2 hashWithSalt hashWithSalt +hashWithSalt2 :: (Hashable2 f, Hashable a, Hashable b) => Int -> f a b -> Int +hashWithSalt2 = liftHashWithSalt2 hashWithSalt hashWithSalt + +-- | Lift the 'hashWithSalt' function halfway through the type constructor. +-- This function makes a suitable default implementation of 'liftHashWithSalt', +-- given that the type constructor @t@ in question can unify with @f a@. +defaultLiftHashWithSalt :: (Hashable2 f, Hashable a) => (Int -> b -> Int) -> Int -> f a b -> Int +defaultLiftHashWithSalt h = liftHashWithSalt2 hashWithSalt h + +-- | Since we support a generic implementation of 'hashWithSalt' we +-- cannot also provide a default implementation for that method for +-- the non-generic instance use case. Instead we provide +-- 'defaultHashWith'. +defaultHashWithSalt :: Hashable a => Int -> a -> Int +defaultHashWithSalt salt x = salt `combine` hash x + +-- | Transform a value into a 'Hashable' value, then hash the +-- transformed value using the given salt. +-- +-- This is a useful shorthand in cases where a type can easily be +-- mapped to another type that is already an instance of 'Hashable'. +-- Example: +-- +-- > data Foo = Foo | Bar +-- > deriving (Enum) +-- > +-- > instance Hashable Foo where +-- > hashWithSalt = hashUsing fromEnum +hashUsing :: (Hashable b) => + (a -> b) -- ^ Transformation function. + -> Int -- ^ Salt. + -> a -- ^ Value to transform. + -> Int +hashUsing f salt x = hashWithSalt salt (f x) +{-# INLINE hashUsing #-} + +instance Hashable Int where + hash = id + hashWithSalt = defaultHashWithSalt + +instance Hashable Int8 where + hash = fromIntegral + hashWithSalt = defaultHashWithSalt + +instance Hashable Int16 where + hash = fromIntegral + hashWithSalt = defaultHashWithSalt + +instance Hashable Int32 where + hash = fromIntegral + hashWithSalt = defaultHashWithSalt + +instance Hashable Int64 where + hash n +#if MIN_VERSION_base(4,7,0) + | finiteBitSize (undefined :: Int) == 64 = fromIntegral n +#else + | bitSize (undefined :: Int) == 64 = fromIntegral n +#endif + | otherwise = fromIntegral (fromIntegral n `xor` + (fromIntegral n `shiftR` 32 :: Word64)) + hashWithSalt = defaultHashWithSalt + +instance Hashable Word where + hash = fromIntegral + hashWithSalt = defaultHashWithSalt + +instance Hashable Word8 where + hash = fromIntegral + hashWithSalt = defaultHashWithSalt + +instance Hashable Word16 where + hash = fromIntegral + hashWithSalt = defaultHashWithSalt + +instance Hashable Word32 where + hash = fromIntegral + hashWithSalt = defaultHashWithSalt + +instance Hashable Word64 where + hash n +#if MIN_VERSION_base(4,7,0) + | finiteBitSize (undefined :: Int) == 64 = fromIntegral n +#else + | bitSize (undefined :: Int) == 64 = fromIntegral n +#endif + | otherwise = fromIntegral (n `xor` (n `shiftR` 32)) + hashWithSalt = defaultHashWithSalt + +instance Hashable () where + hash = fromEnum + hashWithSalt = defaultHashWithSalt + +instance Hashable Bool where + hash = fromEnum + hashWithSalt = defaultHashWithSalt + +instance Hashable Ordering where + hash = fromEnum + hashWithSalt = defaultHashWithSalt + +instance Hashable Char where + hash = fromEnum + hashWithSalt = defaultHashWithSalt + +#if defined(MIN_VERSION_integer_gmp_1_0_0) +instance Hashable BigNat where + hashWithSalt salt (BN# ba) = hashByteArrayWithSalt ba 0 numBytes salt + `hashWithSalt` size + where + size = numBytes `quot` SIZEOF_HSWORD + numBytes = I# (sizeofByteArray# ba) +#endif + +#if MIN_VERSION_base(4,8,0) +instance Hashable Natural where +# if defined(MIN_VERSION_integer_gmp_1_0_0) + hash (NatS# n) = hash (W# n) + hash (NatJ# bn) = hash bn + + hashWithSalt salt (NatS# n) = hashWithSalt salt (W# n) + hashWithSalt salt (NatJ# bn) = hashWithSalt salt bn +# else + hash (Natural n) = hash n + + hashWithSalt salt (Natural n) = hashWithSalt salt n +# endif +#endif + +instance Hashable Integer where +#if defined(VERSION_integer_gmp) +# if defined(MIN_VERSION_integer_gmp_1_0_0) + hash (S# n) = (I# n) + hash (Jp# bn) = hash bn + hash (Jn# bn) = negate (hash bn) + + hashWithSalt salt (S# n) = hashWithSalt salt (I# n) + hashWithSalt salt (Jp# bn) = hashWithSalt salt bn + hashWithSalt salt (Jn# bn) = negate (hashWithSalt salt bn) +# else + hash (S# int) = I# int + hash n@(J# size# byteArray) + | n >= minInt && n <= maxInt = fromInteger n :: Int + | otherwise = let size = I# size# + numBytes = SIZEOF_HSWORD * abs size + in hashByteArrayWithSalt byteArray 0 numBytes defaultSalt + `hashWithSalt` size + where minInt = fromIntegral (minBound :: Int) + maxInt = fromIntegral (maxBound :: Int) + + hashWithSalt salt (S# n) = hashWithSalt salt (I# n) + hashWithSalt salt n@(J# size# byteArray) + | n >= minInt && n <= maxInt = hashWithSalt salt (fromInteger n :: Int) + | otherwise = let size = I# size# + numBytes = SIZEOF_HSWORD * abs size + in hashByteArrayWithSalt byteArray 0 numBytes salt + `hashWithSalt` size + where minInt = fromIntegral (minBound :: Int) + maxInt = fromIntegral (maxBound :: Int) +# endif +#else + hashWithSalt salt = foldl' hashWithSalt salt . go + where + go n | inBounds n = [fromIntegral n :: Int] + | otherwise = fromIntegral n : go (n `shiftR` WORD_SIZE_IN_BITS) + maxInt = fromIntegral (maxBound :: Int) + inBounds x = x >= fromIntegral (minBound :: Int) && x <= maxInt +#endif + +#if MIN_VERSION_base(4,9,0) +-- Starting with base-4.9, numerator/denominator don't need 'Integral' anymore +instance Hashable a => Hashable (Ratio a) where +#else +instance (Integral a, Hashable a) => Hashable (Ratio a) where +#endif + {-# SPECIALIZE instance Hashable (Ratio Integer) #-} + hash a = hash (numerator a) `hashWithSalt` denominator a + hashWithSalt s a = s `hashWithSalt` numerator a `hashWithSalt` denominator a + +instance Hashable Float where + hash x + | isIEEE x = + assert (sizeOf x >= sizeOf (0::Word32) && + alignment x >= alignment (0::Word32)) $ + hash ((unsafeDupablePerformIO $ with x $ peek . castPtr) :: Word32) + | otherwise = hash (show x) + hashWithSalt = defaultHashWithSalt + +instance Hashable Double where + hash x + | isIEEE x = + assert (sizeOf x >= sizeOf (0::Word64) && + alignment x >= alignment (0::Word64)) $ + hash ((unsafeDupablePerformIO $ with x $ peek . castPtr) :: Word64) + | otherwise = hash (show x) + hashWithSalt = defaultHashWithSalt + +-- | A value with bit pattern (01)* (or 5* in hexa), for any size of Int. +-- It is used as data constructor distinguisher. GHC computes its value during +-- compilation. +distinguisher :: Int +distinguisher = fromIntegral $ (maxBound :: Word) `quot` 3 +{-# INLINE distinguisher #-} + +instance Hashable a => Hashable (Maybe a) where + hash Nothing = 0 + hash (Just a) = distinguisher `hashWithSalt` a + hashWithSalt = hashWithSalt1 + +instance Hashable1 Maybe where + liftHashWithSalt _ s Nothing = s `combine` 0 + liftHashWithSalt h s (Just a) = s `combine` distinguisher `h` a + +instance (Hashable a, Hashable b) => Hashable (Either a b) where + hash (Left a) = 0 `hashWithSalt` a + hash (Right b) = distinguisher `hashWithSalt` b + hashWithSalt = hashWithSalt1 + +instance Hashable a => Hashable1 (Either a) where + liftHashWithSalt = defaultLiftHashWithSalt + +instance Hashable2 Either where + liftHashWithSalt2 h _ s (Left a) = s `combine` 0 `h` a + liftHashWithSalt2 _ h s (Right b) = s `combine` distinguisher `h` b + +instance (Hashable a1, Hashable a2) => Hashable (a1, a2) where + hash (a1, a2) = hash a1 `hashWithSalt` a2 + hashWithSalt = hashWithSalt1 + +instance Hashable a1 => Hashable1 ((,) a1) where + liftHashWithSalt = defaultLiftHashWithSalt + +instance Hashable2 (,) where + liftHashWithSalt2 h1 h2 s (a1, a2) = s `h1` a1 `h2` a2 + +instance (Hashable a1, Hashable a2, Hashable a3) => Hashable (a1, a2, a3) where + hash (a1, a2, a3) = hash a1 `hashWithSalt` a2 `hashWithSalt` a3 + hashWithSalt = hashWithSalt1 + +instance (Hashable a1, Hashable a2) => Hashable1 ((,,) a1 a2) where + liftHashWithSalt = defaultLiftHashWithSalt + +instance Hashable a1 => Hashable2 ((,,) a1) where + liftHashWithSalt2 h1 h2 s (a1, a2, a3) = + (s `hashWithSalt` a1) `h1` a2 `h2` a3 + +instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4) => + Hashable (a1, a2, a3, a4) where + hash (a1, a2, a3, a4) = hash a1 `hashWithSalt` a2 + `hashWithSalt` a3 `hashWithSalt` a4 + hashWithSalt = hashWithSalt1 + +instance (Hashable a1, Hashable a2, Hashable a3) => Hashable1 ((,,,) a1 a2 a3) where + liftHashWithSalt = defaultLiftHashWithSalt + +instance (Hashable a1, Hashable a2) => Hashable2 ((,,,) a1 a2) where + liftHashWithSalt2 h1 h2 s (a1, a2, a3, a4) = + (s `hashWithSalt` a1 `hashWithSalt` a2) `h1` a3 `h2` a4 + +instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, Hashable a5) + => Hashable (a1, a2, a3, a4, a5) where + hash (a1, a2, a3, a4, a5) = + hash a1 `hashWithSalt` a2 `hashWithSalt` a3 + `hashWithSalt` a4 `hashWithSalt` a5 + hashWithSalt = hashWithSalt1 + +instance (Hashable a1, Hashable a2, Hashable a3, + Hashable a4) => Hashable1 ((,,,,) a1 a2 a3 a4) where + liftHashWithSalt = defaultLiftHashWithSalt + +instance (Hashable a1, Hashable a2, Hashable a3) + => Hashable2 ((,,,,) a1 a2 a3) where + liftHashWithSalt2 h1 h2 s (a1, a2, a3, a4, a5) = + (s `hashWithSalt` a1 `hashWithSalt` a2 + `hashWithSalt` a3) `h1` a4 `h2` a5 + + +instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, Hashable a5, + Hashable a6) => Hashable (a1, a2, a3, a4, a5, a6) where + hash (a1, a2, a3, a4, a5, a6) = + hash a1 `hashWithSalt` a2 `hashWithSalt` a3 + `hashWithSalt` a4 `hashWithSalt` a5 `hashWithSalt` a6 + hashWithSalt = hashWithSalt1 + +instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, + Hashable a5) => Hashable1 ((,,,,,) a1 a2 a3 a4 a5) where + liftHashWithSalt = defaultLiftHashWithSalt + +instance (Hashable a1, Hashable a2, Hashable a3, + Hashable a4) => Hashable2 ((,,,,,) a1 a2 a3 a4) where + liftHashWithSalt2 h1 h2 s (a1, a2, a3, a4, a5, a6) = + (s `hashWithSalt` a1 `hashWithSalt` a2 `hashWithSalt` a3 + `hashWithSalt` a4) `h1` a5 `h2` a6 + + +instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, Hashable a5, + Hashable a6, Hashable a7) => + Hashable (a1, a2, a3, a4, a5, a6, a7) where + hash (a1, a2, a3, a4, a5, a6, a7) = + hash a1 `hashWithSalt` a2 `hashWithSalt` a3 + `hashWithSalt` a4 `hashWithSalt` a5 `hashWithSalt` a6 `hashWithSalt` a7 + hashWithSalt s (a1, a2, a3, a4, a5, a6, a7) = + s `hashWithSalt` a1 `hashWithSalt` a2 `hashWithSalt` a3 + `hashWithSalt` a4 `hashWithSalt` a5 `hashWithSalt` a6 `hashWithSalt` a7 + +instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, Hashable a5, Hashable a6) => Hashable1 ((,,,,,,) a1 a2 a3 a4 a5 a6) where + liftHashWithSalt = defaultLiftHashWithSalt + +instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, + Hashable a5) => Hashable2 ((,,,,,,) a1 a2 a3 a4 a5) where + liftHashWithSalt2 h1 h2 s (a1, a2, a3, a4, a5, a6, a7) = + (s `hashWithSalt` a1 `hashWithSalt` a2 `hashWithSalt` a3 + `hashWithSalt` a4 `hashWithSalt` a5) `h1` a6 `h2` a7 + +instance Hashable (StableName a) where + hash = hashStableName + hashWithSalt = defaultHashWithSalt + +-- Auxillary type for Hashable [a] definition +data SPInt = SP !Int !Int + +instance Hashable a => Hashable [a] where + {-# SPECIALIZE instance Hashable [Char] #-} + hashWithSalt = hashWithSalt1 + +instance Hashable1 [] where + liftHashWithSalt h salt arr = finalise (foldl' step (SP salt 0) arr) + where + finalise (SP s l) = hashWithSalt s l + step (SP s l) x = SP (h s x) (l + 1) + +-- | Compute the hash of a ThreadId. +hashThreadId :: ThreadId -> Int +hashThreadId (ThreadId t) = hash (fromIntegral (getThreadId t) :: Int) + +foreign import ccall unsafe "rts_getThreadId" getThreadId + :: ThreadId# -> CInt + +instance Hashable ThreadId where + hash = hashThreadId + hashWithSalt = defaultHashWithSalt + +instance Hashable (Ptr a) where + hashWithSalt salt p = hashWithSalt salt $ ptrToIntPtr p + +instance Hashable (FunPtr a) where + hashWithSalt salt p = hashWithSalt salt $ castFunPtrToPtr p + +instance Hashable IntPtr where + hash n = fromIntegral n + hashWithSalt = defaultHashWithSalt + +instance Hashable WordPtr where + hash n = fromIntegral n + hashWithSalt = defaultHashWithSalt + +#if __GLASGOW_HASKELL__ < 801 +-- | Compute the hash of a TypeRep, in various GHC versions we can do this quickly. +hashTypeRep :: TypeRep -> Int +{-# INLINE hashTypeRep #-} +#if __GLASGOW_HASKELL__ >= 710 +-- Fingerprint is just the MD5, so taking any Int from it is fine +hashTypeRep tr = let Fingerprint x _ = typeRepFingerprint tr in fromIntegral x +#elif __GLASGOW_HASKELL__ >= 702 +-- Fingerprint is just the MD5, so taking any Int from it is fine +hashTypeRep (TypeRep (Fingerprint x _) _ _) = fromIntegral x +#elif __GLASGOW_HASKELL__ >= 606 +hashTypeRep = unsafeDupablePerformIO . typeRepKey +#else +hashTypeRep = hash . show +#endif + +instance Hashable TypeRep where + hash = hashTypeRep + hashWithSalt = defaultHashWithSalt + {-# INLINE hash #-} + +#else + +hashTypeRep :: Type.Reflection.TypeRep a -> Int +hashTypeRep tr = + let Fingerprint x _ = typeRepFingerprint tr in fromIntegral x + +instance Hashable Type.Reflection.SomeTypeRep where + hash (Type.Reflection.SomeTypeRep r) = hashTypeRep r + hashWithSalt = defaultHashWithSalt + {-# INLINE hash #-} + +instance Hashable (Type.Reflection.TypeRep a) where + hash = hashTypeRep + hashWithSalt = defaultHashWithSalt + {-# INLINE hash #-} +#endif + +#if MIN_VERSION_base(4,8,0) +instance Hashable Void where + hashWithSalt _ = absurd +#endif + +-- | Compute a hash value for the content of this pointer. +hashPtr :: Ptr a -- ^ pointer to the data to hash + -> Int -- ^ length, in bytes + -> IO Int -- ^ hash value +hashPtr p len = hashPtrWithSalt p len defaultSalt + +-- | Compute a hash value for the content of this pointer, using an +-- initial salt. +-- +-- This function can for example be used to hash non-contiguous +-- segments of memory as if they were one contiguous segment, by using +-- the output of one hash as the salt for the next. +hashPtrWithSalt :: Ptr a -- ^ pointer to the data to hash + -> Int -- ^ length, in bytes + -> Int -- ^ salt + -> IO Int -- ^ hash value +hashPtrWithSalt p len salt = + fromIntegral `fmap` c_hashCString (castPtr p) (fromIntegral len) + (fromIntegral salt) + +foreign import ccall unsafe "hashable_fnv_hash" c_hashCString + :: CString -> CLong -> CLong -> IO CLong + +-- | Compute a hash value for the content of this 'ByteArray#', +-- beginning at the specified offset, using specified number of bytes. +hashByteArray :: ByteArray# -- ^ data to hash + -> Int -- ^ offset, in bytes + -> Int -- ^ length, in bytes + -> Int -- ^ hash value +hashByteArray ba0 off len = hashByteArrayWithSalt ba0 off len defaultSalt +{-# INLINE hashByteArray #-} + +-- | Compute a hash value for the content of this 'ByteArray#', using +-- an initial salt. +-- +-- This function can for example be used to hash non-contiguous +-- segments of memory as if they were one contiguous segment, by using +-- the output of one hash as the salt for the next. +hashByteArrayWithSalt + :: ByteArray# -- ^ data to hash + -> Int -- ^ offset, in bytes + -> Int -- ^ length, in bytes + -> Int -- ^ salt + -> Int -- ^ hash value +hashByteArrayWithSalt ba !off !len !h = + fromIntegral $ c_hashByteArray ba (fromIntegral off) (fromIntegral len) + (fromIntegral h) + +foreign import ccall unsafe "hashable_fnv_hash_offset" c_hashByteArray + :: ByteArray# -> CLong -> CLong -> CLong -> CLong + +-- | Combine two given hash values. 'combine' has zero as a left +-- identity. +combine :: Int -> Int -> Int +combine h1 h2 = (h1 * 16777619) `xor` h2 + +instance Hashable Unique where + hash = hashUnique + hashWithSalt = defaultHashWithSalt + +instance Hashable Version where + hashWithSalt salt (Version branch tags) = + salt `hashWithSalt` branch `hashWithSalt` tags + +#if MIN_VERSION_base(4,7,0) +-- Using hashWithSalt1 would cause needless constraint +instance Hashable (Fixed a) where + hashWithSalt salt (MkFixed i) = hashWithSalt salt i +instance Hashable1 Fixed where + liftHashWithSalt _ salt (MkFixed i) = hashWithSalt salt i +#endif + +#if MIN_VERSION_base(4,8,0) +instance Hashable a => Hashable (Identity a) where + hashWithSalt = hashWithSalt1 +instance Hashable1 Identity where + liftHashWithSalt h salt (Identity x) = h salt x +#endif + +-- Using hashWithSalt1 would cause needless constraint +instance Hashable a => Hashable (Const a b) where + hashWithSalt salt (Const x) = hashWithSalt salt x + +instance Hashable a => Hashable1 (Const a) where + liftHashWithSalt = defaultLiftHashWithSalt + +instance Hashable2 Const where + liftHashWithSalt2 f _ salt (Const x) = f salt x + +#if MIN_VERSION_base(4,7,0) +instance Hashable (Proxy a) where + hash _ = 0 + hashWithSalt s _ = s + +instance Hashable1 Proxy where + liftHashWithSalt _ s _ = s +#endif + +-- instances formerly provided by 'semigroups' package +#if MIN_VERSION_base(4,9,0) +instance Hashable a => Hashable (NE.NonEmpty a) where + hashWithSalt p (a NE.:| as) = p `hashWithSalt` a `hashWithSalt` as + +instance Hashable a => Hashable (Min a) where + hashWithSalt p (Min a) = hashWithSalt p a + +instance Hashable a => Hashable (Max a) where + hashWithSalt p (Max a) = hashWithSalt p a + +instance (Hashable a, Hashable b) => Hashable (Arg a b) where + hashWithSalt p (Arg a b) = hashWithSalt p a `hashWithSalt` b + +instance Hashable a => Hashable (First a) where + hashWithSalt p (First a) = hashWithSalt p a + +instance Hashable a => Hashable (Last a) where + hashWithSalt p (Last a) = hashWithSalt p a + +instance Hashable a => Hashable (WrappedMonoid a) where + hashWithSalt p (WrapMonoid a) = hashWithSalt p a + +instance Hashable a => Hashable (Option a) where + hashWithSalt p (Option a) = hashWithSalt p a +#endif + +-- instances for @Data.Functor.{Product,Sum,Compose}@, present +-- in base-4.9 and onward. +#if MIN_VERSION_base(4,9,0) +-- | In general, @hash (Compose x) ≠ hash x@. However, @hashWithSalt@ satisfies +-- its variant of this equivalence. +instance (Hashable1 f, Hashable1 g, Hashable a) => Hashable (Compose f g a) where + hashWithSalt = hashWithSalt1 + +instance (Hashable1 f, Hashable1 g) => Hashable1 (Compose f g) where + liftHashWithSalt h s = liftHashWithSalt (liftHashWithSalt h) s . getCompose + +instance (Hashable1 f, Hashable1 g) => Hashable1 (FP.Product f g) where + liftHashWithSalt h s (FP.Pair a b) = liftHashWithSalt h (liftHashWithSalt h s a) b + +instance (Hashable1 f, Hashable1 g, Hashable a) => Hashable (FP.Product f g a) where + hashWithSalt = hashWithSalt1 + +instance (Hashable1 f, Hashable1 g) => Hashable1 (FS.Sum f g) where + liftHashWithSalt h s (FS.InL a) = liftHashWithSalt h (s `combine` 0) a + liftHashWithSalt h s (FS.InR a) = liftHashWithSalt h (s `combine` distinguisher) a + +instance (Hashable1 f, Hashable1 g, Hashable a) => Hashable (FS.Sum f g a) where + hashWithSalt = hashWithSalt1 +#endif + +-- | A hashable value along with the result of the 'hash' function. +data Hashed a = Hashed a {-# UNPACK #-} !Int + deriving (Typeable) + +-- | Wrap a hashable value, caching the 'hash' function result. +hashed :: Hashable a => a -> Hashed a +hashed a = Hashed a (hash a) + +-- | Unwrap hashed value. +unhashed :: Hashed a -> a +unhashed (Hashed a _) = a + +-- | Uses precomputed hash to detect inequality faster +instance Eq a => Eq (Hashed a) where + Hashed a ha == Hashed b hb = ha == hb && a == b + +instance Ord a => Ord (Hashed a) where + Hashed a _ `compare` Hashed b _ = a `compare` b + +instance Show a => Show (Hashed a) where + showsPrec d (Hashed a _) = showParen (d > 10) $ + showString "hashed" . showChar ' ' . showsPrec 11 a + +instance Hashable (Hashed a) where + hashWithSalt = defaultHashWithSalt + hash (Hashed _ h) = h + +-- This instance is a little unsettling. It is unusal for +-- 'liftHashWithSalt' to ignore its first argument when a +-- value is actually available for it to work on. +instance Hashable1 Hashed where + liftHashWithSalt _ s (Hashed _ h) = defaultHashWithSalt s h + +instance (IsString a, Hashable a) => IsString (Hashed a) where + fromString s = let r = fromString s in Hashed r (hash r) + +instance F.Foldable Hashed where + foldr f acc (Hashed a _) = f a acc + +-- | 'Hashed' cannot be 'Functor' +mapHashed :: Hashable b => (a -> b) -> Hashed a -> Hashed b +mapHashed f (Hashed a _) = hashed (f a) + +-- | 'Hashed' cannot be 'Traversable' +traverseHashed :: (Hashable b, Functor f) => (a -> f b) -> Hashed a -> f (Hashed b) +traverseHashed f (Hashed a _) = fmap hashed (f a) + +-- instances for @Data.Functor.Classes@ higher rank typeclasses +-- in base-4.9 and onward. +#if MIN_VERSION_base(4,9,0) +instance Eq1 Hashed where + liftEq f (Hashed a ha) (Hashed b hb) = ha == hb && f a b + +instance Ord1 Hashed where + liftCompare f (Hashed a _) (Hashed b _) = f a b + +instance Show1 Hashed where + liftShowsPrec sp _ d (Hashed a _) = showsUnaryWith sp "hashed" d a +#endif + + diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Generic.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Generic.hs new file mode 100644 index 0000000..3e8fe65 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Generic.hs @@ -0,0 +1,125 @@ +{-# LANGUAGE BangPatterns, FlexibleInstances, KindSignatures, + ScopedTypeVariables, TypeOperators, + MultiParamTypeClasses, GADTs, FlexibleContexts #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +------------------------------------------------------------------------ +-- | +-- Module : Data.Hashable.Generic +-- Copyright : (c) Bryan O'Sullivan 2012 +-- License : BSD-style +-- Maintainer : bos@serpentine.com +-- Stability : provisional +-- Portability : GHC >= 7.2 +-- +-- Hashable support for GHC generics. + +module Data.Hashable.Generic + ( + ) where + +import Data.Bits (shiftR) +import Data.Hashable.Class +import GHC.Generics + +-- Type without constructors +instance GHashable arity V1 where + ghashWithSalt _ salt _ = hashWithSalt salt () + +-- Constructor without arguments +instance GHashable arity U1 where + ghashWithSalt _ salt U1 = hashWithSalt salt () + +instance (GHashable arity a, GHashable arity b) => GHashable arity (a :*: b) where + ghashWithSalt toHash salt (x :*: y) = + (ghashWithSalt toHash (ghashWithSalt toHash salt x) y) + +-- Metadata (constructor name, etc) +instance GHashable arity a => GHashable arity (M1 i c a) where + ghashWithSalt targs salt = ghashWithSalt targs salt . unM1 + +-- Constants, additional parameters, and rank-1 recursion +instance Hashable a => GHashable arity (K1 i a) where + ghashWithSalt _ = hashUsing unK1 + +instance GHashable One Par1 where + ghashWithSalt (HashArgs1 h) salt = h salt . unPar1 + +instance Hashable1 f => GHashable One (Rec1 f) where + ghashWithSalt (HashArgs1 h) salt = liftHashWithSalt h salt . unRec1 + +instance (Hashable1 f, GHashable One g) => GHashable One (f :.: g) where + ghashWithSalt targs salt = liftHashWithSalt (ghashWithSalt targs) salt . unComp1 + +class SumSize f => GSum arity f where + hashSum :: HashArgs arity a -> Int -> Int -> f a -> Int + -- hashSum args salt index value = ... + +-- [Note: Hashing a sum type] +-- +-- The tree structure is used in GHC.Generics to represent the sum (and +-- product) part of the generic represention of the type, e.g.: +-- +-- (C0 ... :+: C1 ...) :+: (C2 ... :+: (C3 ... :+: C4 ...)) +-- +-- The value constructed with C2 constructor is represented as (R1 (L1 ...)). +-- Yet, if we think that this tree is a flat (heterogenous) list: +-- +-- [C0 ..., C1 ..., C2 ..., C3 ..., C4... ] +-- +-- then the value constructed with C2 is a (dependent) pair (2, ...), and +-- hashing it is simple: +-- +-- salt `hashWithSalt` (2 :: Int) `hashWithSalt` ... +-- +-- This is what we do below. When drilling down the tree, we count how many +-- leafs are to the left (`index` variable). At the leaf case C1, we'll have an +-- actual index into the sum. +-- +-- This works well for balanced data. However for recursive types like: +-- +-- data Nat = Z | S Nat +-- +-- the `hashWithSalt salt (S (S (S Z)))` is +-- +-- salt `hashWithSalt` (1 :: Int) -- first S +-- `hashWithSalt` (1 :: Int) -- second S +-- `hashWithSalt` (1 :: Int) -- third S +-- `hashWithSalt` (0 :: Int) -- Z +-- `hashWithSalt` () -- U1 +-- +-- For that type the manual implementation: +-- +-- instance Hashable Nat where +-- hashWithSalt salt n = hashWithSalt salt (natToInteger n) +-- +-- would be better performing CPU and hash-quality wise (assuming that +-- Integer's Hashable is of high quality). +-- +instance (GSum arity a, GSum arity b) => GHashable arity (a :+: b) where + ghashWithSalt toHash salt = hashSum toHash salt 0 + +instance (GSum arity a, GSum arity b) => GSum arity (a :+: b) where + hashSum toHash !salt !index s = case s of + L1 x -> hashSum toHash salt index x + R1 x -> hashSum toHash salt (index + sizeL) x + where + sizeL = unTagged (sumSize :: Tagged a) + {-# INLINE hashSum #-} + +instance GHashable arity a => GSum arity (C1 c a) where + hashSum toHash !salt !index (M1 x) = ghashWithSalt toHash (hashWithSalt salt index) x + {-# INLINE hashSum #-} + +class SumSize f where + sumSize :: Tagged f + +newtype Tagged (s :: * -> *) = Tagged {unTagged :: Int} + +instance (SumSize a, SumSize b) => SumSize (a :+: b) where + sumSize = Tagged $ unTagged (sumSize :: Tagged a) + + unTagged (sumSize :: Tagged b) + +instance SumSize (C1 c a) where + sumSize = Tagged 1 + diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Lifted.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Lifted.hs new file mode 100644 index 0000000..e477518 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/Lifted.hs @@ -0,0 +1,96 @@ +------------------------------------------------------------------------ +-- | +-- Module : Data.Hashable.Class +-- Copyright : (c) Milan Straka 2010 +-- (c) Johan Tibell 2011 +-- (c) Bryan O'Sullivan 2011, 2012 +-- License : BSD-style +-- Maintainer : johan.tibell@gmail.com +-- Stability : provisional +-- Portability : portable +-- +-- Lifting of the 'Hashable' class to unary and binary type constructors. +-- These classes are needed to express the constraints on arguments of +-- types that are parameterized by type constructors. Fixed-point data +-- types and monad transformers are such types. + +module Data.Hashable.Lifted + ( -- * Type Classes + Hashable1(..) + , Hashable2(..) + -- * Auxiliary Functions + , hashWithSalt1 + , hashWithSalt2 + , defaultLiftHashWithSalt + -- * Motivation + -- $motivation + ) where + +import Data.Hashable.Class + +-- $motivation +-- +-- This type classes provided in this module are used to express constraints +-- on type constructors in a Haskell98-compatible fashion. As an example, consider +-- the following two types (Note that these instances are not actually provided +-- because @hashable@ does not have @transformers@ or @free@ as a dependency): +-- +-- > newtype WriterT w m a = WriterT { runWriterT :: m (a, w) } +-- > data Free f a = Pure a | Free (f (Free f a)) +-- +-- The 'Hashable1' instances for @WriterT@ and @Free@ could be written as: +-- +-- > instance (Hashable w, Hashable1 m) => Hashable1 (WriterT w m) where +-- > liftHashWithSalt h s (WriterT m) = +-- > liftHashWithSalt (liftHashWithSalt2 h hashWithSalt) s m +-- > instance Hashable1 f => Hashable1 (Free f) where +-- > liftHashWithSalt h = go where +-- > go s x = case x of +-- > Pure a -> h s a +-- > Free p -> liftHashWithSalt go s p +-- +-- The 'Hashable' instances for these types can be trivially recovered with +-- 'hashWithSalt1': +-- +-- > instance (Hashable w, Hashable1 m, Hashable a) => Hashable (WriterT w m a) where +-- > hashWithSalt = hashWithSalt1 +-- > instance (Hashable1 f, Hashable a) => Hashable (Free f a) where +-- > hashWithSalt = hashWithSalt1 + +-- +-- $discussion +-- +-- Regardless of whether 'hashWithSalt1' is used to provide an implementation +-- of 'hashWithSalt', they should produce the same hash when called with +-- the same arguments. This is the only law that 'Hashable1' and 'Hashable2' +-- are expected to follow. +-- +-- The typeclasses in this module only provide lifting for 'hashWithSalt', not +-- for 'hash'. This is because such liftings cannot be defined in a way that +-- would satisfy the @liftHash@ variant of the above law. As an illustration +-- of the problem we run into, let us assume that 'Hashable1' were +-- given a 'liftHash' method: +-- +-- > class Hashable1 t where +-- > liftHash :: (Int -> a) -> t a -> Int +-- > liftHashWithSalt :: (Int -> a -> Int) -> Int -> t a -> Int +-- +-- Even for a type as simple as 'Maybe', the problem manifests itself. The +-- 'Hashable' instance for 'Maybe' is: +-- +-- > distinguisher :: Int +-- > distinguisher = ... +-- > +-- > instance Hashable a => Hashable (Maybe a) where +-- > hash Nothing = 0 +-- > hash (Just a) = distinguisher `hashWithSalt` a +-- > hashWithSalt s Nothing = ... +-- > hashWithSalt s (Just a) = ... +-- +-- The implementation of 'hash' calls 'hashWithSalt' on @a@. The hypothetical +-- @liftHash@ defined earlier only accepts an argument that corresponds to +-- the implementation of 'hash' for @a@. Consequently, this formulation of +-- @liftHash@ would not provide a way to match the current behavior of 'hash' +-- for 'Maybe'. This problem gets worse when 'Either' and @[]@ are considered. +-- The solution adopted in this library is to omit @liftHash@ entirely. + diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/RandomSource.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/RandomSource.hs new file mode 100644 index 0000000..678d04a --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/RandomSource.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE CPP, ForeignFunctionInterface #-} +#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Trustworthy #-} +#endif + +module Data.Hashable.RandomSource + ( + getRandomBytes + , getRandomBytes_ + ) where + +import Data.ByteString as B +import Data.ByteString.Internal (create) +import Foreign.C.Error (throwErrnoIfMinus1_) +#if MIN_VERSION_base(4,5,0) +import Foreign.C.Types (CInt(CInt)) +#else +import Foreign.C.Types (CInt) +#endif +import Foreign.Ptr (Ptr) + +getRandomBytes :: Int -> IO ByteString +getRandomBytes nbytes + | nbytes <= 0 = return B.empty + | otherwise = create nbytes $ flip (getRandomBytes_ "getRandomBytes") nbytes + +getRandomBytes_ :: String -> Ptr a -> Int -> IO () +getRandomBytes_ what ptr nbytes = do + throwErrnoIfMinus1_ what $ c_getRandomBytes ptr (fromIntegral nbytes) + +foreign import ccall unsafe "hashable_getRandomBytes" c_getRandomBytes + :: Ptr a -> CInt -> IO CInt diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/SipHash.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/SipHash.hs new file mode 100644 index 0000000..aef6cbd --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Data/Hashable/SipHash.hs @@ -0,0 +1,159 @@ +{-# LANGUAGE BangPatterns, CPP, GeneralizedNewtypeDeriving, RecordWildCards #-} +{-# OPTIONS_GHC -fno-warn-name-shadowing #-} + +module Data.Hashable.SipHash + ( + LE64 + , Sip + , fromWord64 + , fullBlock + , lastBlock + , finalize + , hashByteString + ) where + +#include "MachDeps.h" + +import Data.Bits ((.|.), (.&.), rotateL, shiftL, xor) +#if MIN_VERSION_base(4,5,0) +import Data.Bits (unsafeShiftL) +#endif +import Data.Word (Word8, Word64) +import Foreign.ForeignPtr (withForeignPtr) +import Foreign.Ptr (Ptr, castPtr, plusPtr) +import Data.ByteString.Internal (ByteString(PS), inlinePerformIO) +import Foreign.Storable (peek) +import Numeric (showHex) + +newtype LE64 = LE64 { fromLE64 :: Word64 } + deriving (Eq) + +instance Show LE64 where + show (LE64 !v) = let s = showHex v "" + in "0x" ++ replicate (16 - length s) '0' ++ s + +data Sip = Sip { + v0 :: {-# UNPACK #-} !Word64, v1 :: {-# UNPACK #-} !Word64 + , v2 :: {-# UNPACK #-} !Word64, v3 :: {-# UNPACK #-} !Word64 + } + +fromWord64 :: Word64 -> LE64 +#ifndef WORDS_BIGENDIAN +fromWord64 = LE64 +#else +#error big endian support TBD +#endif + +initState :: (Sip -> r) -> Word64 -> Word64 -> r +initState k k0 k1 = k (Sip s0 s1 s2 s3) + where !s0 = (k0 `xor` 0x736f6d6570736575) + !s1 = (k1 `xor` 0x646f72616e646f6d) + !s2 = (k0 `xor` 0x6c7967656e657261) + !s3 = (k1 `xor` 0x7465646279746573) + +sipRound :: (Sip -> r) -> Sip -> r +sipRound k Sip{..} = k (Sip v0_c v1_d v2_c v3_d) + where v0_a = v0 + v1 + v2_a = v2 + v3 + v1_a = v1 `rotateL` 13 + v3_a = v3 `rotateL` 16 + v1_b = v1_a `xor` v0_a + v3_b = v3_a `xor` v2_a + v0_b = v0_a `rotateL` 32 + v2_b = v2_a + v1_b + v0_c = v0_b + v3_b + v1_c = v1_b `rotateL` 17 + v3_c = v3_b `rotateL` 21 + v1_d = v1_c `xor` v2_b + v3_d = v3_c `xor` v0_c + v2_c = v2_b `rotateL` 32 + +fullBlock :: Int -> LE64 -> (Sip -> r) -> Sip -> r +fullBlock c m k st@Sip{..} + | c == 2 = sipRound (sipRound k') st' + | otherwise = runRounds c k' st' + where k' st1@Sip{..} = k st1{ v0 = v0 `xor` fromLE64 m } + st' = st{ v3 = v3 `xor` fromLE64 m } +{-# INLINE fullBlock #-} + +runRounds :: Int -> (Sip -> r) -> Sip -> r +runRounds c k = go 0 + where go i st + | i < c = sipRound (go (i+1)) st + | otherwise = k st +{-# INLINE runRounds #-} + +lastBlock :: Int -> Int -> LE64 -> (Sip -> r) -> Sip -> r +lastBlock !c !len !m k st = +#ifndef WORDS_BIGENDIAN + fullBlock c (LE64 m') k st +#else +#error big endian support TBD +#endif + where m' = fromLE64 m .|. ((fromIntegral len .&. 0xff) `shiftL` 56) +{-# INLINE lastBlock #-} + +finalize :: Int -> (Word64 -> r) -> Sip -> r +finalize d k st@Sip{..} + | d == 4 = sipRound (sipRound (sipRound (sipRound k'))) st' + | otherwise = runRounds d k' st' + where k' Sip{..} = k $! v0 `xor` v1 `xor` v2 `xor` v3 + st' = st{ v2 = v2 `xor` 0xff } +{-# INLINE finalize #-} + +hashByteString :: Int -> Int -> Word64 -> Word64 -> ByteString -> Word64 +hashByteString !c !d k0 k1 (PS fp off len) = + inlinePerformIO . withForeignPtr fp $ \basePtr -> + let ptr0 = basePtr `plusPtr` off + scant = len .&. 7 + endBlocks = ptr0 `plusPtr` (len - scant) + go !ptr st + | ptr == endBlocks = readLast ptr + | otherwise = do + m <- peekLE64 ptr + fullBlock c m (go (ptr `plusPtr` 8)) st + where + zero !m _ _ = lastBlock c len (LE64 m) (finalize d return) st + one k m p s = do + w <- fromIntegral `fmap` peekByte p + k (m .|. (w `unsafeShiftL` s)) (p `plusPtr` 1) (s+8) + readLast p = + case scant of + 0 -> zero 0 p (0::Int) + 1 -> one zero 0 p 0 + 2 -> one (one zero) 0 p 0 + 3 -> one (one (one zero)) 0 p 0 + 4 -> one (one (one (one zero))) 0 p 0 + 5 -> one (one (one (one (one zero)))) 0 p 0 + 6 -> one (one (one (one (one (one zero))))) 0 p 0 + _ -> one (one (one (one (one (one (one zero)))))) 0 p 0 + in initState (go ptr0) k0 k1 + +peekByte :: Ptr Word8 -> IO Word8 +peekByte = peek + +peekLE64 :: Ptr Word8 -> IO LE64 +#if defined(x86_64_HOST_ARCH) || defined(i386_HOST_ARCH) +-- platforms on which unaligned loads are legal and usually fast +peekLE64 p = LE64 `fmap` peek (castPtr p) +#else +peekLE64 p = do + let peek8 d = fromIntegral `fmap` peekByte (p `plusPtr` d) + b0 <- peek8 0 + b1 <- peek8 1 + b2 <- peek8 2 + b3 <- peek8 3 + b4 <- peek8 4 + b5 <- peek8 5 + b6 <- peek8 6 + b7 <- peek8 7 + let !w = (b7 `shiftL` 56) .|. (b6 `shiftL` 48) .|. (b5 `shiftL` 40) .|. + (b4 `shiftL` 32) .|. (b3 `shiftL` 24) .|. (b2 `shiftL` 16) .|. + (b1 `shiftL` 8) .|. b0 + return (fromWord64 w) +#endif + +#if !(MIN_VERSION_base(4,5,0)) +unsafeShiftL :: Word64 -> Int -> Word64 +unsafeShiftL = shiftL +#endif diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/LICENSE b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/LICENSE new file mode 100644 index 0000000..7130957 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/LICENSE @@ -0,0 +1,30 @@ +Copyright Milan Straka 2010 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Milan Straka nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/README.md b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/README.md new file mode 100644 index 0000000..d692480 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/README.md @@ -0,0 +1,7 @@ +The hashable package +==================== + +This package defines a class, `Hashable`, for types that can be +converted to a hash value. This class exists for the benefit of +hashing-based data structures. The package provides instances for +basic types and a way to combine hash values. diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Setup.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Setup.hs new file mode 100644 index 0000000..cd7dc32 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/Setup.hs @@ -0,0 +1,3 @@ +#!/usr/bin/env runhaskell +import Distribution.Simple +main = defaultMain diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/Benchmarks.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/Benchmarks.hs new file mode 100644 index 0000000..331642e --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/Benchmarks.hs @@ -0,0 +1,314 @@ +{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, MagicHash, + UnboxedTuples, DeriveGeneric #-} + +module Main (main) where + +import Control.Monad.ST +import Criterion.Main +import Data.Hashable +import Data.Hashable.SipHash +import Data.Int +import Foreign.ForeignPtr +import GHC.Exts +import GHC.ST (ST(..)) +import Data.Word +import Foreign.C.Types (CInt(..), CLong(..), CSize(..)) +import Foreign.Ptr +import Data.ByteString.Internal +import GHC.Generics (Generic) +import qualified Data.ByteString.Lazy as BL +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import qualified Crypto.MAC.SipHash as HS +import qualified Data.ByteString.Char8 as B8 + +-- Benchmark English words (5 and 8), base64 encoded integers (11), +-- SHA1 hashes as hex (40), and large blobs (1 Mb). +main :: IO () +main = do + -- We do not actually care about the contents of these pointers. + fp5 <- mallocForeignPtrBytes 5 + fp8 <- mallocForeignPtrBytes 8 + fp11 <- mallocForeignPtrBytes 11 + fp40 <- mallocForeignPtrBytes 40 + fp128 <- mallocForeignPtrBytes 128 + fp512 <- mallocForeignPtrBytes 512 + let !mb = 2^(20 :: Int) -- 1 Mb + fp1Mb <- mallocForeignPtrBytes mb + + let exP = P 22.0203 234.19 'x' 6424 + exS = S3 + exPS = PS3 'z' 7715 + + -- We don't care about the contents of these either. + let !ba5 = new 5; !ba8 = new 8; !ba11 = new 11; !ba40 = new 40 + !ba128 = new 128; !ba512 = new 512; !ba1Mb = new 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) + + let k0 = 0x4a7330fae70f52e8 + k1 = 0x919ea5953a9a1ec9 + sipHash = hashByteString 2 4 k0 k1 + hsSipHash = HS.hash (HS.SipKey k0 k1) + cSipHash (PS fp off len) = + inlinePerformIO . withForeignPtr fp $ \ptr -> + return $! c_siphash 2 4 k0 k1 (ptr `plusPtr` off) (fromIntegral len) + cSipHash24 (PS fp off len) = + inlinePerformIO . withForeignPtr fp $ \ptr -> + return $! c_siphash24 k0 k1 (ptr `plusPtr` off) (fromIntegral len) + fnvHash (PS fp off len) = + inlinePerformIO . withForeignPtr fp $ \ptr -> + return $! fnv_hash (ptr `plusPtr` off) (fromIntegral len) 2166136261 +#ifdef HAVE_SSE2 + sse2SipHash (PS fp off len) = + inlinePerformIO . withForeignPtr fp $ \ptr -> + return $! sse2_siphash k0 k1 (ptr `plusPtr` off) (fromIntegral len) +#endif +#ifdef HAVE_SSE41 + sse41SipHash (PS fp off len) = + inlinePerformIO . withForeignPtr fp $ \ptr -> + return $! sse41_siphash k0 k1 (ptr `plusPtr` off) (fromIntegral len) +#endif + + withForeignPtr fp5 $ \ p5 -> + withForeignPtr fp8 $ \ p8 -> + withForeignPtr fp11 $ \ p11 -> + withForeignPtr fp40 $ \ p40 -> + withForeignPtr fp128 $ \ p128 -> + withForeignPtr fp512 $ \ p512 -> + withForeignPtr fp1Mb $ \ p1Mb -> + defaultMain + [ bgroup "hashPtr" + [ bench "5" $ whnfIO $ hashPtr p5 5 + , bench "8" $ whnfIO $ hashPtr p8 8 + , bench "11" $ whnfIO $ hashPtr p11 11 + , bench "40" $ whnfIO $ hashPtr p40 40 + , bench "128" $ whnfIO $ hashPtr p128 128 + , bench "512" $ whnfIO $ hashPtr p512 512 + , bench "2^20" $ whnfIO $ hashPtr p1Mb mb + ] + , bgroup "hashByteArray" + [ bench "5" $ whnf (hashByteArray ba5 0) 5 + , bench "8" $ whnf (hashByteArray ba8 0) 8 + , bench "11" $ whnf (hashByteArray ba11 0) 11 + , bench "40" $ whnf (hashByteArray ba40 0) 40 + , bench "128" $ whnf (hashByteArray ba128 0) 128 + , bench "512" $ whnf (hashByteArray ba512 0) 512 + , bench "2^20" $ whnf (hashByteArray ba1Mb 0) mb + ] + , bgroup "hash" + [ bgroup "ByteString" + [ bgroup "strict" + [ bench "5" $ whnf hash bs5 + , bench "8" $ whnf hash bs8 + , bench "11" $ whnf hash bs11 + , bench "40" $ whnf hash bs40 + , bench "128" $ whnf hash bs128 + , bench "512" $ whnf hash bs512 + , bench "2^20" $ whnf hash bs1Mb + ] + , bgroup "lazy" + [ bench "5" $ whnf hash bl5 + , bench "8" $ whnf hash bl8 + , bench "11" $ whnf hash bl11 + , bench "40" $ whnf hash bl40 + , bench "128" $ whnf hash bl128 + , bench "512" $ whnf hash bl512 + , bench "2^20_40" $ whnf hash bl1Mb_40 + , bench "2^20_128" $ whnf hash bl1Mb_128 + , bench "2^20_64k" $ whnf hash bl1Mb_64k + ] + ] + , bgroup "String" + [ bench "5" $ whnf hash s5 + , bench "8" $ whnf hash s8 + , bench "11" $ whnf hash s11 + , bench "40" $ whnf hash s40 + , bench "128" $ whnf hash s128 + , bench "512" $ whnf hash s512 + , bench "2^20" $ whnf hash s1Mb + ] + , bgroup "Text" + [ bgroup "strict" + [ bench "5" $ whnf hash t5 + , bench "8" $ whnf hash t8 + , bench "11" $ whnf hash t11 + , bench "40" $ whnf hash t40 + , bench "128" $ whnf hash t128 + , bench "512" $ whnf hash t512 + , bench "2^20" $ whnf hash t1Mb + ] + , bgroup "lazy" + [ bench "5" $ whnf hash tl5 + , bench "8" $ whnf hash tl8 + , bench "11" $ whnf hash tl11 + , bench "40" $ whnf hash tl40 + , bench "128" $ whnf hash tl128 + , bench "512" $ whnf hash tl512 + , bench "2^20_40" $ whnf hash tl1Mb_40 + , bench "2^20_128" $ whnf hash tl1Mb_128 + , bench "2^20_64k" $ whnf hash tl1Mb_64k + ] + ] + , bench "Int8" $ whnf hash (0xef :: Int8) + , bench "Int16" $ whnf hash (0x7eef :: Int16) + , bench "Int32" $ whnf hash (0x7eadbeef :: Int32) + , bench "Int" $ whnf hash (0x7eadbeefdeadbeef :: Int) + , bench "Int64" $ whnf hash (0x7eadbeefdeadbeef :: Int64) + , bench "Double" $ whnf hash (0.3780675796601578 :: Double) + ] + , bgroup "sipHash" + [ bench "5" $ whnf sipHash bs5 + , bench "8" $ whnf sipHash bs8 + , bench "11" $ whnf sipHash bs11 + , bench "40" $ whnf sipHash bs40 + , bench "128" $ whnf sipHash bs128 + , bench "512" $ whnf sipHash bs512 + , bench "2^20" $ whnf sipHash bs1Mb + ] + , bgroup "cSipHash" + [ bench "5" $ whnf cSipHash bs5 + , bench "8" $ whnf cSipHash bs8 + , bench "11" $ whnf cSipHash bs11 + , bench "40" $ whnf cSipHash bs40 + , bench "128" $ whnf cSipHash bs128 + , bench "512" $ whnf cSipHash bs512 + , bench "2^20" $ whnf cSipHash bs1Mb + ] + , bgroup "cSipHash24" + [ bench "5" $ whnf cSipHash24 bs5 + , bench "8" $ whnf cSipHash24 bs8 + , bench "11" $ whnf cSipHash24 bs11 + , bench "40" $ whnf cSipHash24 bs40 + , bench "128" $ whnf cSipHash24 bs128 + , bench "512" $ whnf cSipHash24 bs512 + , bench "2^20" $ whnf cSipHash24 bs1Mb + ] +#ifdef HAVE_SSE2 + , bgroup "sse2SipHash" + [ bench "5" $ whnf sse2SipHash bs5 + , bench "8" $ whnf sse2SipHash bs8 + , bench "11" $ whnf sse2SipHash bs11 + , bench "40" $ whnf sse2SipHash bs40 + , bench "128" $ whnf sse2SipHash bs128 + , bench "512" $ whnf sse2SipHash bs512 + , bench "2^20" $ whnf sse2SipHash bs1Mb + ] +#endif +#ifdef HAVE_SSE41 + , bgroup "sse41SipHash" + [ bench "5" $ whnf sse41SipHash bs5 + , bench "8" $ whnf sse41SipHash bs8 + , bench "11" $ whnf sse41SipHash bs11 + , bench "40" $ whnf sse41SipHash bs40 + , bench "128" $ whnf sse41SipHash bs128 + , bench "512" $ whnf sse41SipHash bs512 + , bench "2^20" $ whnf sse41SipHash bs1Mb + ] +#endif + , bgroup "pkgSipHash" + [ bench "5" $ whnf hsSipHash bs5 + , bench "8" $ whnf hsSipHash bs8 + , bench "11" $ whnf hsSipHash bs11 + , bench "40" $ whnf hsSipHash bs40 + , bench "128" $ whnf hsSipHash bs128 + , bench "512" $ whnf hsSipHash bs512 + , bench "2^20" $ whnf hsSipHash bs1Mb + ] + , bgroup "fnv" + [ bench "5" $ whnf fnvHash bs5 + , bench "8" $ whnf fnvHash bs8 + , bench "11" $ whnf fnvHash bs11 + , bench "40" $ whnf fnvHash bs40 + , bench "128" $ whnf fnvHash bs128 + , bench "512" $ whnf fnvHash bs512 + , bench "2^20" $ whnf fnvHash bs1Mb + ] + , bgroup "Int" + [ bench "id32" $ whnf id (0x7eadbeef :: Int32) + , bench "id64" $ whnf id (0x7eadbeefdeadbeef :: Int64) + , bench "wang32" $ whnf hash_wang_32 0xdeadbeef + , bench "wang64" $ whnf hash_wang_64 0xdeadbeefdeadbeef + , bench "jenkins32a" $ whnf hash_jenkins_32a 0xdeadbeef + , bench "jenkins32b" $ whnf hash_jenkins_32b 0xdeadbeef + ] + , bgroup "Generic" + [ bench "product" $ whnf hash exP + , bench "sum" $ whnf hash exS + , bench "product and sum" $ whnf hash exPS + ] + ] + +data ByteArray = BA { unBA :: !ByteArray# } + +new :: Int -> ByteArray# +new (I# n#) = unBA (runST $ ST $ \s1 -> + case newByteArray# n# s1 of + (# s2, ary #) -> case unsafeFreezeByteArray# ary s2 of + (# s3, ba #) -> (# s3, BA ba #)) + +foreign import ccall unsafe "hashable_siphash" c_siphash + :: CInt -> CInt -> Word64 -> Word64 -> Ptr Word8 -> CSize -> Word64 +foreign import ccall unsafe "hashable_siphash24" c_siphash24 + :: Word64 -> Word64 -> Ptr Word8 -> CSize -> Word64 +#ifdef HAVE_SSE2 +foreign import ccall unsafe "hashable_siphash24_sse2" sse2_siphash + :: Word64 -> Word64 -> Ptr Word8 -> CSize -> Word64 +#endif +#ifdef HAVE_SSE41 +foreign import ccall unsafe "hashable_siphash24_sse41" sse41_siphash + :: Word64 -> Word64 -> Ptr Word8 -> CSize -> Word64 +#endif + +foreign import ccall unsafe "hashable_fnv_hash" fnv_hash + :: Ptr Word8 -> CLong -> CLong -> CLong + +foreign import ccall unsafe "hashable_wang_32" hash_wang_32 + :: Word32 -> Word32 +foreign import ccall unsafe "hashable_wang_64" hash_wang_64 + :: Word64 -> Word64 +foreign import ccall unsafe "hash_jenkins_32a" hash_jenkins_32a + :: Word32 -> Word32 +foreign import ccall unsafe "hash_jenkins_32b" hash_jenkins_32b + :: Word32 -> Word32 + +data PS + = PS1 Int Char Bool + | PS2 String () + | PS3 Char Int + deriving (Generic) + +data P = P Double Float Char Int + deriving (Generic) + +data S = S1 | S2 | S3 | S4 | S5 + deriving (Generic) + +instance Hashable PS +instance Hashable P +instance Hashable S + diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/inthash.c b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/inthash.c new file mode 100644 index 0000000..5fc32e9 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/inthash.c @@ -0,0 +1,28 @@ +#include + +/* + * 32-bit hashes by Bob Jenkins. + */ + +uint32_t hash_jenkins_32a(uint32_t a) +{ + a = (a+0x7ed55d16) + (a<<12); + a = (a^0xc761c23c) ^ (a>>19); + a = (a+0x165667b1) + (a<<5); + a = (a+0xd3a2646c) ^ (a<<9); + a = (a+0xfd7046c5) + (a<<3); + a = (a^0xb55a4f09) ^ (a>>16); + return a; +} + +uint32_t hash_jenkins_32b(uint32_t a) +{ + a -= (a<<6); + a ^= (a>>17); + a -= (a<<9); + a ^= (a<<4); + a -= (a<<3); + a ^= (a<<10); + a ^= (a>>15); + return a; +} diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash-sse2.c b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash-sse2.c new file mode 100644 index 0000000..74762bc --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash-sse2.c @@ -0,0 +1,129 @@ +/* + * The original code was developed by Samuel Neves, and has been + * only lightly modified. + * + * Used with permission. + */ +#pragma GCC target("sse2") + +#include +#include "siphash.h" + +#define _mm_roti_epi64(x, c) ((16 == (c)) ? _mm_shufflelo_epi16((x), _MM_SHUFFLE(2,1,0,3)) : _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c)))) + +u64 hashable_siphash24_sse2(u64 ik0, u64 ik1, const u8 *m, size_t n) +{ + __m128i v0, v1, v2, v3; + __m128i k0, k1; + __m128i mi, mask, len; + size_t i, k; + union { u64 gpr; __m128i xmm; } hash; + const u8 *p; + + /* We used to use the _mm_seti_epi32 intrinsic to initialize + SSE2 registers. This compiles to a movdqa instruction, + which requires 16-byte alignment. On 32-bit Windows, it + looks like ghc's runtime linker doesn't align ".rdata" + sections as requested, so we got segfaults for our trouble. + + Now we use an intrinsic that cares less about alignment + (_mm_loadu_si128, aka movdqu) instead, and all seems + happy. */ + + static const u32 const iv[6][4] = { + { 0x70736575, 0x736f6d65, 0, 0 }, + { 0x6e646f6d, 0x646f7261, 0, 0 }, + { 0x6e657261, 0x6c796765, 0, 0 }, + { 0x79746573, 0x74656462, 0, 0 }, + { -1, -1, 0, 0 }, + { 255, 0, 0, 0 }, + }; + + k0 = _mm_loadl_epi64((__m128i*)(&ik0)); + k1 = _mm_loadl_epi64((__m128i*)(&ik1)); + + v0 = _mm_xor_si128(k0, _mm_loadu_si128((__m128i*) &iv[0])); + v1 = _mm_xor_si128(k1, _mm_loadu_si128((__m128i*) &iv[1])); + v2 = _mm_xor_si128(k0, _mm_loadu_si128((__m128i*) &iv[2])); + v3 = _mm_xor_si128(k1, _mm_loadu_si128((__m128i*) &iv[3])); + +#define HALF_ROUND(a,b,c,d,s,t) \ + do \ + { \ + a = _mm_add_epi64(a, b); c = _mm_add_epi64(c, d); \ + b = _mm_roti_epi64(b, s); d = _mm_roti_epi64(d, t); \ + b = _mm_xor_si128(b, a); d = _mm_xor_si128(d, c); \ + } while(0) + +#define COMPRESS(v0,v1,v2,v3) \ + do \ + { \ + HALF_ROUND(v0,v1,v2,v3,13,16); \ + v0 = _mm_shufflelo_epi16(v0, _MM_SHUFFLE(1,0,3,2)); \ + HALF_ROUND(v2,v1,v0,v3,17,21); \ + v2 = _mm_shufflelo_epi16(v2, _MM_SHUFFLE(1,0,3,2)); \ + } while(0) + + for(i = 0; i < (n-n%8); i += 8) + { + mi = _mm_loadl_epi64((__m128i*)(m + i)); + v3 = _mm_xor_si128(v3, mi); + if (SIPHASH_ROUNDS == 2) { + COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3); + } else { + for (k = 0; k < SIPHASH_ROUNDS; ++k) + COMPRESS(v0,v1,v2,v3); + } + v0 = _mm_xor_si128(v0, mi); + } + + p = m + n; + + /* We must be careful to not trigger a segfault by reading an + unmapped page. So where is the end of our input? */ + + if (((uintptr_t) p & 4095) == 0) + /* Exactly at a page boundary: do not read past the end. */ + mi = _mm_setzero_si128(); + else if (((uintptr_t) p & 4095) <= 4088) + /* Inside a page: safe to read past the end, as we'll + mask out any bits we shouldn't have looked at below. */ + mi = _mm_loadl_epi64((__m128i*)(m + i)); + else + /* Within 8 bytes of the end of a page: ensure that + our final read re-reads some bytes so that we do + not cross the page boundary, then shift our result + right so that the re-read bytes vanish. */ + mi = _mm_srli_epi64(_mm_loadl_epi64((__m128i*)(((uintptr_t) m + i) & ~7)), + 8 * (((uintptr_t) m + i) % 8)); + + len = _mm_set_epi32(0, 0, (n&0xff) << 24, 0); + mask = _mm_srli_epi64(_mm_loadu_si128((__m128i*) &iv[4]), 8*(8-n%8)); + mi = _mm_xor_si128(_mm_and_si128(mi, mask), len); + + v3 = _mm_xor_si128(v3, mi); + if (SIPHASH_ROUNDS == 2) { + COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3); + } else { + for (k = 0; k < SIPHASH_ROUNDS; ++k) + COMPRESS(v0,v1,v2,v3); + } + v0 = _mm_xor_si128(v0, mi); + + v2 = _mm_xor_si128(v2, _mm_loadu_si128((__m128i*) &iv[5])); + if (SIPHASH_FINALROUNDS == 4) { + COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3); + COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3); + } else { + for (k = 0; k < SIPHASH_FINALROUNDS; ++k) + COMPRESS(v0,v1,v2,v3); + } + + v0 = _mm_xor_si128(_mm_xor_si128(v0, v1), _mm_xor_si128(v2, v3)); + hash.xmm = v0; + +#undef COMPRESS +#undef HALF_ROUND + //return _mm_extract_epi32(v0, 0) | (((u64)_mm_extract_epi32(v0, 1)) << 32); + return hash.gpr; +} diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash-sse41.c b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash-sse41.c new file mode 100644 index 0000000..a8cf081 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash-sse41.c @@ -0,0 +1,86 @@ +/* + * The original code was developed by Samuel Neves, and has been + * only lightly modified. + * + * Used with permission. + */ +#pragma GCC target("sse4.1") + +#include +#include "siphash.h" + +// Specialized for siphash, do not reuse +#define rotate16(x) _mm_shufflehi_epi16((x), _MM_SHUFFLE(2,1,0,3)) + +#define _mm_roti_epi64(x, c) (((c) == 16) ? rotate16((x)) : _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c)))) +//#define _mm_roti_epi64(x, c) _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c))) + + +u64 hashable_siphash24_sse41(u64 _k0, u64 _k1, const unsigned char *m, size_t n) +{ + __m128i v0, v1, v02, v13; + __m128i k0; + __m128i mi, mask, len, h; + const __m128i zero = _mm_setzero_si128(); + size_t i, k; + union { u64 gpr; __m128i xmm; } hash; + unsigned char key[16]; + + ((u64 *)key)[0] = _k0; + ((u64 *)key)[1] = _k1; + + k0 = _mm_loadu_si128((__m128i*)(key + 0)); + + v0 = _mm_xor_si128(k0, _mm_set_epi32(0x646f7261, 0x6e646f6d, 0x736f6d65, 0x70736575)); + v1 = _mm_xor_si128(k0, _mm_set_epi32(0x74656462, 0x79746573, 0x6c796765, 0x6e657261)); + + v02 = _mm_unpacklo_epi64(v0, v1); + v13 = _mm_unpackhi_epi64(v0, v1); + +#define HALF_ROUND(a,b,s,t) \ +do \ +{ \ + __m128i b1,b2; \ + a = _mm_add_epi64(a, b); \ + b1 = _mm_roti_epi64(b, s); b2 = _mm_roti_epi64(b, t); b = _mm_blend_epi16(b1, b2, 0xF0); \ + b = _mm_xor_si128(b, a); \ +} while(0) + +#define COMPRESS(v02,v13) \ + do \ + { \ + HALF_ROUND(v02,v13,13,16); \ + v02 = _mm_shuffle_epi32(v02, _MM_SHUFFLE(0,1,3,2)); \ + HALF_ROUND(v02,v13,17,21); \ + v02 = _mm_shuffle_epi32(v02, _MM_SHUFFLE(0,1,3,2)); \ + } while(0) + + for(i = 0; i < (n-n%8); i += 8) + { + mi = _mm_loadl_epi64((__m128i*)(m + i)); + v13 = _mm_xor_si128(v13, _mm_unpacklo_epi64(zero, mi)); + for(k = 0; k < SIPHASH_ROUNDS; ++k) COMPRESS(v02,v13); + v02 = _mm_xor_si128(v02, mi); + } + + mi = _mm_loadl_epi64((__m128i*)(m + i)); + len = _mm_set_epi32(0, 0, (n&0xff) << 24, 0); + mask = _mm_srli_epi64(_mm_set_epi32(0, 0, 0xffffffff, 0xffffffff), 8*(8-n%8)); + mi = _mm_xor_si128(_mm_and_si128(mi, mask), len); + + v13 = _mm_xor_si128(v13, _mm_unpacklo_epi64(zero, mi)); + for(k = 0; k < SIPHASH_ROUNDS; ++k) COMPRESS(v02,v13); + v02 = _mm_xor_si128(v02, mi); + + v02 = _mm_xor_si128(v02, _mm_set_epi32(0, 0xff, 0, 0)); + for(k = 0; k < SIPHASH_FINALROUNDS; ++k) COMPRESS(v02,v13); + + v0 = _mm_xor_si128(v02, v13); + v0 = _mm_xor_si128(v0, _mm_castps_si128(_mm_movehl_ps(_mm_castsi128_ps(zero), _mm_castsi128_ps(v0)))); + hash.xmm = v0; + +#undef COMPRESS +#undef HALF_ROUND + //return _mm_extract_epi32(v0, 0) | (((u64)_mm_extract_epi32(v0, 1)) << 32); + return hash.gpr; +} diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash.c b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash.c new file mode 100644 index 0000000..7ed11d8 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash.c @@ -0,0 +1,262 @@ +/* Almost a verbatim copy of the reference implementation. */ + +#include +#include "siphash.h" + +#define ROTL(x,b) (u64)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define SIPROUND \ + do { \ + v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \ + v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \ + v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \ + v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \ + } while(0) + +#if defined(__i386) +# define _siphash24 plain_siphash24 +#endif + +static inline u64 odd_read(const u8 *p, int count, u64 val, int shift) +{ + switch (count) { + case 7: val |= ((u64)p[6]) << (shift + 48); + case 6: val |= ((u64)p[5]) << (shift + 40); + case 5: val |= ((u64)p[4]) << (shift + 32); + case 4: val |= ((u64)p[3]) << (shift + 24); + case 3: val |= ((u64)p[2]) << (shift + 16); + case 2: val |= ((u64)p[1]) << (shift + 8); + case 1: val |= ((u64)p[0]) << shift; + } + return val; +} + +static inline u64 _siphash(int c, int d, u64 k0, u64 k1, + const u8 *str, size_t len) +{ + u64 v0 = 0x736f6d6570736575ull ^ k0; + u64 v1 = 0x646f72616e646f6dull ^ k1; + u64 v2 = 0x6c7967656e657261ull ^ k0; + u64 v3 = 0x7465646279746573ull ^ k1; + const u8 *end, *p; + u64 b; + int i; + + for (p = str, end = str + (len & ~7); p < end; p += 8) { + u64 m = peek_u64le((u64 *) p); + v3 ^= m; + if (c == 2) { + SIPROUND; + SIPROUND; + } else { + for (i = 0; i < c; i++) + SIPROUND; + } + v0 ^= m; + } + + b = odd_read(p, len & 7, ((u64) len) << 56, 0); + + v3 ^= b; + if (c == 2) { + SIPROUND; + SIPROUND; + } else { + for (i = 0; i < c; i++) + SIPROUND; + } + v0 ^= b; + + v2 ^= 0xff; + if (d == 4) { + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + } else { + for (i = 0; i < d; i++) + SIPROUND; + } + b = v0 ^ v1 ^ v2 ^ v3; + return b; +} + + +static inline u64 _siphash24(u64 k0, u64 k1, const u8 *str, size_t len) +{ + return _siphash(2, 4, k0, k1, str, len); +} + +#if defined(__i386) +# undef _siphash24 + +static u64 (*_siphash24)(u64 k0, u64 k1, const u8 *, size_t); + +static void maybe_use_sse() + __attribute__((constructor)); + +static void maybe_use_sse() +{ + uint32_t eax = 1, ebx, ecx, edx; + + __asm volatile + ("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */ + "cpuid;" + "mov %%ebx, %%esi;" + "mov %%edi, %%ebx;" + :"+a" (eax), "=S" (ebx), "=c" (ecx), "=d" (edx) + : :"edi"); + +#if defined(HAVE_SSE2) + if (edx & (1 << 26)) + _siphash24 = hashable_siphash24_sse2; +#if defined(HAVE_SSE41) + else if (ecx & (1 << 19)) + _siphash24 = hashable_siphash24_sse41; +#endif + else +#endif + _siphash24 = plain_siphash24; +} + +#endif + +/* ghci's linker fails to call static initializers. */ +static inline void ensure_sse_init() +{ +#if defined(__i386) + if (_siphash24 == NULL) + maybe_use_sse(); +#endif +} + +u64 hashable_siphash(int c, int d, u64 k0, u64 k1, const u8 *str, size_t len) +{ + return _siphash(c, d, k0, k1, str, len); +} + +u64 hashable_siphash24(u64 k0, u64 k1, const u8 *str, size_t len) +{ + ensure_sse_init(); + return _siphash24(k0, k1, str, len); +} + +/* Used for ByteArray#s. We can't treat them like pointers in + native Haskell, but we can in unsafe FFI calls. + */ +u64 hashable_siphash24_offset(u64 k0, u64 k1, + const u8 *str, size_t off, size_t len) +{ + ensure_sse_init(); + return _siphash24(k0, k1, str + off, len); +} + +static int _siphash_chunk(int c, int d, int buffered, u64 v[5], + const u8 *str, size_t len, size_t totallen) +{ + u64 v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3], m, b; + const u8 *p, *end; + u64 carry = 0; + int i; + + if (buffered > 0) { + int unbuffered = 8 - buffered; + int tobuffer = unbuffered > len ? len : unbuffered; + int shift = buffered << 3; + + m = odd_read(str, tobuffer, v[4], shift); + str += tobuffer; + buffered += tobuffer; + len -= tobuffer; + + if (buffered < 8) + carry = m; + else { + v3 ^= m; + if (c == 2) { + SIPROUND; + SIPROUND; + } else { + for (i = 0; i < c; i++) + SIPROUND; + } + v0 ^= m; + buffered = 0; + m = 0; + } + } + + for (p = str, end = str + (len & ~7); p < end; p += 8) { + m = peek_u64le((u64 *) p); + v3 ^= m; + if (c == 2) { + SIPROUND; + SIPROUND; + } else { + for (i = 0; i < c; i++) + SIPROUND; + } + v0 ^= m; + } + + b = odd_read(p, len & 7, 0, 0); + + if (totallen == -1) { + v[0] = v0; + v[1] = v1; + v[2] = v2; + v[3] = v3; + v[4] = b | carry; + + return buffered + (len & 7); + } + + b |= ((u64) totallen) << 56; + + v3 ^= b; + if (c == 2) { + SIPROUND; + SIPROUND; + } else { + for (i = 0; i < c; i++) + SIPROUND; + } + v0 ^= b; + + v2 ^= 0xff; + if (d == 4) { + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + } else { + for (i = 0; i < d; i++) + SIPROUND; + } + v[4] = v0 ^ v1 ^ v2 ^ v3; + return 0; +} + +void hashable_siphash_init(u64 k0, u64 k1, u64 *v) +{ + v[0] = 0x736f6d6570736575ull ^ k0; + v[1] = 0x646f72616e646f6dull ^ k1; + v[2] = 0x6c7967656e657261ull ^ k0; + v[3] = 0x7465646279746573ull ^ k1; + v[4] = 0; +} + +int hashable_siphash24_chunk(int buffered, u64 v[5], const u8 *str, + size_t len, size_t totallen) +{ + return _siphash_chunk(2, 4, buffered, v, str, len, totallen); +} + +/* + * Used for ByteArray#. + */ +int hashable_siphash24_chunk_offset(int buffered, u64 v[5], const u8 *str, + size_t off, size_t len, size_t totallen) +{ + return _siphash_chunk(2, 4, buffered, v, str + off, len, totallen); +} diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash.h b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash.h new file mode 100644 index 0000000..05a565f --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/siphash.h @@ -0,0 +1,68 @@ +#ifndef _hashable_siphash_h +#define _hashable_siphash_h + +#include +#include + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +#define SIPHASH_ROUNDS 2 +#define SIPHASH_FINALROUNDS 4 + +u64 hashable_siphash(int, int, u64, u64, const u8 *, size_t); +u64 hashable_siphash24(u64, u64, const u8 *, size_t); + +#if defined(__i386) + +/* To use SSE instructions, we have to adjust the stack from its + default of 4-byte alignment to use 16-byte alignment. */ + +# define ALIGNED_STACK __attribute__((force_align_arg_pointer)) + +u64 hashable_siphash24_sse2(u64, u64, const u8 *, size_t) ALIGNED_STACK; +u64 hashable_siphash24_sse41(u64, u64, const u8 *, size_t) ALIGNED_STACK; +#endif + +#if defined(_WIN32) +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __BYTE_ORDER __LITTLE_ENDIAN + +#elif (defined(__FreeBSD__) && __FreeBSD_version >= 470000) || defined(__OpenBSD__) || defined(__NetBSD__) +# include +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __BYTE_ORDER BYTE_ORDER + +#elif (defined(BSD) && (BSD >= 199103)) || defined(__APPLE__) +# include +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __BYTE_ORDER BYTE_ORDER + +#elif defined(__linux__) +# include +#endif + +static inline u64 peek_u64le(const u64 *p) +{ + u64 x = *p; + +#if __BYTE_ORDER == __BIG_ENDIAN + x = ((x & 0xff00000000000000ull) >> 56) | + ((x & 0x00ff000000000000ull) >> 40) | + ((x & 0x0000ff0000000000ull) >> 24) | + ((x & 0x000000ff00000000ull) >> 8) | + ((x & 0x00000000ff000000ull) << 8) | + ((x & 0x0000000000ff0000ull) << 24) | + ((x & 0x000000000000ff00ull) << 40) | + ((x & 0x00000000000000ffull) << 56); +#endif + + return x; +} + +#endif /* _hashable_siphash_h */ diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/wang.c b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/wang.c new file mode 100644 index 0000000..ea37ff9 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/benchmarks/cbits/wang.c @@ -0,0 +1,29 @@ +/* + * These hash functions were developed by Thomas Wang. + * + * http://www.concentric.net/~ttwang/tech/inthash.htm + */ + +#include + +uint32_t hashable_wang_32(uint32_t a) +{ + a = (a ^ 61) ^ (a >> 16); + a = a + (a << 3); + a = a ^ (a >> 4); + a = a * 0x27d4eb2d; + a = a ^ (a >> 15); + return a; +} + +uint64_t hashable_wang_64(uint64_t key) +{ + key = (~key) + (key << 21); // key = (key << 21) - key - 1; + key = key ^ ((key >> 24) | (key << 40)); + key = (key + (key << 3)) + (key << 8); // key * 265 + key = key ^ ((key >> 14) | (key << 50)); + key = (key + (key << 2)) + (key << 4); // key * 21 + key = key ^ ((key >> 28) | (key << 36)); + key = key + (key << 31); + return key; +} diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/cbits/fnv.c b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/cbits/fnv.c new file mode 100644 index 0000000..5640b66 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/cbits/fnv.c @@ -0,0 +1,53 @@ +/* +Copyright Johan Tibell 2011 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Johan Tibell nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* FNV-1 hash + * + * The FNV-1 hash description: http://isthe.com/chongo/tech/comp/fnv/ + * The FNV-1 hash is public domain: http://isthe.com/chongo/tech/comp/fnv/#public_domain + */ +long hashable_fnv_hash(const unsigned char* str, long len, long hash) { + + while (len--) { + hash = (hash * 16777619) ^ *str++; + } + + return hash; +} + +/* Used for ByteArray#s. We can't treat them like pointers in + native Haskell, but we can in unsafe FFI calls. + */ +long hashable_fnv_hash_offset(const unsigned char* str, long offset, long len, long hash) { + return hashable_fnv_hash(str + offset, len, hash); +} diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/cbits/getRandomBytes.c b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/cbits/getRandomBytes.c new file mode 100644 index 0000000..25fb369 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/cbits/getRandomBytes.c @@ -0,0 +1,93 @@ +/* +Copyright Bryan O'Sullivan 2012 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Johan Tibell nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "MachDeps.h" + +int hashable_getRandomBytes(unsigned char *dest, int nbytes); + +#if defined(mingw32_HOST_OS) || defined(__MINGW32__) + +#include +#include + +int hashable_getRandomBytes(unsigned char *dest, int nbytes) +{ + HCRYPTPROV hCryptProv; + int ret; + + if (!CryptAcquireContextA(&hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return -1; + + ret = CryptGenRandom(hCryptProv, (DWORD) nbytes, (BYTE *) dest) ? nbytes : -1; + + CryptReleaseContext(hCryptProv, 0); + + bail: + return ret; +} + +#else + +#include +#include +#include + +/* Assumptions: /dev/urandom exists and does something sane, and does + not block. */ + +int hashable_getRandomBytes(unsigned char *dest, int nbytes) +{ + ssize_t off, nread; + int fd; + + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + return -1; + + for (off = 0; nbytes > 0; nbytes -= nread) { + nread = read(fd, dest + off, nbytes); + off += nread; + if (nread == -1) { + off = -1; + break; + } + } + + bail: + close(fd); + + return off; +} + +#endif diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/examples/Main.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/examples/Main.hs new file mode 100644 index 0000000..5d3fa28 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/examples/Main.hs @@ -0,0 +1,50 @@ +{-# LANGUAGE DeriveGeneric #-} +import Data.Hashable +import Data.Hashable.Lifted +import GHC.Generics (Generic) + +data Foo + = Foo1 Int Char Bool + | Foo2 String () + deriving (Generic) + +instance Hashable Foo + +data Bar = Bar Double Float + deriving (Generic) + +instance Hashable Bar + +-- printHash :: (Hashable a, Show a) => a -> IO () +-- printHash = print . hash + +main :: IO () +main = do + putStrLn "Hashing Foo1" + print . hash $ Foo1 22 'y' True + putStrLn "Hashing Foo2" + print . hash $ Foo2 "hello" () + putStrLn "Hashing Bar" + print . hash $ Bar 55.50 9.125 + +----------------------------------- +-- Higher Rank Hashable Examples -- +----------------------------------- + +newtype WriterT w m a = WriterT { runWriterT :: m (a, w) } +data Free f a = Pure a | Free (f (Free f a)) + +instance (Hashable w, Hashable1 m) => Hashable1 (WriterT w m) where + liftHashWithSalt h s (WriterT m) = + liftHashWithSalt (liftHashWithSalt2 h hashWithSalt) s m +instance Hashable1 f => Hashable1 (Free f) where + liftHashWithSalt h = go where + go s x = case x of + Pure a -> h s a + Free p -> liftHashWithSalt go s p + +instance (Hashable w, Hashable1 m, Hashable a) => Hashable (WriterT w m a) where + hashWithSalt = hashWithSalt1 +instance (Hashable1 f, Hashable a) => Hashable (Free f a) where + hashWithSalt = hashWithSalt1 + diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/hashable.cabal b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/hashable.cabal new file mode 100644 index 0000000..2f6df40 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/hashable.cabal @@ -0,0 +1,172 @@ +Name: hashable +Version: 1.2.6.1 +x-revision: 2 +Synopsis: A class for types that can be converted to a hash value +Description: This package defines a class, 'Hashable', for types that + can be converted to a hash value. This class + exists for the benefit of hashing-based data + structures. The package provides instances for + basic types and a way to combine hash values. +Homepage: http://github.com/tibbe/hashable +License: BSD3 +License-file: LICENSE +Author: Milan Straka + Johan Tibell +Maintainer: johan.tibell@gmail.com +bug-reports: https://github.com/tibbe/hashable/issues +Stability: Provisional +Category: Data +Build-type: Simple +Cabal-version: >=1.8 +-- tests/Properties.hs shouldn't have to go here, but the source files +-- for the test-suite stanzas don't get picked up by `cabal sdist`. +Extra-source-files: + CHANGES.md, README.md, tests/Properties.hs, + benchmarks/Benchmarks.hs, benchmarks/cbits/*.c, benchmarks/cbits/*.h + +Flag integer-gmp + Description: Are we using integer-gmp to provide fast Integer instances? + Default: True + +Flag sse2 + Description: Do we want to assume that a target supports SSE 2? + Default: True + Manual: True + +Flag sse41 + Description: Do we want to assume that a target supports SSE 4.1? + Default: False + Manual: True + +Flag examples + Description: Build example modules + Default: False + Manual: True + +Library + Exposed-modules: Data.Hashable + Data.Hashable.Lifted + Other-modules: Data.Hashable.Class + Build-depends: base >= 4.4 && < 4.11, + bytestring >= 0.9 && < 0.11, + deepseq >= 1.3 + if impl(ghc) + Build-depends: ghc-prim, + text >= 0.11.0.5 + if impl(ghc) && flag(integer-gmp) + Build-depends: integer-gmp >= 0.2 + + if impl(ghc >= 7.2.1) + CPP-Options: -DGENERICS + Other-modules: Data.Hashable.Generic + + C-sources: + cbits/fnv.c + + Ghc-options: -Wall + if impl(ghc >= 6.8) + Ghc-options: -fwarn-tabs + else + c-sources: cbits/getRandomBytes.c + other-modules: Data.Hashable.RandomSource + if os(windows) + extra-libraries: advapi32 + +Test-suite tests + Type: exitcode-stdio-1.0 + Hs-source-dirs: tests + Main-is: Main.hs + Other-modules: Properties Regress + Build-depends: base >= 4.0 && < 5.0, + bytestring, + ghc-prim, + hashable, + test-framework >= 0.3.3, + test-framework-hunit, + test-framework-quickcheck2 >= 0.2.9, + HUnit, + QuickCheck >= 2.4.0.1, + random >= 1.0 && < 1.2, + text >= 0.11.0.5 + if !os(windows) + Build-depends: unix + CPP-options: -DHAVE_MMAP + Other-modules: Regress.Mmap + + Ghc-options: -Wall -fno-warn-orphans + if impl(ghc >= 7.2.1) + CPP-Options: -DGENERICS + +benchmark benchmarks + -- We cannot depend on the hashable library directly as that creates + -- a dependency cycle. + hs-source-dirs: . benchmarks + + main-is: Benchmarks.hs + other-modules: + Data.Hashable + Data.Hashable.Class + Data.Hashable.RandomSource + Data.Hashable.SipHash + type: exitcode-stdio-1.0 + + build-depends: + base, + bytestring, + criterion >= 1.0, + ghc-prim, + siphash, + text + + if impl(ghc) + Build-depends: ghc-prim, + text >= 0.11.0.5 + if impl(ghc) && flag(integer-gmp) + Build-depends: integer-gmp >= 0.2 + + if impl(ghc >= 7.2.1) + CPP-Options: -DGENERICS + + include-dirs: + benchmarks/cbits + + includes: + siphash.h + + c-sources: + benchmarks/cbits/inthash.c + benchmarks/cbits/siphash.c + benchmarks/cbits/wang.c + cbits/fnv.c + + if (arch(i386) || arch(x86_64)) && flag(sse2) + cpp-options: -DHAVE_SSE2 + c-sources: + benchmarks/cbits/siphash-sse2.c + + if flag(sse41) + cpp-options: -DHAVE_SSE41 + c-sources: + benchmarks/cbits/siphash-sse41.c + + Ghc-options: -Wall -O2 + if impl(ghc >= 6.8) + Ghc-options: -fwarn-tabs + else + c-sources: cbits/getRandomBytes.c + other-modules: Data.Hashable.RandomSource + if os(windows) + extra-libraries: advapi32 + + +Executable hashable-examples + if flag(examples) + build-depends: base, hashable + else + buildable: False + hs-source-dirs: examples + main-is: Main.hs + +source-repository head + type: git + location: https://github.com/tibbe/hashable.git diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Main.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Main.hs new file mode 100644 index 0000000..844096f --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Main.hs @@ -0,0 +1,14 @@ +-- | Tests for the 'Data.Hashable' module. We test functions by +-- comparing the C and Haskell implementations. + +module Main (main) where + +import Properties (properties) +import Regress (regressions) +import Test.Framework (defaultMain, testGroup) + +main :: IO () +main = defaultMain [ + testGroup "properties" properties + , testGroup "regressions" regressions + ] diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Properties.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Properties.hs new file mode 100644 index 0000000..7a63cc4 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Properties.hs @@ -0,0 +1,264 @@ +{-# LANGUAGE BangPatterns, CPP, GeneralizedNewtypeDeriving, MagicHash, + Rank2Types, UnboxedTuples #-} +#ifdef GENERICS +{-# LANGUAGE DeriveGeneric, ScopedTypeVariables #-} +#endif + +-- | QuickCheck tests for the 'Data.Hashable' module. We test +-- functions by comparing the C and Haskell implementations. + +module Properties (properties) where + +import Data.Hashable (Hashable, hash, hashByteArray, hashPtr, + Hashed, hashed, unhashed, hashWithSalt) +import Data.Hashable.Lifted (hashWithSalt1) +import qualified Data.ByteString as B +import qualified Data.ByteString.Lazy as BL +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import Data.List (nub) +import Control.Monad (ap, liftM) +import System.IO.Unsafe (unsafePerformIO) +import Foreign.Marshal.Array (withArray) +import GHC.Base (ByteArray#, Int(..), newByteArray#, unsafeCoerce#, + writeWord8Array#) +import GHC.ST (ST(..), runST) +import GHC.Word (Word8(..)) +import Test.QuickCheck hiding ((.&.)) +import Test.Framework (Test, testGroup) +import Test.Framework.Providers.QuickCheck2 (testProperty) +#ifdef GENERICS +import GHC.Generics +#endif + +#if MIN_VERSION_bytestring(0,10,4) +import qualified Data.ByteString.Short as BS +#endif + +------------------------------------------------------------------------ +-- * Properties + +instance Arbitrary T.Text where + arbitrary = T.pack `fmap` arbitrary + +instance Arbitrary TL.Text where + arbitrary = TL.pack `fmap` arbitrary + +instance Arbitrary B.ByteString where + arbitrary = B.pack `fmap` arbitrary + +instance Arbitrary BL.ByteString where + arbitrary = sized $ \n -> resize (round (sqrt (toEnum n :: Double))) + ((BL.fromChunks . map (B.pack . nonEmpty)) `fmap` arbitrary) + where nonEmpty (NonEmpty a) = a + +#if MIN_VERSION_bytestring(0,10,4) +instance Arbitrary BS.ShortByteString where + arbitrary = BS.pack `fmap` arbitrary +#endif + +-- | Validate the implementation by comparing the C and Haskell +-- versions. +pHash :: [Word8] -> Bool +pHash xs = unsafePerformIO $ withArray xs $ \ p -> + (hashByteArray (fromList xs) 0 len ==) `fmap` hashPtr p len + where len = length xs + +-- | Content equality implies hash equality. +pText :: T.Text -> T.Text -> Bool +pText a b = if (a == b) then (hash a == hash b) else True + +-- | Content equality implies hash equality. +pTextLazy :: TL.Text -> TL.Text -> Bool +pTextLazy a b = if (a == b) then (hash a == hash b) else True + +-- | A small positive integer. +newtype ChunkSize = ChunkSize { unCS :: Int } + deriving (Eq, Ord, Num, Integral, Real, Enum) + +instance Show ChunkSize where show = show . unCS + +instance Arbitrary ChunkSize where + arbitrary = (ChunkSize . (`mod` maxChunkSize)) `fmap` + (arbitrary `suchThat` ((/=0) . (`mod` maxChunkSize))) + where maxChunkSize = 16 + +-- | Ensure that the rechunk function causes a rechunked string to +-- still match its original form. +pTextRechunk :: T.Text -> NonEmptyList ChunkSize -> Bool +pTextRechunk t cs = TL.fromStrict t == rechunkText t cs + +-- | Lazy strings must hash to the same value no matter how they are +-- chunked. +pTextLazyRechunked :: T.Text + -> NonEmptyList ChunkSize -> NonEmptyList ChunkSize -> Bool +pTextLazyRechunked t cs0 cs1 = + hash (rechunkText t cs0) == hash (rechunkText t cs1) + +-- | Break up a string into chunks of different sizes. +rechunkText :: T.Text -> NonEmptyList ChunkSize -> TL.Text +rechunkText t0 (NonEmpty cs0) = TL.fromChunks . go t0 . cycle $ cs0 + where + go t _ | T.null t = [] + go t (c:cs) = a : go b cs + where (a,b) = T.splitAt (unCS c) t + go _ [] = error "Properties.rechunk - The 'impossible' happened!" + +#if MIN_VERSION_bytestring(0,10,4) +-- | Content equality implies hash equality. +pBSShort :: BS.ShortByteString -> BS.ShortByteString -> Bool +pBSShort a b = if (a == b) then (hash a == hash b) else True +#endif + +-- | Content equality implies hash equality. +pBS :: B.ByteString -> B.ByteString -> Bool +pBS a b = if (a == b) then (hash a == hash b) else True + +-- | Content equality implies hash equality. +pBSLazy :: BL.ByteString -> BL.ByteString -> Bool +pBSLazy a b = if (a == b) then (hash a == hash b) else True + +-- | Break up a string into chunks of different sizes. +rechunkBS :: B.ByteString -> NonEmptyList ChunkSize -> BL.ByteString +rechunkBS t0 (NonEmpty cs0) = BL.fromChunks . go t0 . cycle $ cs0 + where + go t _ | B.null t = [] + go t (c:cs) = a : go b cs + where (a,b) = B.splitAt (unCS c) t + go _ [] = error "Properties.rechunkBS - The 'impossible' happened!" + +-- | Ensure that the rechunk function causes a rechunked string to +-- still match its original form. +pBSRechunk :: B.ByteString -> NonEmptyList ChunkSize -> Bool +pBSRechunk t cs = fromStrict t == rechunkBS t cs + +-- | Lazy bytestrings must hash to the same value no matter how they +-- are chunked. +pBSLazyRechunked :: B.ByteString + -> NonEmptyList ChunkSize -> NonEmptyList ChunkSize -> Bool +pBSLazyRechunked t cs1 cs2 = hash (rechunkBS t cs1) == hash (rechunkBS t cs2) + +-- This wrapper is required by 'runST'. +data ByteArray = BA { unBA :: ByteArray# } + +-- | Create a 'ByteArray#' from a list of 'Word8' values. +fromList :: [Word8] -> ByteArray# +fromList xs0 = unBA (runST $ ST $ \ s1# -> + case newByteArray# len# s1# of + (# s2#, marr# #) -> case go s2# 0 marr# xs0 of + s3# -> (# s3#, BA (unsafeCoerce# marr#) #)) + where + !(I# len#) = length xs0 + go s# _ _ [] = s# + go s# i@(I# i#) marr# ((W8# x):xs) = + case writeWord8Array# marr# i# x s# of + s2# -> go s2# (i + 1) marr# xs + +-- Generics + +#ifdef GENERICS + +data Product2 a b = Product2 a b + deriving (Generic) + +instance (Arbitrary a, Arbitrary b) => Arbitrary (Product2 a b) where + arbitrary = Product2 `liftM` arbitrary `ap` arbitrary + +instance (Hashable a, Hashable b) => Hashable (Product2 a b) + +data Product3 a b c = Product3 a b c + deriving (Generic) + +instance (Arbitrary a, Arbitrary b, Arbitrary c) => + Arbitrary (Product3 a b c) where + arbitrary = Product3 `liftM` arbitrary `ap` arbitrary `ap` arbitrary + +instance (Hashable a, Hashable b, Hashable c) => Hashable (Product3 a b c) + +-- Hashes of all product types of the same shapes should be the same. + +pProduct2 :: Int -> String -> Bool +pProduct2 x y = hash (x, y) == hash (Product2 x y) + +pProduct3 :: Double -> Maybe Bool -> (Int, String) -> Bool +pProduct3 x y z = hash (x, y, z) == hash (Product3 x y z) + +data Sum2 a b = S2a a | S2b b + deriving (Eq, Ord, Show, Generic) + +instance (Hashable a, Hashable b) => Hashable (Sum2 a b) + +data Sum3 a b c = S3a a | S3b b | S3c c + deriving (Eq, Ord, Show, Generic) + +instance (Hashable a, Hashable b, Hashable c) => Hashable (Sum3 a b c) + +-- Hashes of the same parameter, but with different sum constructors, +-- should differ. (They might legitimately collide, but that's +-- vanishingly unlikely.) + +pSum2_differ :: Int -> Bool +pSum2_differ x = nub hs == hs + where hs = [ hash (S2a x :: Sum2 Int Int) + , hash (S2b x :: Sum2 Int Int) ] + +pSum3_differ :: Int -> Bool +pSum3_differ x = nub hs == hs + where hs = [ hash (S3a x :: Sum3 Int Int Int) + , hash (S3b x :: Sum3 Int Int Int) + , hash (S3c x :: Sum3 Int Int Int) ] + +#endif + +instance (Arbitrary a, Hashable a) => Arbitrary (Hashed a) where + arbitrary = fmap hashed arbitrary + shrink xs = map hashed $ shrink $ unhashed xs + +pLiftedHashed :: Int -> Hashed (Either Int String) -> Bool +pLiftedHashed s h = hashWithSalt s h == hashWithSalt1 s h + +properties :: [Test] +properties = + [ testProperty "bernstein" pHash + , testGroup "text" + [ testProperty "text/strict" pText + , testProperty "text/lazy" pTextLazy + , testProperty "text/rechunk" pTextRechunk + , testProperty "text/rechunked" pTextLazyRechunked + ] + , testGroup "bytestring" + [ testProperty "bytestring/strict" pBS + , testProperty "bytestring/lazy" pBSLazy +#if MIN_VERSION_bytestring(0,10,4) + , testProperty "bytestring/short" pBSShort +#endif + , testProperty "bytestring/rechunk" pBSRechunk + , testProperty "bytestring/rechunked" pBSLazyRechunked + ] +#ifdef GENERICS + , testGroup "generics" + [ + -- Note: "product2" and "product3" have been temporarily + -- disabled until we have added a 'hash' method to the GHashable + -- class. Until then (a,b) hashes to a different value than (a + -- :*: b). While this is not incorrect, it would be nicer if + -- they didn't. testProperty "product2" pProduct2 , testProperty + -- "product3" pProduct3 + testProperty "sum2_differ" pSum2_differ + , testProperty "sum3_differ" pSum3_differ + ] +#endif + , testGroup "lifted law" + [ testProperty "Hashed" pLiftedHashed + ] + ] + +------------------------------------------------------------------------ +-- Utilities + +fromStrict :: B.ByteString -> BL.ByteString +#if MIN_VERSION_bytestring(0,10,0) +fromStrict = BL.fromStrict +#else +fromStrict b = BL.fromChunks [b] +#endif diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Regress.hs b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Regress.hs new file mode 100644 index 0000000..d06d0a0 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Regress.hs @@ -0,0 +1,47 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveGeneric #-} + +module Regress (regressions) where + +import qualified Test.Framework as F +import Test.Framework.Providers.HUnit (testCase) +import Test.HUnit ((@=?)) +import GHC.Generics (Generic) +import Data.List (nub) + +#ifdef HAVE_MMAP +import qualified Regress.Mmap as Mmap +#endif + +import Data.Hashable + +regressions :: [F.Test] +regressions = [] ++ +#ifdef HAVE_MMAP + Mmap.regressions ++ +#endif + [ F.testGroup "Generic: sum of nullary constructors" + [ testCase "0" $ nullaryCase 0 S0 + , testCase "1" $ nullaryCase 1 S1 + , testCase "2" $ nullaryCase 2 S2 + , testCase "3" $ nullaryCase 3 S3 + , testCase "4" $ nullaryCase 4 S4 + ] + , testCase "Generic: Peano https://github.com/tibbe/hashable/issues/135" $ do + let ns = take 20 $ iterate S Z + let hs = map hash ns + hs @=? nub hs + ] + where + nullaryCase :: Int -> SumOfNullary -> IO () + nullaryCase n s = do + let salt = 42 + let expected = salt `hashWithSalt` n `hashWithSalt` () + let actual = hashWithSalt salt s + expected @=? actual + +data SumOfNullary = S0 | S1 | S2 | S3 | S4 deriving (Generic) +instance Hashable SumOfNullary + +data Nat = Z | S Nat deriving (Generic) +instance Hashable Nat diff --git a/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Regress/Mmap.hsc b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Regress/Mmap.hsc new file mode 100644 index 0000000..b106af3 --- /dev/null +++ b/benchmarks/PPoPP2019/src/hashable-1.2.6.1/tests/Regress/Mmap.hsc @@ -0,0 +1,73 @@ +{-# LANGUAGE ForeignFunctionInterface #-} + +module Regress.Mmap (regressions) where + +#include + +import Control.Exception (bracket, evaluate) +import Control.Monad (forM_) +import Data.Bits ((.|.)) +import Data.ByteString.Internal (ByteString(..)) +import Data.Hashable (hash) +import Foreign.C.Error (throwErrnoIf, throwErrnoIfMinus1, throwErrnoIfMinus1_) +import Foreign.C.Types (CInt(..), CSize(..)) +import Foreign.Ptr (Ptr, intPtrToPtr, nullPtr, plusPtr) +import GHC.ForeignPtr (newForeignPtr_) +import System.Posix.Types (COff(..)) +import Test.Framework (Test) +import Test.Framework.Providers.HUnit (testCase) +import qualified Data.ByteString as B + +withMapping :: (Ptr a -> Int -> IO ()) -> IO () +withMapping go = do + pageSize <- fromIntegral `fmap` getPageSize + let mappingSize = pageSize * 2 + bracket (mmap + nullPtr + mappingSize + ((#const PROT_READ) .|. (#const PROT_WRITE)) + ((#const MAP_ANON) .|. (#const MAP_PRIVATE)) + (-1) + 0) + (flip munmap mappingSize) $ \mappingPtr -> do + go mappingPtr (fromIntegral pageSize) + mprotect (mappingPtr `plusPtr` fromIntegral pageSize) + pageSize (#const PROT_NONE) + +hashNearPageBoundary :: IO () +hashNearPageBoundary = + withMapping $ \ptr pageSize -> do + let initialSize = 16 + fp <- newForeignPtr_ (ptr `plusPtr` (pageSize - initialSize)) + let bs0 = PS fp 0 initialSize + forM_ (B.tails bs0) $ \bs -> do + evaluate (hash bs) + +regressions :: [Test] +regressions = [ + testCase "hashNearPageBoundary" hashNearPageBoundary + ] + +mmap :: Ptr a -> CSize -> CInt -> CInt -> CInt -> COff -> IO (Ptr a) +mmap addr len prot flags fd offset = + throwErrnoIf (== intPtrToPtr (#const MAP_FAILED)) "mmap" $ + c_mmap addr len prot flags fd offset + +munmap :: Ptr a -> CSize -> IO CInt +munmap addr len = throwErrnoIfMinus1 "munmap" $ c_munmap addr len + +mprotect :: Ptr a -> CSize -> CInt -> IO () +mprotect addr len prot = + throwErrnoIfMinus1_ "mprotect" $ c_mprotect addr len prot + +foreign import ccall unsafe "sys/mman.h mmap" + c_mmap :: Ptr a -> CSize -> CInt -> CInt -> CInt -> COff -> IO (Ptr a) + +foreign import ccall unsafe "sys/mman.h munmap" + c_munmap :: Ptr a -> CSize -> IO CInt + +foreign import ccall unsafe "sys/mman.h mprotect" + c_mprotect :: Ptr a -> CSize -> CInt -> IO CInt + +foreign import ccall unsafe "unistd.h getpagesize" + getPageSize :: IO CInt diff --git a/benchmarks/PPoPP2019/src/loch-th-0.2.1/Debug/Trace/LocationTH.hs b/benchmarks/PPoPP2019/src/loch-th-0.2.1/Debug/Trace/LocationTH.hs new file mode 100644 index 0000000..8432328 --- /dev/null +++ b/benchmarks/PPoPP2019/src/loch-th-0.2.1/Debug/Trace/LocationTH.hs @@ -0,0 +1,184 @@ +-- | +-- Module : Debug.Trace.LocationTH +-- Copyright : (c) Tomas Janousek 2011 +-- License : BSD-style +-- Maintainer : tomi@nomi.cz +-- Stability : experimental +-- Portability : non-portable (requires Template haskell) +-- Tested : GHC 7.0.3 +-- +-- This module provides a Template Haskell based mechanism to tag failures +-- with the location of the failure call. The location message includes the +-- file name, line and column numbers. +-- + +{-# LANGUAGE TemplateHaskell #-} +module Debug.Trace.LocationTH + ( __LOCATION__ + , assert + , failure + , undef + , check + , checkIO + , checkTrace + , checkTraceIO + ) where + +import qualified Control.Exception as C +import Control.Exception (throw, AssertionFailed(..)) +import Language.Haskell.TH.Ppr (pprint) +import Language.Haskell.TH.Syntax (location, Loc(..), Q, Exp, lift) +import System.IO.Unsafe (unsafePerformIO) +import Text.PrettyPrint.HughesPJ + +ppUnless :: Bool -> Doc -> Doc +ppUnless True _ = empty +ppUnless False doc = doc + +-- | Pretty print Loc. Mostly copypasted pprUserSpan from GHC 7. +pprLoc :: Loc -> Doc +pprLoc (Loc { loc_filename = src_path + , loc_start = (sline, start_col) + , loc_end = (eline, end_col) }) + | sline == eline = hcat + [ text src_path <> colon + , int sline, char ':', int start_col + , ppUnless (end_col - start_col <= 1) + (char '-' <> int (end_col-1)) + ] + | otherwise = hcat + [ text src_path <> colon + , parens (int sline <> char ',' <> int start_col) + , char '-' + , parens (int eline <> char ',' <> + if end_col == 0 then int end_col else int (end_col-1)) + ] + +-- +-- | Get the location of current splice as a 'String'. +-- +-- @$__LOCATION__ :: 'String'@ +-- +-- >>> $__LOCATION__ +-- ":1:1-13" +-- +__LOCATION__ :: Q Exp +__LOCATION__ = lift =<< (render . pprLoc) `fmap` location + +-- +-- | If the first argument evaluates to 'True', then the result is the second +-- argument. Otherwise an 'AssertionFailed' exception is raised, containing a +-- 'String' with the source file and line number of the call to 'assert'. +-- +-- @$(assert [| 'False' |]) :: a -> a@ +-- +-- >>> $(assert [| 5 + 5 == 9 |]) "foo" +-- "*** Exception: :1:3-25: Assertion `(5 GHC.Num.+ 5) GHC.Classes.== 9' failed +-- +assert :: Q Exp -> Q Exp +assert t = do + st <- pprint `fmap` t + [| assert' $t $__LOCATION__ st |] + +assert' :: Bool -> String -> String -> a -> a +assert' False loc st _ = throw $ AssertionFailed $ + loc ++ ": Assertion `" ++ st ++ "' failed" +assert' True _ _ x = x + +-- +-- | A location-emitting 'error' call. +-- +-- @$failure :: 'String' -> a@ +-- +-- >>> $failure "no such thing." +-- *** Exception: :1:1-8: no such thing. +-- +failure :: Q Exp +failure = [| failure' $__LOCATION__ |] + +failure' :: String -> String -> a +failure' loc t = error $ loc ++ ": " ++ t + +-- +-- | A location-emitting 'undefined'. +-- +-- @$undef :: a@ +-- +-- >>> $undef +-- *** Exception: :1:1-6: undefined +-- +undef :: Q Exp +undef = [| $failure "undefined" |] + +-- +-- | 'check' wraps a pure, partial function in a location-emitting +-- handler, should an exception be thrown. So instead of producing an +-- anonymous call to 'error', a location will be tagged to the error +-- message. +-- +-- @$check :: c -> c@ +-- +-- >>> $check $ head [] +-- *** Exception: :1:1-6: Prelude.head: empty list +-- +-- Be careful with laziness as the argument is only evaluated to weak head +-- normal form: +-- +-- >>> $check $ Just $ head "" +-- Just *** Exception: Prelude.head: empty list +-- >>> Just $ $check $ head "" +-- Just *** Exception: :9:8-13: Prelude.head: empty list +-- >>> $check $ join deepseq $ Just $ head "" +-- *** Exception: :1:1-6: Prelude.head: empty list +-- +check :: Q Exp +check = [| unsafePerformIO . $checkIO . C.evaluate |] + +-- +-- | 'checkIO' wraps an IO function in a location-emitting handler, +-- should an exception be thrown. So instead of producing an anonymous +-- call to 'error', a location will be tagged to the error message. +-- +-- @$checkIO :: IO a -> IO a@ +-- +-- >>> $checkIO $ readFile "/foo" +-- "*** Exception: :1:1-8: /foo: openFile: does not exist (No such file or directory) +-- +checkIO :: Q Exp +checkIO = [| checkIO' $__LOCATION__ |] + +checkIO' :: String -> IO a -> IO a +checkIO' loc a = C.catch a $ \e -> return $ failure' loc (showEx e) + +-- +-- | 'checkTrace' extends 'check' with the ability to add a custom string +-- to the error message. +-- +-- @$checkTrace :: String -> c -> c@ +-- +-- >>> $checkTrace "XXX" $ head [] +-- *** Exception: :1:1-6 XXX: Prelude.head: empty list +-- +checkTrace :: Q Exp +checkTrace = [| checkTrace' $__LOCATION__ |] + +checkTrace' :: String -> String -> a -> a +checkTrace' loc t = unsafePerformIO . checkTraceIO' loc t . C.evaluate + +-- +-- | 'checkTraceIO' extends 'checkIO' with the ability to add a custom +-- string to the error message. +-- +-- @$checkTraceIO :: String -> IO a -> IO a@ +-- +-- >>> $checkTraceIO "XXX" $ readFile "/foo" +-- "*** Exception: :1:1-8 XXX: /foo: openFile: does not exist (No such file or directory) +-- +checkTraceIO :: Q Exp +checkTraceIO = [| checkTraceIO' $__LOCATION__ |] + +checkTraceIO' :: String -> String -> IO a -> IO a +checkTraceIO' loc t = checkIO' (loc ++ " " ++ t) + +showEx :: C.SomeException -> String +showEx = show diff --git a/benchmarks/PPoPP2019/src/loch-th-0.2.1/LICENSE b/benchmarks/PPoPP2019/src/loch-th-0.2.1/LICENSE new file mode 100644 index 0000000..4f35ab7 --- /dev/null +++ b/benchmarks/PPoPP2019/src/loch-th-0.2.1/LICENSE @@ -0,0 +1,30 @@ +Copyright (c)2011, Tomas Janousek + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Tomas Janousek nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/benchmarks/PPoPP2019/src/loch-th-0.2.1/Setup.hs b/benchmarks/PPoPP2019/src/loch-th-0.2.1/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/benchmarks/PPoPP2019/src/loch-th-0.2.1/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/benchmarks/PPoPP2019/src/loch-th-0.2.1/loch-th.cabal b/benchmarks/PPoPP2019/src/loch-th-0.2.1/loch-th.cabal new file mode 100644 index 0000000..ca28ec0 --- /dev/null +++ b/benchmarks/PPoPP2019/src/loch-th-0.2.1/loch-th.cabal @@ -0,0 +1,19 @@ +Name: loch-th +Version: 0.2.1 +Synopsis: Support for precise error locations in source files (Template Haskell version) +Description: This module provides a Template Haskell based mechanism to + tag failures with the location of the failure call. The + location message includes the file name, line and column + numbers. +Homepage: https://github.com/liskin/loch-th +License: BSD3 +License-file: LICENSE +Author: Tomas Janousek +Maintainer: tomi@nomi.cz +Category: Development +Build-type: Simple +Cabal-version: >=1.6 + +Library + Exposed-modules: Debug.Trace.LocationTH + Build-depends: base == 4.*, template-haskell, pretty diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Control/Monad/Primitive.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Control/Monad/Primitive.hs new file mode 100644 index 0000000..91fd902 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Control/Monad/Primitive.hs @@ -0,0 +1,271 @@ +{-# LANGUAGE CPP, MagicHash, UnboxedTuples, TypeFamilies #-} +{-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-} +{-# LANGUAGE ScopedTypeVariables #-} + +-- | +-- Module : Control.Monad.Primitive +-- Copyright : (c) Roman Leshchinskiy 2009 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Primitive state-transformer monads +-- + +module Control.Monad.Primitive ( + PrimMonad(..), RealWorld, primitive_, + PrimBase(..), + liftPrim, primToPrim, primToIO, primToST, ioToPrim, stToPrim, + unsafePrimToPrim, unsafePrimToIO, unsafePrimToST, unsafeIOToPrim, + unsafeSTToPrim, unsafeInlinePrim, unsafeInlineIO, unsafeInlineST, + touch, evalPrim +) where + +import GHC.Prim ( State#, RealWorld, touch# ) +import GHC.Base ( unsafeCoerce#, realWorld# ) +#if MIN_VERSION_base(4,4,0) +import GHC.Base ( seq# ) +#else +-- import Control.Exception (evaluate) +#endif +#if MIN_VERSION_base(4,2,0) +import GHC.IO ( IO(..) ) +#else +import GHC.IOBase ( IO(..) ) +#endif +import GHC.ST ( ST(..) ) + +import Control.Monad.Trans.Class (lift) +#if !MIN_VERSION_base(4,8,0) +import Data.Monoid (Monoid) +#endif + +import Control.Monad.Trans.Cont ( ContT ) +import Control.Monad.Trans.Identity ( IdentityT (IdentityT) ) +import Control.Monad.Trans.List ( ListT ) +import Control.Monad.Trans.Maybe ( MaybeT ) +import Control.Monad.Trans.Error ( ErrorT, Error) +import Control.Monad.Trans.Reader ( ReaderT ) +import Control.Monad.Trans.State ( StateT ) +import Control.Monad.Trans.Writer ( WriterT ) +import Control.Monad.Trans.RWS ( RWST ) + +#if MIN_VERSION_transformers(0,4,0) +import Control.Monad.Trans.Except ( ExceptT ) +#endif + +#if MIN_VERSION_transformers(0,5,3) +import Control.Monad.Trans.Accum ( AccumT ) +import Control.Monad.Trans.Select ( SelectT ) +#endif + +import qualified Control.Monad.Trans.RWS.Strict as Strict ( RWST ) +import qualified Control.Monad.Trans.State.Strict as Strict ( StateT ) +import qualified Control.Monad.Trans.Writer.Strict as Strict ( WriterT ) + +-- | Class of monads which can perform primitive state-transformer actions +class Monad m => PrimMonad m where + -- | State token type + type PrimState m + + -- | Execute a primitive operation + primitive :: (State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a + +-- | Class of primitive monads for state-transformer actions. +-- +-- Unlike 'PrimMonad', this typeclass requires that the @Monad@ be fully +-- expressed as a state transformer, therefore disallowing other monad +-- transformers on top of the base @IO@ or @ST@. +class PrimMonad m => PrimBase m where + -- | Expose the internal structure of the monad + internal :: m a -> State# (PrimState m) -> (# State# (PrimState m), a #) + +-- | Execute a primitive operation with no result +primitive_ :: PrimMonad m + => (State# (PrimState m) -> State# (PrimState m)) -> m () +{-# INLINE primitive_ #-} +primitive_ f = primitive (\s# -> + case f s# of + s'# -> (# s'#, () #)) + +instance PrimMonad IO where + type PrimState IO = RealWorld + primitive = IO + {-# INLINE primitive #-} +instance PrimBase IO where + internal (IO p) = p + {-# INLINE internal #-} + +instance PrimMonad m => PrimMonad (ContT r m) where + type PrimState (ContT r m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance PrimMonad m => PrimMonad (IdentityT m) where + type PrimState (IdentityT m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance PrimBase m => PrimBase (IdentityT m) where + internal (IdentityT m) = internal m + {-# INLINE internal #-} +instance PrimMonad m => PrimMonad (ListT m) where + type PrimState (ListT m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance PrimMonad m => PrimMonad (MaybeT m) where + type PrimState (MaybeT m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance (Error e, PrimMonad m) => PrimMonad (ErrorT e m) where + type PrimState (ErrorT e m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance PrimMonad m => PrimMonad (ReaderT r m) where + type PrimState (ReaderT r m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance PrimMonad m => PrimMonad (StateT s m) where + type PrimState (StateT s m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance (Monoid w, PrimMonad m) => PrimMonad (WriterT w m) where + type PrimState (WriterT w m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance (Monoid w, PrimMonad m) => PrimMonad (RWST r w s m) where + type PrimState (RWST r w s m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} + +#if MIN_VERSION_transformers(0,4,0) +instance PrimMonad m => PrimMonad (ExceptT e m) where + type PrimState (ExceptT e m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +#endif + +#if MIN_VERSION_transformers(0,5,3) +instance ( Monoid w + , PrimMonad m +# if !(MIN_VERSION_base(4,8,0)) + , Functor m +# endif + ) => PrimMonad (AccumT w m) where + type PrimState (AccumT w m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance PrimMonad m => PrimMonad (SelectT r m) where + type PrimState (SelectT r m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +#endif + +instance PrimMonad m => PrimMonad (Strict.StateT s m) where + type PrimState (Strict.StateT s m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance (Monoid w, PrimMonad m) => PrimMonad (Strict.WriterT w m) where + type PrimState (Strict.WriterT w m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} +instance (Monoid w, PrimMonad m) => PrimMonad (Strict.RWST r w s m) where + type PrimState (Strict.RWST r w s m) = PrimState m + primitive = lift . primitive + {-# INLINE primitive #-} + +instance PrimMonad (ST s) where + type PrimState (ST s) = s + primitive = ST + {-# INLINE primitive #-} +instance PrimBase (ST s) where + internal (ST p) = p + {-# INLINE internal #-} + +-- | Lifts a 'PrimBase' into another 'PrimMonad' with the same underlying state +-- token type. +liftPrim + :: (PrimBase m1, PrimMonad m2, PrimState m1 ~ PrimState m2) => m1 a -> m2 a +{-# INLINE liftPrim #-} +liftPrim = primToPrim + +-- | Convert a 'PrimBase' to another monad with the same state token. +primToPrim :: (PrimBase m1, PrimMonad m2, PrimState m1 ~ PrimState m2) + => m1 a -> m2 a +{-# INLINE primToPrim #-} +primToPrim m = primitive (internal m) + +-- | Convert a 'PrimBase' with a 'RealWorld' state token to 'IO' +primToIO :: (PrimBase m, PrimState m ~ RealWorld) => m a -> IO a +{-# INLINE primToIO #-} +primToIO = primToPrim + +-- | Convert a 'PrimBase' to 'ST' +primToST :: PrimBase m => m a -> ST (PrimState m) a +{-# INLINE primToST #-} +primToST = primToPrim + +-- | Convert an 'IO' action to a 'PrimMonad'. +ioToPrim :: (PrimMonad m, PrimState m ~ RealWorld) => IO a -> m a +{-# INLINE ioToPrim #-} +ioToPrim = primToPrim + +-- | Convert an 'ST' action to a 'PrimMonad'. +stToPrim :: PrimMonad m => ST (PrimState m) a -> m a +{-# INLINE stToPrim #-} +stToPrim = primToPrim + +-- | Convert a 'PrimBase' to another monad with a possibly different state +-- token. This operation is highly unsafe! +unsafePrimToPrim :: (PrimBase m1, PrimMonad m2) => m1 a -> m2 a +{-# INLINE unsafePrimToPrim #-} +unsafePrimToPrim m = primitive (unsafeCoerce# (internal m)) + +-- | Convert any 'PrimBase' to 'ST' with an arbitrary state token. This +-- operation is highly unsafe! +unsafePrimToST :: PrimBase m => m a -> ST s a +{-# INLINE unsafePrimToST #-} +unsafePrimToST = unsafePrimToPrim + +-- | Convert any 'PrimBase' to 'IO'. This operation is highly unsafe! +unsafePrimToIO :: PrimBase m => m a -> IO a +{-# INLINE unsafePrimToIO #-} +unsafePrimToIO = unsafePrimToPrim + +-- | Convert an 'ST' action with an arbitraty state token to any 'PrimMonad'. +-- This operation is highly unsafe! +unsafeSTToPrim :: PrimMonad m => ST s a -> m a +{-# INLINE unsafeSTToPrim #-} +unsafeSTToPrim = unsafePrimToPrim + +-- | Convert an 'IO' action to any 'PrimMonad'. This operation is highly +-- unsafe! +unsafeIOToPrim :: PrimMonad m => IO a -> m a +{-# INLINE unsafeIOToPrim #-} +unsafeIOToPrim = unsafePrimToPrim + +unsafeInlinePrim :: PrimBase m => m a -> a +{-# INLINE unsafeInlinePrim #-} +unsafeInlinePrim m = unsafeInlineIO (unsafePrimToIO m) + +unsafeInlineIO :: IO a -> a +{-# INLINE unsafeInlineIO #-} +unsafeInlineIO m = case internal m realWorld# of (# _, r #) -> r + +unsafeInlineST :: ST s a -> a +{-# INLINE unsafeInlineST #-} +unsafeInlineST = unsafeInlinePrim + +touch :: PrimMonad m => a -> m () +{-# INLINE touch #-} +touch x = unsafePrimToPrim + $ (primitive (\s -> case touch# x s of { s' -> (# s', () #) }) :: IO ()) + +-- | Create an action to force a value; generalizes 'Control.Exception.evaluate' +evalPrim :: forall a m . PrimMonad m => a -> m a +#if MIN_VERSION_base(4,4,0) +evalPrim a = primitive (\s -> seq# a s) +#else +-- This may or may not work so well, but there's probably nothing better to do. +{-# NOINLINE evalPrim #-} +evalPrim a = unsafePrimToPrim (evaluate a :: IO a) +#endif diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive.hs new file mode 100644 index 0000000..0581211 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE MagicHash #-} +{-# OPTIONS_GHC -fno-warn-duplicate-exports #-} +-- | +-- Module : Data.Primitive +-- Copyright : (c) Roman Leshchinskiy 2009-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Reexports all primitive operations +-- +module Data.Primitive ( + module Data.Primitive.Types, + module Data.Primitive.Array, + module Data.Primitive.ByteArray, + module Data.Primitive.Addr, + + sizeOf, alignment +) where + +import Data.Primitive.Types +import Data.Primitive.Array +import Data.Primitive.ByteArray +import Data.Primitive.Addr diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Addr.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Addr.hs new file mode 100644 index 0000000..311dc31 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Addr.hs @@ -0,0 +1,102 @@ +{-# LANGUAGE MagicHash, UnboxedTuples #-} + +-- | +-- Module : Data.Primitive.Addr +-- Copyright : (c) Roman Leshchinskiy 2009-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Primitive operations on machine addresses +-- + +module Data.Primitive.Addr ( + -- * Types + Addr(..), + + -- * Address arithmetic + nullAddr, plusAddr, minusAddr, remAddr, + + -- * Element access + indexOffAddr, readOffAddr, writeOffAddr, + + -- * Block operations + copyAddr, moveAddr, setAddr +) where + +import Control.Monad.Primitive +import Data.Primitive.Types + +import GHC.Base ( Int(..) ) +import GHC.Prim + +import GHC.Ptr +import Foreign.Marshal.Utils + + +-- | The null address +nullAddr :: Addr +nullAddr = Addr nullAddr# + +infixl 6 `plusAddr`, `minusAddr` +infixl 7 `remAddr` + +-- | Offset an address by the given number of bytes +plusAddr :: Addr -> Int -> Addr +plusAddr (Addr a#) (I# i#) = Addr (plusAddr# a# i#) + +-- | Distance in bytes between two addresses. The result is only valid if the +-- difference fits in an 'Int'. +minusAddr :: Addr -> Addr -> Int +minusAddr (Addr a#) (Addr b#) = I# (minusAddr# a# b#) + +-- | The remainder of the address and the integer. +remAddr :: Addr -> Int -> Int +remAddr (Addr a#) (I# i#) = I# (remAddr# a# i#) + +-- | Read a value from a memory position given by an address and an offset. +-- The memory block the address refers to must be immutable. The offset is in +-- elements of type @a@ rather than in bytes. +indexOffAddr :: Prim a => Addr -> Int -> a +{-# INLINE indexOffAddr #-} +indexOffAddr (Addr addr#) (I# i#) = indexOffAddr# addr# i# + +-- | Read a value from a memory position given by an address and an offset. +-- The offset is in elements of type @a@ rather than in bytes. +readOffAddr :: (Prim a, PrimMonad m) => Addr -> Int -> m a +{-# INLINE readOffAddr #-} +readOffAddr (Addr addr#) (I# i#) = primitive (readOffAddr# addr# i#) + +-- | Write a value to a memory position given by an address and an offset. +-- The offset is in elements of type @a@ rather than in bytes. +writeOffAddr :: (Prim a, PrimMonad m) => Addr -> Int -> a -> m () +{-# INLINE writeOffAddr #-} +writeOffAddr (Addr addr#) (I# i#) x = primitive_ (writeOffAddr# addr# i# x) + +-- | Copy the given number of bytes from the second 'Addr' to the first. The +-- areas may not overlap. +copyAddr :: PrimMonad m => Addr -- ^ destination address + -> Addr -- ^ source address + -> Int -- ^ number of bytes + -> m () +{-# INLINE copyAddr #-} +copyAddr (Addr dst#) (Addr src#) n + = unsafePrimToPrim $ copyBytes (Ptr dst#) (Ptr src#) n + +-- | Copy the given number of bytes from the second 'Addr' to the first. The +-- areas may overlap. +moveAddr :: PrimMonad m => Addr -- ^ destination address + -> Addr -- ^ source address + -> Int -- ^ number of bytes + -> m () +{-# INLINE moveAddr #-} +moveAddr (Addr dst#) (Addr src#) n + = unsafePrimToPrim $ moveBytes (Ptr dst#) (Ptr src#) n + +-- | Fill a memory block of with the given value. The length is in +-- elements of type @a@ rather than in bytes. +setAddr :: (Prim a, PrimMonad m) => Addr -> Int -> a -> m () +{-# INLINE setAddr #-} +setAddr (Addr addr#) (I# n#) x = primitive_ (setOffAddr# addr# 0# n# x) + diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Array.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Array.hs new file mode 100644 index 0000000..32f4c76 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Array.hs @@ -0,0 +1,584 @@ +{-# LANGUAGE CPP, MagicHash, UnboxedTuples, DeriveDataTypeable, BangPatterns #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TypeFamilies #-} + +-- | +-- Module : Data.Primitive.Array +-- Copyright : (c) Roman Leshchinskiy 2009-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Primitive arrays of boxed values. +-- + +module Data.Primitive.Array ( + Array(..), MutableArray(..), + + newArray, readArray, writeArray, indexArray, indexArrayM, + freezeArray, thawArray, + unsafeFreezeArray, unsafeThawArray, sameMutableArray, + copyArray, copyMutableArray, + cloneArray, cloneMutableArray, + sizeofArray, sizeofMutableArray, + fromListN, fromList +) where + +import Control.Monad.Primitive + +import GHC.Base ( Int(..) ) +import GHC.Prim +import qualified GHC.Exts as Exts +#if (MIN_VERSION_base(4,7,0)) +import GHC.Exts (fromListN, fromList) +#endif + +import Data.Typeable ( Typeable ) +import Data.Data + (Data(..), DataType, mkDataType, Constr, mkConstr, Fixity(..), constrIndex) +import Data.Primitive.Internal.Compat ( isTrue#, mkNoRepType ) + +import Control.Monad.ST(ST,runST) + +import Control.Applicative +import Control.Monad (MonadPlus(..)) +import Control.Monad.Fix +#if MIN_VERSION_base(4,4,0) +import Control.Monad.Zip +#endif +import Data.Foldable (Foldable(..), toList) +#if !(MIN_VERSION_base(4,8,0)) +import Data.Traversable (Traversable(..)) +import Data.Monoid +#endif +#if MIN_VERSION_base(4,9,0) +import qualified Data.Foldable as F +import Data.Semigroup +#endif + +import Text.ParserCombinators.ReadP + +-- | Boxed arrays +data Array a = Array + { array# :: Array# a +#if (__GLASGOW_HASKELL__ < 702) + , sizeofArray :: {-# UNPACK #-} !Int +#endif + } + deriving ( Typeable ) + +-- | Mutable boxed arrays associated with a primitive state token. +data MutableArray s a = MutableArray + { marray# :: MutableArray# s a +#if (__GLASGOW_HASKELL__ < 702) + , sizeofMutableArray :: {-# UNPACK #-} !Int +#endif + } + deriving ( Typeable ) + +#if (__GLASGOW_HASKELL__ >= 702) +sizeofArray :: Array a -> Int +sizeofArray a = I# (sizeofArray# (array# a)) +{-# INLINE sizeofArray #-} + +sizeofMutableArray :: MutableArray s a -> Int +sizeofMutableArray a = I# (sizeofMutableArray# (marray# a)) +{-# INLINE sizeofMutableArray #-} +#endif + +-- | Create a new mutable array of the specified size and initialise all +-- elements with the given value. +newArray :: PrimMonad m => Int -> a -> m (MutableArray (PrimState m) a) +{-# INLINE newArray #-} +newArray (I# n#) x = primitive + (\s# -> case newArray# n# x s# of + (# s'#, arr# #) -> + let ma = MutableArray arr# +#if (__GLASGOW_HASKELL__ < 702) + (I# n#) +#endif + in (# s'# , ma #)) + +-- | Read a value from the array at the given index. +readArray :: PrimMonad m => MutableArray (PrimState m) a -> Int -> m a +{-# INLINE readArray #-} +readArray arr (I# i#) = primitive (readArray# (marray# arr) i#) + +-- | Write a value to the array at the given index. +writeArray :: PrimMonad m => MutableArray (PrimState m) a -> Int -> a -> m () +{-# INLINE writeArray #-} +writeArray arr (I# i#) x = primitive_ (writeArray# (marray# arr) i# x) + +-- | Read a value from the immutable array at the given index. +indexArray :: Array a -> Int -> a +{-# INLINE indexArray #-} +indexArray arr (I# i#) = case indexArray# (array# arr) i# of (# x #) -> x + +-- | Monadically read a value from the immutable array at the given index. +-- This allows us to be strict in the array while remaining lazy in the read +-- element which is very useful for collective operations. Suppose we want to +-- copy an array. We could do something like this: +-- +-- > copy marr arr ... = do ... +-- > writeArray marr i (indexArray arr i) ... +-- > ... +-- +-- But since primitive arrays are lazy, the calls to 'indexArray' will not be +-- evaluated. Rather, @marr@ will be filled with thunks each of which would +-- retain a reference to @arr@. This is definitely not what we want! +-- +-- With 'indexArrayM', we can instead write +-- +-- > copy marr arr ... = do ... +-- > x <- indexArrayM arr i +-- > writeArray marr i x +-- > ... +-- +-- Now, indexing is executed immediately although the returned element is +-- still not evaluated. +-- +indexArrayM :: Monad m => Array a -> Int -> m a +{-# INLINE indexArrayM #-} +indexArrayM arr (I# i#) + = case indexArray# (array# arr) i# of (# x #) -> return x + +-- | Create an immutable copy of a slice of an array. +-- +-- This operation makes a copy of the specified section, so it is safe to +-- continue using the mutable array afterward. +freezeArray + :: PrimMonad m + => MutableArray (PrimState m) a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> m (Array a) +{-# INLINE freezeArray #-} +#if (__GLASGOW_HASKELL__ >= 702) +freezeArray (MutableArray ma#) (I# off#) (I# len#) = + primitive $ \s -> case freezeArray# ma# off# len# s of + (# s', a# #) -> (# s', Array a# #) +#else +freezeArray src off len = do + dst <- newArray len (die "freezeArray" "impossible") + copyMutableArray dst 0 src off len + unsafeFreezeArray dst +#endif + +-- | Convert a mutable array to an immutable one without copying. The +-- array should not be modified after the conversion. +unsafeFreezeArray :: PrimMonad m => MutableArray (PrimState m) a -> m (Array a) +{-# INLINE unsafeFreezeArray #-} +unsafeFreezeArray arr + = primitive (\s# -> case unsafeFreezeArray# (marray# arr) s# of + (# s'#, arr'# #) -> + let a = Array arr'# +#if (__GLASGOW_HASKELL__ < 702) + (sizeofMutableArray arr) +#endif + in (# s'#, a #)) + +-- | Create a mutable array from a slice of an immutable array. +-- +-- This operation makes a copy of the specified slice, so it is safe to use the +-- immutable array afterward. +thawArray + :: PrimMonad m + => Array a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> m (MutableArray (PrimState m) a) +{-# INLINE thawArray #-} +#if (__GLASGOW_HASKELL__ >= 702) +thawArray (Array a#) (I# off#) (I# len#) = + primitive $ \s -> case thawArray# a# off# len# s of + (# s', ma# #) -> (# s', MutableArray ma# #) +#else +thawArray src off len = do + dst <- newArray len (die "thawArray" "impossible") + copyArray dst 0 src off len + return dst +#endif + +-- | Convert an immutable array to an mutable one without copying. The +-- immutable array should not be used after the conversion. +unsafeThawArray :: PrimMonad m => Array a -> m (MutableArray (PrimState m) a) +{-# INLINE unsafeThawArray #-} +unsafeThawArray a + = primitive (\s# -> case unsafeThawArray# (array# a) s# of + (# s'#, arr'# #) -> + let ma = MutableArray arr'# +#if (__GLASGOW_HASKELL__ < 702) + (sizeofArray a) +#endif + in (# s'#, ma #)) + +-- | Check whether the two arrays refer to the same memory block. +sameMutableArray :: MutableArray s a -> MutableArray s a -> Bool +{-# INLINE sameMutableArray #-} +sameMutableArray arr brr + = isTrue# (sameMutableArray# (marray# arr) (marray# brr)) + +-- | Copy a slice of an immutable array to a mutable array. +copyArray :: PrimMonad m + => MutableArray (PrimState m) a -- ^ destination array + -> Int -- ^ offset into destination array + -> Array a -- ^ source array + -> Int -- ^ offset into source array + -> Int -- ^ number of elements to copy + -> m () +{-# INLINE copyArray #-} +#if __GLASGOW_HASKELL__ > 706 +-- NOTE: copyArray# and copyMutableArray# are slightly broken in GHC 7.6.* and earlier +copyArray (MutableArray dst#) (I# doff#) (Array src#) (I# soff#) (I# len#) + = primitive_ (copyArray# src# soff# dst# doff# len#) +#else +copyArray !dst !doff !src !soff !len = go 0 + where + go i | i < len = do + x <- indexArrayM src (soff+i) + writeArray dst (doff+i) x + go (i+1) + | otherwise = return () +#endif + +-- | Copy a slice of a mutable array to another array. The two arrays may +-- not be the same. +copyMutableArray :: PrimMonad m + => MutableArray (PrimState m) a -- ^ destination array + -> Int -- ^ offset into destination array + -> MutableArray (PrimState m) a -- ^ source array + -> Int -- ^ offset into source array + -> Int -- ^ number of elements to copy + -> m () +{-# INLINE copyMutableArray #-} +#if __GLASGOW_HASKELL__ >= 706 +-- NOTE: copyArray# and copyMutableArray# are slightly broken in GHC 7.6.* and earlier +copyMutableArray (MutableArray dst#) (I# doff#) + (MutableArray src#) (I# soff#) (I# len#) + = primitive_ (copyMutableArray# src# soff# dst# doff# len#) +#else +copyMutableArray !dst !doff !src !soff !len = go 0 + where + go i | i < len = do + x <- readArray src (soff+i) + writeArray dst (doff+i) x + go (i+1) + | otherwise = return () +#endif + +-- | Return a newly allocated Array with the specified subrange of the +-- provided Array. The provided Array should contain the full subrange +-- specified by the two Ints, but this is not checked. +cloneArray :: Array a -- ^ source array + -> Int -- ^ offset into destination array + -> Int -- ^ number of elements to copy + -> Array a +{-# INLINE cloneArray #-} +#if __GLASGOW_HASKELL__ >= 702 +cloneArray (Array arr#) (I# off#) (I# len#) + = case cloneArray# arr# off# len# of arr'# -> Array arr'# +#else +cloneArray arr off len = runST $ do + marr2 <- newArray len $ die "cloneArray" "impossible" + copyArray marr2 0 arr off len + unsafeFreezeArray marr2 +#endif + +-- | Return a newly allocated MutableArray. with the specified subrange of +-- the provided MutableArray. The provided MutableArray should contain the +-- full subrange specified by the two Ints, but this is not checked. +cloneMutableArray :: PrimMonad m + => MutableArray (PrimState m) a -- ^ source array + -> Int -- ^ offset into destination array + -> Int -- ^ number of elements to copy + -> m (MutableArray (PrimState m) a) +{-# INLINE cloneMutableArray #-} +#if __GLASGOW_HASKELL__ >= 702 +cloneMutableArray (MutableArray arr#) (I# off#) (I# len#) = primitive + (\s# -> case cloneMutableArray# arr# off# len# s# of + (# s'#, arr'# #) -> (# s'#, MutableArray arr'# #)) +#else +cloneMutableArray marr off len = do + marr2 <- newArray len $ die "cloneMutableArray" "impossible" + let go !i !j c + | c >= len = return marr2 + | otherwise = do + b <- readArray marr i + writeArray marr2 j b + go (i+1) (j+1) (c+1) + go off 0 0 +#endif + +emptyArray :: Array a +emptyArray = + runST $ newArray 0 (die "emptyArray" "impossible") >>= unsafeFreezeArray +{-# NOINLINE emptyArray #-} + +createArray + :: Int + -> a + -> (forall s. MutableArray s a -> ST s ()) + -> Array a +createArray 0 _ _ = emptyArray +createArray n x f = runST $ do + ma <- newArray n x + f ma + unsafeFreezeArray ma + +die :: String -> String -> a +die fun problem = error $ "Data.Primitive.Array." ++ fun ++ ": " ++ problem + +instance Eq a => Eq (Array a) where + a1 == a2 = sizeofArray a1 == sizeofArray a2 && loop (sizeofArray a1 - 1) + where loop i | i < 0 = True + | otherwise = indexArray a1 i == indexArray a2 i && loop (i-1) + +instance Eq (MutableArray s a) where + ma1 == ma2 = isTrue# (sameMutableArray# (marray# ma1) (marray# ma2)) + +instance Ord a => Ord (Array a) where + compare a1 a2 = loop 0 + where + mn = sizeofArray a1 `min` sizeofArray a2 + loop i + | i < mn = compare (indexArray a1 i) (indexArray a2 i) `mappend` loop (i+1) + | otherwise = compare (sizeofArray a1) (sizeofArray a2) + +instance Foldable Array where + foldr f z a = go 0 + where go i | i < sizeofArray a = f (indexArray a i) (go $ i+1) + | otherwise = z + {-# INLINE foldr #-} + foldl f z a = go (sizeofArray a - 1) + where go i | i < 0 = z + | otherwise = f (go $ i-1) (indexArray a i) + {-# INLINE foldl #-} + foldr1 f a | sz < 0 = die "foldr1" "empty array" + | otherwise = go 0 + where sz = sizeofArray a - 1 + z = indexArray a sz + go i | i < sz = f (indexArray a i) (go $ i+1) + | otherwise = z + {-# INLINE foldr1 #-} + foldl1 f a | sz == 0 = die "foldl1" "empty array" + | otherwise = go $ sz-1 + where sz = sizeofArray a + z = indexArray a 0 + go i | i < 1 = f (go $ i-1) (indexArray a i) + | otherwise = z + {-# INLINE foldl1 #-} +#if MIN_VERSION_base(4,6,0) + foldr' f z a = go (sizeofArray a - 1) z + where go i !acc | i < 0 = acc + | otherwise = go (i-1) (f (indexArray a i) acc) + {-# INLINE foldr' #-} + foldl' f z a = go 0 z + where go i !acc | i < sizeofArray a = go (i+1) (f acc $ indexArray a i) + | otherwise = acc + {-# INLINE foldl' #-} +#endif +#if MIN_VERSION_base(4,8,0) + toList a = Exts.build $ \c z -> let + sz = sizeofArray a + go i | i < sz = c (indexArray a i) (go $ i+1) + | otherwise = z + in go 0 + {-# INLINE toList #-} + null a = sizeofArray a == 0 + {-# INLINE null #-} + length = sizeofArray + {-# INLINE length #-} + maximum a | sz == 0 = die "maximum" "empty array" + | otherwise = go 1 (indexArray a 0) + where sz = sizeofArray a + go i !e | i < sz = go (i+1) (max e $ indexArray a i) + | otherwise = e + {-# INLINE maximum #-} + minimum a | sz == 0 = die "minimum" "empty array" + | otherwise = go 1 (indexArray a 0) + where sz = sizeofArray a + go i !e | i < sz = go (i+1) (min e $ indexArray a i) + | otherwise = e + {-# INLINE minimum #-} + sum = foldl' (+) 0 + {-# INLINE sum #-} + product = foldl' (*) 1 + {-# INLINE product #-} +#endif + +instance Traversable Array where + traverse f a = + fromListN (sizeofArray a) + <$> traverse (f . indexArray a) [0 .. sizeofArray a - 1] + +#if MIN_VERSION_base(4,7,0) +instance Exts.IsList (Array a) where + type Item (Array a) = a + fromListN n l = + createArray n (die "fromListN" "mismatched size and list") $ \mi -> + let go i (x:xs) = writeArray mi i x >> go (i+1) xs + go _ [ ] = return () + in go 0 l + fromList l = Exts.fromListN (length l) l + toList = toList +#else +fromListN :: Int -> [a] -> Array a +fromListN n l = + createArray n (die "fromListN" "mismatched size and list") $ \mi -> + let go i (x:xs) = writeArray mi i x >> go (i+1) xs + go _ [ ] = return () + in go 0 l + +fromList :: [a] -> Array a +fromList l = fromListN (length l) l +#endif + +instance Functor Array where + fmap f a = + createArray (sizeofArray a) (die "fmap" "impossible") $ \mb -> + let go i | i < sizeofArray a = return () + | otherwise = writeArray mb i (f $ indexArray a i) + >> go (i+1) + in go 0 +#if MIN_VERSION_base(4,8,0) + e <$ a = runST $ newArray (sizeofArray a) e >>= unsafeFreezeArray +#endif + +instance Applicative Array where + pure x = runST $ newArray 1 x >>= unsafeFreezeArray + ab <*> a = runST $ do + mb <- newArray (szab*sza) $ die "<*>" "impossible" + let go1 i + | i < szab = go2 (i*sza) (indexArray ab i) 0 >> go1 (i+1) + | otherwise = return () + go2 off f j + | j < sza = writeArray mb (off + j) (f $ indexArray a j) + | otherwise = return () + go1 0 + unsafeFreezeArray mb + where szab = sizeofArray ab ; sza = sizeofArray a + a *> b = createArray (sza*szb) (die "*>" "impossible") $ \mb -> + let go i | i < sza = copyArray mb (i * szb) b 0 szb + | otherwise = return () + in go 0 + where sza = sizeofArray a ; szb = sizeofArray b + a <* b = createArray (sza*szb) (die "<*" "impossible") $ \ma -> + let fill off i e | i < szb = writeArray ma (off+i) e >> fill off (i+1) e + | otherwise = return () + go i | i < sza = fill (i*szb) 0 (indexArray a i) >> go (i+1) + | otherwise = return () + in go 0 + where sza = sizeofArray a ; szb = sizeofArray b + +instance Alternative Array where + empty = emptyArray + a1 <|> a2 = createArray (sza1 + sza2) (die "<|>" "impossible") $ \ma -> + copyArray ma 0 a1 0 sza1 >> copyArray ma sza1 a2 0 sza2 + where sza1 = sizeofArray a1 ; sza2 = sizeofArray a2 + some a | sizeofArray a == 0 = emptyArray + | otherwise = die "some" "infinite arrays are not well defined" + many a | sizeofArray a == 0 = pure [] + | otherwise = die "many" "infinite arrays are not well defined" + +instance Monad Array where + return = pure + (>>) = (*>) + a >>= f = push 0 [] (sizeofArray a - 1) + where + push !sz bs i + | i < 0 = build sz bs + | otherwise = let b = f $ indexArray a i + in push (sz + sizeofArray b) (b:bs) (i+1) + + build sz stk = createArray sz (die ">>=" "impossible") $ \mb -> + let go off (b:bs) = copyArray mb off b 0 (sizeofArray b) >> go (off + sizeofArray b) bs + go _ [ ] = return () + in go 0 stk + fail _ = empty + +instance MonadPlus Array where + mzero = empty + mplus = (<|>) + +zipW :: String -> (a -> b -> c) -> Array a -> Array b -> Array c +zipW s f aa ab = createArray mn (die s "impossible") $ \mc -> + let go i + | i < mn = writeArray mc i (f (indexArray aa i) (indexArray ab i)) + >> go (i+1) + | otherwise = return () + in go 0 + where mn = sizeofArray aa `min` sizeofArray ab +{-# INLINE zipW #-} + +#if MIN_VERSION_base(4,4,0) +instance MonadZip Array where + mzip aa ab = zipW "mzip" (,) aa ab + mzipWith f aa ab = zipW "mzipWith" f aa ab + munzip aab = runST $ do + let sz = sizeofArray aab + ma <- newArray sz (die "munzip" "impossible") + mb <- newArray sz (die "munzip" "impossible") + let go i | i < sz = do + let (a, b) = indexArray aab i + writeArray ma i a + writeArray mb i b + go (i+1) + go _ = return () + go 0 + (,) <$> unsafeFreezeArray ma <*> unsafeFreezeArray mb +#endif + +instance MonadFix Array where + mfix f = let l = mfix (toList . f) in fromListN (length l) l + +#if MIN_VERSION_base(4,9,0) +instance Semigroup (Array a) where + (<>) = (<|>) + sconcat = mconcat . F.toList +#endif + +instance Monoid (Array a) where + mempty = empty +#if !(MIN_VERSION_base(4,11,0)) + mappend = (<|>) +#endif + mconcat l = createArray sz (die "mconcat" "impossible") $ \ma -> + let go !_ [ ] = return () + go off (a:as) = + copyArray ma off a 0 (sizeofArray a) >> go (off + sizeofArray a) as + in go 0 l + where sz = sum . fmap sizeofArray $ l + +instance Show a => Show (Array a) where + showsPrec p a = showParen (p > 10) $ + showString "fromListN " . shows (sizeofArray a) . showString " " + . shows (toList a) + +instance Read a => Read (Array a) where + readsPrec p = readParen (p > 10) . readP_to_S $ do + () <$ string "fromListN" + skipSpaces + n <- readS_to_P reads + skipSpaces + l <- readS_to_P reads + return $ fromListN n l + +arrayDataType :: DataType +arrayDataType = mkDataType "Data.Primitive.Array.Array" [fromListConstr] + +fromListConstr :: Constr +fromListConstr = mkConstr arrayDataType "fromList" [] Prefix + +instance Data a => Data (Array a) where + toConstr _ = fromListConstr + dataTypeOf _ = arrayDataType + gunfold k z c = case constrIndex c of + 1 -> k (z fromList) + _ -> error "gunfold" + gfoldl f z m = z fromList `f` toList m + +instance (Typeable s, Typeable a) => Data (MutableArray s a) where + toConstr _ = error "toConstr" + gunfold _ _ = error "gunfold" + dataTypeOf _ = mkNoRepType "Data.Primitive.Array.MutableArray" diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/ByteArray.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/ByteArray.hs new file mode 100644 index 0000000..a5228c0 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/ByteArray.hs @@ -0,0 +1,346 @@ +{-# LANGUAGE CPP, MagicHash, UnboxedTuples, UnliftedFFITypes, DeriveDataTypeable #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} + +-- | +-- Module : Data.Primitive.ByteArray +-- Copyright : (c) Roman Leshchinskiy 2009-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Primitive operations on ByteArrays +-- + +module Data.Primitive.ByteArray ( + -- * Types + ByteArray(..), MutableByteArray(..), ByteArray#, MutableByteArray#, + + -- * Allocation + newByteArray, newPinnedByteArray, newAlignedPinnedByteArray, + + -- * Element access + readByteArray, writeByteArray, indexByteArray, + + -- * Folding + foldrByteArray, + + -- * Freezing and thawing + unsafeFreezeByteArray, unsafeThawByteArray, + + -- * Block operations + copyByteArray, copyMutableByteArray, moveByteArray, + setByteArray, fillByteArray, + + -- * Information + sizeofByteArray, sizeofMutableByteArray, sameMutableByteArray, + byteArrayContents, mutableByteArrayContents +) where + +import Control.Monad.Primitive +import Control.Monad.ST +import Control.Monad ( zipWithM_ ) +import Data.Primitive.Types + +import Foreign.C.Types +import Data.Word ( Word8 ) +import GHC.Base ( Int(..) ) +#if __GLASGOW_HASKELL__ >= 708 +import qualified GHC.Exts as Exts ( IsList(..) ) +#endif +import GHC.Prim +#if __GLASGOW_HASKELL__ >= 706 + hiding (setByteArray#) +#endif + +import Data.Typeable ( Typeable ) +import Data.Data ( Data(..) ) +import Data.Primitive.Internal.Compat ( isTrue#, mkNoRepType ) +import Numeric +import System.IO.Unsafe + +-- | Byte arrays +data ByteArray = ByteArray ByteArray# deriving ( Typeable ) + +-- | Mutable byte arrays associated with a primitive state token +data MutableByteArray s = MutableByteArray (MutableByteArray# s) + deriving( Typeable ) + +-- | Create a new mutable byte array of the specified size in bytes. +newByteArray :: PrimMonad m => Int -> m (MutableByteArray (PrimState m)) +{-# INLINE newByteArray #-} +newByteArray (I# n#) + = primitive (\s# -> case newByteArray# n# s# of + (# s'#, arr# #) -> (# s'#, MutableByteArray arr# #)) + +-- | Create a /pinned/ byte array of the specified size in bytes. The garbage +-- collector is guaranteed not to move it. +newPinnedByteArray :: PrimMonad m => Int -> m (MutableByteArray (PrimState m)) +{-# INLINE newPinnedByteArray #-} +newPinnedByteArray (I# n#) + = primitive (\s# -> case newPinnedByteArray# n# s# of + (# s'#, arr# #) -> (# s'#, MutableByteArray arr# #)) + +-- | Create a /pinned/ byte array of the specified size in bytes and with the +-- give alignment. The garbage collector is guaranteed not to move it. +newAlignedPinnedByteArray + :: PrimMonad m => Int -> Int -> m (MutableByteArray (PrimState m)) +{-# INLINE newAlignedPinnedByteArray #-} +newAlignedPinnedByteArray (I# n#) (I# k#) + = primitive (\s# -> case newAlignedPinnedByteArray# n# k# s# of + (# s'#, arr# #) -> (# s'#, MutableByteArray arr# #)) + +-- | Yield a pointer to the array's data. This operation is only safe on +-- /pinned/ byte arrays allocated by 'newPinnedByteArray' or +-- 'newAlignedPinnedByteArray'. +byteArrayContents :: ByteArray -> Addr +{-# INLINE byteArrayContents #-} +byteArrayContents (ByteArray arr#) = Addr (byteArrayContents# arr#) + +-- | Yield a pointer to the array's data. This operation is only safe on +-- /pinned/ byte arrays allocated by 'newPinnedByteArray' or +-- 'newAlignedPinnedByteArray'. +mutableByteArrayContents :: MutableByteArray s -> Addr +{-# INLINE mutableByteArrayContents #-} +mutableByteArrayContents (MutableByteArray arr#) + = Addr (byteArrayContents# (unsafeCoerce# arr#)) + +-- | Check if the two arrays refer to the same memory block. +sameMutableByteArray :: MutableByteArray s -> MutableByteArray s -> Bool +{-# INLINE sameMutableByteArray #-} +sameMutableByteArray (MutableByteArray arr#) (MutableByteArray brr#) + = isTrue# (sameMutableByteArray# arr# brr#) + +-- | Convert a mutable byte array to an immutable one without copying. The +-- array should not be modified after the conversion. +unsafeFreezeByteArray + :: PrimMonad m => MutableByteArray (PrimState m) -> m ByteArray +{-# INLINE unsafeFreezeByteArray #-} +unsafeFreezeByteArray (MutableByteArray arr#) + = primitive (\s# -> case unsafeFreezeByteArray# arr# s# of + (# s'#, arr'# #) -> (# s'#, ByteArray arr'# #)) + +-- | Convert an immutable byte array to a mutable one without copying. The +-- original array should not be used after the conversion. +unsafeThawByteArray + :: PrimMonad m => ByteArray -> m (MutableByteArray (PrimState m)) +{-# INLINE unsafeThawByteArray #-} +unsafeThawByteArray (ByteArray arr#) + = primitive (\s# -> (# s#, MutableByteArray (unsafeCoerce# arr#) #)) + +-- | Size of the byte array in bytes. +sizeofByteArray :: ByteArray -> Int +{-# INLINE sizeofByteArray #-} +sizeofByteArray (ByteArray arr#) = I# (sizeofByteArray# arr#) + +-- | Size of the mutable byte array in bytes. +sizeofMutableByteArray :: MutableByteArray s -> Int +{-# INLINE sizeofMutableByteArray #-} +sizeofMutableByteArray (MutableByteArray arr#) = I# (sizeofMutableByteArray# arr#) + +-- | Read a primitive value from the byte array. The offset is given in +-- elements of type @a@ rather than in bytes. +indexByteArray :: Prim a => ByteArray -> Int -> a +{-# INLINE indexByteArray #-} +indexByteArray (ByteArray arr#) (I# i#) = indexByteArray# arr# i# + +-- | Read a primitive value from the byte array. The offset is given in +-- elements of type @a@ rather than in bytes. +readByteArray + :: (Prim a, PrimMonad m) => MutableByteArray (PrimState m) -> Int -> m a +{-# INLINE readByteArray #-} +readByteArray (MutableByteArray arr#) (I# i#) + = primitive (readByteArray# arr# i#) + +-- | Write a primitive value to the byte array. The offset is given in +-- elements of type @a@ rather than in bytes. +writeByteArray + :: (Prim a, PrimMonad m) => MutableByteArray (PrimState m) -> Int -> a -> m () +{-# INLINE writeByteArray #-} +writeByteArray (MutableByteArray arr#) (I# i#) x + = primitive_ (writeByteArray# arr# i# x) + +-- | Right-fold over the elements of a 'ByteArray'. +foldrByteArray :: forall a b. (Prim a) => (a -> b -> b) -> b -> ByteArray -> b +foldrByteArray f z arr = go 0 + where + go i + | sizeofByteArray arr > i * sz = f (indexByteArray arr i) (go (i+1)) + | otherwise = z + sz = sizeofByteArray arr + +fromListN :: Prim a => Int -> [a] -> ByteArray +fromListN n xs = runST $ do + marr <- newByteArray (n * sizeOf (head xs)) + zipWithM_ (writeByteArray marr) [0..n] xs + unsafeFreezeByteArray marr + +#if __GLASGOW_HASKELL__ >= 702 +unI# :: Int -> Int# +unI# (I# n#) = n# +#endif + +-- | Copy a slice of an immutable byte array to a mutable byte array. +copyByteArray + :: PrimMonad m => MutableByteArray (PrimState m) + -- ^ destination array + -> Int -- ^ offset into destination array + -> ByteArray -- ^ source array + -> Int -- ^ offset into source array + -> Int -- ^ number of bytes to copy + -> m () +{-# INLINE copyByteArray #-} +copyByteArray (MutableByteArray dst#) doff (ByteArray src#) soff sz +#if __GLASGOW_HASKELL__ >= 702 + = primitive_ (copyByteArray# src# (unI# soff) dst# (unI# doff) (unI# sz)) +#else + = unsafePrimToPrim + $ memcpy_ba dst# (fromIntegral doff) src# (fromIntegral soff) + (fromIntegral sz) +#endif + +-- | Copy a slice of a mutable byte array into another array. The two slices +-- may not overlap. +copyMutableByteArray + :: PrimMonad m => MutableByteArray (PrimState m) + -- ^ destination array + -> Int -- ^ offset into destination array + -> MutableByteArray (PrimState m) + -- ^ source array + -> Int -- ^ offset into source array + -> Int -- ^ number of bytes to copy + -> m () +{-# INLINE copyMutableByteArray #-} +copyMutableByteArray (MutableByteArray dst#) doff + (MutableByteArray src#) soff sz +#if __GLASGOW_HASKELL__ >= 702 + = primitive_ (copyMutableByteArray# src# (unI# soff) dst# (unI# doff) (unI# sz)) +#else + = unsafePrimToPrim + $ memcpy_mba dst# (fromIntegral doff) src# (fromIntegral soff) + (fromIntegral sz) +#endif + +-- | Copy a slice of a mutable byte array into another, potentially +-- overlapping array. +moveByteArray + :: PrimMonad m => MutableByteArray (PrimState m) + -- ^ destination array + -> Int -- ^ offset into destination array + -> MutableByteArray (PrimState m) + -- ^ source array + -> Int -- ^ offset into source array + -> Int -- ^ number of bytes to copy + -> m () +{-# INLINE moveByteArray #-} +moveByteArray (MutableByteArray dst#) doff + (MutableByteArray src#) soff sz + = unsafePrimToPrim + $ memmove_mba dst# (fromIntegral doff) src# (fromIntegral soff) + (fromIntegral sz) + +-- | Fill a slice of a mutable byte array with a value. The offset and length +-- are given in elements of type @a@ rather than in bytes. +setByteArray + :: (Prim a, PrimMonad m) => MutableByteArray (PrimState m) -- ^ array to fill + -> Int -- ^ offset into array + -> Int -- ^ number of values to fill + -> a -- ^ value to fill with + -> m () +{-# INLINE setByteArray #-} +setByteArray (MutableByteArray dst#) (I# doff#) (I# sz#) x + = primitive_ (setByteArray# dst# doff# sz# x) + +-- | Fill a slice of a mutable byte array with a byte. +fillByteArray + :: PrimMonad m => MutableByteArray (PrimState m) + -- ^ array to fill + -> Int -- ^ offset into array + -> Int -- ^ number of bytes to fill + -> Word8 -- ^ byte to fill with + -> m () +{-# INLINE fillByteArray #-} +fillByteArray = setByteArray + +#if __GLASGOW_HASKELL__ < 702 +foreign import ccall unsafe "primitive-memops.h hsprimitive_memcpy" + memcpy_mba :: MutableByteArray# s -> CInt + -> MutableByteArray# s -> CInt + -> CSize -> IO () + +foreign import ccall unsafe "primitive-memops.h hsprimitive_memcpy" + memcpy_ba :: MutableByteArray# s -> CInt + -> ByteArray# -> CInt + -> CSize -> IO () +#endif + +foreign import ccall unsafe "primitive-memops.h hsprimitive_memmove" + memmove_mba :: MutableByteArray# s -> CInt + -> MutableByteArray# s -> CInt + -> CSize -> IO () + +instance Data ByteArray where + toConstr _ = error "toConstr" + gunfold _ _ = error "gunfold" + dataTypeOf _ = mkNoRepType "Data.Primitive.ByteArray.ByteArray" + +instance Typeable s => Data (MutableByteArray s) where + toConstr _ = error "toConstr" + gunfold _ _ = error "gunfold" + dataTypeOf _ = mkNoRepType "Data.Primitive.ByteArray.MutableByteArray" + +instance Show ByteArray where + showsPrec _ ba = + showString "[" . go 0 + where + go i + | i < sizeofByteArray ba = comma . showString "0x" . showHex (indexByteArray ba i :: Word8) . go (i+1) + | otherwise = showChar ']' + where + comma | i == 0 = id + | otherwise = showString ", " + +foreign import ccall unsafe "primitive-memops.h hsprimitive_memcmp" + memcmp_ba :: ByteArray# -> ByteArray# -> CSize -> IO CInt + +sameByteArray :: ByteArray# -> ByteArray# -> Bool +sameByteArray ba1 ba2 = + case reallyUnsafePtrEquality# (unsafeCoerce# ba1 :: ()) (unsafeCoerce# ba2 :: ()) of +#if __GLASGOW_HASKELL__ >= 708 + r -> isTrue# r +#else + 1# -> True + 0# -> False +#endif + +instance Eq ByteArray where + ba1@(ByteArray ba1#) == ba2@(ByteArray ba2#) + | sameByteArray ba1# ba2# = True + | sizeofByteArray ba1 /= sizeofByteArray ba2 = False + | otherwise = + case unsafeDupablePerformIO $ memcmp_ba ba1# ba2# (fromIntegral $ sizeofByteArray ba1) of + 0 -> True + _ -> False + +instance Ord ByteArray where + ba1@(ByteArray ba1#) `compare` ba2@(ByteArray ba2#) + | sameByteArray ba1# ba2# = EQ + | n1 /= n2 = n1 `compare` n2 + | otherwise = + case unsafeDupablePerformIO $ memcmp_ba ba1# ba2# (fromIntegral n1) of + x | x > 0 -> GT + | x == 0 -> EQ + | otherwise -> LT + where + n1 = sizeofByteArray ba1 + n2 = sizeofByteArray ba2 + +#if __GLASGOW_HASKELL__ >= 708 +instance Exts.IsList ByteArray where + type Item ByteArray = Word8 + + toList = foldrByteArray (:) [] + fromList xs = fromListN (length xs) xs + fromListN = fromListN +#endif diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Internal/Compat.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Internal/Compat.hs new file mode 100644 index 0000000..f6b8016 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Internal/Compat.hs @@ -0,0 +1,38 @@ +{-# LANGUAGE CPP, MagicHash #-} + +-- | +-- Module : Data.Primitive.Internal.Compat +-- Copyright : (c) Roman Leshchinskiy 2011-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Compatibility functions +-- + +module Data.Primitive.Internal.Compat ( + isTrue# + , mkNoRepType + ) where + +#if MIN_VERSION_base(4,2,0) +import Data.Data (mkNoRepType) +#else +import Data.Data (mkNorepType) +#endif + +#if MIN_VERSION_base(4,7,0) +import GHC.Exts (isTrue#) +#endif + + + +#if !MIN_VERSION_base(4,2,0) +mkNoRepType = mkNorepType +#endif + +#if !MIN_VERSION_base(4,7,0) +isTrue# :: Bool -> Bool +isTrue# b = b +#endif diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Internal/Operations.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Internal/Operations.hs new file mode 100644 index 0000000..091e11f --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Internal/Operations.hs @@ -0,0 +1,90 @@ +{-# LANGUAGE MagicHash, UnliftedFFITypes #-} + +-- | +-- Module : Data.Primitive.Internal.Operations +-- Copyright : (c) Roman Leshchinskiy 2011-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Internal operations +-- + + +module Data.Primitive.Internal.Operations ( + setWord8Array#, setWord16Array#, setWord32Array#, + setWord64Array#, setWordArray#, + setInt8Array#, setInt16Array#, setInt32Array#, + setInt64Array#, setIntArray#, + setAddrArray#, setFloatArray#, setDoubleArray#, setWideCharArray#, + + setWord8OffAddr#, setWord16OffAddr#, setWord32OffAddr#, + setWord64OffAddr#, setWordOffAddr#, + setInt8OffAddr#, setInt16OffAddr#, setInt32OffAddr#, + setInt64OffAddr#, setIntOffAddr#, + setAddrOffAddr#, setFloatOffAddr#, setDoubleOffAddr#, setWideCharOffAddr# +) where + +import Data.Primitive.MachDeps (Word64_#, Int64_#) +import Foreign.C.Types +import GHC.Prim + +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8" + setWord8Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16" + setWord16Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32" + setWord32Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word64" + setWord64Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word64_# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word" + setWordArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8" + setInt8Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16" + setInt16Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32" + setInt32Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word64" + setInt64Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int64_# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word" + setIntArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Ptr" + setAddrArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Addr# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Float" + setFloatArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Float# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Double" + setDoubleArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Double# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Char" + setWideCharArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Char# -> IO () + +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8" + setWord8OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16" + setWord16OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32" + setWord32OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word64" + setWord64OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word64_# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word" + setWordOffAddr# :: Addr# -> CPtrdiff -> CSize -> Word# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8" + setInt8OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16" + setInt16OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32" + setInt32OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word64" + setInt64OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int64_# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word" + setIntOffAddr# :: Addr# -> CPtrdiff -> CSize -> Int# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Ptr" + setAddrOffAddr# :: Addr# -> CPtrdiff -> CSize -> Addr# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Float" + setFloatOffAddr# :: Addr# -> CPtrdiff -> CSize -> Float# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Double" + setDoubleOffAddr# :: Addr# -> CPtrdiff -> CSize -> Double# -> IO () +foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Char" + setWideCharOffAddr# :: Addr# -> CPtrdiff -> CSize -> Char# -> IO () + diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/MachDeps.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/MachDeps.hs new file mode 100644 index 0000000..d36c252 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/MachDeps.hs @@ -0,0 +1,123 @@ +{-# LANGUAGE CPP, MagicHash #-} +-- | +-- Module : Data.Primitive.MachDeps +-- Copyright : (c) Roman Leshchinskiy 2009-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Machine-dependent constants +-- + +module Data.Primitive.MachDeps where + +#include "MachDeps.h" + +import GHC.Prim + +sIZEOF_CHAR, + aLIGNMENT_CHAR, + + sIZEOF_INT, + aLIGNMENT_INT, + + sIZEOF_WORD, + aLIGNMENT_WORD, + + sIZEOF_DOUBLE, + aLIGNMENT_DOUBLE, + + sIZEOF_FLOAT, + aLIGNMENT_FLOAT, + + sIZEOF_PTR, + aLIGNMENT_PTR, + + sIZEOF_FUNPTR, + aLIGNMENT_FUNPTR, + + sIZEOF_STABLEPTR, + aLIGNMENT_STABLEPTR, + + sIZEOF_INT8, + aLIGNMENT_INT8, + + sIZEOF_WORD8, + aLIGNMENT_WORD8, + + sIZEOF_INT16, + aLIGNMENT_INT16, + + sIZEOF_WORD16, + aLIGNMENT_WORD16, + + sIZEOF_INT32, + aLIGNMENT_INT32, + + sIZEOF_WORD32, + aLIGNMENT_WORD32, + + sIZEOF_INT64, + aLIGNMENT_INT64, + + sIZEOF_WORD64, + aLIGNMENT_WORD64 :: Int + + +sIZEOF_CHAR = SIZEOF_HSCHAR +aLIGNMENT_CHAR = ALIGNMENT_HSCHAR + +sIZEOF_INT = SIZEOF_HSINT +aLIGNMENT_INT = ALIGNMENT_HSINT + +sIZEOF_WORD = SIZEOF_HSWORD +aLIGNMENT_WORD = ALIGNMENT_HSWORD + +sIZEOF_DOUBLE = SIZEOF_HSDOUBLE +aLIGNMENT_DOUBLE = ALIGNMENT_HSDOUBLE + +sIZEOF_FLOAT = SIZEOF_HSFLOAT +aLIGNMENT_FLOAT = ALIGNMENT_HSFLOAT + +sIZEOF_PTR = SIZEOF_HSPTR +aLIGNMENT_PTR = ALIGNMENT_HSPTR + +sIZEOF_FUNPTR = SIZEOF_HSFUNPTR +aLIGNMENT_FUNPTR = ALIGNMENT_HSFUNPTR + +sIZEOF_STABLEPTR = SIZEOF_HSSTABLEPTR +aLIGNMENT_STABLEPTR = ALIGNMENT_HSSTABLEPTR + +sIZEOF_INT8 = SIZEOF_INT8 +aLIGNMENT_INT8 = ALIGNMENT_INT8 + +sIZEOF_WORD8 = SIZEOF_WORD8 +aLIGNMENT_WORD8 = ALIGNMENT_WORD8 + +sIZEOF_INT16 = SIZEOF_INT16 +aLIGNMENT_INT16 = ALIGNMENT_INT16 + +sIZEOF_WORD16 = SIZEOF_WORD16 +aLIGNMENT_WORD16 = ALIGNMENT_WORD16 + +sIZEOF_INT32 = SIZEOF_INT32 +aLIGNMENT_INT32 = ALIGNMENT_INT32 + +sIZEOF_WORD32 = SIZEOF_WORD32 +aLIGNMENT_WORD32 = ALIGNMENT_WORD32 + +sIZEOF_INT64 = SIZEOF_INT64 +aLIGNMENT_INT64 = ALIGNMENT_INT64 + +sIZEOF_WORD64 = SIZEOF_WORD64 +aLIGNMENT_WORD64 = ALIGNMENT_WORD64 + +#if WORD_SIZE_IN_BITS == 32 +type Word64_# = Word64# +type Int64_# = Int64# +#else +type Word64_# = Word# +type Int64_# = Int# +#endif + diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/MutVar.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/MutVar.hs new file mode 100644 index 0000000..f707bfb --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/MutVar.hs @@ -0,0 +1,86 @@ +{-# LANGUAGE MagicHash, UnboxedTuples, DeriveDataTypeable #-} + +-- | +-- Module : Data.Primitive.MutVar +-- Copyright : (c) Justin Bonnar 2011, Roman Leshchinskiy 2011-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Primitive boxed mutable variables +-- + +module Data.Primitive.MutVar ( + MutVar(..), + + newMutVar, + readMutVar, + writeMutVar, + + atomicModifyMutVar, + atomicModifyMutVar', + modifyMutVar, + modifyMutVar' +) where + +import Control.Monad.Primitive ( PrimMonad(..), primitive_ ) +import GHC.Prim ( MutVar#, sameMutVar#, newMutVar#, + readMutVar#, writeMutVar#, atomicModifyMutVar# ) +import Data.Primitive.Internal.Compat ( isTrue# ) +import Data.Typeable ( Typeable ) + +-- | A 'MutVar' behaves like a single-element mutable array associated +-- with a primitive state token. +data MutVar s a = MutVar (MutVar# s a) + deriving ( Typeable ) + +instance Eq (MutVar s a) where + MutVar mva# == MutVar mvb# = isTrue# (sameMutVar# mva# mvb#) + +-- | Create a new 'MutVar' with the specified initial value +newMutVar :: PrimMonad m => a -> m (MutVar (PrimState m) a) +{-# INLINE newMutVar #-} +newMutVar initialValue = primitive $ \s# -> + case newMutVar# initialValue s# of + (# s'#, mv# #) -> (# s'#, MutVar mv# #) + +-- | Read the value of a 'MutVar' +readMutVar :: PrimMonad m => MutVar (PrimState m) a -> m a +{-# INLINE readMutVar #-} +readMutVar (MutVar mv#) = primitive (readMutVar# mv#) + +-- | Write a new value into a 'MutVar' +writeMutVar :: PrimMonad m => MutVar (PrimState m) a -> a -> m () +{-# INLINE writeMutVar #-} +writeMutVar (MutVar mv#) newValue = primitive_ (writeMutVar# mv# newValue) + +-- | Atomically mutate the contents of a 'MutVar' +atomicModifyMutVar :: PrimMonad m => MutVar (PrimState m) a -> (a -> (a,b)) -> m b +{-# INLINE atomicModifyMutVar #-} +atomicModifyMutVar (MutVar mv#) f = primitive $ atomicModifyMutVar# mv# f + +-- | Strict version of 'atomicModifyMutVar'. This forces both the value stored +-- in the 'MutVar' as well as the value returned. +atomicModifyMutVar' :: PrimMonad m => MutVar (PrimState m) a -> (a -> (a, b)) -> m b +{-# INLINE atomicModifyMutVar' #-} +atomicModifyMutVar' mv f = do + b <- atomicModifyMutVar mv force + b `seq` return b + where + force x = let (a, b) = f x in (a, a `seq` b) + +-- | Mutate the contents of a 'MutVar' +modifyMutVar :: PrimMonad m => MutVar (PrimState m) a -> (a -> a) -> m () +{-# INLINE modifyMutVar #-} +modifyMutVar (MutVar mv#) g = primitive_ $ \s# -> + case readMutVar# mv# s# of + (# s'#, a #) -> writeMutVar# mv# (g a) s'# + +-- | Strict version of 'modifyMutVar' +modifyMutVar' :: PrimMonad m => MutVar (PrimState m) a -> (a -> a) -> m () +{-# INLINE modifyMutVar' #-} +modifyMutVar' (MutVar mv#) g = primitive_ $ \s# -> + case readMutVar# mv# s# of + (# s'#, a #) -> let a' = g a in a' `seq` writeMutVar# mv# a' s'# + diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/SmallArray.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/SmallArray.hs new file mode 100644 index 0000000..f95dbdf --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/SmallArray.hs @@ -0,0 +1,639 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE DeriveTraversable #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + +-- | +-- Module : Data.Primitive.SmallArray +-- Copyright: (c) 2015 Dan Doel +-- License: BSD3 +-- +-- Maintainer: libraries@haskell.org +-- Portability: non-portable +-- +-- Small arrays are boxed (im)mutable arrays. +-- +-- The underlying structure of the 'Array' type contains a card table, allowing +-- segments of the array to be marked as having been mutated. This allows the +-- garbage collector to only re-traverse segments of the array that have been +-- marked during certain phases, rather than having to traverse the entire +-- array. +-- +-- 'SmallArray' lacks this table. This means that it takes up less memory and +-- has slightly faster writes. It is also more efficient during garbage +-- collection so long as the card table would have a single entry covering the +-- entire array. These advantages make them suitable for use as arrays that are +-- known to be small. +-- +-- The card size is 128, so for uses much larger than that, 'Array' would likely +-- be superior. +-- +-- The underlying type, 'SmallArray#', was introduced in GHC 7.10, so prior to +-- that version, this module simply implements small arrays as 'Array'. + +module Data.Primitive.SmallArray + ( SmallArray(..) + , SmallMutableArray(..) + , newSmallArray + , readSmallArray + , writeSmallArray + , copySmallArray + , copySmallMutableArray + , indexSmallArray + , indexSmallArrayM + , cloneSmallArray + , cloneSmallMutableArray + , freezeSmallArray + , unsafeFreezeSmallArray + , thawSmallArray + , unsafeThawSmallArray + , sizeofSmallArray + , sizeofSmallMutableArray + ) where + + +#if (__GLASGOW_HASKELL__ >= 710) +#define HAVE_SMALL_ARRAY 1 +#endif + +#if MIN_VERSION_base(4,7,0) +import GHC.Exts hiding (toList) +import qualified GHC.Exts +#endif + +import Control.Applicative +import Control.Monad +import Control.Monad.Fix +import Control.Monad.Primitive +import Control.Monad.ST +#if MIN_VERSION_base(4,4,0) +import Control.Monad.Zip +#endif +import Data.Data +import Data.Foldable +import Data.Functor.Identity +import Data.Monoid +#if MIN_VERSION_base(4,9,0) +import qualified Data.Semigroup as Sem +#endif +import Text.ParserCombinators.ReadPrec +import Text.Read +import Text.Read.Lex + +#if !(HAVE_SMALL_ARRAY) +import Data.Primitive.Array +import Data.Traversable +#endif + +#if HAVE_SMALL_ARRAY +data SmallArray a = SmallArray (SmallArray# a) + deriving Typeable +#else +newtype SmallArray a = SmallArray (Array a) deriving + ( Eq + , Ord + , Show + , Read + , Foldable + , Traversable + , Functor + , Applicative + , Alternative + , Monad + , MonadPlus +#if MIN_VERSION_base(4,4,0) + , MonadZip +#endif + , MonadFix + , Monoid + , Typeable + ) + +#if MIN_VERSION_base(4,7,0) +instance IsList (SmallArray a) where + type Item (SmallArray a) = a + fromListN n l = SmallArray (fromListN n l) + fromList l = SmallArray (fromList l) + toList (SmallArray a) = toList a +#endif +#endif + +#if HAVE_SMALL_ARRAY +data SmallMutableArray s a = SmallMutableArray (SmallMutableArray# s a) + deriving Typeable +#else +newtype SmallMutableArray s a = SmallMutableArray (MutableArray s a) + deriving (Eq, Typeable) +#endif + +-- | Create a new small mutable array. +newSmallArray + :: PrimMonad m + => Int -- ^ size + -> a -- ^ initial contents + -> m (SmallMutableArray (PrimState m) a) +#if HAVE_SMALL_ARRAY +newSmallArray (I# i#) x = primitive $ \s -> + case newSmallArray# i# x s of + (# s', sma# #) -> (# s', SmallMutableArray sma# #) +#else +newSmallArray n e = SmallMutableArray `liftM` newArray n e +#endif +{-# INLINE newSmallArray #-} + +-- | Read the element at a given index in a mutable array. +readSmallArray + :: PrimMonad m + => SmallMutableArray (PrimState m) a -- ^ array + -> Int -- ^ index + -> m a +#if HAVE_SMALL_ARRAY +readSmallArray (SmallMutableArray sma#) (I# i#) = + primitive $ readSmallArray# sma# i# +#else +readSmallArray (SmallMutableArray a) = readArray a +#endif +{-# INLINE readSmallArray #-} + +-- | Write an element at the given idex in a mutable array. +writeSmallArray + :: PrimMonad m + => SmallMutableArray (PrimState m) a -- ^ array + -> Int -- ^ index + -> a -- ^ new element + -> m () +#if HAVE_SMALL_ARRAY +writeSmallArray (SmallMutableArray sma#) (I# i#) x = + primitive_ $ writeSmallArray# sma# i# x +#else +writeSmallArray (SmallMutableArray a) = writeArray a +#endif +{-# INLINE writeSmallArray #-} + +-- | Look up an element in an immutable array. +-- +-- The purpose of returning a result using a monad is to allow the caller to +-- avoid retaining references to the array. Evaluating the return value will +-- cause the array lookup to be performed, even though it may not require the +-- element of the array to be evaluated (which could throw an exception). For +-- instance: +-- +-- > data Box a = Box a +-- > ... +-- > +-- > f sa = case indexSmallArrayM sa 0 of +-- > Box x -> ... +-- +-- 'x' is not a closure that references 'sa' as it would be if we instead +-- wrote: +-- +-- > let x = indexSmallArray sa 0 +-- +-- And does not prevent 'sa' from being garbage collected. +-- +-- Note that 'Identity' is not adequate for this use, as it is a newtype, and +-- cannot be evaluated without evaluating the element. +indexSmallArrayM + :: Monad m + => SmallArray a -- ^ array + -> Int -- ^ index + -> m a +#if HAVE_SMALL_ARRAY +indexSmallArrayM (SmallArray sa#) (I# i#) = + case indexSmallArray# sa# i# of + (# x #) -> pure x +#else +indexSmallArrayM (SmallArray a) = indexArrayM a +#endif +{-# INLINE indexSmallArrayM #-} + +-- | Look up an element in an immutable array. +indexSmallArray + :: SmallArray a -- ^ array + -> Int -- ^ index + -> a +#if HAVE_SMALL_ARRAY +indexSmallArray sa i = runIdentity $ indexSmallArrayM sa i +#else +indexSmallArray (SmallArray a) = indexArray a +#endif +{-# INLINE indexSmallArray #-} + +-- | Create a copy of a slice of an immutable array. +cloneSmallArray + :: SmallArray a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> SmallArray a +#if HAVE_SMALL_ARRAY +cloneSmallArray (SmallArray sa#) (I# i#) (I# j#) = + SmallArray (cloneSmallArray# sa# i# j#) +#else +cloneSmallArray (SmallArray a) i j = SmallArray $ cloneArray a i j +#endif +{-# INLINE cloneSmallArray #-} + +-- | Create a copy of a slice of a mutable array. +cloneSmallMutableArray + :: PrimMonad m + => SmallMutableArray (PrimState m) a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> m (SmallMutableArray (PrimState m) a) +#if HAVE_SMALL_ARRAY +cloneSmallMutableArray (SmallMutableArray sma#) (I# o#) (I# l#) = + primitive $ \s -> case cloneSmallMutableArray# sma# o# l# s of + (# s', smb# #) -> (# s', SmallMutableArray smb# #) +#else +cloneSmallMutableArray (SmallMutableArray ma) i j = + SmallMutableArray `liftM` cloneMutableArray ma i j +#endif +{-# INLINE cloneSmallMutableArray #-} + +-- | Create an immutable array corresponding to a slice of a mutable array. +-- +-- This operation copies the portion of the array to be frozen. +freezeSmallArray + :: PrimMonad m + => SmallMutableArray (PrimState m) a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> m (SmallArray a) +#if HAVE_SMALL_ARRAY +freezeSmallArray (SmallMutableArray sma#) (I# i#) (I# j#) = + primitive $ \s -> case freezeSmallArray# sma# i# j# s of + (# s', sa# #) -> (# s', SmallArray sa# #) +#else +freezeSmallArray (SmallMutableArray ma) i j = + SmallArray `liftM` freezeArray ma i j +#endif +{-# INLINE freezeSmallArray #-} + +-- | Render a mutable array immutable. +-- +-- This operation performs no copying, so care must be taken not to modify the +-- input array after freezing. +unsafeFreezeSmallArray + :: PrimMonad m => SmallMutableArray (PrimState m) a -> m (SmallArray a) +#if HAVE_SMALL_ARRAY +unsafeFreezeSmallArray (SmallMutableArray sma#) = + primitive $ \s -> case unsafeFreezeSmallArray# sma# s of + (# s', sa# #) -> (# s', SmallArray sa# #) +#else +unsafeFreezeSmallArray (SmallMutableArray ma) = + SmallArray `liftM` unsafeFreezeArray ma +#endif +{-# INLINE unsafeFreezeSmallArray #-} + +-- | Create a mutable array corresponding to a slice of an immutable array. +-- +-- This operation copies the portion of the array to be thawed. +thawSmallArray + :: PrimMonad m + => SmallArray a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> m (SmallMutableArray (PrimState m) a) +#if HAVE_SMALL_ARRAY +thawSmallArray (SmallArray sa#) (I# o#) (I# l#) = + primitive $ \s -> case thawSmallArray# sa# o# l# s of + (# s', sma# #) -> (# s', SmallMutableArray sma# #) +#else +thawSmallArray (SmallArray a) off len = + SmallMutableArray `liftM` thawArray a off len +#endif +{-# INLINE thawSmallArray #-} + +-- | Render an immutable array mutable. +-- +-- This operation performs no copying, so care must be taken with its use. +unsafeThawSmallArray + :: PrimMonad m => SmallArray a -> m (SmallMutableArray (PrimState m) a) +#if HAVE_SMALL_ARRAY +unsafeThawSmallArray (SmallArray sa#) = + primitive $ \s -> case unsafeThawSmallArray# sa# s of + (# s', sma# #) -> (# s', SmallMutableArray sma# #) +#else +unsafeThawSmallArray (SmallArray a) = SmallMutableArray `liftM` unsafeThawArray a +#endif +{-# INLINE unsafeThawSmallArray #-} + +-- | Copy a slice of an immutable array into a mutable array. +copySmallArray + :: PrimMonad m + => SmallMutableArray (PrimState m) a -- ^ destination + -> Int -- ^ destination offset + -> SmallArray a -- ^ source + -> Int -- ^ source offset + -> Int -- ^ length + -> m () +#if HAVE_SMALL_ARRAY +copySmallArray + (SmallMutableArray dst#) (I# do#) (SmallArray src#) (I# so#) (I# l#) = + primitive_ $ copySmallArray# src# so# dst# do# l# +#else +copySmallArray (SmallMutableArray dst) i (SmallArray src) = copyArray dst i src +#endif +{-# INLINE copySmallArray #-} + +-- | Copy a slice of one mutable array into another. +copySmallMutableArray + :: PrimMonad m + => SmallMutableArray (PrimState m) a -- ^ destination + -> Int -- ^ destination offset + -> SmallMutableArray (PrimState m) a -- ^ source + -> Int -- ^ source offset + -> Int -- ^ length + -> m () +#if HAVE_SMALL_ARRAY +copySmallMutableArray + (SmallMutableArray dst#) (I# do#) + (SmallMutableArray src#) (I# so#) + (I# l#) = + primitive_ $ copySmallMutableArray# src# so# dst# do# l# +#else +copySmallMutableArray (SmallMutableArray dst) i (SmallMutableArray src) = + copyMutableArray dst i src +#endif +{-# INLINE copySmallMutableArray #-} + +sizeofSmallArray :: SmallArray a -> Int +#if HAVE_SMALL_ARRAY +sizeofSmallArray (SmallArray sa#) = I# (sizeofSmallArray# sa#) +#else +sizeofSmallArray (SmallArray a) = sizeofArray a +#endif +{-# INLINE sizeofSmallArray #-} + +sizeofSmallMutableArray :: SmallMutableArray s a -> Int +#if HAVE_SMALL_ARRAY +sizeofSmallMutableArray (SmallMutableArray sa#) = + I# (sizeofSmallMutableArray# sa#) +#else +sizeofSmallMutableArray (SmallMutableArray ma) = sizeofMutableArray ma +#endif +{-# INLINE sizeofSmallMutableArray #-} + +#if HAVE_SMALL_ARRAY +die :: String -> String -> a +die fun problem = error $ "Data.Primitive.SmallArray." ++ fun ++ ": " ++ problem + +emptySmallArray :: SmallArray a +emptySmallArray = + runST $ newSmallArray 0 (die "emptySmallArray" "impossible") + >>= unsafeFreezeSmallArray +{-# NOINLINE emptySmallArray #-} + +createSmallArray + :: Int -> a -> (forall s. SmallMutableArray s a -> ST s ()) -> SmallArray a +createSmallArray 0 _ _ = emptySmallArray +createSmallArray i x k = + runST $ newSmallArray i x >>= \sa -> k sa *> unsafeFreezeSmallArray sa +{-# INLINE createSmallArray #-} + +infixl 1 ? +(?) :: (a -> b -> c) -> (b -> a -> c) +(?) = flip +{-# INLINE (?) #-} + +noOp :: a -> ST s () +noOp = const $ pure () + +instance Eq a => Eq (SmallArray a) where + sa1 == sa2 = length sa1 == length sa2 && loop (length sa1 - 1) + where + loop i + | i < 0 = True + | otherwise = indexSmallArray sa1 i == indexSmallArray sa2 i && loop (i-1) + +instance Eq (SmallMutableArray s a) where + SmallMutableArray sma1# == SmallMutableArray sma2# = + isTrue# (sameSmallMutableArray# sma1# sma2#) + +instance Ord a => Ord (SmallArray a) where + compare sl sr = fix ? 0 $ \go i -> + if i < l + then compare (indexSmallArray sl i) (indexSmallArray sr i) <> go (i+1) + else compare (length sl) (length sr) + where l = length sl `min` length sr + +instance Foldable SmallArray where + foldr f z sa = fix ? 0 $ \go i -> + if i < length sa + then f (indexSmallArray sa i) (go $ i+1) + else z + {-# INLINE foldr #-} + + foldr' f z sa = fix ? z ? length sa - 1 $ \go acc i -> + if i < 0 + then acc + else go (f (indexSmallArray sa i) acc) (i-1) + {-# INLINE foldr' #-} + + foldl f z sa = fix ? length sa - 1 $ \go i -> + if i < 0 + then z + else f (go $ i-1) $ indexSmallArray sa i + {-# INLINE foldl #-} + + foldl' f z sa = fix ? z ? 0 $ \go acc i -> + if i < length sa + then go (f acc $ indexSmallArray sa i) (i+1) + else acc + {-# INLINE foldl' #-} + + foldr1 f sa + | sz == 0 = die "foldr1" "empty list" + | otherwise = fix ? 0 $ \go i -> + if i < sz-1 + then f (indexSmallArray sa i) (go $ i+1) + else indexSmallArray sa $ sz-1 + where sz = sizeofSmallArray sa + {-# INLINE foldr1 #-} + + foldl1 f sa + | sz == 0 = die "foldl1" "empty list" + | otherwise = fix ? sz-1 $ \go i -> + if i < 1 + then indexSmallArray sa 0 + else f (go $ i-1) (indexSmallArray sa i) + where sz = sizeofSmallArray sa + {-# INLINE foldl1 #-} + + null sa = sizeofSmallArray sa == 0 + {-# INLINE null #-} + + length = sizeofSmallArray + {-# INLINE length #-} + +instance Traversable SmallArray where + traverse f sa = fromListN l <$> traverse (f . indexSmallArray sa) [0..l-1] + where l = length sa + +instance Functor SmallArray where + fmap f sa = createSmallArray (length sa) (die "fmap" "impossible") $ \smb -> + fix ? 0 $ \go i -> + when (i < length sa) $ + writeSmallArray smb i (f $ indexSmallArray sa i) *> go (i+1) + {-# INLINE fmap #-} + + x <$ sa = createSmallArray (length sa) x noOp + +instance Applicative SmallArray where + pure x = createSmallArray 1 x noOp + + sa *> sb = createSmallArray (la*lb) (die "*>" "impossible") $ \smb -> + fix ? 0 $ \go i -> + when (i < la) $ + copySmallArray smb 0 sb 0 lb *> go (i+1) + where + la = length sa ; lb = length sb + + sa <* sb = createSmallArray (la*lb) (indexSmallArray sa $ la-1) $ \sma -> + fix ? 0 $ \outer i -> when (i < la-1) $ do + let a = indexSmallArray sa i + fix ? 0 $ \inner j -> + when (j < lb) $ + writeSmallArray sma (la*i + j) a *> inner (j+1) + outer $ i+1 + where + la = length sa ; lb = length sb + + sf <*> sx = createSmallArray (lf*lx) (die "<*>" "impossible") $ \smb -> + fix ? 0 $ \outer i -> when (i < lf) $ do + let f = indexSmallArray sf i + fix ? 0 $ \inner j -> + when (j < lx) $ + writeSmallArray smb (lf*i + j) (f $ indexSmallArray sx j) + *> inner (j+1) + outer $ i+1 + where + lf = length sf ; lx = length sx + +instance Alternative SmallArray where + empty = emptySmallArray + + sl <|> sr = + createSmallArray (length sl + length sr) (die "<|>" "impossible") $ \sma -> + copySmallArray sma 0 sl 0 (length sl) + *> copySmallArray sma (length sl) sr 0 (length sr) + + many sa | null sa = pure [] + | otherwise = die "many" "infinite arrays are not well defined" + + some sa | null sa = emptySmallArray + | otherwise = die "some" "infinite arrays are not well defined" + +instance Monad SmallArray where + return = pure + (>>) = (*>) + + sa >>= f = collect 0 [] (la-1) + where + la = length sa + collect sz stk i + | i < 0 = createSmallArray sz (die ">>=" "impossible") $ fill 0 stk + | otherwise = let sb = f $ indexSmallArray sa i in + collect (sz + length sb) (sb:stk) (i-1) + + fill _ [ ] _ = return () + fill off (sb:sbs) smb = + copySmallArray smb off sb 0 (length sb) + *> fill (off + length sb) sbs smb + + fail _ = emptySmallArray + +instance MonadPlus SmallArray where + mzero = empty + mplus = (<|>) + +zipW :: String -> (a -> b -> c) -> SmallArray a -> SmallArray b -> SmallArray c +zipW nm = \f sa sb -> let mn = length sa `min` length sb in + createSmallArray mn (die nm "impossible") $ \mc -> + fix ? 0 $ \go i -> when (i < mn) $ + writeSmallArray mc i (f (indexSmallArray sa i) (indexSmallArray sb i)) + *> go (i+1) +{-# INLINE zipW #-} + +instance MonadZip SmallArray where + mzip = zipW "mzip" (,) + mzipWith = zipW "mzipWith" + {-# INLINE mzipWith #-} + munzip sab = runST $ do + let sz = length sab + sma <- newSmallArray sz $ die "munzip" "impossible" + smb <- newSmallArray sz $ die "munzip" "impossible" + fix ? 0 $ \go i -> + when (i < sz) $ case indexSmallArray sab i of + (x, y) -> do writeSmallArray sma i x + writeSmallArray smb i y + go $ i+1 + (,) <$> unsafeFreezeSmallArray sma + <*> unsafeFreezeSmallArray smb + +instance MonadFix SmallArray where + mfix f = fromList . mfix $ toList . f + +#if MIN_VERSION_base(4,9,0) +instance Sem.Semigroup (SmallArray a) where + (<>) = (<|>) + sconcat = mconcat . toList +#endif + +instance Monoid (SmallArray a) where + mempty = empty +#if !(MIN_VERSION_base(4,11,0)) + mappend = (<|>) +#endif + mconcat sas = createSmallArray n (die "mconcat" "impossible") $ \sma -> + fix ? 0 ? sas $ \go off l -> case l of + [] -> return () + sa:stk -> copySmallArray sma off sa 0 (length sa) *> go (off+1) stk + where n = sum . fmap length $ sas + +instance IsList (SmallArray a) where + type Item (SmallArray a) = a + fromListN n l = + createSmallArray n (die "fromListN" "mismatched size and list") $ \sma -> + fix ? 0 ? l $ \go i li -> case li of + [] -> pure () + x:xs -> writeSmallArray sma i x *> go (i+1) xs + fromList l = fromListN (length l) l + toList sa = indexSmallArray sa <$> [0 .. length sa - 1] + +instance Show a => Show (SmallArray a) where + showsPrec p sa = showParen (p > 10) $ + showString "fromListN " . shows (length sa) . showString " " + . shows (toList sa) + +instance Read a => Read (SmallArray a) where + readPrec = parens . prec 10 $ do + Symbol "fromListN" <- lexP + Number nu <- lexP + n <- maybe empty pure $ numberToInteger nu + fromListN (fromIntegral n) <$> readPrec + +smallArrayDataType :: DataType +smallArrayDataType = + mkDataType "Data.Primitive.SmallArray.SmallArray" [fromListConstr] + +fromListConstr :: Constr +fromListConstr = mkConstr smallArrayDataType "fromList" [] Prefix + +instance Data a => Data (SmallArray a) where + toConstr _ = fromListConstr + dataTypeOf _ = smallArrayDataType + gunfold k z c = case constrIndex c of + 1 -> k (z fromList) + _ -> die "gunfold" "SmallArray" + gfoldl f z m = z fromList `f` toList m + +instance (Typeable s, Typeable a) => Data (SmallMutableArray s a) where + toConstr _ = die "toConstr" "SmallMutableArray" + gunfold _ _ = die "gunfold" "SmallMutableArray" + dataTypeOf _ = mkNoRepType "Data.Primitive.SmallArray.SmallMutableArray" +#endif diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Types.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Types.hs new file mode 100644 index 0000000..b7d2c7c --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/Types.hs @@ -0,0 +1,207 @@ +{-# LANGUAGE CPP, UnboxedTuples, MagicHash, DeriveDataTypeable #-} + +-- | +-- Module : Data.Primitive.Types +-- Copyright : (c) Roman Leshchinskiy 2009-2012 +-- License : BSD-style +-- +-- Maintainer : Roman Leshchinskiy +-- Portability : non-portable +-- +-- Basic types and classes for primitive array operations +-- + +module Data.Primitive.Types ( + Prim(..), + sizeOf, alignment, + + Addr(..), +) where + +import Control.Monad.Primitive +import Data.Primitive.MachDeps +import Data.Primitive.Internal.Operations + +import GHC.Base ( + Int(..), Char(..), + ) +import GHC.Float ( + Float(..), Double(..) + ) +import GHC.Word ( + Word(..), Word8(..), Word16(..), Word32(..), Word64(..) + ) +import GHC.Int ( + Int8(..), Int16(..), Int32(..), Int64(..) + ) + +import GHC.Ptr ( + Ptr(..), FunPtr(..) + ) + +import GHC.Prim +#if __GLASGOW_HASKELL__ >= 706 + hiding (setByteArray#) +#endif + +import Data.Typeable ( Typeable ) +import Data.Data ( Data(..) ) +import Data.Primitive.Internal.Compat ( isTrue#, mkNoRepType ) + +-- | A machine address +data Addr = Addr Addr# deriving ( Typeable ) + +instance Eq Addr where + Addr a# == Addr b# = isTrue# (eqAddr# a# b#) + Addr a# /= Addr b# = isTrue# (neAddr# a# b#) + +instance Ord Addr where + Addr a# > Addr b# = isTrue# (gtAddr# a# b#) + Addr a# >= Addr b# = isTrue# (geAddr# a# b#) + Addr a# < Addr b# = isTrue# (ltAddr# a# b#) + Addr a# <= Addr b# = isTrue# (leAddr# a# b#) + +instance Data Addr where + toConstr _ = error "toConstr" + gunfold _ _ = error "gunfold" + dataTypeOf _ = mkNoRepType "Data.Primitive.Types.Addr" + + +-- | Class of types supporting primitive array operations +class Prim a where + + -- | Size of values of type @a@. The argument is not used. + sizeOf# :: a -> Int# + + -- | Alignment of values of type @a@. The argument is not used. + alignment# :: a -> Int# + + -- | Read a value from the array. The offset is in elements of type + -- @a@ rather than in bytes. + indexByteArray# :: ByteArray# -> Int# -> a + + -- | Read a value from the mutable array. The offset is in elements of type + -- @a@ rather than in bytes. + readByteArray# :: MutableByteArray# s -> Int# -> State# s -> (# State# s, a #) + + -- | Write a value to the mutable array. The offset is in elements of type + -- @a@ rather than in bytes. + writeByteArray# :: MutableByteArray# s -> Int# -> a -> State# s -> State# s + + -- | Fill a slice of the mutable array with a value. The offset and length + -- of the chunk are in elements of type @a@ rather than in bytes. + setByteArray# :: MutableByteArray# s -> Int# -> Int# -> a -> State# s -> State# s + + -- | Read a value from a memory position given by an address and an offset. + -- The memory block the address refers to must be immutable. The offset is in + -- elements of type @a@ rather than in bytes. + indexOffAddr# :: Addr# -> Int# -> a + + -- | Read a value from a memory position given by an address and an offset. + -- The offset is in elements of type @a@ rather than in bytes. + readOffAddr# :: Addr# -> Int# -> State# s -> (# State# s, a #) + + -- | Write a value to a memory position given by an address and an offset. + -- The offset is in elements of type @a@ rather than in bytes. + writeOffAddr# :: Addr# -> Int# -> a -> State# s -> State# s + + -- | Fill a memory block given by an address, an offset and a length. + -- The offset and length are in elements of type @a@ rather than in bytes. + setOffAddr# :: Addr# -> Int# -> Int# -> a -> State# s -> State# s + +-- | Size of values of type @a@. The argument is not used. +sizeOf :: Prim a => a -> Int +sizeOf x = I# (sizeOf# x) + +-- | Alignment of values of type @a@. The argument is not used. +alignment :: Prim a => a -> Int +alignment x = I# (alignment# x) + +#define derivePrim(ty, ctr, sz, align, idx_arr, rd_arr, wr_arr, set_arr, idx_addr, rd_addr, wr_addr, set_addr) \ +instance Prim (ty) where { \ + sizeOf# _ = unI# sz \ +; alignment# _ = unI# align \ +; indexByteArray# arr# i# = ctr (idx_arr arr# i#) \ +; readByteArray# arr# i# s# = case rd_arr arr# i# s# of \ + { (# s1#, x# #) -> (# s1#, ctr x# #) } \ +; writeByteArray# arr# i# (ctr x#) s# = wr_arr arr# i# x# s# \ +; setByteArray# arr# i# n# (ctr x#) s# \ + = let { i = fromIntegral (I# i#) \ + ; n = fromIntegral (I# n#) \ + } in \ + case unsafeCoerce# (internal (set_arr arr# i n x#)) s# of \ + { (# s1#, _ #) -> s1# } \ + \ +; indexOffAddr# addr# i# = ctr (idx_addr addr# i#) \ +; readOffAddr# addr# i# s# = case rd_addr addr# i# s# of \ + { (# s1#, x# #) -> (# s1#, ctr x# #) } \ +; writeOffAddr# addr# i# (ctr x#) s# = wr_addr addr# i# x# s# \ +; setOffAddr# addr# i# n# (ctr x#) s# \ + = let { i = fromIntegral (I# i#) \ + ; n = fromIntegral (I# n#) \ + } in \ + case unsafeCoerce# (internal (set_addr addr# i n x#)) s# of \ + { (# s1#, _ #) -> s1# } \ +; {-# INLINE sizeOf# #-} \ +; {-# INLINE alignment# #-} \ +; {-# INLINE indexByteArray# #-} \ +; {-# INLINE readByteArray# #-} \ +; {-# INLINE writeByteArray# #-} \ +; {-# INLINE setByteArray# #-} \ +; {-# INLINE indexOffAddr# #-} \ +; {-# INLINE readOffAddr# #-} \ +; {-# INLINE writeOffAddr# #-} \ +; {-# INLINE setOffAddr# #-} \ +} + +unI# :: Int -> Int# +unI# (I# n#) = n# + +derivePrim(Word, W#, sIZEOF_WORD, aLIGNMENT_WORD, + indexWordArray#, readWordArray#, writeWordArray#, setWordArray#, + indexWordOffAddr#, readWordOffAddr#, writeWordOffAddr#, setWordOffAddr#) +derivePrim(Word8, W8#, sIZEOF_WORD8, aLIGNMENT_WORD8, + indexWord8Array#, readWord8Array#, writeWord8Array#, setWord8Array#, + indexWord8OffAddr#, readWord8OffAddr#, writeWord8OffAddr#, setWord8OffAddr#) +derivePrim(Word16, W16#, sIZEOF_WORD16, aLIGNMENT_WORD16, + indexWord16Array#, readWord16Array#, writeWord16Array#, setWord16Array#, + indexWord16OffAddr#, readWord16OffAddr#, writeWord16OffAddr#, setWord16OffAddr#) +derivePrim(Word32, W32#, sIZEOF_WORD32, aLIGNMENT_WORD32, + indexWord32Array#, readWord32Array#, writeWord32Array#, setWord32Array#, + indexWord32OffAddr#, readWord32OffAddr#, writeWord32OffAddr#, setWord32OffAddr#) +derivePrim(Word64, W64#, sIZEOF_WORD64, aLIGNMENT_WORD64, + indexWord64Array#, readWord64Array#, writeWord64Array#, setWord64Array#, + indexWord64OffAddr#, readWord64OffAddr#, writeWord64OffAddr#, setWord64OffAddr#) +derivePrim(Int, I#, sIZEOF_INT, aLIGNMENT_INT, + indexIntArray#, readIntArray#, writeIntArray#, setIntArray#, + indexIntOffAddr#, readIntOffAddr#, writeIntOffAddr#, setIntOffAddr#) +derivePrim(Int8, I8#, sIZEOF_INT8, aLIGNMENT_INT8, + indexInt8Array#, readInt8Array#, writeInt8Array#, setInt8Array#, + indexInt8OffAddr#, readInt8OffAddr#, writeInt8OffAddr#, setInt8OffAddr#) +derivePrim(Int16, I16#, sIZEOF_INT16, aLIGNMENT_INT16, + indexInt16Array#, readInt16Array#, writeInt16Array#, setInt16Array#, + indexInt16OffAddr#, readInt16OffAddr#, writeInt16OffAddr#, setInt16OffAddr#) +derivePrim(Int32, I32#, sIZEOF_INT32, aLIGNMENT_INT32, + indexInt32Array#, readInt32Array#, writeInt32Array#, setInt32Array#, + indexInt32OffAddr#, readInt32OffAddr#, writeInt32OffAddr#, setInt32OffAddr#) +derivePrim(Int64, I64#, sIZEOF_INT64, aLIGNMENT_INT64, + indexInt64Array#, readInt64Array#, writeInt64Array#, setInt64Array#, + indexInt64OffAddr#, readInt64OffAddr#, writeInt64OffAddr#, setInt64OffAddr#) +derivePrim(Float, F#, sIZEOF_FLOAT, aLIGNMENT_FLOAT, + indexFloatArray#, readFloatArray#, writeFloatArray#, setFloatArray#, + indexFloatOffAddr#, readFloatOffAddr#, writeFloatOffAddr#, setFloatOffAddr#) +derivePrim(Double, D#, sIZEOF_DOUBLE, aLIGNMENT_DOUBLE, + indexDoubleArray#, readDoubleArray#, writeDoubleArray#, setDoubleArray#, + indexDoubleOffAddr#, readDoubleOffAddr#, writeDoubleOffAddr#, setDoubleOffAddr#) +derivePrim(Char, C#, sIZEOF_CHAR, aLIGNMENT_CHAR, + indexWideCharArray#, readWideCharArray#, writeWideCharArray#, setWideCharArray#, + indexWideCharOffAddr#, readWideCharOffAddr#, writeWideCharOffAddr#, setWideCharOffAddr#) +derivePrim(Addr, Addr, sIZEOF_PTR, aLIGNMENT_PTR, + indexAddrArray#, readAddrArray#, writeAddrArray#, setAddrArray#, + indexAddrOffAddr#, readAddrOffAddr#, writeAddrOffAddr#, setAddrOffAddr#) +derivePrim(Ptr a, Ptr, sIZEOF_PTR, aLIGNMENT_PTR, + indexAddrArray#, readAddrArray#, writeAddrArray#, setAddrArray#, + indexAddrOffAddr#, readAddrOffAddr#, writeAddrOffAddr#, setAddrOffAddr#) +derivePrim(FunPtr a, FunPtr, sIZEOF_PTR, aLIGNMENT_PTR, + indexAddrArray#, readAddrArray#, writeAddrArray#, setAddrArray#, + indexAddrOffAddr#, readAddrOffAddr#, writeAddrOffAddr#, setAddrOffAddr#) diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/UnliftedArray.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/UnliftedArray.hs new file mode 100644 index 0000000..ce26845 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Data/Primitive/UnliftedArray.hs @@ -0,0 +1,365 @@ +{-# Language MagicHash #-} +{-# Language UnboxedTuples #-} +{-# Language DeriveDataTypeable #-} + +-- | +-- Module : Data.Primitive.UnliftedArray +-- Copyright : (c) Dan Doel 2016 +-- License : BSD-style +-- +-- Maintainer : Libraries +-- Portability : non-portable +-- +-- GHC contains three general classes of value types: +-- +-- 1. Unboxed types: values are machine values made up of fixed numbers of bytes +-- 2. Unlifted types: values are pointers, but strictly evaluated +-- 3. Lifted types: values are pointers, lazily evaluated +-- +-- The first category can be stored in a 'ByteArray', and this allows types in +-- category 3 that are simple wrappers around category 1 types to be stored +-- more efficiently using a 'ByteArray'. This module provides the same facility +-- for category 2 types. +-- +-- GHC has two primitive types, 'ArrayArray#' and 'MutableArrayArray#'. These +-- are arrays of pointers, but of category 2 values, so they are known to not +-- be bottom. This allows types that are wrappers around such types to be stored +-- in an array without an extra level of indirection. +-- +-- The way that the 'ArrayArray#' API works is that one can read and write +-- 'ArrayArray#' values to the positions. This works because all category 2 +-- types share a uniform representation, unlike unboxed values which are +-- represented by varying (by type) numbers of bytes. However, using the +-- this makes the internal API very unsafe to use, as one has to coerce values +-- to and from 'ArrayArray#'. +-- +-- The API presented by this module is more type safe. 'UnliftedArray' and +-- 'MutableUnliftedArray' are parameterized by the type of arrays they contain, and +-- the coercions necessary are abstracted into a class, 'PrimUnlifted', of things +-- that are eligible to be stored. + +module Data.Primitive.UnliftedArray + ( UnliftedArray(..) + , MutableUnliftedArray(..) + , PrimUnlifted(..) + , unsafeNewUnliftedArray + , newUnliftedArray + , setUnliftedArray + , sizeofUnliftedArray + , sizeofMutableUnliftedArray + , readUnliftedArray + , writeUnliftedArray + , indexUnliftedArray + , indexUnliftedArrayM + , unsafeFreezeUnliftedArray + , freezeUnliftedArray + , thawUnliftedArray + , sameMutableUnliftedArray + , copyUnliftedArray + , copyMutableUnliftedArray + , cloneUnliftedArray + , cloneMutableUnliftedArray +-- Missing operations: +-- , unsafeThawUnliftedArray + ) where + +import Data.Typeable + +import GHC.Prim +import GHC.Base (Int(..)) + +import Control.Monad.Primitive + +import Control.Monad.ST (runST) + +import Data.Primitive.Internal.Compat ( isTrue# ) + +import Data.Primitive.Array (Array) +import qualified Data.Primitive.Array as A +import Data.Primitive.ByteArray (ByteArray) +import qualified Data.Primitive.ByteArray as BA +import qualified Data.Primitive.SmallArray as SA +import qualified Data.Primitive.MutVar as MV + +-- | Immutable arrays that efficiently store types that are simple wrappers +-- around unlifted primitive types. The values of the unlifted type are +-- stored directly, eliminating a layer of indirection. +data UnliftedArray e = UnliftedArray ArrayArray# + deriving (Typeable) + +-- | Mutable arrays that efficiently store types that are simple wrappers +-- around unlifted primitive types. The values of the unlifted type are +-- stored directly, eliminating a layer of indirection. +data MutableUnliftedArray s e = MutableUnliftedArray (MutableArrayArray# s) + deriving (Typeable) + +-- | Classifies the types that are able to be stored in 'UnliftedArray' and +-- 'MutableUnliftedArray'. These should be types that are just liftings of the +-- unlifted pointer types, so that their internal contents can be safely coerced +-- into an 'ArrayArray#'. +class PrimUnlifted a where + toArrayArray# :: a -> ArrayArray# + fromArrayArray# :: ArrayArray# -> a + +instance PrimUnlifted (UnliftedArray e) where + toArrayArray# (UnliftedArray aa#) = aa# + fromArrayArray# aa# = UnliftedArray aa# + +instance PrimUnlifted (MutableUnliftedArray s e) where + toArrayArray# (MutableUnliftedArray maa#) = unsafeCoerce# maa# + fromArrayArray# aa# = MutableUnliftedArray (unsafeCoerce# aa#) + +instance PrimUnlifted (Array a) where + toArrayArray# (A.Array a#) = unsafeCoerce# a# + fromArrayArray# aa# = A.Array (unsafeCoerce# aa#) + +instance PrimUnlifted (A.MutableArray s a) where + toArrayArray# (A.MutableArray ma#) = unsafeCoerce# ma# + fromArrayArray# aa# = A.MutableArray (unsafeCoerce# aa#) + +instance PrimUnlifted ByteArray where + toArrayArray# (BA.ByteArray ba#) = unsafeCoerce# ba# + fromArrayArray# aa# = BA.ByteArray (unsafeCoerce# aa#) + +instance PrimUnlifted (BA.MutableByteArray s) where + toArrayArray# (BA.MutableByteArray mba#) = unsafeCoerce# mba# + fromArrayArray# aa# = BA.MutableByteArray (unsafeCoerce# aa#) + +instance PrimUnlifted (SA.SmallArray a) where + toArrayArray# (SA.SmallArray sa#) = unsafeCoerce# sa# + fromArrayArray# aa# = SA.SmallArray (unsafeCoerce# aa#) + +instance PrimUnlifted (SA.SmallMutableArray s a) where + toArrayArray# (SA.SmallMutableArray sma#) = unsafeCoerce# sma# + fromArrayArray# aa# = SA.SmallMutableArray (unsafeCoerce# aa#) + +instance PrimUnlifted (MV.MutVar s a) where + toArrayArray# (MV.MutVar mv#) = unsafeCoerce# mv# + fromArrayArray# aa# = MV.MutVar (unsafeCoerce# aa#) + +-- | Creates a new 'MutableUnliftedArray'. This function is unsafe, because it +-- allows access to the raw contents of the underlying 'ArrayArray#'. +unsafeNewUnliftedArray + :: (PrimMonad m) + => Int -- ^ size + -> m (MutableUnliftedArray (PrimState m) a) +unsafeNewUnliftedArray (I# i#) = primitive $ \s -> case newArrayArray# i# s of + (# s', maa# #) -> (# s', MutableUnliftedArray maa# #) +{-# inline unsafeNewUnliftedArray #-} + +-- | Sets all the positions in an unlifted array to the designated value. +setUnliftedArray + :: (PrimMonad m, PrimUnlifted a) + => MutableUnliftedArray (PrimState m) a -- ^ destination + -> a -- ^ value to fill with + -> m () +setUnliftedArray mua v = loop $ sizeofMutableUnliftedArray mua - 1 + where + loop i | i < 0 = return () + | otherwise = writeUnliftedArray mua i v >> loop (i-1) +{-# inline setUnliftedArray #-} + +-- | Creates a new 'MutableUnliftedArray' with the specified value as initial +-- contents. This is slower than 'unsafeNewUnliftedArray', but safer. +newUnliftedArray + :: (PrimMonad m, PrimUnlifted a) + => Int -- ^ size + -> a -- ^ initial value + -> m (MutableUnliftedArray (PrimState m) a) +newUnliftedArray len v = + unsafeNewUnliftedArray len >>= \mua -> setUnliftedArray mua v >> return mua +{-# inline newUnliftedArray #-} + +-- | Yields the length of an 'UnliftedArray'. +sizeofUnliftedArray :: UnliftedArray e -> Int +sizeofUnliftedArray (UnliftedArray aa#) = I# (sizeofArrayArray# aa#) +{-# inline sizeofUnliftedArray #-} + +-- | Yields the length of a 'MutableUnliftedArray'. +sizeofMutableUnliftedArray :: MutableUnliftedArray s e -> Int +sizeofMutableUnliftedArray (MutableUnliftedArray maa#) + = I# (sizeofMutableArrayArray# maa#) +{-# inline sizeofMutableUnliftedArray #-} + +-- Internal indexing function. +-- +-- Note: ArrayArray# is strictly evaluated, so this should have similar +-- consequences to indexArray#, where matching on the unboxed single causes the +-- array access to happen. +indexUnliftedArrayU + :: PrimUnlifted a + => UnliftedArray a + -> Int + -> (# a #) +indexUnliftedArrayU (UnliftedArray src#) (I# i#) + = case indexArrayArrayArray# src# i# of + aa# -> (# fromArrayArray# aa# #) +{-# inline indexUnliftedArrayU #-} + +-- | Gets the value at the specified position of an 'UnliftedArray'. +indexUnliftedArray + :: PrimUnlifted a + => UnliftedArray a -- ^ source + -> Int -- ^ index + -> a +indexUnliftedArray ua i + = case indexUnliftedArrayU ua i of (# v #) -> v +{-# inline indexUnliftedArray #-} + +-- | Gets the value at the specified position of an 'UnliftedArray'. +-- The purpose of the 'Monad' is to allow for being eager in the +-- 'UnliftedArray' value without having to introduce a data dependency +-- directly on the result value. +-- +-- It should be noted that this is not as much of a problem as with a normal +-- 'Array', because elements of an 'UnliftedArray' are guaranteed to not +-- be exceptional. This function is provided in case it is more desirable +-- than being strict in the result value. +indexUnliftedArrayM + :: (PrimUnlifted a, Monad m) + => UnliftedArray a -- ^ source + -> Int -- ^ index + -> m a +indexUnliftedArrayM ua i + = case indexUnliftedArrayU ua i of + (# v #) -> return v +{-# inline indexUnliftedArrayM #-} + +-- | Gets the value at the specified position of a 'MutableUnliftedArray'. +readUnliftedArray + :: (PrimMonad m, PrimUnlifted a) + => MutableUnliftedArray (PrimState m) a -- ^ source + -> Int -- ^ index + -> m a +readUnliftedArray (MutableUnliftedArray maa#) (I# i#) + = primitive $ \s -> case readArrayArrayArray# maa# i# s of + (# s', aa# #) -> (# s', fromArrayArray# aa# #) +{-# inline readUnliftedArray #-} + +-- | Sets the value at the specified position of a 'MutableUnliftedArray'. +writeUnliftedArray + :: (PrimMonad m, PrimUnlifted a) + => MutableUnliftedArray (PrimState m) a -- ^ destination + -> Int -- ^ index + -> a -- ^ value + -> m () +writeUnliftedArray (MutableUnliftedArray maa#) (I# i#) a + = primitive_ (writeArrayArrayArray# maa# i# (toArrayArray# a)) +{-# inline writeUnliftedArray #-} + +-- | Freezes a 'MutableUnliftedArray', yielding an 'UnliftedArray'. This simply +-- marks the array as frozen in place, so it should only be used when no further +-- modifications to the mutable array will be performed. +unsafeFreezeUnliftedArray + :: (PrimMonad m) + => MutableUnliftedArray (PrimState m) a + -> m (UnliftedArray a) +unsafeFreezeUnliftedArray (MutableUnliftedArray maa#) + = primitive $ \s -> case unsafeFreezeArrayArray# maa# s of + (# s', aa# #) -> (# s', UnliftedArray aa# #) +{-# inline unsafeFreezeUnliftedArray #-} + +-- | Determines whether two 'MutableUnliftedArray' values are the same. This is +-- object/pointer identity, not based on the contents. +sameMutableUnliftedArray + :: MutableUnliftedArray s a + -> MutableUnliftedArray s a + -> Bool +sameMutableUnliftedArray (MutableUnliftedArray maa1#) (MutableUnliftedArray maa2#) + = isTrue# (sameMutableArrayArray# maa1# maa2#) +{-# inline sameMutableUnliftedArray #-} + +-- | Copies the contents of an immutable array into a mutable array. +copyUnliftedArray + :: (PrimMonad m) + => MutableUnliftedArray (PrimState m) a -- ^ destination + -> Int -- ^ offset into destination + -> UnliftedArray a -- ^ source + -> Int -- ^ offset into source + -> Int -- ^ number of elements to copy + -> m () +copyUnliftedArray + (MutableUnliftedArray dst) (I# doff) + (UnliftedArray src) (I# soff) (I# ln) = + primitive_ $ copyArrayArray# src soff dst doff ln +{-# inline copyUnliftedArray #-} + +-- | Copies the contents of one mutable array into another. +copyMutableUnliftedArray + :: (PrimMonad m) + => MutableUnliftedArray (PrimState m) a -- ^ destination + -> Int -- ^ offset into destination + -> MutableUnliftedArray (PrimState m) a -- ^ source + -> Int -- ^ offset into source + -> Int -- ^ number of elements to copy + -> m () +copyMutableUnliftedArray + (MutableUnliftedArray dst) (I# doff) + (MutableUnliftedArray src) (I# soff) (I# ln) = + primitive_ $ copyMutableArrayArray# src soff dst doff ln +{-# inline copyMutableUnliftedArray #-} + +-- | Freezes a portion of a 'MutableUnliftedArray', yielding an 'UnliftedArray'. +-- This operation is safe, in that it copies the frozen portion, and the +-- existing mutable array may still be used afterward. +freezeUnliftedArray + :: (PrimMonad m) + => MutableUnliftedArray (PrimState m) a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> m (UnliftedArray a) +freezeUnliftedArray src off len = do + dst <- unsafeNewUnliftedArray len + copyMutableUnliftedArray dst 0 src off len + unsafeFreezeUnliftedArray dst +{-# inline freezeUnliftedArray #-} + +-- | Thaws a portion of an 'UnliftedArray', yielding a 'MutableUnliftedArray'. +-- This copies the thawed portion, so mutations will not affect the original +-- array. +thawUnliftedArray + :: (PrimMonad m) + => UnliftedArray a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> m (MutableUnliftedArray (PrimState m) a) +thawUnliftedArray src off len = do + dst <- unsafeNewUnliftedArray len + copyUnliftedArray dst 0 src off len + return dst +{-# inline thawUnliftedArray #-} + +-- | Creates a copy of a portion of an 'UnliftedArray' +cloneUnliftedArray + :: UnliftedArray a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> UnliftedArray a +cloneUnliftedArray src off len = + runST $ thawUnliftedArray src off len >>= unsafeFreezeUnliftedArray +{-# inline cloneUnliftedArray #-} + +-- | Creates a new 'MutableUnliftedArray' containing a copy of a portion of +-- another mutable array. +cloneMutableUnliftedArray + :: (PrimMonad m) + => MutableUnliftedArray (PrimState m) a -- ^ source + -> Int -- ^ offset + -> Int -- ^ length + -> m (MutableUnliftedArray (PrimState m) a) +cloneMutableUnliftedArray src off len = do + dst <- unsafeNewUnliftedArray len + copyMutableUnliftedArray dst 0 src off len + return dst +{-# inline cloneMutableUnliftedArray #-} + +instance Eq (MutableUnliftedArray s a) where + (==) = sameMutableUnliftedArray + +instance (Eq a, PrimUnlifted a) => Eq (UnliftedArray a) where + aa1 == aa2 = sizeofUnliftedArray aa1 == sizeofUnliftedArray aa2 + && loop (sizeofUnliftedArray aa1 - 1) + where + loop i + | i < 0 = True + | otherwise = indexUnliftedArray aa1 i == indexUnliftedArray aa2 i && loop (i-1) diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/LICENSE b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/LICENSE new file mode 100644 index 0000000..fc213a6 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2008-2009, Roman Leshchinskiy +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +- Neither name of the University nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF +GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Setup.hs b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Setup.hs new file mode 100644 index 0000000..200a2e5 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/Setup.hs @@ -0,0 +1,3 @@ +import Distribution.Simple +main = defaultMain + diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/cbits/primitive-memops.c b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/cbits/primitive-memops.c new file mode 100644 index 0000000..81b1d6f --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/cbits/primitive-memops.c @@ -0,0 +1,56 @@ +#include +#include "primitive-memops.h" + +void hsprimitive_memcpy( void *dst, ptrdiff_t doff, void *src, ptrdiff_t soff, size_t len ) +{ + memcpy( (char *)dst + doff, (char *)src + soff, len ); +} + +void hsprimitive_memmove( void *dst, ptrdiff_t doff, void *src, ptrdiff_t soff, size_t len ) +{ + memmove( (char *)dst + doff, (char *)src + soff, len ); +} + +#define MEMSET(TYPE, ATYPE) \ +void hsprimitive_memset_ ## TYPE (Hs ## TYPE *p, ptrdiff_t off, size_t n, ATYPE x) \ +{ \ + p += off; \ + if (x == 0) \ + memset(p, 0, n * sizeof(Hs ## TYPE)); \ + else if (sizeof(Hs ## TYPE) == sizeof(int)*2) { \ + int *q = (int *)p; \ + const int *r = (const int *)(void *)&x; \ + while (n>0) { \ + q[0] = r[0]; \ + q[1] = r[1]; \ + q += 2; \ + --n; \ + } \ + } \ + else { \ + while (n>0) { \ + *p++ = x; \ + --n; \ + } \ + } \ +} + +int hsprimitive_memcmp( HsWord8 *s1, HsWord8 *s2, size_t n ) +{ + return memcmp( s1, s2, n ); +} + +void hsprimitive_memset_Word8 (HsWord8 *p, ptrdiff_t off, size_t n, HsWord x) +{ + memset( (char *)(p+off), x, n ); +} + +/* MEMSET(HsWord8, HsWord) */ +MEMSET(Word16, HsWord) +MEMSET(Word32, HsWord) +MEMSET(Word64, HsWord64) +MEMSET(Word, HsWord) +MEMSET(Ptr, HsPtr) +MEMSET(Float, HsFloat) +MEMSET(Double, HsDouble) +MEMSET(Char, HsChar) diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/cbits/primitive-memops.h b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/cbits/primitive-memops.h new file mode 100644 index 0000000..d7c3396 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/cbits/primitive-memops.h @@ -0,0 +1,23 @@ +#ifndef haskell_primitive_memops_h +#define haskell_primitive_memops_h + +#include +#include +#include + +void hsprimitive_memcpy( void *dst, ptrdiff_t doff, void *src, ptrdiff_t soff, size_t len ); +void hsprimitive_memmove( void *dst, ptrdiff_t doff, void *src, ptrdiff_t soff, size_t len ); +int hsprimitive_memcmp( HsWord8 *s1, HsWord8 *s2, size_t n ); + +void hsprimitive_memset_Word8 (HsWord8 *, ptrdiff_t, size_t, HsWord); +void hsprimitive_memset_Word16 (HsWord16 *, ptrdiff_t, size_t, HsWord); +void hsprimitive_memset_Word32 (HsWord32 *, ptrdiff_t, size_t, HsWord); +void hsprimitive_memset_Word64 (HsWord64 *, ptrdiff_t, size_t, HsWord64); +void hsprimitive_memset_Word (HsWord *, ptrdiff_t, size_t, HsWord); +void hsprimitive_memset_Ptr (HsPtr *, ptrdiff_t, size_t, HsPtr); +void hsprimitive_memset_Float (HsFloat *, ptrdiff_t, size_t, HsFloat); +void hsprimitive_memset_Double (HsDouble *, ptrdiff_t, size_t, HsDouble); +void hsprimitive_memset_Char (HsChar *, ptrdiff_t, size_t, HsChar); + +#endif + diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/changelog.md b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/changelog.md new file mode 100644 index 0000000..7ecfaec --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/changelog.md @@ -0,0 +1,103 @@ +## Changes in version 0.6.3.0 + + * Add `PrimMonad` instances for `ContT`, `AccumT`, and `SelectT` from + `transformers` + + * Add `Eq`, `Ord`, `Show`, and `IsList` instances for `ByteArray` + + * Add `Semigroup` instances for `Array` and `SmallArray`. This allows + `primitive` to build on GHC 8.4 and later. + +## Changes in version 0.6.2.0 + + * Drop support for GHCs before 7.4 + + * `SmallArray` support + + * `ArrayArray#` based support for more efficient arrays of unlifted pointer types + + * Make `Array` and the like instances of various classes for convenient use + + * Add `Prim` instances for Ptr and FunPtr + + * Add `ioToPrim`, `stToPrim` and unsafe counterparts for situations that would + otherwise require type ascriptions on `primToPrim` + + * Add `evalPrim` + + * Add `PrimBase` instance for `IdentityT` + +## Changes in version 0.6.1.0 + + * Use more appropriate types in internal memset functions, which prevents + overflows/segfaults on 64-bit systems. + + * Fixed a warning on GHC 7.10 + + * Worked around a -dcore-lint bug in GHC 7.6/7.7 + +## Changes in version 0.6 + + * Split PrimMonad into two classes to allow automatic lifting of primitive + operations into monad transformers. The `internal` operation has moved to the + `PrimBase` class. + + * Fixed the test suite on older GHCs + +## Changes in version 0.5.4.0 + + * Changed primitive_ to work around an oddity with GHC's code generation + on certain versions that led to side effects not happening when used + in conjunction with certain very unsafe IO performers. + + * Allow primitive to build on GHC 7.9 + +## Changes in version 0.5.3.0 + + * Implement `cloneArray` and `cloneMutableArray` primitives + (with fall-back implementations for GHCs prior to version 7.2.1) + +## Changes in version 0.5.2.1 + + * Add strict variants of `MutVar` modification functions + `atomicModifyMutVar'` and `modifyMutVar'` + + * Fix compilation on Solaris 10 with GNU C 3.4.3 + +## Changes in version 0.5.1.0 + + * Add support for GHC 7.7's new primitive `Bool` representation + +## Changes in version 0.5.0.1 + + * Disable array copying primitives for GHC 7.6.* and earlier + +## Changes in version 0.5 + + * New in `Data.Primitive.MutVar`: `atomicModifyMutVar` + + * Efficient block fill operations: `setByteArray`, `setAddr` + +## Changes in version 0.4.1 + + * New module `Data.Primitive.MutVar` + +## Changes in version 0.4.0.1 + + * Critical bug fix in `fillByteArray` + +## Changes in version 0.4 + + * Support for GHC 7.2 array copying primitives + + * New in `Data.Primitive.ByteArray`: `copyByteArray`, + `copyMutableByteArray`, `moveByteArray`, `fillByteArray` + + * Deprecated in `Data.Primitive.ByteArray`: `memcpyByteArray`, + `memcpyByteArray'`, `memmoveByteArray`, `memsetByteArray` + + * New in `Data.Primitive.Array`: `copyArray`, `copyMutableByteArray` + + * New in `Data.Primitive.Addr`: `copyAddr`, `moveAddr` + + * Deprecated in `Data.Primitive.Addr`: `memcpyAddr` diff --git a/benchmarks/PPoPP2019/src/primitive-0.6.3.0/primitive.cabal b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/primitive.cabal new file mode 100644 index 0000000..f5a4644 --- /dev/null +++ b/benchmarks/PPoPP2019/src/primitive-0.6.3.0/primitive.cabal @@ -0,0 +1,78 @@ +Name: primitive +Version: 0.6.3.0 +License: BSD3 +License-File: LICENSE + +Author: Roman Leshchinskiy +Maintainer: libraries@haskell.org +Copyright: (c) Roman Leshchinskiy 2009-2012 +Homepage: https://github.com/haskell/primitive +Bug-Reports: https://github.com/haskell/primitive/issues +Category: Data +Synopsis: Primitive memory-related operations +Cabal-Version: >= 1.10 +Build-Type: Simple +Description: This package provides various primitive memory-related operations. + +Extra-Source-Files: changelog.md + +Tested-With: + GHC == 7.4.2, + GHC == 7.6.3, + GHC == 7.8.4, + GHC == 7.10.3, + GHC == 8.0.2, + GHC == 8.2.2, + GHC == 8.4.1 + +Library + Default-Language: Haskell2010 + Other-Extensions: + BangPatterns, CPP, DeriveDataTypeable, + MagicHash, TypeFamilies, UnboxedTuples, UnliftedFFITypes + + Exposed-Modules: + Control.Monad.Primitive + Data.Primitive + Data.Primitive.MachDeps + Data.Primitive.Types + Data.Primitive.Array + Data.Primitive.ByteArray + Data.Primitive.SmallArray + Data.Primitive.UnliftedArray + Data.Primitive.Addr + Data.Primitive.MutVar + + Other-Modules: + Data.Primitive.Internal.Compat + Data.Primitive.Internal.Operations + + Build-Depends: base >= 4.5 && < 4.12 + , ghc-prim >= 0.2 && < 0.6 + , transformers >= 0.2 && < 0.6 + + Ghc-Options: -O2 -Wall + + Include-Dirs: cbits + Install-Includes: primitive-memops.h + includes: primitive-memops.h + c-sources: cbits/primitive-memops.c + cc-options: -O3 -fomit-frame-pointer -Wall + if !os(solaris) + cc-options: -ftree-vectorize + if arch(i386) || arch(x86_64) + cc-options: -msse2 + +test-suite test + Default-Language: Haskell2010 + hs-source-dirs: test + main-is: main.hs + type: exitcode-stdio-1.0 + build-depends: base + , ghc-prim + , primitive + ghc-options: -O2 + +source-repository head + type: git + location: https://github.com/haskell/primitive diff --git a/benchmarks/PPoPP2019/src/transformers-base-0.4.4/LICENSE b/benchmarks/PPoPP2019/src/transformers-base-0.4.4/LICENSE new file mode 100644 index 0000000..9d51261 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-base-0.4.4/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011, Mikhail Vorozhtsov, Bas van Dijk +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +- Neither the names of the copyright owners nor the names of the + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/benchmarks/PPoPP2019/src/transformers-base-0.4.4/Setup.hs b/benchmarks/PPoPP2019/src/transformers-base-0.4.4/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-base-0.4.4/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/benchmarks/PPoPP2019/src/transformers-base-0.4.4/src/Control/Monad/Base.hs b/benchmarks/PPoPP2019/src/transformers-base-0.4.4/src/Control/Monad/Base.hs new file mode 100644 index 0000000..ac0c886 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-base-0.4.4/src/Control/Monad/Base.hs @@ -0,0 +1,114 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE UnicodeSyntax #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE UndecidableInstances #-} + +#if MIN_VERSION_base(4,4,0) +{-# LANGUAGE Safe #-} +#endif + +#if MIN_VERSION_transformers(0,4,0) +-- Hide warnings for the deprecated ErrorT transformer: +{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-} +#endif + +module Control.Monad.Base + ( MonadBase(..) + , liftBaseDefault + ) where + +import Data.Monoid +import Data.Functor.Identity +import Control.Applicative (Applicative(..)) +import Control.Monad.Trans.Class +import Control.Monad.Trans.Identity +import Control.Monad.Trans.Maybe +import Control.Monad.Trans.List +import Control.Monad.Trans.Reader +import qualified Control.Monad.Trans.Writer.Lazy as L +import qualified Control.Monad.Trans.Writer.Strict as S +import qualified Control.Monad.Trans.State.Lazy as L +import qualified Control.Monad.Trans.State.Strict as S +import qualified Control.Monad.Trans.RWS.Lazy as L +import qualified Control.Monad.Trans.RWS.Strict as S +import Control.Monad.Trans.Error +import Control.Monad.Trans.Cont +import Control.Monad.Trans.Except +#if !MIN_VERSION_base(4,4,0) && HS_TRANSFORMERS_BASE__ORPHANS +import Control.Monad (ap) +import qualified Control.Monad.ST.Lazy as L +import qualified Control.Monad.ST.Strict as S +#endif +#if MIN_VERSION_base(4,4,0) +import qualified Control.Monad.ST.Lazy.Safe as L +import qualified Control.Monad.ST.Safe as S +#endif +import Control.Monad.STM (STM) + +class (Applicative b, Applicative m, Monad b, Monad m) + ⇒ MonadBase b m | m → b where + -- | Lift a computation from the base monad + liftBase ∷ b α → m α + +#define BASE(M) \ +instance MonadBase (M) (M) where liftBase = id + +BASE(IO) +BASE(Maybe) +BASE(Either e) +BASE([]) +BASE((→) r) +BASE(Identity) + +BASE(STM) + +#if !MIN_VERSION_base(4,4,0) && HS_TRANSFORMERS_BASE__ORPHANS +instance Applicative (L.ST s) where + pure = return + (<*>) = ap + +instance Applicative (S.ST s) where + pure = return + (<*>) = ap + +BASE(L.ST s) +BASE(S.ST s) +#endif + +#if MIN_VERSION_base(4,4,0) +BASE(L.ST s) +BASE(S.ST s) +#endif + +#undef BASE + +-- | Can be used as a default implementation for 'liftBase'. +-- +-- Note that: @liftBaseDefault = 'lift' . 'liftBase'@ +liftBaseDefault ∷ (MonadTrans t, MonadBase b m) ⇒ b α → t m α +liftBaseDefault = lift . liftBase + +#define TRANS(T) \ +instance (MonadBase b m) ⇒ MonadBase b (T m) where liftBase = liftBaseDefault + +TRANS(IdentityT) +TRANS(MaybeT) +TRANS(ListT) +TRANS(ReaderT r) +TRANS(L.StateT s) +TRANS(S.StateT s) +TRANS(ContT r) +TRANS(ExceptT e) +#undef TRANS + +#define TRANS_CTX(CTX, T) \ +instance (CTX, MonadBase b m) ⇒ MonadBase b (T m) where liftBase = liftBaseDefault + +TRANS_CTX(Monoid w, L.WriterT w) +TRANS_CTX(Monoid w, S.WriterT w) +TRANS_CTX(Monoid w, L.RWST r w s) +TRANS_CTX(Monoid w, S.RWST r w s) +TRANS_CTX(Error e, ErrorT e) +#undef TRANS_CTX diff --git a/benchmarks/PPoPP2019/src/transformers-base-0.4.4/transformers-base.cabal b/benchmarks/PPoPP2019/src/transformers-base-0.4.4/transformers-base.cabal new file mode 100644 index 0000000..352f8ed --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-base-0.4.4/transformers-base.cabal @@ -0,0 +1,50 @@ +Name: transformers-base +Version: 0.4.4 +x-revision: 1 +Category: Control +Stability: experimental +Synopsis: Lift computations from the bottom of a transformer stack +Description: + This package provides a straightforward port of @monadLib@'s BaseM + typeclass to @transformers@. + +Homepage: https://github.com/mvv/transformers-base +Bug-Reports: https://github.com/mvv/transformers-base/issues + +Author: + Mikhail Vorozhtsov , + Bas van Dijk +Maintainer: Mikhail Vorozhtsov +Copyright: + 2011 Mikhail Vorozhtsov , + Bas van Dijk +License: BSD3 +License-File: LICENSE + +Cabal-Version: >= 1.6.0 +Build-Type: Simple + +Source-Repository head + Type: git + Location: https://github.com/mvv/transformers-base.git + +Flag OrphanInstances + Description: + Declare orphan Applicative instances for lazy and strict ST if needed + Default: True + +Library + Build-Depends: + base >= 3 && < 5, + base < 4.4 || >= 4.5, + stm >= 2.3, + transformers >= 0.2, + transformers-compat >= 0.2 + Hs-Source-Dirs: src + GHC-Options: -Wall + if flag(OrphanInstances) + CPP-Options: -DHS_TRANSFORMERS_BASE__ORPHANS=1 + else + CPP-Options: -DHS_TRANSFORMERS_BASE__ORPHANS=0 + Exposed-Modules: + Control.Monad.Base diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.ghci b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.ghci new file mode 100644 index 0000000..231eb17 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.ghci @@ -0,0 +1 @@ +:set -isrc -idist/build/autogen -optP-include -optPdist/build/autogen/cabal_macros.h diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.gitignore b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.gitignore new file mode 100644 index 0000000..51f70d3 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.gitignore @@ -0,0 +1,9 @@ +dist +docs +wiki +TAGS +tags +wip +.DS_Store +.*.swp +.*.swo diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.travis.yml b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.travis.yml new file mode 100644 index 0000000..a067492 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.travis.yml @@ -0,0 +1,29 @@ +language: haskell +before_install: + # Uncomment whenever hackage is down. + # - mkdir -p ~/.cabal && cp config ~/.cabal/config && cabal update + + # Try installing some of the build-deps with apt-get for speed. + - ./travis-cabal-apt-install --only-dependencies --force-reinstall $mode + + - sudo apt-get -q -y install hlint || cabal install hlint + +install: + - cabal configure $mode + - cabal build + +script: + - $script + - hlint 0.2 --cpp-define HLINT + - hlint 0.3 --cpp-define HLINT + +notifications: + irc: + channels: + - "irc.freenode.org#haskell-lens" + skip_join: true + template: + - "\x0313transformers-compat\x03/\x0306%{branch}\x03 \x0314%{commit}\x03 %{build_url} %{message}" + +env: + - mode="--enable-tests" script="cabal test" diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.vim.custom b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.vim.custom new file mode 100644 index 0000000..86321a8 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/.vim.custom @@ -0,0 +1,31 @@ +" Add the following to your .vimrc to automatically load this on startup + +" if filereadable(".vim.custom") +" so .vim.custom +" endif + +function StripTrailingWhitespace() + let myline=line(".") + let mycolumn = col(".") + silent %s/ *$// + call cursor(myline, mycolumn) +endfunction + +" enable syntax highlighting +syntax on + +" search for the tags file anywhere between here and / +set tags=TAGS;/ + +" highlight tabs and trailing spaces +set listchars=tab:‗‗,trail:‗ +set list + +" f2 runs hasktags +map :exec ":!hasktags -x -c --ignore src" + +" strip trailing whitespace before saving +" au BufWritePre *.hs,*.markdown silent! cal StripTrailingWhitespace() + +" rebuild hasktags after saving +au BufWritePost *.hs silent! :exec ":!hasktags -x -c --ignore src" diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Control/Applicative/Backwards.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Control/Applicative/Backwards.hs new file mode 100644 index 0000000..62da141 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Control/Applicative/Backwards.hs @@ -0,0 +1,82 @@ +{-# LANGUAGE CPP #-} + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Safe #-} +# endif +# if __GLASGOW_HASKELL__ >= 706 +{-# LANGUAGE PolyKinds #-} +# endif +#endif +-- | +-- Module : Control.Applicative.Backwards +-- Copyright : (c) Russell O'Connor 2009 +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : libraries@haskell.org +-- Stability : experimental +-- Portability : portable +-- +-- Making functors with an 'Applicative' instance that performs actions +-- in the reverse order. +-- +-- NB: This module is only included in @lens@ for backwards compatibility with +-- @transformers@ versions before 3.0. +module Control.Applicative.Backwards where + +import Data.Functor.Classes + +import Prelude hiding (foldr, foldr1, foldl, foldl1) +import Control.Applicative +import Data.Foldable +import Data.Traversable + +-- | The same functor, but with an 'Applicative' instance that performs +-- actions in the reverse order. +newtype Backwards f a = Backwards { forwards :: f a } + +instance (Eq1 f) => Eq1 (Backwards f) where + liftEq eq (Backwards x) (Backwards y) = liftEq eq x y + +instance (Ord1 f) => Ord1 (Backwards f) where + liftCompare comp (Backwards x) (Backwards y) = liftCompare comp x y + +instance (Read1 f) => Read1 (Backwards f) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp rl) "Backwards" Backwards + +instance (Show1 f) => Show1 (Backwards f) where + liftShowsPrec sp sl d (Backwards x) = + showsUnaryWith (liftShowsPrec sp sl) "Backwards" d x + +instance (Eq1 f, Eq a) => Eq (Backwards f a) where (==) = eq1 +instance (Ord1 f, Ord a) => Ord (Backwards f a) where compare = compare1 +instance (Read1 f, Read a) => Read (Backwards f a) where readsPrec = readsPrec1 +instance (Show1 f, Show a) => Show (Backwards f a) where showsPrec = showsPrec1 + +-- | Derived instance. +instance (Functor f) => Functor (Backwards f) where + fmap f (Backwards a) = Backwards (fmap f a) + +-- | Apply @f@-actions in the reverse order. +instance (Applicative f) => Applicative (Backwards f) where + pure a = Backwards (pure a) + Backwards f <*> Backwards a = Backwards (a <**> f) + +-- | Try alternatives in the same order as @f@. +instance (Alternative f) => Alternative (Backwards f) where + empty = Backwards empty + Backwards x <|> Backwards y = Backwards (x <|> y) + +-- | Derived instance. +instance (Foldable f) => Foldable (Backwards f) where + foldMap f (Backwards t) = foldMap f t + foldr f z (Backwards t) = foldr f z t + foldl f z (Backwards t) = foldl f z t + foldr1 f (Backwards t) = foldr1 f t + foldl1 f (Backwards t) = foldl1 f t + +-- | Derived instance. +instance (Traversable f) => Traversable (Backwards f) where + traverse f (Backwards t) = fmap Backwards (traverse f t) + sequenceA (Backwards t) = fmap Backwards (sequenceA t) diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Control/Applicative/Lift.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Control/Applicative/Lift.hs new file mode 100644 index 0000000..c9310e0 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Control/Applicative/Lift.hs @@ -0,0 +1,138 @@ +{-# LANGUAGE CPP #-} + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 704 +{-# LANGUAGE Safe #-} +# elif __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Trustworthy #-} +# endif +#endif +-- | +-- Module : Control.Applicative.Lift +-- Copyright : (c) Ross Paterson 2010 +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : ross@soi.city.ac.uk +-- Stability : experimental +-- Portability : portable +-- +-- Adding a new kind of pure computation to an applicative functor. +-- +-- NB: This module is only included in @lens@ for backwards compatibility with +-- @transformers@ versions before 3.0. + +module Control.Applicative.Lift ( + -- * Lifting an applicative + Lift(..), + unLift, + mapLift, + -- * Collecting errors + Errors, + runErrors, + failure + ) where + +import Data.Functor.Classes + +import Control.Applicative +import Data.Foldable (Foldable(foldMap)) +import Data.Functor.Constant +import Data.Monoid (Monoid(..)) +import Data.Traversable (Traversable(traverse)) + +-- | Applicative functor formed by adding pure computations to a given +-- applicative functor. +data Lift f a = Pure a | Other (f a) + +instance (Eq1 f) => Eq1 (Lift f) where + liftEq eq (Pure x1) (Pure x2) = eq x1 x2 + liftEq _ (Pure _) (Other _) = False + liftEq _ (Other _) (Pure _) = False + liftEq eq (Other y1) (Other y2) = liftEq eq y1 y2 + +instance (Ord1 f) => Ord1 (Lift f) where + liftCompare comp (Pure x1) (Pure x2) = comp x1 x2 + liftCompare _ (Pure _) (Other _) = LT + liftCompare _ (Other _) (Pure _) = GT + liftCompare comp (Other y1) (Other y2) = liftCompare comp y1 y2 + +instance (Read1 f) => Read1 (Lift f) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith rp "Pure" Pure `mappend` + readsUnaryWith (liftReadsPrec rp rl) "Other" Other + +instance (Show1 f) => Show1 (Lift f) where + liftShowsPrec sp _ d (Pure x) = showsUnaryWith sp "Pure" d x + liftShowsPrec sp sl d (Other y) = + showsUnaryWith (liftShowsPrec sp sl) "Other" d y + +instance (Eq1 f, Eq a) => Eq (Lift f a) where (==) = eq1 +instance (Ord1 f, Ord a) => Ord (Lift f a) where compare = compare1 +instance (Read1 f, Read a) => Read (Lift f a) where readsPrec = readsPrec1 +instance (Show1 f, Show a) => Show (Lift f a) where showsPrec = showsPrec1 + +instance (Functor f) => Functor (Lift f) where + fmap f (Pure x) = Pure (f x) + fmap f (Other y) = Other (fmap f y) + +instance (Foldable f) => Foldable (Lift f) where + foldMap f (Pure x) = f x + foldMap f (Other y) = foldMap f y + +instance (Traversable f) => Traversable (Lift f) where + traverse f (Pure x) = Pure <$> f x + traverse f (Other y) = Other <$> traverse f y + +-- | A combination is 'Pure' only if both parts are. +instance (Applicative f) => Applicative (Lift f) where + pure = Pure + Pure f <*> Pure x = Pure (f x) + Pure f <*> Other y = Other (f <$> y) + Other f <*> Pure x = Other (($ x) <$> f) + Other f <*> Other y = Other (f <*> y) + +-- | A combination is 'Pure' only either part is. +instance (Alternative f) => Alternative (Lift f) where + empty = Other empty + Pure x <|> _ = Pure x + Other _ <|> Pure y = Pure y + Other x <|> Other y = Other (x <|> y) + +-- | Projection to the other functor. +unLift :: (Applicative f) => Lift f a -> f a +unLift (Pure x) = pure x +unLift (Other e) = e + +-- | Apply a transformation to the other computation. +mapLift :: (f a -> g a) -> Lift f a -> Lift g a +mapLift _ (Pure x) = Pure x +mapLift f (Other e) = Other (f e) + +-- | An applicative functor that collects a monoid (e.g. lists) of errors. +-- A sequence of computations fails if any of its components do, but +-- unlike monads made with 'ExceptT' from "Control.Monad.Trans.Except", +-- these computations continue after an error, collecting all the errors. +-- +-- * @'pure' f '<*>' 'pure' x = 'pure' (f x)@ +-- +-- * @'pure' f '<*>' 'failure' e = 'failure' e@ +-- +-- * @'failure' e '<*>' 'pure' x = 'failure' e@ +-- +-- * @'failure' e1 '<*>' 'failure' e2 = 'failure' (e1 '<>' e2)@ +-- +type Errors e = Lift (Constant e) + +-- | Extractor for computations with accumulating errors. +-- +-- * @'runErrors' ('pure' x) = 'Right' x@ +-- +-- * @'runErrors' ('failure' e) = 'Left' e@ +-- +runErrors :: Errors e a -> Either e a +runErrors (Other (Constant e)) = Left e +runErrors (Pure x) = Right x + +-- | Report an error. +failure :: e -> Errors e a +failure e = Other (Constant e) diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Data/Functor/Reverse.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Data/Functor/Reverse.hs new file mode 100644 index 0000000..d8c8957 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.2/Data/Functor/Reverse.hs @@ -0,0 +1,87 @@ +{-# LANGUAGE CPP #-} + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Safe #-} +# endif +# if __GLASGOW_HASKELL__ >= 706 +{-# LANGUAGE PolyKinds #-} +# endif +#endif +-- | +-- Module : Data.Functor.Reverse +-- Copyright : (c) Russell O'Connor 2009 +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : libraries@haskell.org +-- Stability : experimental +-- Portability : portable +-- +-- Making functors whose elements are notionally in the reverse order +-- from the original functor. +-- +-- /NB:/ Note this module is only included in @lens@ for backwards +-- compatibility with older @containers@ versions. + +module Data.Functor.Reverse where + +import Control.Applicative.Backwards +import Data.Functor.Classes + +import Prelude hiding (foldr, foldr1, foldl, foldl1) +import Control.Applicative +import Data.Foldable +import Data.Traversable +import Data.Monoid + +-- | The same functor, but with 'Foldable' and 'Traversable' instances +-- that process the elements in the reverse order. +newtype Reverse f a = Reverse { getReverse :: f a } + +instance (Eq1 f) => Eq1 (Reverse f) where + liftEq eq (Reverse x) (Reverse y) = liftEq eq x y + +instance (Ord1 f) => Ord1 (Reverse f) where + liftCompare comp (Reverse x) (Reverse y) = liftCompare comp x y + +instance (Read1 f) => Read1 (Reverse f) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp rl) "Reverse" Reverse + +instance (Show1 f) => Show1 (Reverse f) where + liftShowsPrec sp sl d (Reverse x) = + showsUnaryWith (liftShowsPrec sp sl) "Reverse" d x + +instance (Eq1 f, Eq a) => Eq (Reverse f a) where (==) = eq1 +instance (Ord1 f, Ord a) => Ord (Reverse f a) where compare = compare1 +instance (Read1 f, Read a) => Read (Reverse f a) where readsPrec = readsPrec1 +instance (Show1 f, Show a) => Show (Reverse f a) where showsPrec = showsPrec1 + +-- | Derived instance. +instance (Functor f) => Functor (Reverse f) where + fmap f (Reverse a) = Reverse (fmap f a) + +-- | Derived instance. +instance (Applicative f) => Applicative (Reverse f) where + pure a = Reverse (pure a) + Reverse f <*> Reverse a = Reverse (f <*> a) + +-- | Derived instance. +instance (Alternative f) => Alternative (Reverse f) where + empty = Reverse empty + Reverse x <|> Reverse y = Reverse (x <|> y) + +-- | Fold from right to left. +instance (Foldable f) => Foldable (Reverse f) where + foldMap f (Reverse t) = getDual (foldMap (Dual . f) t) + foldr f z (Reverse t) = foldl (flip f) z t + foldl f z (Reverse t) = foldr (flip f) z t + foldr1 f (Reverse t) = foldl1 (flip f) t + foldl1 f (Reverse t) = foldr1 (flip f) t + +-- | Traverse from right to left. +instance (Traversable f) => Traversable (Reverse f) where + traverse f (Reverse t) = + fmap Reverse . forwards $ traverse (Backwards . f) t + sequenceA (Reverse t) = + fmap Reverse . forwards $ sequenceA (fmap Backwards t) diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Control/Monad/Signatures.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Control/Monad/Signatures.hs new file mode 100644 index 0000000..e23b6a0 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Control/Monad/Signatures.hs @@ -0,0 +1,59 @@ +{-# LANGUAGE CPP #-} + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Safe #-} +# endif +# if __GLASGOW_HASKELL__ >= 706 +{-# LANGUAGE PolyKinds #-} +# endif +#endif +----------------------------------------------------------------------------- +-- | +-- Module : Control.Monad.Signatures +-- Copyright : (c) Ross Paterson 2012 +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : ross@soi.city.ac.uk +-- Stability : experimental +-- Portability : portable +-- +-- Signatures for monad operations that require specialized lifting. +-- Each signature has a uniformity property that the lifting should satisfy. +----------------------------------------------------------------------------- + +module Control.Monad.Signatures ( + CallCC, Catch, Listen, Pass + ) where + +-- | Signature of the @callCC@ operation, +-- introduced in "Control.Monad.Trans.Cont". +-- Any lifting function @liftCallCC@ should satisfy +-- +-- * @'lift' (f k) = f' ('lift' . k) => 'lift' (cf f) = liftCallCC cf f'@ +-- +type CallCC m a b = ((a -> m b) -> m a) -> m a + +-- | Signature of the @catchE@ operation, +-- introduced in "Control.Monad.Trans.Except". +-- Any lifting function @liftCatch@ should satisfy +-- +-- * @'lift' (cf m f) = liftCatch ('lift' . cf) ('lift' f)@ +-- +type Catch e m a = m a -> (e -> m a) -> m a + +-- | Signature of the @listen@ operation, +-- introduced in "Control.Monad.Trans.Writer". +-- Any lifting function @liftListen@ should satisfy +-- +-- * @'lift' . liftListen = liftListen . 'lift'@ +-- +type Listen w m a = m a -> m (a, w) + +-- | Signature of the @pass@ operation, +-- introduced in "Control.Monad.Trans.Writer". +-- Any lifting function @liftPass@ should satisfy +-- +-- * @'lift' . liftPass = liftPass . 'lift'@ +-- +type Pass w m a = m (a, w -> w) -> m a diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Control/Monad/Trans/Except.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Control/Monad/Trans/Except.hs new file mode 100644 index 0000000..99ed86e --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Control/Monad/Trans/Except.hs @@ -0,0 +1,314 @@ +{-# LANGUAGE CPP #-} + +#ifndef MIN_VERSION_base +#define MIN_VERSION_base(x,y,z) 1 +#endif + +#ifndef MIN_VERSION_mtl +#define MIN_VERSION_mtl(x,y,z) 1 +#endif + +#ifndef HASKELL98 +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE UndecidableInstances #-} +# if __GLASGOW_HASKELL__ >= 704 +{-# LANGUAGE Safe #-} +# elif __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Trustworthy #-} +# endif +#endif +----------------------------------------------------------------------------- +-- | +-- Module : Control.Monad.Trans.Except +-- Copyright : (C) 2013 Ross Paterson +-- (C) 2015 Edward Kmett +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : ross@soi.city.ac.uk +-- Stability : experimental +-- Portability : portable +-- +-- This monad transformer extends a monad with the ability throw exceptions. +-- +-- A sequence of actions terminates normally, producing a value, +-- only if none of the actions in the sequence throws an exception. +-- If one throws an exception, the rest of the sequence is skipped and +-- the composite action exits with that exception. +-- +-- If the value of the exception is not required, the variant in +-- "Control.Monad.Trans.Maybe" may be used instead. +----------------------------------------------------------------------------- + +module Control.Monad.Trans.Except ( + -- * The Except monad + Except, + except, + runExcept, + mapExcept, + withExcept, + -- * The ExceptT monad transformer + ExceptT(..), + mapExceptT, + withExceptT, + -- * Exception operations + throwE, + catchE, + -- * Lifting other operations + liftCallCC, + liftListen, + liftPass, + ) where + +import Control.Applicative +import Control.Monad +import Control.Monad.Fix +import Control.Monad.IO.Class +import Control.Monad.Signatures +import Control.Monad.Trans.Class +#if MIN_VERSION_base(4,4,0) +import Control.Monad.Zip (MonadZip(mzipWith)) +#endif + +#ifndef HASKELL98 +import Control.Monad.Writer.Class +import Control.Monad.State.Class +import Control.Monad.Reader.Class +import Control.Monad.Cont.Class +import Control.Monad.Error.Class +import Control.Monad.RWS.Class +#endif + +import Data.Foldable (Foldable(foldMap)) +import Data.Functor.Classes +import Data.Functor.Identity +import Data.Monoid +import Data.Traversable (Traversable(traverse)) + +-- | The parameterizable exception monad. +-- +-- Computations are either exceptions or normal values. +-- +-- The 'return' function returns a normal value, while @>>=@ exits +-- on the first exception. +type Except e = ExceptT e Identity + +-- | Constructor for computations in the exception monad. +-- (The inverse of 'runExcept'). +except :: Either e a -> Except e a +except m = ExceptT (Identity m) + +-- | Extractor for computations in the exception monad. +-- (The inverse of 'except'). +runExcept :: Except e a -> Either e a +runExcept (ExceptT m) = runIdentity m + +-- | Map the unwrapped computation using the given function. +-- +-- * @'runExcept' ('mapExcept' f m) = f ('runExcept' m)@ +mapExcept :: (Either e a -> Either e' b) + -> Except e a + -> Except e' b +mapExcept f = mapExceptT (Identity . f . runIdentity) + +-- | Transform any exceptions thrown by the computation using the given +-- function (a specialization of 'withExceptT'). +withExcept :: (e -> e') -> Except e a -> Except e' a +withExcept = withExceptT + +-- | A monad transformer that adds exceptions to other monads. +-- +-- @ExceptT@ constructs a monad parameterized over two things: +-- +-- * e - The exception type. +-- +-- * m - The inner monad. +-- +-- The 'return' function yields a computation that produces the given +-- value, while @>>=@ sequences two subcomputations, exiting on the +-- first exception. +newtype ExceptT e m a = ExceptT { runExceptT :: m (Either e a) } + +instance (Eq e, Eq1 m) => Eq1 (ExceptT e m) where + liftEq eq (ExceptT x) (ExceptT y) = liftEq (liftEq eq) x y + +instance (Ord e, Ord1 m) => Ord1 (ExceptT e m) where + liftCompare comp (ExceptT x) (ExceptT y) = + liftCompare (liftCompare comp) x y + +instance (Read e, Read1 m) => Read1 (ExceptT e m) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp' rl') "ExceptT" ExceptT + where + rp' = liftReadsPrec rp rl + rl' = liftReadList rp rl + +instance (Show e, Show1 m) => Show1 (ExceptT e m) where + liftShowsPrec sp sl d (ExceptT m) = + showsUnaryWith (liftShowsPrec sp' sl') "ExceptT" d m + where + sp' = liftShowsPrec sp sl + sl' = liftShowList sp sl + +instance (Eq e, Eq1 m, Eq a) => Eq (ExceptT e m a) where (==) = eq1 +instance (Ord e, Ord1 m, Ord a) => Ord (ExceptT e m a) where compare = compare1 +instance (Read e, Read1 m, Read a) => Read (ExceptT e m a) where + readsPrec = readsPrec1 +instance (Show e, Show1 m, Show a) => Show (ExceptT e m a) where + showsPrec = showsPrec1 + +-- | Map the unwrapped computation using the given function. +-- +-- * @'runExceptT' ('mapExceptT' f m) = f ('runExceptT' m)@ +mapExceptT :: (m (Either e a) -> n (Either e' b)) + -> ExceptT e m a + -> ExceptT e' n b +mapExceptT f m = ExceptT $ f (runExceptT m) + +-- | Transform any exceptions thrown by the computation using the +-- given function. +withExceptT :: (Functor m) => (e -> e') -> ExceptT e m a -> ExceptT e' m a +withExceptT f = mapExceptT $ fmap $ either (Left . f) Right + +instance (Functor m) => Functor (ExceptT e m) where + fmap f = ExceptT . fmap (fmap f) . runExceptT + +instance (Foldable f) => Foldable (ExceptT e f) where + foldMap f (ExceptT a) = foldMap (either (const mempty) f) a + +instance (Traversable f) => Traversable (ExceptT e f) where + traverse f (ExceptT a) = + ExceptT <$> traverse (either (pure . Left) (fmap Right . f)) a + +instance (Functor m, Monad m) => Applicative (ExceptT e m) where + pure a = ExceptT $ return (Right a) + ExceptT f <*> ExceptT v = ExceptT $ do + mf <- f + case mf of + Left e -> return (Left e) + Right k -> do + mv <- v + case mv of + Left e -> return (Left e) + Right x -> return (Right (k x)) + +instance (Functor m, Monad m, Monoid e) => Alternative (ExceptT e m) where + empty = ExceptT $ return (Left mempty) + ExceptT mx <|> ExceptT my = ExceptT $ do + ex <- mx + case ex of + Left e -> liftM (either (Left . mappend e) Right) my + Right x -> return (Right x) + +instance (Monad m) => Monad (ExceptT e m) where + return a = ExceptT $ return (Right a) + m >>= k = ExceptT $ do + a <- runExceptT m + case a of + Left e -> return (Left e) + Right x -> runExceptT (k x) + fail = ExceptT . fail + +instance (Monad m, Monoid e) => MonadPlus (ExceptT e m) where + mzero = ExceptT $ return (Left mempty) + ExceptT m `mplus` ExceptT n = ExceptT $ do + a <- m + case a of + Left e -> liftM (either (Left . mappend e) Right) n + Right x -> return (Right x) + +instance (MonadFix m) => MonadFix (ExceptT e m) where + mfix f = ExceptT (mfix (runExceptT . f . either (const bomb) id)) + where bomb = error "mfix (ExceptT): inner computation returned Left value" + +instance MonadTrans (ExceptT e) where + lift = ExceptT . liftM Right + +instance (MonadIO m) => MonadIO (ExceptT e m) where + liftIO = lift . liftIO + +#if MIN_VERSION_base(4,4,0) +instance (MonadZip m) => MonadZip (ExceptT e m) where + mzipWith f (ExceptT a) (ExceptT b) = ExceptT $ mzipWith (liftA2 f) a b +#endif + +-- | Signal an exception value @e@. +-- +-- * @'runExceptT' ('throwE' e) = 'return' ('Left' e)@ +-- +-- * @'throwE' e >>= m = 'throwE' e@ +throwE :: (Monad m) => e -> ExceptT e m a +throwE = ExceptT . return . Left + +-- | Handle an exception. +-- +-- * @'catchE' h ('lift' m) = 'lift' m@ +-- +-- * @'catchE' h ('throwE' e) = h e@ +catchE :: (Monad m) => + ExceptT e m a -- ^ the inner computation + -> (e -> ExceptT e' m a) -- ^ a handler for exceptions in the inner + -- computation + -> ExceptT e' m a +m `catchE` h = ExceptT $ do + a <- runExceptT m + case a of + Left l -> runExceptT (h l) + Right r -> return (Right r) + +-- | Lift a @callCC@ operation to the new monad. +liftCallCC :: CallCC m (Either e a) (Either e b) -> CallCC (ExceptT e m) a b +liftCallCC callCC f = ExceptT $ + callCC $ \ c -> + runExceptT (f (\ a -> ExceptT $ c (Right a))) + +-- | Lift a @listen@ operation to the new monad. +liftListen :: (Monad m) => Listen w m (Either e a) -> Listen w (ExceptT e m) a +liftListen listen = mapExceptT $ \ m -> do + (a, w) <- listen m + return $! fmap (\ r -> (r, w)) a + +-- | Lift a @pass@ operation to the new monad. +liftPass :: (Monad m) => Pass w m (Either e a) -> Pass w (ExceptT e m) a +liftPass pass = mapExceptT $ \ m -> pass $ do + a <- m + return $! case a of + Left l -> (Left l, id) + Right (r, f) -> (Right r, f) + +-- incurring the mtl dependency for these avoids packages that need them introducing orphans. + +#ifndef HASKELL98 + +instance Monad m => MonadError e (ExceptT e m) where + throwError = throwE + catchError = catchE + +instance MonadWriter w m => MonadWriter w (ExceptT e m) where + tell = lift . tell + listen = liftListen listen + pass = liftPass pass +#if MIN_VERSION_mtl(2,1,0) + writer = lift . writer +#endif + +instance MonadState s m => MonadState s (ExceptT e m) where + get = lift get + put = lift . put +#if MIN_VERSION_mtl(2,1,0) + state = lift . state +#endif + +instance MonadReader r m => MonadReader r (ExceptT e m) where + ask = lift ask + local = mapExceptT . local +#if MIN_VERSION_mtl(2,1,0) + reader = lift . reader +#endif + +instance MonadRWS r w s m => MonadRWS r w s (ExceptT e m) + +instance MonadCont m => MonadCont (ExceptT e m) where + callCC = liftCallCC callCC + +#endif diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Data/Functor/Classes.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Data/Functor/Classes.hs new file mode 100644 index 0000000..5baddd8 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Data/Functor/Classes.hs @@ -0,0 +1,839 @@ +{-# LANGUAGE CPP #-} + +#ifndef MIN_VERSION_transformers +#define MIN_VERSION_transformers(a,b,c) 1 +#endif + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 704 +{-# LANGUAGE Safe #-} +# elif __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Trustworthy #-} +# endif +# if __GLASGOW_HASKELL__ >= 708 +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE StandaloneDeriving #-} +# endif +#endif +----------------------------------------------------------------------------- +-- | +-- Module : Data.Functor.Classes +-- Copyright : (c) Ross Paterson 2013, Edward Kmett 2014 +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : R.Paterson@city.ac.uk +-- Stability : experimental +-- Portability : portable +-- +-- Liftings of the Prelude classes 'Eq', 'Ord', 'Read' and 'Show' to +-- unary and binary type constructors. +-- +-- These classes are needed to express the constraints on arguments of +-- transformers in portable Haskell. Thus for a new transformer @T@, +-- one might write instances like +-- +-- > instance (Eq1 f) => Eq1 (T f) where ... +-- > instance (Ord1 f) => Ord1 (T f) where ... +-- > instance (Read1 f) => Read1 (T f) where ... +-- > instance (Show1 f) => Show1 (T f) where ... +-- +-- If these instances can be defined, defining instances of the base +-- classes is mechanical: +-- +-- > instance (Eq1 f, Eq a) => Eq (T f a) where (==) = eq1 +-- > instance (Ord1 f, Ord a) => Ord (T f a) where compare = compare1 +-- > instance (Read1 f, Read a) => Read (T f a) where readsPrec = readsPrec1 +-- > instance (Show1 f, Show a) => Show (T f a) where showsPrec = showsPrec1 +-- +----------------------------------------------------------------------------- + +module Data.Functor.Classes ( + -- * Liftings of Prelude classes + -- ** For unary constructors + Eq1(..), eq1, + Ord1(..), compare1, + Read1(..), readsPrec1, + Show1(..), showsPrec1, + -- ** For binary constructors + Eq2(..), eq2, + Ord2(..), compare2, + Read2(..), readsPrec2, + Show2(..), showsPrec2, + -- * Helper functions + -- $example + readsData, + readsUnaryWith, + readsBinaryWith, + showsUnaryWith, + showsBinaryWith, + -- ** Obsolete helpers + readsUnary, + readsUnary1, + readsBinary1, + showsUnary, + showsUnary1, + showsBinary1, + ) where + +import Control.Applicative (Const(Const)) +import Data.Functor.Identity (Identity(Identity)) +import Data.Monoid (mappend) +import Text.Show (showListWith) + +import Control.Monad.Trans.Error +import Control.Monad.Trans.Identity +import Control.Monad.Trans.List +import Control.Monad.Trans.Maybe +import Control.Monad.Trans.Writer.Lazy as Lazy +import Control.Monad.Trans.Writer.Strict as Strict +import Data.Functor.Compose +import Data.Functor.Constant +import Data.Functor.Product + +#if MIN_VERSION_transformers(0,3,0) +import Control.Applicative.Lift +import Control.Applicative.Backwards +import Data.Functor.Reverse +#endif + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 708 +import Data.Typeable +# endif +#endif + +-- | Lifting of the 'Eq' class to unary type constructors. +class Eq1 f where + -- | Lift an equality test through the type constructor. + -- + -- The function will usually be applied to an equality function, + -- but the more general type ensures that the implementation uses + -- it to compare elements of the first container with elements of + -- the second. + liftEq :: (a -> b -> Bool) -> f a -> f b -> Bool + +-- | Lift the standard @('==')@ function through the type constructor. +eq1 :: (Eq1 f, Eq a) => f a -> f a -> Bool +eq1 = liftEq (==) + +-- | Lifting of the 'Ord' class to unary type constructors. +class (Eq1 f) => Ord1 f where + -- | Lift a 'compare' function through the type constructor. + -- + -- The function will usually be applied to a comparison function, + -- but the more general type ensures that the implementation uses + -- it to compare elements of the first container with elements of + -- the second. + liftCompare :: (a -> b -> Ordering) -> f a -> f b -> Ordering + +-- | Lift the standard 'compare' function through the type constructor. +compare1 :: (Ord1 f, Ord a) => f a -> f a -> Ordering +compare1 = liftCompare compare + +-- | Lifting of the 'Read' class to unary type constructors. +class Read1 f where + -- | 'readsPrec' function for an application of the type constructor + -- based on 'readsPrec' and 'readList' functions for the argument type. + liftReadsPrec :: (Int -> ReadS a) -> ReadS [a] -> Int -> ReadS (f a) + + -- | 'readList' function for an application of the type constructor + -- based on 'readsPrec' and 'readList' functions for the argument type. + -- The default implementation using standard list syntax is correct + -- for most types. + liftReadList :: (Int -> ReadS a) -> ReadS [a] -> ReadS [f a] + liftReadList rp rl = readListWith (liftReadsPrec rp rl 0) + +-- | Read a list (using square brackets and commas), given a function +-- for reading elements. +readListWith :: ReadS a -> ReadS [a] +readListWith rp = + readParen False (\r -> [pr | ("[",s) <- lex r, pr <- readl s]) + where + readl s = [([],t) | ("]",t) <- lex s] ++ + [(x:xs,u) | (x,t) <- rp s, (xs,u) <- readl' t] + readl' s = [([],t) | ("]",t) <- lex s] ++ + [(x:xs,v) | (",",t) <- lex s, (x,u) <- rp t, (xs,v) <- readl' u] + +-- | Lift the standard 'readsPrec' and 'readList' functions through the +-- type constructor. +readsPrec1 :: (Read1 f, Read a) => Int -> ReadS (f a) +readsPrec1 = liftReadsPrec readsPrec readList + +-- | Lifting of the 'Show' class to unary type constructors. +class Show1 f where + -- | 'showsPrec' function for an application of the type constructor + -- based on 'showsPrec' and 'showList' functions for the argument type. + liftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> + Int -> f a -> ShowS + + -- | 'showList' function for an application of the type constructor + -- based on 'showsPrec' and 'showList' functions for the argument type. + -- The default implementation using standard list syntax is correct + -- for most types. + liftShowList :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> + [f a] -> ShowS + liftShowList sp sl = showListWith (liftShowsPrec sp sl 0) + +-- | Lift the standard 'showsPrec' and 'showList' functions through the +-- type constructor. +showsPrec1 :: (Show1 f, Show a) => Int -> f a -> ShowS +showsPrec1 = liftShowsPrec showsPrec showList + +-- | Lifting of the 'Eq' class to binary type constructors. +class Eq2 f where + -- | Lift equality tests through the type constructor. + -- + -- The function will usually be applied to equality functions, + -- but the more general type ensures that the implementation uses + -- them to compare elements of the first container with elements of + -- the second. + liftEq2 :: (a -> b -> Bool) -> (c -> d -> Bool) -> f a c -> f b d -> Bool + +-- | Lift the standard @('==')@ function through the type constructor. +eq2 :: (Eq2 f, Eq a, Eq b) => f a b -> f a b -> Bool +eq2 = liftEq2 (==) (==) + +-- | Lifting of the 'Ord' class to binary type constructors. +class (Eq2 f) => Ord2 f where + -- | Lift 'compare' functions through the type constructor. + -- + -- The function will usually be applied to comparison functions, + -- but the more general type ensures that the implementation uses + -- them to compare elements of the first container with elements of + -- the second. + liftCompare2 :: (a -> b -> Ordering) -> (c -> d -> Ordering) -> + f a c -> f b d -> Ordering + +-- | Lift the standard 'compare' function through the type constructor. +compare2 :: (Ord2 f, Ord a, Ord b) => f a b -> f a b -> Ordering +compare2 = liftCompare2 compare compare + +-- | Lifting of the 'Read' class to binary type constructors. +class Read2 f where + -- | 'readsPrec' function for an application of the type constructor + -- based on 'readsPrec' and 'readList' functions for the argument types. + liftReadsPrec2 :: (Int -> ReadS a) -> ReadS [a] -> + (Int -> ReadS b) -> ReadS [b] -> Int -> ReadS (f a b) + + -- | 'readList' function for an application of the type constructor + -- based on 'readsPrec' and 'readList' functions for the argument types. + -- The default implementation using standard list syntax is correct + -- for most types. + liftReadList2 :: (Int -> ReadS a) -> ReadS [a] -> + (Int -> ReadS b) -> ReadS [b] -> ReadS [f a b] + liftReadList2 rp1 rl1 rp2 rl2 = + readListWith (liftReadsPrec2 rp1 rl1 rp2 rl2 0) + +-- | Lift the standard 'readsPrec' function through the type constructor. +readsPrec2 :: (Read2 f, Read a, Read b) => Int -> ReadS (f a b) +readsPrec2 = liftReadsPrec2 readsPrec readList readsPrec readList + +-- | Lifting of the 'Show' class to binary type constructors. +class Show2 f where + -- | 'showsPrec' function for an application of the type constructor + -- based on 'showsPrec' and 'showList' functions for the argument types. + liftShowsPrec2 :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> + (Int -> b -> ShowS) -> ([b] -> ShowS) -> Int -> f a b -> ShowS + + -- | 'showList' function for an application of the type constructor + -- based on 'showsPrec' and 'showList' functions for the argument types. + -- The default implementation using standard list syntax is correct + -- for most types. + liftShowList2 :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> + (Int -> b -> ShowS) -> ([b] -> ShowS) -> [f a b] -> ShowS + liftShowList2 sp1 sl1 sp2 sl2 = + showListWith (liftShowsPrec2 sp1 sl1 sp2 sl2 0) + +-- | Lift the standard 'showsPrec' function through the type constructor. +showsPrec2 :: (Show2 f, Show a, Show b) => Int -> f a b -> ShowS +showsPrec2 = liftShowsPrec2 showsPrec showList showsPrec showList + +-- Instances for Prelude type constructors + +instance Eq1 Maybe where + liftEq _ Nothing Nothing = True + liftEq _ Nothing (Just _) = False + liftEq _ (Just _) Nothing = False + liftEq eq (Just x) (Just y) = eq x y + +instance Ord1 Maybe where + liftCompare _ Nothing Nothing = EQ + liftCompare _ Nothing (Just _) = LT + liftCompare _ (Just _) Nothing = GT + liftCompare comp (Just x) (Just y) = comp x y + +instance Read1 Maybe where + liftReadsPrec rp _ d = + readParen False (\ r -> [(Nothing,s) | ("Nothing",s) <- lex r]) + `mappend` + readsData (readsUnaryWith rp "Just" Just) d + +instance Show1 Maybe where + liftShowsPrec _ _ _ Nothing = showString "Nothing" + liftShowsPrec sp _ d (Just x) = showsUnaryWith sp "Just" d x + +instance Eq1 [] where + liftEq _ [] [] = True + liftEq _ [] (_:_) = False + liftEq _ (_:_) [] = False + liftEq eq (x:xs) (y:ys) = eq x y && liftEq eq xs ys + +instance Ord1 [] where + liftCompare _ [] [] = EQ + liftCompare _ [] (_:_) = LT + liftCompare _ (_:_) [] = GT + liftCompare comp (x:xs) (y:ys) = comp x y `mappend` liftCompare comp xs ys + +instance Read1 [] where + liftReadsPrec _ rl _ = rl + +instance Show1 [] where + liftShowsPrec _ sl _ = sl + +instance Eq2 (,) where + liftEq2 e1 e2 (x1, y1) (x2, y2) = e1 x1 x2 && e2 y1 y2 + +instance Ord2 (,) where + liftCompare2 comp1 comp2 (x1, y1) (x2, y2) = + comp1 x1 x2 `mappend` comp2 y1 y2 + +instance Read2 (,) where + liftReadsPrec2 rp1 _ rp2 _ _ = readParen False $ \ r -> + [((x,y), w) | ("(",s) <- lex r, + (x,t) <- rp1 0 s, + (",",u) <- lex t, + (y,v) <- rp2 0 u, + (")",w) <- lex v] + +instance Show2 (,) where + liftShowsPrec2 sp1 _ sp2 _ _ (x, y) = + showChar '(' . sp1 0 x . showChar ',' . sp2 0 y . showChar ')' + +instance (Eq a) => Eq1 ((,) a) where + liftEq = liftEq2 (==) + +instance (Ord a) => Ord1 ((,) a) where + liftCompare = liftCompare2 compare + +instance (Read a) => Read1 ((,) a) where + liftReadsPrec = liftReadsPrec2 readsPrec readList + +instance (Show a) => Show1 ((,) a) where + liftShowsPrec = liftShowsPrec2 showsPrec showList + +instance Eq2 Either where + liftEq2 e1 _ (Left x) (Left y) = e1 x y + liftEq2 _ _ (Left _) (Right _) = False + liftEq2 _ _ (Right _) (Left _) = False + liftEq2 _ e2 (Right x) (Right y) = e2 x y + +instance Ord2 Either where + liftCompare2 comp1 _ (Left x) (Left y) = comp1 x y + liftCompare2 _ _ (Left _) (Right _) = LT + liftCompare2 _ _ (Right _) (Left _) = GT + liftCompare2 _ comp2 (Right x) (Right y) = comp2 x y + +instance Read2 Either where + liftReadsPrec2 rp1 _ rp2 _ = readsData $ + readsUnaryWith rp1 "Left" Left `mappend` + readsUnaryWith rp2 "Right" Right + +instance Show2 Either where + liftShowsPrec2 sp1 _ _ _ d (Left x) = showsUnaryWith sp1 "Left" d x + liftShowsPrec2 _ _ sp2 _ d (Right x) = showsUnaryWith sp2 "Right" d x + +instance (Eq a) => Eq1 (Either a) where + liftEq = liftEq2 (==) + +instance (Ord a) => Ord1 (Either a) where + liftCompare = liftCompare2 compare + +instance (Read a) => Read1 (Either a) where + liftReadsPrec = liftReadsPrec2 readsPrec readList + +instance (Show a) => Show1 (Either a) where + liftShowsPrec = liftShowsPrec2 showsPrec showList + +-- Instances for other functors defined in the base package + +instance Eq1 Identity where + liftEq eq (Identity x) (Identity y) = eq x y + +instance Ord1 Identity where + liftCompare comp (Identity x) (Identity y) = comp x y + +instance Read1 Identity where + liftReadsPrec rp _ = readsData $ + readsUnaryWith rp "Identity" Identity + +instance Show1 Identity where + liftShowsPrec sp _ d (Identity x) = showsUnaryWith sp "Identity" d x + +instance Eq2 Const where + liftEq2 eq _ (Const x) (Const y) = eq x y + +instance Ord2 Const where + liftCompare2 comp _ (Const x) (Const y) = comp x y + +instance Read2 Const where + liftReadsPrec2 rp _ _ _ = readsData $ + readsUnaryWith rp "Const" Const + +instance Show2 Const where + liftShowsPrec2 sp _ _ _ d (Const x) = showsUnaryWith sp "Const" d x + +instance (Eq a) => Eq1 (Const a) where + liftEq = liftEq2 (==) +instance (Ord a) => Ord1 (Const a) where + liftCompare = liftCompare2 compare +instance (Read a) => Read1 (Const a) where + liftReadsPrec = liftReadsPrec2 readsPrec readList +instance (Show a) => Show1 (Const a) where + liftShowsPrec = liftShowsPrec2 showsPrec showList + +-- Building blocks + +-- | @'readsData' p d@ is a parser for datatypes where each alternative +-- begins with a data constructor. It parses the constructor and +-- passes it to @p@. Parsers for various constructors can be constructed +-- with 'readsUnary', 'readsUnary1' and 'readsBinary1', and combined with +-- @mappend@ from the @Monoid@ class. +readsData :: (String -> ReadS a) -> Int -> ReadS a +readsData reader d = + readParen (d > 10) $ \ r -> [res | (kw,s) <- lex r, res <- reader kw s] + +-- | @'readsUnaryWith' rp n c n'@ matches the name of a unary data constructor +-- and then parses its argument using @rp@. +readsUnaryWith :: (Int -> ReadS a) -> String -> (a -> t) -> String -> ReadS t +readsUnaryWith rp name cons kw s = + [(cons x,t) | kw == name, (x,t) <- rp 11 s] + +-- | @'readsBinaryWith' rp1 rp2 n c n'@ matches the name of a binary +-- data constructor and then parses its arguments using @rp1@ and @rp2@ +-- respectively. +readsBinaryWith :: (Int -> ReadS a) -> (Int -> ReadS b) -> + String -> (a -> b -> t) -> String -> ReadS t +readsBinaryWith rp1 rp2 name cons kw s = + [(cons x y,u) | kw == name, (x,t) <- rp1 11 s, (y,u) <- rp2 11 t] + +-- | @'showsUnaryWith' sp n d x@ produces the string representation of a +-- unary data constructor with name @n@ and argument @x@, in precedence +-- context @d@. +showsUnaryWith :: (Int -> a -> ShowS) -> String -> Int -> a -> ShowS +showsUnaryWith sp name d x = showParen (d > 10) $ + showString name . showChar ' ' . sp 11 x + +-- | @'showsBinaryWith' sp1 sp2 n d x y@ produces the string +-- representation of a binary data constructor with name @n@ and arguments +-- @x@ and @y@, in precedence context @d@. +showsBinaryWith :: (Int -> a -> ShowS) -> (Int -> b -> ShowS) -> + String -> Int -> a -> b -> ShowS +showsBinaryWith sp1 sp2 name d x y = showParen (d > 10) $ + showString name . showChar ' ' . sp1 11 x . showChar ' ' . sp2 11 y + +-- Obsolete building blocks + +-- | @'readsUnary' n c n'@ matches the name of a unary data constructor +-- and then parses its argument using 'readsPrec'. +{-# DEPRECATED readsUnary "Use readsUnaryWith to define liftReadsPrec" #-} +readsUnary :: (Read a) => String -> (a -> t) -> String -> ReadS t +readsUnary name cons kw s = + [(cons x,t) | kw == name, (x,t) <- readsPrec 11 s] + +-- | @'readsUnary1' n c n'@ matches the name of a unary data constructor +-- and then parses its argument using 'readsPrec1'. +{-# DEPRECATED readsUnary1 "Use readsUnaryWith to define liftReadsPrec" #-} +readsUnary1 :: (Read1 f, Read a) => String -> (f a -> t) -> String -> ReadS t +readsUnary1 name cons kw s = + [(cons x,t) | kw == name, (x,t) <- readsPrec1 11 s] + +-- | @'readsBinary1' n c n'@ matches the name of a binary data constructor +-- and then parses its arguments using 'readsPrec1'. +{-# DEPRECATED readsBinary1 "Use readsBinaryWith to define liftReadsPrec" #-} +readsBinary1 :: (Read1 f, Read1 g, Read a) => + String -> (f a -> g a -> t) -> String -> ReadS t +readsBinary1 name cons kw s = + [(cons x y,u) | kw == name, + (x,t) <- readsPrec1 11 s, (y,u) <- readsPrec1 11 t] + +-- | @'showsUnary' n d x@ produces the string representation of a unary data +-- constructor with name @n@ and argument @x@, in precedence context @d@. +{-# DEPRECATED showsUnary "Use showsUnaryWith to define liftShowsPrec" #-} +showsUnary :: (Show a) => String -> Int -> a -> ShowS +showsUnary name d x = showParen (d > 10) $ + showString name . showChar ' ' . showsPrec 11 x + +-- | @'showsUnary1' n d x@ produces the string representation of a unary data +-- constructor with name @n@ and argument @x@, in precedence context @d@. +{-# DEPRECATED showsUnary1 "Use showsUnaryWith to define liftShowsPrec" #-} +showsUnary1 :: (Show1 f, Show a) => String -> Int -> f a -> ShowS +showsUnary1 name d x = showParen (d > 10) $ + showString name . showChar ' ' . showsPrec1 11 x + +-- | @'showsBinary1' n d x y@ produces the string representation of a binary +-- data constructor with name @n@ and arguments @x@ and @y@, in precedence +-- context @d@. +{-# DEPRECATED showsBinary1 "Use showsBinaryWith to define liftShowsPrec" #-} +showsBinary1 :: (Show1 f, Show1 g, Show a) => + String -> Int -> f a -> g a -> ShowS +showsBinary1 name d x y = showParen (d > 10) $ + showString name . showChar ' ' . showsPrec1 11 x . + showChar ' ' . showsPrec1 11 y + + +instance (Eq e, Eq1 m) => Eq1 (ErrorT e m) where + liftEq eq (ErrorT x) (ErrorT y) = liftEq (liftEq eq) x y + +instance (Ord e, Ord1 m) => Ord1 (ErrorT e m) where + liftCompare comp (ErrorT x) (ErrorT y) = liftCompare (liftCompare comp) x y + +instance (Read e, Read1 m) => Read1 (ErrorT e m) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp' rl') "ErrorT" ErrorT + where + rp' = liftReadsPrec rp rl + rl' = liftReadList rp rl + +instance (Show e, Show1 m) => Show1 (ErrorT e m) where + liftShowsPrec sp sl d (ErrorT m) = + showsUnaryWith (liftShowsPrec sp' sl') "ErrorT" d m + where + sp' = liftShowsPrec sp sl + sl' = liftShowList sp sl + +instance (Eq e, Eq1 m, Eq a) => Eq (ErrorT e m a) where (==) = eq1 +instance (Ord e, Ord1 m, Ord a) => Ord (ErrorT e m a) where compare = compare1 +instance (Read e, Read1 m, Read a) => Read (ErrorT e m a) where + readsPrec = readsPrec1 +instance (Show e, Show1 m, Show a) => Show (ErrorT e m a) where + showsPrec = showsPrec1 + +instance (Eq1 f) => Eq1 (IdentityT f) where + liftEq eq (IdentityT x) (IdentityT y) = liftEq eq x y + +instance (Ord1 f) => Ord1 (IdentityT f) where + liftCompare comp (IdentityT x) (IdentityT y) = liftCompare comp x y + +instance (Read1 f) => Read1 (IdentityT f) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp rl) "IdentityT" IdentityT + +instance (Show1 f) => Show1 (IdentityT f) where + liftShowsPrec sp sl d (IdentityT m) = + showsUnaryWith (liftShowsPrec sp sl) "IdentityT" d m + +instance (Eq1 f, Eq a) => Eq (IdentityT f a) where (==) = eq1 +instance (Ord1 f, Ord a) => Ord (IdentityT f a) where compare = compare1 +instance (Read1 f, Read a) => Read (IdentityT f a) where readsPrec = readsPrec1 +instance (Show1 f, Show a) => Show (IdentityT f a) where showsPrec = showsPrec1 + +instance (Eq1 m) => Eq1 (ListT m) where + liftEq eq (ListT x) (ListT y) = liftEq (liftEq eq) x y + +instance (Ord1 m) => Ord1 (ListT m) where + liftCompare comp (ListT x) (ListT y) = liftCompare (liftCompare comp) x y + +instance (Read1 m) => Read1 (ListT m) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp' rl') "ListT" ListT + where + rp' = liftReadsPrec rp rl + rl' = liftReadList rp rl + +instance (Show1 m) => Show1 (ListT m) where + liftShowsPrec sp sl d (ListT m) = + showsUnaryWith (liftShowsPrec sp' sl') "ListT" d m + where + sp' = liftShowsPrec sp sl + sl' = liftShowList sp sl + +instance (Eq1 m, Eq a) => Eq (ListT m a) where (==) = eq1 +instance (Ord1 m, Ord a) => Ord (ListT m a) where compare = compare1 +instance (Read1 m, Read a) => Read (ListT m a) where readsPrec = readsPrec1 +instance (Show1 m, Show a) => Show (ListT m a) where showsPrec = showsPrec1 + +instance (Eq1 m) => Eq1 (MaybeT m) where + liftEq eq (MaybeT x) (MaybeT y) = liftEq (liftEq eq) x y + +instance (Ord1 m) => Ord1 (MaybeT m) where + liftCompare comp (MaybeT x) (MaybeT y) = liftCompare (liftCompare comp) x y + +instance (Read1 m) => Read1 (MaybeT m) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp' rl') "MaybeT" MaybeT + where + rp' = liftReadsPrec rp rl + rl' = liftReadList rp rl + +instance (Show1 m) => Show1 (MaybeT m) where + liftShowsPrec sp sl d (MaybeT m) = + showsUnaryWith (liftShowsPrec sp' sl') "MaybeT" d m + where + sp' = liftShowsPrec sp sl + sl' = liftShowList sp sl + +instance (Eq1 m, Eq a) => Eq (MaybeT m a) where (==) = eq1 +instance (Ord1 m, Ord a) => Ord (MaybeT m a) where compare = compare1 +instance (Read1 m, Read a) => Read (MaybeT m a) where readsPrec = readsPrec1 +instance (Show1 m, Show a) => Show (MaybeT m a) where showsPrec = showsPrec1 + +instance (Eq w, Eq1 m) => Eq1 (Lazy.WriterT w m) where + liftEq eq (Lazy.WriterT m1) (Lazy.WriterT m2) = + liftEq (liftEq2 eq (==)) m1 m2 + +instance (Ord w, Ord1 m) => Ord1 (Lazy.WriterT w m) where + liftCompare comp (Lazy.WriterT m1) (Lazy.WriterT m2) = + liftCompare (liftCompare2 comp compare) m1 m2 + +instance (Read w, Read1 m) => Read1 (Lazy.WriterT w m) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp' rl') "WriterT" Lazy.WriterT + where + rp' = liftReadsPrec2 rp rl readsPrec readList + rl' = liftReadList2 rp rl readsPrec readList + +instance (Show w, Show1 m) => Show1 (Lazy.WriterT w m) where + liftShowsPrec sp sl d (Lazy.WriterT m) = + showsUnaryWith (liftShowsPrec sp' sl') "WriterT" d m + where + sp' = liftShowsPrec2 sp sl showsPrec showList + sl' = liftShowList2 sp sl showsPrec showList + +instance (Eq w, Eq1 m, Eq a) => Eq (Lazy.WriterT w m a) where + (==) = eq1 +instance (Ord w, Ord1 m, Ord a) => Ord (Lazy.WriterT w m a) where + compare = compare1 +instance (Read w, Read1 m, Read a) => Read (Lazy.WriterT w m a) where + readsPrec = readsPrec1 +instance (Show w, Show1 m, Show a) => Show (Lazy.WriterT w m a) where + showsPrec = showsPrec1 + +instance (Eq w, Eq1 m) => Eq1 (Strict.WriterT w m) where + liftEq eq (Strict.WriterT m1) (Strict.WriterT m2) = + liftEq (liftEq2 eq (==)) m1 m2 + +instance (Ord w, Ord1 m) => Ord1 (Strict.WriterT w m) where + liftCompare comp (Strict.WriterT m1) (Strict.WriterT m2) = + liftCompare (liftCompare2 comp compare) m1 m2 + +instance (Read w, Read1 m) => Read1 (Strict.WriterT w m) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp' rl') "WriterT" Strict.WriterT + where + rp' = liftReadsPrec2 rp rl readsPrec readList + rl' = liftReadList2 rp rl readsPrec readList + +instance (Show w, Show1 m) => Show1 (Strict.WriterT w m) where + liftShowsPrec sp sl d (Strict.WriterT m) = + showsUnaryWith (liftShowsPrec sp' sl') "WriterT" d m + where + sp' = liftShowsPrec2 sp sl showsPrec showList + sl' = liftShowList2 sp sl showsPrec showList + +instance (Eq w, Eq1 m, Eq a) => Eq (Strict.WriterT w m a) where + (==) = eq1 +instance (Ord w, Ord1 m, Ord a) => Ord (Strict.WriterT w m a) where + compare = compare1 +instance (Read w, Read1 m, Read a) => Read (Strict.WriterT w m a) where + readsPrec = readsPrec1 +instance (Show w, Show1 m, Show a) => Show (Strict.WriterT w m a) where + showsPrec = showsPrec1 + +instance (Eq1 f, Eq1 g) => Eq1 (Compose f g) where + liftEq eq (Compose x) (Compose y) = liftEq (liftEq eq) x y + +instance (Ord1 f, Ord1 g) => Ord1 (Compose f g) where + liftCompare comp (Compose x) (Compose y) = + liftCompare (liftCompare comp) x y + +instance (Read1 f, Read1 g) => Read1 (Compose f g) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp' rl') "Compose" Compose + where + rp' = liftReadsPrec rp rl + rl' = liftReadList rp rl + +instance (Show1 f, Show1 g) => Show1 (Compose f g) where + liftShowsPrec sp sl d (Compose x) = + showsUnaryWith (liftShowsPrec sp' sl') "Compose" d x + where + sp' = liftShowsPrec sp sl + sl' = liftShowList sp sl + +instance (Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where + (==) = eq1 +instance (Ord1 f, Ord1 g, Ord a) => Ord (Compose f g a) where + compare = compare1 +instance (Read1 f, Read1 g, Read a) => Read (Compose f g a) where + readsPrec = readsPrec1 +instance (Show1 f, Show1 g, Show a) => Show (Compose f g a) where + showsPrec = showsPrec1 + +instance (Eq1 f, Eq1 g) => Eq1 (Product f g) where + liftEq eq (Pair x1 y1) (Pair x2 y2) = liftEq eq x1 x2 && liftEq eq y1 y2 + +instance (Ord1 f, Ord1 g) => Ord1 (Product f g) where + liftCompare comp (Pair x1 y1) (Pair x2 y2) = + liftCompare comp x1 x2 `mappend` liftCompare comp y1 y2 + +instance (Read1 f, Read1 g) => Read1 (Product f g) where + liftReadsPrec rp rl = readsData $ + readsBinaryWith (liftReadsPrec rp rl) (liftReadsPrec rp rl) "Pair" Pair + +instance (Show1 f, Show1 g) => Show1 (Product f g) where + liftShowsPrec sp sl d (Pair x y) = + showsBinaryWith (liftShowsPrec sp sl) (liftShowsPrec sp sl) "Pair" d x y + +instance (Eq1 f, Eq1 g, Eq a) => Eq (Product f g a) + where (==) = eq1 +instance (Ord1 f, Ord1 g, Ord a) => Ord (Product f g a) where + compare = compare1 +instance (Read1 f, Read1 g, Read a) => Read (Product f g a) where + readsPrec = readsPrec1 +instance (Show1 f, Show1 g, Show a) => Show (Product f g a) where + showsPrec = showsPrec1 + +instance Eq2 Constant where + liftEq2 eq _ (Constant x) (Constant y) = eq x y +instance Ord2 Constant where + liftCompare2 comp _ (Constant x) (Constant y) = comp x y +instance Read2 Constant where + liftReadsPrec2 rp _ _ _ = readsData $ + readsUnaryWith rp "Constant" Constant +instance Show2 Constant where + liftShowsPrec2 sp _ _ _ d (Constant x) = showsUnaryWith sp "Constant" d x + +instance (Eq a) => Eq1 (Constant a) where + liftEq = liftEq2 (==) +instance (Ord a) => Ord1 (Constant a) where + liftCompare = liftCompare2 compare +instance (Read a) => Read1 (Constant a) where + liftReadsPrec = liftReadsPrec2 readsPrec readList +instance (Show a) => Show1 (Constant a) where + liftShowsPrec = liftShowsPrec2 showsPrec showList + +instance Eq a => Eq (Constant a b) where + Constant a == Constant b = a == b +instance Ord a => Ord (Constant a b) where + compare (Constant a) (Constant b) = compare a b +instance (Read a) => Read (Constant a b) where + readsPrec = readsData $ + readsUnaryWith readsPrec "Constant" Constant +instance (Show a) => Show (Constant a b) where + showsPrec d (Constant x) = showsUnaryWith showsPrec "Constant" d x + +instance Show a => Show (Identity a) where + showsPrec d (Identity a) = showParen (d > 10) $ + showString "Identity " . showsPrec 11 a +instance Read a => Read (Identity a) where + readsPrec d = readParen (d > 10) (\r -> [(Identity m,t) | ("Identity",s) <- lex r, (m,t) <- readsPrec 11 s]) +instance Eq a => Eq (Identity a) where + Identity a == Identity b = a == b +instance Ord a => Ord (Identity a) where + compare (Identity a) (Identity b) = compare a b + +#if MIN_VERSION_transformers(0,3,0) +instance (Eq1 f) => Eq1 (Lift f) where + liftEq eq (Pure x1) (Pure x2) = eq x1 x2 + liftEq _ (Pure _) (Other _) = False + liftEq _ (Other _) (Pure _) = False + liftEq eq (Other y1) (Other y2) = liftEq eq y1 y2 + +instance (Ord1 f) => Ord1 (Lift f) where + liftCompare comp (Pure x1) (Pure x2) = comp x1 x2 + liftCompare _ (Pure _) (Other _) = LT + liftCompare _ (Other _) (Pure _) = GT + liftCompare comp (Other y1) (Other y2) = liftCompare comp y1 y2 + +instance (Read1 f) => Read1 (Lift f) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith rp "Pure" Pure `mappend` + readsUnaryWith (liftReadsPrec rp rl) "Other" Other + +instance (Show1 f) => Show1 (Lift f) where + liftShowsPrec sp _ d (Pure x) = showsUnaryWith sp "Pure" d x + liftShowsPrec sp sl d (Other y) = + showsUnaryWith (liftShowsPrec sp sl) "Other" d y + +instance (Eq1 f, Eq a) => Eq (Lift f a) where (==) = eq1 +instance (Ord1 f, Ord a) => Ord (Lift f a) where compare = compare1 +instance (Read1 f, Read a) => Read (Lift f a) where readsPrec = readsPrec1 +instance (Show1 f, Show a) => Show (Lift f a) where showsPrec = showsPrec1 + +instance (Eq1 f) => Eq1 (Backwards f) where + liftEq eq (Backwards x) (Backwards y) = liftEq eq x y + +instance (Ord1 f) => Ord1 (Backwards f) where + liftCompare comp (Backwards x) (Backwards y) = liftCompare comp x y + +instance (Read1 f) => Read1 (Backwards f) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp rl) "Backwards" Backwards + +instance (Show1 f) => Show1 (Backwards f) where + liftShowsPrec sp sl d (Backwards x) = + showsUnaryWith (liftShowsPrec sp sl) "Backwards" d x + +instance (Eq1 f, Eq a) => Eq (Backwards f a) where (==) = eq1 +instance (Ord1 f, Ord a) => Ord (Backwards f a) where compare = compare1 +instance (Read1 f, Read a) => Read (Backwards f a) where readsPrec = readsPrec1 +instance (Show1 f, Show a) => Show (Backwards f a) where showsPrec = showsPrec1 + +instance (Eq1 f) => Eq1 (Reverse f) where + liftEq eq (Reverse x) (Reverse y) = liftEq eq x y + +instance (Ord1 f) => Ord1 (Reverse f) where + liftCompare comp (Reverse x) (Reverse y) = liftCompare comp x y + +instance (Read1 f) => Read1 (Reverse f) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp rl) "Reverse" Reverse + +instance (Show1 f) => Show1 (Reverse f) where + liftShowsPrec sp sl d (Reverse x) = + showsUnaryWith (liftShowsPrec sp sl) "Reverse" d x + +instance (Eq1 f, Eq a) => Eq (Reverse f a) where (==) = eq1 +instance (Ord1 f, Ord a) => Ord (Reverse f a) where compare = compare1 +instance (Read1 f, Read a) => Read (Reverse f a) where readsPrec = readsPrec1 +instance (Show1 f, Show a) => Show (Reverse f a) where showsPrec = showsPrec1 +#endif + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 708 +deriving instance Typeable Eq1 +deriving instance Typeable Eq2 +deriving instance Typeable Ord1 +deriving instance Typeable Ord2 +deriving instance Typeable Read1 +deriving instance Typeable Read2 +deriving instance Typeable Show1 +deriving instance Typeable Show2 +# endif +#endif + +{- $example +These functions can be used to assemble 'Read' and 'Show' instances for +new algebraic types. For example, given the definition + +> data T f a = Zero a | One (f a) | Two a (f a) + +a standard 'Read1' instance may be defined as + +> instance (Read1 f) => Read1 (T f) where +> liftReadsPrec rp rl = readsData $ +> readsUnaryWith rp "Zero" Zero `mappend` +> readsUnaryWith (liftReadsPrec rp rl) "One" One `mappend` +> readsBinaryWith rp (liftReadsPrec rp rl) "Two" Two + +and the corresponding 'Show1' instance as + +> instance (Show1 f) => Show1 (T f) where +> liftShowsPrec sp _ d (Zero x) = +> showsUnaryWith sp "Zero" d x +> liftShowsPrec sp sl d (One x) = +> showsUnaryWith (liftShowsPrec sp sl) "One" d x +> liftShowsPrec sp sl d (Two x y) = +> showsBinaryWith sp (liftShowsPrec sp sl) "Two" d x y + +-} diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Data/Functor/Sum.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Data/Functor/Sum.hs new file mode 100644 index 0000000..18e28cc --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/0.3/Data/Functor/Sum.hs @@ -0,0 +1,133 @@ +{-# LANGUAGE CPP #-} + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE EmptyDataDecls #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +# endif +# if __GLASGOW_HASKELL__ >= 706 +{-# LANGUAGE PolyKinds #-} +# endif +# if __GLASGOW_HASKELL__ >= 708 +{-# LANGUAGE AutoDeriveTypeable #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE KindSignatures #-} +# endif +#endif +-- | +-- Module : Data.Functor.Sum +-- Copyright : (c) Ross Paterson 2014 +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : ross@soi.city.ac.uk +-- Stability : experimental +-- Portability : portable +-- +-- Sums, lifted to functors. + +module Data.Functor.Sum ( + Sum(..), + ) where + +import Control.Applicative +import Data.Foldable (Foldable(foldMap)) +import Data.Functor.Classes +import Data.Monoid (mappend) +import Data.Traversable (Traversable(traverse)) + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 702 +import GHC.Generics +# endif +# if __GLASGOW_HASKELL__ >= 708 +import Data.Data +# endif +#endif + +-- | Lifted sum of functors. +data Sum f g a = InL (f a) | InR (g a) + +#ifndef HASKELL98 +# if __GLASGOW_HASKELL__ >= 702 +deriving instance Generic (Sum f g a) + +instance Generic1 (Sum f g) where + type Rep1 (Sum f g) = + D1 MDSum (C1 MCInL (S1 NoSelector (Rec1 f)) + :+: C1 MCInR (S1 NoSelector (Rec1 g))) + from1 (InL f) = M1 (L1 (M1 (M1 (Rec1 f)))) + from1 (InR g) = M1 (R1 (M1 (M1 (Rec1 g)))) + to1 (M1 (L1 (M1 (M1 f)))) = InL (unRec1 f) + to1 (M1 (R1 (M1 (M1 g)))) = InR (unRec1 g) + +data MDSum +data MCInL +data MCInR + +instance Datatype MDSum where + datatypeName _ = "Sum" + moduleName _ = "Data.Functor.Sum" + +instance Constructor MCInL where + conName _ = "InL" + +instance Constructor MCInR where + conName _ = "InR" +# endif + +# if __GLASGOW_HASKELL__ >= 708 +deriving instance Typeable Sum +deriving instance (Data (f a), Data (g a), Typeable f, Typeable g, Typeable a) + => Data (Sum (f :: * -> *) (g :: * -> *) (a :: *)) +# endif +#endif + +instance (Eq1 f, Eq1 g) => Eq1 (Sum f g) where + liftEq eq (InL x1) (InL x2) = liftEq eq x1 x2 + liftEq _ (InL _) (InR _) = False + liftEq _ (InR _) (InL _) = False + liftEq eq (InR y1) (InR y2) = liftEq eq y1 y2 + +instance (Ord1 f, Ord1 g) => Ord1 (Sum f g) where + liftCompare comp (InL x1) (InL x2) = liftCompare comp x1 x2 + liftCompare _ (InL _) (InR _) = LT + liftCompare _ (InR _) (InL _) = GT + liftCompare comp (InR y1) (InR y2) = liftCompare comp y1 y2 + +instance (Read1 f, Read1 g) => Read1 (Sum f g) where + liftReadsPrec rp rl = readsData $ + readsUnaryWith (liftReadsPrec rp rl) "InL" InL `mappend` + readsUnaryWith (liftReadsPrec rp rl) "InR" InR + +instance (Show1 f, Show1 g) => Show1 (Sum f g) where + liftShowsPrec sp sl d (InL x) = + showsUnaryWith (liftShowsPrec sp sl) "InL" d x + liftShowsPrec sp sl d (InR y) = + showsUnaryWith (liftShowsPrec sp sl) "InR" d y + +instance (Eq1 f, Eq1 g, Eq a) => Eq (Sum f g a) where + (==) = eq1 +instance (Ord1 f, Ord1 g, Ord a) => Ord (Sum f g a) where + compare = compare1 +instance (Read1 f, Read1 g, Read a) => Read (Sum f g a) where + readsPrec = readsPrec1 +instance (Show1 f, Show1 g, Show a) => Show (Sum f g a) where + showsPrec = showsPrec1 + +instance (Functor f, Functor g) => Functor (Sum f g) where + fmap f (InL x) = InL (fmap f x) + fmap f (InR y) = InR (fmap f y) + +instance (Foldable f, Foldable g) => Foldable (Sum f g) where + foldMap f (InL x) = foldMap f x + foldMap f (InR y) = foldMap f y + +instance (Traversable f, Traversable g) => Traversable (Sum f g) where + traverse f (InL x) = InL <$> traverse f x + traverse f (InR y) = InR <$> traverse f y diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/CHANGELOG.markdown b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/CHANGELOG.markdown new file mode 100644 index 0000000..2be1633 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/CHANGELOG.markdown @@ -0,0 +1,58 @@ +0.4.0.4 +------- + +0.4.0.3 +------- + +0.4.0.2 +------- +* Each of these is a build with a different set of flags configured. Building this way allows us to work around bugs in `cabal`'s backtracker. + +0.4 +--- +* Added support for the missing `ExceptT` instances from `mtl`. + + This was not done lightly. While this means that by default incurring a dependency on `transformers-compat` drags in `mtl` when you are + using an old `transformers`, it means that users do not have to orphan these instances and permits wider adoption of `ExceptT`. + + If you absolutely can't stand `mtl` and really want this package to build as valid `Haskell98`, then you can use `cabal install transformers-compat -f-mtl` to avoid incurring the dependency to get these instances. However, that is effectively an unsupported configuration. + +0.3.3.4 +------- + +0.3.3.3 +------- + +0.3.3.2 +------- +* These releases were a successful attempt to fix build problems caused by the cabal backtracker. +* Each of these is a build with a different set of flags configured. + +0.3.2 +----- +* This release was a failed (or at least, only partially successful) attempt to fix build problems caused by the cabal backtracker. + +0.3.1 +----- +* `transformers 0.4.1` compatibility + +0.3 +--- +* Added the instances for `Data.Functor.Classes` from `transformers 0.4` +* Switched `Control.Applicative.Backwards` and `Data.Functor.Reverse` to the split constructor/accessor style from `transformers 0.4`. + +0.2 +--- +* Added the new types and classes from `transformers 0.4` + +0.1.1.1 +------- +* Wrote a better synopsis + +0.1.1 +----- +* Updated to trick `cabal` into building an empty `libHStransformers-compat-0.1.a` on GHC 7.6. + +0.1 +--- +* Repository initialized by pulling the `transformers-0.2` compatibility layer out of `lens`. diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/HLint.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/HLint.hs new file mode 100644 index 0000000..7578b03 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/HLint.hs @@ -0,0 +1 @@ +ignore "Warning: Avoid lambda" diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/LICENSE b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/LICENSE new file mode 100644 index 0000000..50586d1 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/LICENSE @@ -0,0 +1,30 @@ +Copyright 2012-2015 Edward Kmett + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the author nor the names of his contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/README.markdown b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/README.markdown new file mode 100644 index 0000000..60e9361 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/README.markdown @@ -0,0 +1,17 @@ +transformers-compat +=================== + +[![Hackage](https://img.shields.io/hackage/v/transformers-compat.svg)](https://hackage.haskell.org/package/transformers-compat) [![Build Status](https://secure.travis-ci.org/ekmett/transformers-compat.png?branch=master)](http://travis-ci.org/ekmett/transformers-compat) + +This provides a thin compatibility shim on top of transformers-0.2 to add the types that were added in transformers-0.3. + +This enables users to maintain haskell-platform compatibility, while still gaining access ot the new functionality. + +Contact Information +------------------- + +Contributions and bug reports are welcome! + +Please feel free to contact me through github or on the #haskell IRC channel on irc.freenode.net. + +-Edward Kmett diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/Setup.lhs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/Setup.lhs new file mode 100644 index 0000000..6cbd928 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/Setup.lhs @@ -0,0 +1,7 @@ +#!/usr/bin/runhaskell +> module Main (main) where + +> import Distribution.Simple + +> main :: IO () +> main = defaultMain diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/config b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/config new file mode 100644 index 0000000..a9287ce --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/config @@ -0,0 +1,16 @@ +-- This provides a custom ~/.cabal/config file for use when hackage is down that should work on unix +-- +-- This is particularly useful for travis-ci to get it to stop complaining +-- about a broken build when everything is still correct on our end. +-- +-- This uses Luite Stegeman's mirror of hackage provided by his 'hdiff' site instead +-- +-- To enable this, uncomment the before_script in .travis.yml + +remote-repo: hdiff.luite.com:http://hdiff.luite.com/packages/archive +remote-repo-cache: ~/.cabal/packages +world-file: ~/.cabal/world +build-summary: ~/.cabal/logs/build.log +remote-build-reporting: anonymous +install-dirs user +install-dirs global diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/src/Control/Monad/Trans/Instances.hs b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/src/Control/Monad/Trans/Instances.hs new file mode 100644 index 0000000..75b4735 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/src/Control/Monad/Trans/Instances.hs @@ -0,0 +1,386 @@ +{-# LANGUAGE CPP #-} + +#ifndef HASKELL98 +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE EmptyDataDecls #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} + +# if __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE Trustworthy #-} +# endif + +# if __GLASGOW_HASKELL__ >= 706 +{-# LANGUAGE PolyKinds #-} +# endif + +# if __GLASGOW_HASKELL__ >= 708 +{-# LANGUAGE DataKinds #-} +# endif +#endif + +{-# OPTIONS_GHC -fno-warn-deprecations #-} +----------------------------------------------------------------------------- +-- | +-- Module : Control.Monad.Trans.Instances +-- Copyright : (C) 2012-16 Edward Kmett +-- License : BSD-style (see the file LICENSE) +-- Maintainer : Edward Kmett +-- Stability : provisional +-- Portability : portable +-- +-- Backports orphan instances which are not provided by other modules in +-- @transformers-compat@. +---------------------------------------------------------------------------- +module Control.Monad.Trans.Instances () where + +#ifndef MIN_VERSION_base +#define MIN_VERSION_base(a,b,c) 1 +#endif + +#ifndef MIN_VERSION_transformers +#define MIN_VERSION_transformers(a,b,c) 1 +#endif + +import Control.Applicative.Backwards (Backwards(..)) +import Control.Applicative.Lift (Lift(..)) +import Control.Monad.IO.Class (MonadIO) +import Control.Monad.Trans.Class (MonadTrans) +import Control.Monad.Trans.Cont (ContT(..)) +import Control.Monad.Trans.Error (ErrorT(..)) +import Control.Monad.Trans.Except () +import Control.Monad.Trans.Identity (IdentityT(..)) +import Control.Monad.Trans.List (ListT(..)) +import Control.Monad.Trans.Maybe (MaybeT(..)) +import qualified Control.Monad.Trans.RWS.Lazy as Lazy (RWST(..)) +import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST(..)) +import Control.Monad.Trans.Reader (ReaderT(..)) +import qualified Control.Monad.Trans.State.Lazy as Lazy (StateT(..)) +import qualified Control.Monad.Trans.State.Strict as Strict (StateT(..)) +import qualified Control.Monad.Trans.Writer.Lazy as Lazy (WriterT(..)) +import qualified Control.Monad.Trans.Writer.Strict as Strict (WriterT(..)) +import Data.Functor.Classes +import Data.Functor.Compose (Compose(..)) +import Data.Functor.Constant (Constant(..)) +import Data.Functor.Identity (Identity(..)) +import Data.Functor.Product (Product(..)) +import Data.Functor.Reverse (Reverse(..)) +import Data.Functor.Sum () + +import Control.Applicative +import Control.Monad (MonadPlus(..)) +import Control.Monad.Fix (MonadFix(..)) +import Data.Foldable (Foldable(..)) +import Data.Maybe (fromMaybe) +import Data.Monoid (Monoid(..)) +import Data.Traversable (Traversable(..)) + +#if MIN_VERSION_base(4,4,0) +import Control.Monad.Zip (MonadZip(..)) +#endif + +#if MIN_VERSION_base(4,8,0) +import Data.Bifunctor (Bifunctor(..)) +#endif + +#ifndef HASKELL98 +import Data.Data (Data) +import Data.Typeable + +# if __GLASGOW_HASKELL__ >= 702 +import GHC.Generics +# endif +#endif + +#if !(MIN_VERSION_transformers(0,3,0)) +-- Foldable/Traversable instances +instance (Foldable f) => Foldable (ErrorT e f) where + foldMap f (ErrorT a) = foldMap (either (const mempty) f) a + +instance (Traversable f) => Traversable (ErrorT e f) where + traverse f (ErrorT a) = + ErrorT <$> traverse (either (pure . Left) (fmap Right . f)) a + +instance (Foldable f) => Foldable (IdentityT f) where + foldMap f (IdentityT a) = foldMap f a + +instance (Traversable f) => Traversable (IdentityT f) where + traverse f (IdentityT a) = IdentityT <$> traverse f a + +instance (Foldable f) => Foldable (ListT f) where + foldMap f (ListT a) = foldMap (foldMap f) a + +instance (Traversable f) => Traversable (ListT f) where + traverse f (ListT a) = ListT <$> traverse (traverse f) a + +instance (Foldable f) => Foldable (MaybeT f) where + foldMap f (MaybeT a) = foldMap (foldMap f) a + +instance (Traversable f) => Traversable (MaybeT f) where + traverse f (MaybeT a) = MaybeT <$> traverse (traverse f) a + +instance (Foldable f) => Foldable (Lazy.WriterT w f) where + foldMap f = foldMap (f . fst) . Lazy.runWriterT + +instance (Traversable f) => Traversable (Lazy.WriterT w f) where + traverse f = fmap Lazy.WriterT . traverse f' . Lazy.runWriterT where + f' (a, b) = fmap (\ c -> (c, b)) (f a) + +instance (Foldable f) => Foldable (Strict.WriterT w f) where + foldMap f = foldMap (f . fst) . Strict.runWriterT + +instance (Traversable f) => Traversable (Strict.WriterT w f) where + traverse f = fmap Strict.WriterT . traverse f' . Strict.runWriterT where + f' (a, b) = fmap (\ c -> (c, b)) (f a) + +-- MonadFix instances for IdentityT and MaybeT +instance (MonadFix m) => MonadFix (IdentityT m) where + mfix f = IdentityT (mfix (runIdentityT . f)) + +instance (MonadFix m) => MonadFix (MaybeT m) where + mfix f = MaybeT (mfix (runMaybeT . f . fromMaybe bomb)) + where bomb = error "mfix (MaybeT): inner computation returned Nothing" + +# if !(MIN_VERSION_base(4,9,0)) +-- Monad instances for Product +instance (Monad f, Monad g) => Monad (Product f g) where + return x = Pair (return x) (return x) + Pair m n >>= f = Pair (m >>= fstP . f) (n >>= sndP . f) + where + fstP (Pair a _) = a + sndP (Pair _ b) = b + +instance (MonadPlus f, MonadPlus g) => MonadPlus (Product f g) where + mzero = Pair mzero mzero + Pair x1 y1 `mplus` Pair x2 y2 = Pair (x1 `mplus` x2) (y1 `mplus` y2) + +instance (MonadFix f, MonadFix g) => MonadFix (Product f g) where + mfix f = Pair (mfix (fstP . f)) (mfix (sndP . f)) + where + fstP (Pair a _) = a + sndP (Pair _ b) = b +# endif +#endif + +#if !(MIN_VERSION_transformers(0,4,0)) +-- Alternative IO instance +# if !(MIN_VERSION_base(4,9,0)) +-- The version bounds of transformers prior to 0.4.0.0 should prevent this +-- instance from being compiled on base-4.8.0.0 and later, but we'll put +-- a check here just to be safe. +instance Alternative IO where + empty = mzero + (<|>) = mplus +# endif +#endif + +#if MIN_VERSION_transformers(0,4,0) && !(MIN_VERSION_transformers(0,4,3)) +-- transformers-0.4-specific Eq1, Ord1, Read1, and Show1 instances for Const +instance (Eq a) => Eq1 (Const a) where + eq1 (Const x) (Const y) = x == y +instance (Ord a) => Ord1 (Const a) where + compare1 (Const x) (Const y) = compare x y +instance (Read a) => Read1 (Const a) where + readsPrec1 = readsData $ readsUnary "Const" Const +instance (Show a) => Show1 (Const a) where + showsPrec1 d (Const x) = showsUnary "Const" d x +#endif + +#if !(MIN_VERSION_transformers(0,5,0)) +-- Monoid Constant instance +instance (Monoid a) => Monoid (Constant a b) where + mempty = Constant mempty + Constant x `mappend` Constant y = Constant (x `mappend` y) + +-- MonadZip instances +# if MIN_VERSION_base(4,4,0) +instance (MonadZip m) => MonadZip (IdentityT m) where + mzipWith f (IdentityT a) (IdentityT b) = IdentityT (mzipWith f a b) + +instance (MonadZip m) => MonadZip (ListT m) where + mzipWith f (ListT a) (ListT b) = ListT $ mzipWith (zipWith f) a b + +instance (MonadZip m) => MonadZip (MaybeT m) where + mzipWith f (MaybeT a) (MaybeT b) = MaybeT $ mzipWith (liftA2 f) a b + +instance (MonadZip m) => MonadZip (ReaderT r m) where + mzipWith f (ReaderT m) (ReaderT n) = ReaderT $ \ a -> + mzipWith f (m a) (n a) + +instance (Monoid w, MonadZip m) => MonadZip (Lazy.WriterT w m) where + mzipWith f (Lazy.WriterT x) (Lazy.WriterT y) = Lazy.WriterT $ + mzipWith (\ ~(a, w) ~(b, w') -> (f a b, w `mappend` w')) x y + +instance (Monoid w, MonadZip m) => MonadZip (Strict.WriterT w m) where + mzipWith f (Strict.WriterT x) (Strict.WriterT y) = Strict.WriterT $ + mzipWith (\ (a, w) (b, w') -> (f a b, w `mappend` w')) x y + +# if !(MIN_VERSION_base(4,8,0)) +instance MonadZip Identity where + mzipWith f (Identity x) (Identity y) = Identity (f x y) + munzip (Identity (a, b)) = (Identity a, Identity b) +# endif + +# if !(MIN_VERSION_base(4,9,0)) +instance (MonadZip f, MonadZip g) => MonadZip (Product f g) where + mzipWith f (Pair x1 y1) (Pair x2 y2) = Pair (mzipWith f x1 x2) (mzipWith f y1 y2) +# endif +# endif + +# if MIN_VERSION_base(4,8,0) +-- Bifunctor Constant instance +instance Bifunctor Constant where + first f (Constant x) = Constant (f x) + second _ (Constant x) = Constant x +# else +-- Monoid Identity instance +instance (Monoid a) => Monoid (Identity a) where + mempty = Identity mempty + mappend (Identity x) (Identity y) = Identity (mappend x y) +# endif + +# ifndef HASKELL98 +-- Typeable instances +# if __GLASGOW_HASKELL__ >= 708 && __GLASGOW_HASKELL__ < 710 +deriving instance Typeable Backwards +deriving instance Typeable Constant +deriving instance Typeable ContT +deriving instance Typeable ErrorT +deriving instance Typeable IdentityT +deriving instance Typeable Lift +deriving instance Typeable ListT +deriving instance Typeable MaybeT +deriving instance Typeable MonadTrans +deriving instance Typeable Lazy.RWST +deriving instance Typeable Strict.RWST +deriving instance Typeable ReaderT +deriving instance Typeable Reverse +deriving instance Typeable Lazy.StateT +deriving instance Typeable Strict.StateT + +# if !(MIN_VERSION_base(4,9,0)) +deriving instance Typeable Compose +deriving instance Typeable MonadIO +deriving instance Typeable Product +# endif +# endif + +-- Identity instances +# if !(MIN_VERSION_base(4,8,0)) +deriving instance Typeable1 Identity +deriving instance Data a => Data (Identity a) + +# if __GLASGOW_HASKELL__ >= 702 +instance Generic (Identity a) where + type Rep (Identity a) = D1 MDIdentity (C1 MCIdentity (S1 MSIdentity (Rec0 a))) + from (Identity x) = M1 (M1 (M1 (K1 x))) + to (M1 (M1 (M1 (K1 x)))) = Identity x + +instance Generic1 Identity where + type Rep1 Identity = D1 MDIdentity (C1 MCIdentity (S1 MSIdentity Par1)) + from1 (Identity x) = M1 (M1 (M1 (Par1 x))) + to1 (M1 (M1 (M1 x))) = Identity (unPar1 x) + +data MDIdentity +data MCIdentity +data MSIdentity + +instance Datatype MDIdentity where + datatypeName _ = "Identity" + moduleName _ = "Data.Functor.Identity" +# if __GLASGOW_HASKELL__ >= 708 + isNewtype _ = True +# endif + +instance Constructor MCIdentity where + conName _ = "Identity" + conIsRecord _ = True + +instance Selector MSIdentity where + selName _ = "runIdentity" +# endif + +# if __GLASGOW_HASKELL__ >= 708 +deriving instance Typeable 'Identity +# endif +# endif + +# if !(MIN_VERSION_base(4,9,0)) +# if __GLASGOW_HASKELL__ >= 702 +-- Generic(1) instances for Compose +instance Generic (Compose f g a) where + type Rep (Compose f g a) = + D1 MDCompose + (C1 MCCompose + (S1 MSCompose (Rec0 (f (g a))))) + from (Compose x) = M1 (M1 (M1 (K1 x))) + to (M1 (M1 (M1 (K1 x)))) = Compose x + +instance Functor f => Generic1 (Compose f g) where + type Rep1 (Compose f g) = + D1 MDCompose + (C1 MCCompose + (S1 MSCompose (f :.: Rec1 g))) + from1 (Compose x) = M1 (M1 (M1 (Comp1 (fmap Rec1 x)))) + to1 (M1 (M1 (M1 x))) = Compose (fmap unRec1 (unComp1 x)) + +data MDCompose +data MCCompose +data MSCompose + +instance Datatype MDCompose where + datatypeName _ = "Compose" + moduleName _ = "Data.Functor.Compose" +# if __GLASGOW_HASKELL__ >= 708 + isNewtype _ = True +# endif + +instance Constructor MCCompose where + conName _ = "Compose" + conIsRecord _ = True + +instance Selector MSCompose where + selName _ = "getCompose" + +-- Generic(1) instances for Product +instance Generic (Product f g a) where + type Rep (Product f g a) = + D1 MDProduct + (C1 MCPair + (S1 NoSelector (Rec0 (f a)) :*: S1 NoSelector (Rec0 (g a)))) + from (Pair f g) = M1 (M1 (M1 (K1 f) :*: M1 (K1 g))) + to (M1 (M1 (M1 (K1 f) :*: M1 (K1 g)))) = Pair f g + +instance Generic1 (Product f g) where + type Rep1 (Product f g) = + D1 MDProduct + (C1 MCPair + (S1 NoSelector (Rec1 f) :*: S1 NoSelector (Rec1 g))) + from1 (Pair f g) = M1 (M1 (M1 (Rec1 f) :*: M1 (Rec1 g))) + to1 (M1 (M1 (M1 f :*: M1 g))) = Pair (unRec1 f) (unRec1 g) + +data MDProduct +data MCPair + +instance Datatype MDProduct where + datatypeName _ = "Product" + moduleName _ = "Data.Functor.Product" + +instance Constructor MCPair where + conName _ = "Pair" +# endif + +# if __GLASGOW_HASKELL__ >= 708 +-- Data instances for Compose and Product +deriving instance (Data (f (g a)), Typeable f, Typeable g, Typeable a) + => Data (Compose (f :: * -> *) (g :: * -> *) (a :: *)) +deriving instance (Data (f a), Data (g a), Typeable f, Typeable g, Typeable a) + => Data (Product (f :: * -> *) (g :: * -> *) (a :: *)) +# endif +# endif +# endif +#endif diff --git a/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/transformers-compat.cabal b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/transformers-compat.cabal new file mode 100644 index 0000000..0e9f987 --- /dev/null +++ b/benchmarks/PPoPP2019/src/transformers-compat-0.5.1.4/transformers-compat.cabal @@ -0,0 +1,99 @@ +name: transformers-compat +category: Compatibility +version: 0.5.1.4 +license: BSD3 +cabal-version: >= 1.8 +license-file: LICENSE +author: Edward A. Kmett +maintainer: Edward A. Kmett +stability: provisional +homepage: http://github.com/ekmett/transformers-compat/ +bug-reports: http://github.com/ekmett/transformers-compat/issues +copyright: Copyright (C) 2012-2015 Edward A. Kmett +synopsis: A small compatibility shim exposing the new types from transformers 0.3 and 0.4 to older Haskell platforms. +description: + This package includes backported versions of types that were added + to transformers in transformers 0.3, 0.4, and 0.5 for users who need strict + transformers 0.2 or 0.3 compatibility to run on old versions of the + platform, but also need those types. + . + Those users should be able to just depend on @transformers >= 0.2@ + and @transformers-compat >= 0.3@. + . + Note: missing methods are not supplied, but this at least permits the types to be used. + +build-type: Simple +tested-with: GHC == 7.0.4, GHC == 7.4.1, GHC == 7.4.2, GHC == 7.6.1, GHC == 7.8.2, GHC == 7.10.3, GHC == 8.0.1 +extra-source-files: + .travis.yml + .ghci + .gitignore + .vim.custom + config + HLint.hs + README.markdown + CHANGELOG.markdown + +source-repository head + type: git + location: git://github.com/ekmett/transformers-compat.git + +flag two + default: False + description: Use transformers 0.2. This will be selected by cabal picking the appropriate version. + manual: True + +flag three + default: False + manual: True + description: Use transformers 0.3. This will be selected by cabal picking the appropriate version. + +flag mtl + default: True + manual: True + description: -f-mtl Disables support for mtl for transformers 0.2 and 0.3. That is an unsupported configuration, and results in missing instances for `ExceptT`, but keeps the package Haskell 98. + +library + build-depends: + base >= 4.3 && < 5 + + hs-source-dirs: + src + + exposed-modules: + Control.Monad.Trans.Instances + + other-modules: + Paths_transformers_compat + + if flag(three) + hs-source-dirs: 0.3 + build-depends: + transformers >= 0.3 && < 0.4, + mtl >= 2.1 && < 2.2 + else + if flag(two) + hs-source-dirs: 0.2 0.3 + build-depends: + transformers >= 0.2 && < 0.3, + mtl >= 2.0 && < 2.1 + else + build-depends: transformers >= 0.4.1 && < 0.6 + + if !flag(mtl) + cpp-options: -DHASKELL98 + else + build-depends: ghc-prim + + if flag(two) + exposed-modules: + Control.Applicative.Backwards + Control.Applicative.Lift + Data.Functor.Reverse + + if flag(two) || flag(three) + exposed-modules: + Control.Monad.Trans.Except + Control.Monad.Signatures + Data.Functor.Classes + Data.Functor.Sum diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-12 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-12 new file mode 100644 index 0000000..41d5829 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-12 @@ -0,0 +1,12 @@ +0 +2 +4 +6 +8 +10 +1 +3 +5 +7 +9 +11 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-20 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-20 new file mode 100644 index 0000000..ae810f6 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-20 @@ -0,0 +1,20 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-24 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-24 new file mode 100644 index 0000000..fb671ed --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-24 @@ -0,0 +1,24 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 +21 +23 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-32 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-32 new file mode 100644 index 0000000..01ba158 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-32 @@ -0,0 +1,32 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +24 +26 +28 +30 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 +21 +23 +25 +27 +29 +31 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-34 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-34 new file mode 100644 index 0000000..79dab33 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-34 @@ -0,0 +1,36 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +24 +26 +28 +30 +32 +34 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 +21 +23 +25 +27 +29 +31 +33 +35 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-40 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-40 new file mode 100644 index 0000000..4e96af8 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-40 @@ -0,0 +1,40 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 +20 +22 +24 +26 +28 +30 +32 +34 +36 +38 +21 +23 +25 +27 +29 +31 +33 +35 +37 +39 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-48 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-48 new file mode 100644 index 0000000..355d8ff --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-48 @@ -0,0 +1,48 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 +21 +23 +24 +26 +28 +30 +32 +34 +36 +38 +40 +42 +44 +46 +25 +27 +29 +31 +33 +35 +37 +39 +41 +43 +45 +47 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-64 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-64 new file mode 100644 index 0000000..8bf3107 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-64 @@ -0,0 +1,64 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +24 +26 +28 +30 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 +21 +23 +25 +27 +29 +31 +32 +34 +36 +38 +40 +42 +44 +46 +48 +50 +52 +54 +56 +58 +60 +62 +33 +35 +37 +39 +41 +43 +45 +47 +49 +51 +53 +55 +57 +59 +61 +63 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-72 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-72 new file mode 100644 index 0000000..0d53634 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-72 @@ -0,0 +1,72 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +24 +26 +28 +30 +32 +34 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 +21 +23 +25 +27 +29 +31 +33 +35 +36 +38 +40 +42 +44 +46 +48 +50 +52 +54 +56 +58 +60 +62 +64 +66 +68 +70 +37 +39 +41 +43 +45 +47 +49 +51 +53 +55 +57 +59 +61 +63 +65 +67 +69 +71 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-8 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-8 new file mode 100644 index 0000000..334c477 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-8 @@ -0,0 +1,8 @@ +0 +2 +4 +6 +1 +3 +5 +7 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-Xeon b/benchmarks/PPoPP2019/topo-cores-sockets-threads-Xeon new file mode 100644 index 0000000..0d53634 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-Xeon @@ -0,0 +1,72 @@ +0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +24 +26 +28 +30 +32 +34 +1 +3 +5 +7 +9 +11 +13 +15 +17 +19 +21 +23 +25 +27 +29 +31 +33 +35 +36 +38 +40 +42 +44 +46 +48 +50 +52 +54 +56 +58 +60 +62 +64 +66 +68 +70 +37 +39 +41 +43 +45 +47 +49 +51 +53 +55 +57 +59 +61 +63 +65 +67 +69 +71 diff --git a/benchmarks/PPoPP2019/topo-cores-sockets-threads-i7 b/benchmarks/PPoPP2019/topo-cores-sockets-threads-i7 new file mode 100644 index 0000000..334c477 --- /dev/null +++ b/benchmarks/PPoPP2019/topo-cores-sockets-threads-i7 @@ -0,0 +1,8 @@ +0 +2 +4 +6 +1 +3 +5 +7