content: module Main where
  
  import Control.Monad
  import Foreign.C.Types
  import Foreign.LibFFI
  import System.Posix.DynamicLinker
  import Numeric
  import CPUTime
  import Time
  import Ratio
  import System.Environment
  import System.Exit
  
  main = withDL "" [RTLD_NOW] $ \dl -> do
      args <- getArgs
      sz <- case args of
                  [n] -> return $ (read n * 2^20) `quot` 2
                  []  -> putStrLn "usage: MemSpeed megabytes-to-use" >> exitWith (ExitFailure 1)
  
      memset <- dlsym dl "memset"
      memcpy <- dlsym dl "memcpy"
      malloc <- dlsym dl "malloc"
      free <- dlsym dl "free"
  
      s <- callFFI malloc (retPtr retVoid) [argCSize sz]
      d <- callFFI malloc (retPtr retVoid) [argCSize sz]
      check sz "memcpy 1" $ callFFI memcpy retVoid [argPtr d, argPtr s, argCSize sz]
      check (10*sz) "memcpy 10" $ replicateM_ 10 $ callFFI memcpy retVoid [argPtr d, argPtr s, argCSize sz]
      callFFI free retVoid [argPtr s]
      callFFI free retVoid [argPtr d]
  
      p <- callFFI malloc (retPtr retVoid) [argCSize (2 * sz)]
      check (2*sz) "memset 1" $ callFFI memset retVoid [argPtr p, argCInt 97, argCSize (2 * sz)]
      check (20*sz) "memset 10" $ replicateM_ 10 $ callFFI memset retVoid [argPtr p, argCInt 97, argCSize (2 * sz)]
      callFFI free retVoid [argPtr p]
  
  check sz s a = do
      (r, cpu, clock) <- timeIt a
      putStrLn $ s ++ ": "
                          ++ showf 2 ((fromIntegral sz / cpu) / (2 ^ 20)) ++ " mb/cpu sec  "
                          ++ showf 2 ((fromIntegral sz / clock) / (2 ^ 20)) ++ " mb/clock sec  "
      return r
  
  type TimeIt     = (Integer, ClockTime)
  
  timeItStart     :: IO TimeIt
  timeItStart     = liftM2 (,) getCPUTime getClockTime
  
  timeItEnd       :: TimeIt -> IO (Double, Double)
  timeItEnd (startCPU, startClock) = do
      stopCPU <- getCPUTime
      stopClock <- getClockTime
      let
          cpuTime     = (fromIntegral (stopCPU - startCPU) / 10^12)
          clockTime   = (timeDiffToSec $ diffClockTimes stopClock startClock)
      return (cpuTime, clockTime)
      where
          timeDiffToSec td
              = fromIntegral (tdSec td) + fromIntegral (tdPicosec td) / 10^12
  
  {- | @timeIt action@ executes @action@, then returns
     a tuple of its result, CPU- and wallclock-time elapsed. -}
  timeIt :: IO a -> IO (a, Double, Double)
  timeIt a = do
      t <- timeItStart
      r <- a
      (cpuTime, clockTime) <- timeItEnd t
      return (r, cpuTime, clockTime)
  
  showf           :: RealFloat a => Int -> a -> String
  showf n x
      | x >= 0    = ' ':s
      | otherwise = s
      where
          s       = showFFloat (Just n) x ""
  