packages feed

accelerate-examples-0.12.0.0: examples/quickcheck/Config.hs

{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE CPP #-}

module Config (

  -- options & test configuration
  Options,
  processArgs,
  optBackend, double, float, int64, int32, int16, int8,

  -- running tests
  run, run1

) where

import Data.Char
import Data.List
import Data.Label
import Control.Monad
import Test.Framework
import System.Console.GetOpt                            ( OptDescr(..), ArgDescr(..) )

import Data.Array.Accelerate                            ( Arrays, Acc )
import qualified Data.Array.Accelerate.Interpreter      as Interp

#ifdef ACCELERATE_CUDA_BACKEND
import qualified Foreign.CUDA.Driver                    as CUDA
import qualified Data.Array.Accelerate.CUDA             as CUDA
#endif

data Backend = Interpreter
#ifdef ACCELERATE_CUDA_BACKEND
             | CUDA
#endif
  deriving (Eq, Enum, Bounded, Show)


data Options = Options
  {
    _optBackend         :: !Backend,
    _double             :: !Bool,
    _float              :: !Bool,
    _int64              :: !Bool,
    _int32              :: !Bool,
    _int16              :: !Bool,
    _int8               :: !Bool
  }
  deriving Show

$( mkLabels [''Options] )

defaultOptions :: Options
defaultOptions =  Options
  { _optBackend         = maxBound
  , _double             = True
  , _float              = True
  , _int64              = True
  , _int32              = True
  , _int16              = True
  , _int8               = True
  }


backends :: [OptDescr (Options -> Options)]
backends =
  [ Option [] ["interpreter"]           (NoArg (set optBackend Interpreter))    "reference implementation (sequential)"
#ifdef ACCELERATE_CUDA_BACKEND
  , Option [] ["cuda"]                  (NoArg (set optBackend CUDA))           "implementation for NVIDIA GPUs (parallel)"
#endif
  ]


run :: Arrays a => Options -> Acc a -> a
run opts = case _optBackend opts of
  Interpreter   -> Interp.run
#ifdef ACCELERATE_CUDA_BACKEND
  CUDA          -> CUDA.run
#endif

run1 :: (Arrays a, Arrays b) => Options -> (Acc a -> Acc b) -> a -> b
run1 opts f = case _optBackend opts of
  Interpreter   -> head . Interp.stream f . return
#ifdef ACCELERATE_CUDA_BACKEND
  CUDA          -> CUDA.run1 f
#endif


-- Display a very basic usage message info: specifically, list the available
-- backends and highlight the active one.
--
usageInfo :: Options -> String
usageInfo opts = unlines (header : table)
  where
    active this         = if this == map toLower (show $ get optBackend opts) then "*" else ""
    (ss,bs,ds)          = unzip3 $ map (\(b,d) -> (active b, b, d)) $ concatMap extract backends
    table               = zipWith3 paste (sameLen ss) (sameLen bs) ds
    paste x y z         = "  " ++ x ++ "  " ++ y ++ "  " ++ z
    sameLen xs          = flushLeft ((maximum . map length) xs) xs
    flushLeft n xs      = [ take n (x ++ repeat ' ') | x <- xs ]
    --
    extract (Option _ los _ descr) =
      let losFmt  = intercalate ", " los
      in  case lines descr of
            []          -> [(losFmt, "")]
            (x:xs)      -> (losFmt, x) : [ ("",x') | x' <- xs ]
    --
    header              = intercalate "\n" $
      [ "accelerate-quickcheck (c) 2012 The Accelerate Team"
      , ""
      , "Usage: accelerate-quickcheck [BACKEND] [OPTIONS]"
      , ""
      , "Available backends:"
      ]

-- Once the backend has been selected, we might need to enable or disable
-- certain classes of tests.
--
-- CUDA: double precision is only supported on certain kinds of hardware. We
--       can't get the exact device the backend will choose to run on, but we do
--       know that it will pick the most capable device available.
--
configureBackend :: Options -> IO Options
configureBackend opts = case _optBackend opts of
  Interpreter   -> return opts
#ifdef ACCELERATE_CUDA_BACKEND
  CUDA          -> do
    CUDA.initialise []
    n           <- CUDA.count
    devs        <- mapM CUDA.device [0 .. n-1]
    props       <- mapM CUDA.props devs
    return      $! opts { _double = any (\dev -> CUDA.computeCapability dev >= 1.3) props }
#endif


processArgs :: [String] -> IO (Options, RunnerOptions)
processArgs argv = do
  args                  <- interpretArgs argv
  (options, runner)     <- case args of
    Left msg            -> error msg
    Right (opts, rest)  -> (,opts) `liftM` configureBackend (foldl parse1 defaultOptions rest)
  --
  putStrLn      $ usageInfo options
  return        $ (options, runner)
  where
    parse1 opts x       =
      case filter (\(Option _ [f] _ _) -> map toLower x `isPrefixOf` f) backends of
        [Option _ _ (NoArg go) _]   -> go opts
        _                           -> opts