packages feed

cuda-0.2: Foreign/CUDA/Driver/Device.chs

{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
--------------------------------------------------------------------------------
-- |
-- Module    : Foreign.CUDA.Driver.Device
-- Copyright : (c) [2009..2010] Trevor L. McDonell
-- License   : BSD
--
-- Device management for low-level driver interface
--
--------------------------------------------------------------------------------

module Foreign.CUDA.Driver.Device
  (
    Device(..), -- should be exported abstractly
    DeviceProperties(..), DeviceAttribute(..), ComputeMode(..), InitFlag,

    initialise, capability, device, attribute, count, name, props, totalMem
  )
  where

#include <cuda.h>
{# context lib="cuda" #}

-- Friends
import Foreign.CUDA.Analysis.Device
import Foreign.CUDA.Driver.Error
import Foreign.CUDA.Internal.C2HS
import Foreign.CUDA.Internal.Offsets

-- System
import Foreign
import Foreign.C
import Control.Monad            (liftM)


--------------------------------------------------------------------------------
-- Data Types
--------------------------------------------------------------------------------

newtype Device = Device { useDevice :: {# type CUdevice #}}


-- |
-- Device attributes
--
{# enum CUdevice_attribute as DeviceAttribute
    { underscoreToCase }
    with prefix="CU_DEVICE_ATTRIBUTE" deriving (Eq, Show) #}

{# pointer *CUdevprop as ^ foreign -> CUDevProp nocode #}


--
-- Properties of the compute device (internal helper)
--
data CUDevProp = CUDevProp
  {
    cuMaxThreadsPerBlock :: Int,           -- Maximum number of threads per block
    cuMaxBlockSize       :: (Int,Int,Int), -- Maximum size of each dimension of a block
    cuMaxGridSize        :: (Int,Int,Int), -- Maximum size of each dimension of a grid
    cuSharedMemPerBlock  :: Int64,         -- Shared memory available per block in bytes
    cuTotalConstMem      :: Int64,         -- Constant memory available on device in bytes
    cuWarpSize           :: Int,           -- Warp size in threads (SIMD width)
    cuMemPitch           :: Int64,         -- Maximum pitch in bytes allowed by memory copies
    cuRegsPerBlock       :: Int,           -- 32-bit registers available per block
    cuClockRate          :: Int,           -- Clock frequency in kilohertz
    cuTextureAlignment   :: Int64          -- Alignment requirement for textures
  }
  deriving (Show)


instance Storable CUDevProp where
  sizeOf _    = {#sizeof CUdevprop#}
  alignment _ = alignment (undefined :: Ptr ())

  poke _ _    = error "no instance for Foreign.Storable.poke DeviceProperties"
  peek p      = do
    tb <- cIntConv `fmap` {#get CUdevprop.maxThreadsPerBlock#} p
    sm <- cIntConv `fmap` {#get CUdevprop.sharedMemPerBlock#} p
    cm <- cIntConv `fmap` {#get CUdevprop.totalConstantMemory#} p
    ws <- cIntConv `fmap` {#get CUdevprop.SIMDWidth#} p
    mp <- cIntConv `fmap` {#get CUdevprop.memPitch#} p
    rb <- cIntConv `fmap` {#get CUdevprop.regsPerBlock#} p
    cl <- cIntConv `fmap` {#get CUdevprop.clockRate#} p
    ta <- cIntConv `fmap` {#get CUdevprop.textureAlign#} p

    (t1:t2:t3:_) <- map cIntConv `fmap` peekArray 3 (p `plusPtr` devMaxThreadDimOffset' :: Ptr CInt)
    (g1:g2:g3:_) <- map cIntConv `fmap` peekArray 3 (p `plusPtr` devMaxGridSizeOffset'  :: Ptr CInt)

    return CUDevProp
      {
        cuMaxThreadsPerBlock = tb,
        cuMaxBlockSize       = (t1,t2,t3),
        cuMaxGridSize        = (g1,g2,g3),
        cuSharedMemPerBlock  = sm,
        cuTotalConstMem      = cm,
        cuWarpSize           = ws,
        cuMemPitch           = mp,
        cuRegsPerBlock       = rb,
        cuClockRate          = cl,
        cuTextureAlignment   = ta
      }


-- |
-- Possible option flags for CUDA initialisation. Dummy instance until the API
-- exports actual option values.
--
data InitFlag

instance Enum InitFlag where


--------------------------------------------------------------------------------
-- Initialisation
--------------------------------------------------------------------------------

-- |
-- Initialise the CUDA driver API. Must be called before any other driver
-- function.
--
initialise :: [InitFlag] -> IO ()
initialise flags = nothingIfOk =<< cuInit flags

{# fun unsafe cuInit
  { combineBitMasks `[InitFlag]' } -> `Status' cToEnum #}


--------------------------------------------------------------------------------
-- Device Management
--------------------------------------------------------------------------------

-- |
-- Return the compute compatibility revision supported by the device
--
capability :: Device -> IO Double
capability dev =
  (\(s,a,b) -> resultIfOk (s,cap a b)) =<< cuDeviceComputeCapability dev
  where
    cap a b = let a' = fromIntegral a in
              let b' = fromIntegral b in
              a' + b' / max 10 (10^ ((ceiling . logBase 10) b' :: Int))

{# fun unsafe cuDeviceComputeCapability
  { alloca-   `Int'    peekIntConv*
  , alloca-   `Int'    peekIntConv*
  , useDevice `Device'              } -> `Status' cToEnum #}


-- |
-- Return a device handle
--
device :: Int -> IO Device
device d = resultIfOk =<< cuDeviceGet d

{# fun unsafe cuDeviceGet
  { alloca-  `Device' dev*
  , cIntConv `Int'           } -> `Status' cToEnum #}
  where dev = liftM Device . peek


-- |
-- Return the selected attribute for the given device
--
attribute :: Device -> DeviceAttribute -> IO Int
attribute d a = resultIfOk =<< cuDeviceGetAttribute a d

{# fun unsafe cuDeviceGetAttribute
  { alloca-   `Int'             peekIntConv*
  , cFromEnum `DeviceAttribute'
  , useDevice `Device'                       } -> `Status' cToEnum #}


-- |
-- Return the number of device with compute capability > 1.0
--
count :: IO Int
count = resultIfOk =<< cuDeviceGetCount

{# fun unsafe cuDeviceGetCount
  { alloca- `Int' peekIntConv* } -> `Status' cToEnum #}


-- |
-- Name of the device
--
name :: Device -> IO String
name d = resultIfOk =<< cuDeviceGetName d

{# fun unsafe cuDeviceGetName
  { allocaS-  `String'& peekS*
  , useDevice `Device'         } -> `Status' cToEnum #}
  where
    len       = 512
    allocaS a = allocaBytes len $ \p -> a (p, cIntConv len)
    peekS s _ = peekCString s


-- |
-- Return the properties of the selected device
--

-- Annoyingly, the driver API requires several different functions to extract
-- all device properties that are part of a single structure in the runtime API
--
props :: Device -> IO DeviceProperties
props d = do
  p   <- resultIfOk =<< cuDeviceGetProperties d

  -- And the remaining properties
  --
  n   <- name d
  cc  <- capability d
  gm  <- totalMem d
  pc  <- attribute d MultiprocessorCount
  md  <- toEnum `fmap` attribute d ComputeMode
  ov  <- toBool `fmap` attribute d GpuOverlap
  ke  <- toBool `fmap` attribute d KernelExecTimeout
  tg  <- toBool `fmap` attribute d Integrated
  hm  <- toBool `fmap` attribute d CanMapHostMemory
#if CUDA_VERSION >= 3000
  ck  <- toBool `fmap` attribute d ConcurrentKernels
  ee  <- toBool `fmap` attribute d EccEnabled
  u1  <- attribute d MaximumTexture1dWidth
  u21 <- attribute d MaximumTexture2dWidth
  u22 <- attribute d MaximumTexture2dHeight
  u31 <- attribute d MaximumTexture3dWidth
  u32 <- attribute d MaximumTexture3dHeight
  u33 <- attribute d MaximumTexture3dDepth
#endif

  return DeviceProperties
    {
      deviceName               = n,
      computeCapability        = cc,
      totalGlobalMem           = gm,
      totalConstMem            = cuTotalConstMem p,
      sharedMemPerBlock        = cuSharedMemPerBlock p,
      regsPerBlock             = cuRegsPerBlock p,
      warpSize                 = cuWarpSize p,
      maxThreadsPerBlock       = cuMaxThreadsPerBlock p,
      maxBlockSize             = cuMaxBlockSize p,
      maxGridSize              = cuMaxGridSize p,
      clockRate                = cuClockRate p,
      multiProcessorCount      = pc,
      memPitch                 = cuMemPitch p,
      textureAlignment         = cuTextureAlignment p,
      computeMode              = md,
      deviceOverlap            = ov,
#if CUDA_VERSION >= 3000
      concurrentKernels        = ck,
      eccEnabled               = ee,
      maxTextureDim1D          = u1,
      maxTextureDim2D          = (u21,u22),
      maxTextureDim3D          = (u31,u32,u33),
#endif
      kernelExecTimeoutEnabled = ke,
      integrated               = tg,
      canMapHostMemory         = hm
    }


{# fun unsafe cuDeviceGetProperties
  { alloca-   `CUDevProp' peek*
  , useDevice `Device'          } -> `Status' cToEnum #}


-- |
-- Total memory available on the device (bytes)
--
totalMem :: Device -> IO Int64
totalMem d = resultIfOk =<< cuDeviceTotalMem d

{# fun unsafe cuDeviceTotalMem
  { alloca-   `Int64'  peekIntConv*
  , useDevice `Device'              } -> `Status' cToEnum #}