import Control.Parallel.Strategies (NFData, rnf) import Data.Array.Vector (UArr, toU) import Debug.Trace (trace) import System (getArgs, getProgName) import System.Mem (performGC) import Foreign import Foreign.C.Types import Dist (dist_slow, dist_slow', dist_fast, dist_fast_inlined, c_dist) import Microbench (microbench) -- benchmark a given function by applying it n times to the given value benchmark_trace :: (a -> b) -> a -> Int -> IO() benchmark_trace f x n = do putStrLn "" putStrLn $ "evaluating " ++ (show n) ++ " times" putStrLn "" r <- mapM (\y -> return $! trace "eval" (f y)) (replicate n x) performGC return () -- benchmark a given function by applying it n times to the given value benchmark :: (a -> b) -> a -> Int -> IO() benchmark f x n = do r <- mapM (\y -> return $! f y) (replicate n x) performGC return () -- benchmark distance function benchmark_dist :: (UArr Double -> UArr Double -> Double) -> UArr Double -> UArr Double -> Int -> IO() benchmark_dist dist x y n = do r <- mapM (\z -> return $! dist x z) (replicate n y) performGC return () -- benchmark dist_fast_inlined specifically -- specialized to allow inline to have (positive) effect -- BUT results in 15x slowdown benchmark_dist_fast :: UArr Double -> UArr Double -> Int -> IO() benchmark_dist_fast x y n = do r <- mapM (\z -> return $! dist_fast x z) (replicate n y) performGC return () benchmark_dist_fast_inlined :: UArr Double -> UArr Double -> Int -> IO() benchmark_dist_fast_inlined x y n = do r <- mapM (\z -> return $! dist_fast_inlined x z) (replicate n y) performGC return () -- parse command line arguments and microbench dist_slow using the wrapper main = do args <- getArgs pName <- getProgName --if (length args /= 3) if (length args /= 1) --then error $ "Usage: " ++ pName ++ " " then error $ "[Haskell] Usage: " ++ pName ++ " " else putStrLn "Checked arguments, OK!" let dim = (read $ args !! 0) :: Double -- dimensionality -- try and assure GHC isn't excessively optimizing dist_slow by making assumptions about the input {-e1 = (read $ args !! 1) :: Integer -- value of first vector element for first vector e2 = (read $ args !! 2) :: Integer -- value of second vector element for second vector p1 = map fromInteger [e1..(e1 + dim - 1)] -- construct first vector p2 = map fromInteger [e2..(e2 + dim - 1)] -- construct second vector -} p1 = [1.0..dim] p2 = [1.0..dim] p1' = toU p1 p2' = toU p2 p1'' <- mallocArray (round dim) :: IO (Ptr CDouble) p2'' <- mallocArray (round dim) :: IO (Ptr CDouble) pokeArray p1'' (map fromInteger [1..round dim] :: [CDouble]) pokeArray p2'' (map fromInteger [1..round dim] :: [CDouble]) putStrLn $ "Benchmarking determining Eucl. distance between two points in " ++ (show dim) ++ "D-space" -- make sure all garbage is collected performGC -- benchmark C implementation using FFI microbench "C implementation of dist (using FFI)" (benchmark (c_dist (round dim) p1'') p2'') free p1'' >> free p2'' -- benchmark slow distance implementation length p1 `seq` length p2 `seq` microbench "slow Eucl. dist (using lists of Doubles)" (benchmark (dist_slow p1) p2) -- benchmark slow implementation using strict sum length p1 `seq` length p2 `seq` microbench "slow' Eucl. dist (using lists of Doubles)" (benchmark (dist_slow' p1) p2) -- benchmark fast implementation using uvector package p1' `seq` p2' `seq` microbench "fast Eucl. dist (using uvector package, benchmark (generic))" (benchmark (dist_fast p1') p2') -- benchmark fast implementation using uvector package (using specialized benchmark_dist_fast) p1' `seq` p2' `seq` microbench "fast Eucl. dist (using uvector package, benchmark_dist_fast)" (benchmark_dist_fast p1' p2') -- benchmark fast implementation using uvector package p1' `seq` p2' `seq` microbench "fast Eucl. dist (using uvector package, INLINE, benchmark (generic))" (benchmark (dist_fast_inlined p1') p2') p1' `seq` p2' `seq` microbench "fast Eucl. dist (using uvector package, INLINE, benchmark_dist_fast_inlined)" (benchmark_dist_fast_inlined p1' p2')