packages feed

accelerate-0.10.0.0: Data/Array/Accelerate/CUDA/Analysis/Launch.hs

{-# LANGUAGE CPP, GADTs, RankNTypes #-}
-- |
-- Module      : Data.Array.Accelerate.CUDA.Analysis.Launch
-- Copyright   : [2008..2011] Manuel M T Chakravarty, Gabriele Keller, Sean Lee, Trevor L. McDonell
-- License     : BSD3
--
-- Maintainer  : Manuel M T Chakravarty <chak@cse.unsw.edu.au>
-- Stability   : experimental
-- Portability : non-partable (GHC extensions)
--

module Data.Array.Accelerate.CUDA.Analysis.Launch (launchConfig)
  where

-- friends
import Data.Array.Accelerate.AST
import Data.Array.Accelerate.Type
import Data.Array.Accelerate.Array.Sugar                (Array(..), EltRepr)
import Data.Array.Accelerate.Analysis.Type              hiding (accType, expType)
import Data.Array.Accelerate.Analysis.Shape             hiding (accDim)

import Data.Array.Accelerate.CUDA.State
import Data.Array.Accelerate.CUDA.Compile               (ExecOpenAcc(..))

-- library
import Data.Label.PureM
import Control.Monad.IO.Class

import qualified Foreign.CUDA.Analysis                  as CUDA
import qualified Foreign.CUDA.Driver                    as CUDA
import qualified Foreign.Storable                       as F

#include "accelerate.h"


-- |Reify dimensionality of array computations
--
accDim :: ExecOpenAcc aenv (Array sh e) -> Int
accDim (ExecAcc _ _ _ acc) = preAccDim accDim acc
accDim (ExecAfun _ _)      = error "when I get sad, I stop being sad and be AWESOME instead."

-- |Reify type of arrays and scalar expressions
--
accType :: ExecOpenAcc aenv (Array sh e) -> TupleType (EltRepr e)
accType (ExecAcc _ _ _ acc) = preAccType accType acc
accType (ExecAfun _ _)      = error "TRUE STORY."

expType :: PreOpenExp ExecOpenAcc aenv env t -> TupleType (EltRepr t)
expType = preExpType accType


-- |
-- Determine kernel launch parameters for the given array computation (as well
-- as compiled function module). This consists of the thread block size, number
-- of blocks, and dynamically allocated shared memory (bytes), respectively.
--
-- By default, this launches the kernel with the minimum block size that gives
-- maximum occupancy, and the grid size limited to the maximum number of
-- physically resident blocks. Hence, kernels may need to process multiple
-- elements per thread.
--
launchConfig :: PreOpenAcc ExecOpenAcc aenv a -> Int -> CUDA.Fun -> CIO (Int, Int, Integer)
launchConfig acc n fn = do
  regs <- liftIO $ CUDA.requires fn CUDA.NumRegs
  stat <- liftIO $ CUDA.requires fn CUDA.SharedSizeBytes        -- static memory only
  prop <- gets deviceProps

  let dyn        = sharedMem prop acc
      (cta, occ) = blockSize prop acc regs ((stat+) . dyn)
      mbk        = CUDA.multiProcessorCount prop * CUDA.activeThreadBlocks occ

  return (cta, mbk `min` gridSize prop acc n cta, toInteger (dyn cta))


-- |
-- Determine the optimal thread block size for a given array computation. Fold
-- requires blocks with a power-of-two number of threads.
--
blockSize :: CUDA.DeviceProperties -> PreOpenAcc ExecOpenAcc aenv a -> Int -> (Int -> Int) -> (Int, CUDA.Occupancy)
blockSize p (Fold _ _ _) r s = CUDA.optimalBlockSizeBy p CUDA.incPow2 (const r) s
blockSize p (Fold1 _ _)  r s = CUDA.optimalBlockSizeBy p CUDA.incPow2 (const r) s
blockSize p _            r s = CUDA.optimalBlockSizeBy p CUDA.incWarp (const r) s


-- |
-- Determine the number of blocks of the given size necessary to process the
-- given array expression. This should understand things like #elements per
-- thread for the various kernels.
--
-- foldSeg: 'size' is the number of segments, require one warp per segment
--
gridSize :: CUDA.DeviceProperties -> PreOpenAcc ExecOpenAcc aenv a -> Int -> Int -> Int
gridSize p acc@(FoldSeg _ _ _ _) size cta = split acc (size * CUDA.warpSize p) cta
gridSize p acc@(Fold1Seg _ _ _)  size cta = split acc (size * CUDA.warpSize p) cta
gridSize p acc@(Fold _ _ a)      size cta = if accDim a == 1 then split acc size cta else split acc (size * CUDA.warpSize p) cta
gridSize p acc@(Fold1 _ a)       size cta = if accDim a == 1 then split acc size cta else split acc (size * CUDA.warpSize p) cta
gridSize _ acc                   size cta = split acc size cta

split :: PreOpenAcc ExecOpenAcc aenv a -> Int -> Int -> Int
split acc size cta = (size `between` eltsPerThread acc) `between` cta
  where
    between arr n   = 1 `max` ((n + arr - 1) `div` n)
    eltsPerThread _ = 1


-- |
-- Analyse the given array expression, returning an estimate of dynamic shared
-- memory usage as a function of thread block size. This can be used by the
-- occupancy calculator to optimise kernel launch shape.
--
sharedMem :: CUDA.DeviceProperties -> PreOpenAcc ExecOpenAcc aenv a -> Int -> Int
-- non-computation forms
sharedMem _ (Alet _ _)     _ = INTERNAL_ERROR(error) "sharedMem" "Let"
sharedMem _ (Alet2 _ _)    _ = INTERNAL_ERROR(error) "sharedMem" "Let2"
sharedMem _ (PairArrays _ _) _
                             = INTERNAL_ERROR(error) "sharedMem" "PairArrays"
sharedMem _ (Avar _)      _  = INTERNAL_ERROR(error) "sharedMem" "Avar"
sharedMem _ (Apply _ _)   _  = INTERNAL_ERROR(error) "sharedMem" "Apply"
sharedMem _ (Acond _ _ _) _  = INTERNAL_ERROR(error) "sharedMem" "Acond"
sharedMem _ (Use _)       _  = INTERNAL_ERROR(error) "sharedMem" "Use"
sharedMem _ (Unit _)      _  = INTERNAL_ERROR(error) "sharedMem" "Unit"
sharedMem _ (Reshape _ _) _  = INTERNAL_ERROR(error) "sharedMem" "Reshape"

-- skeleton nodes
sharedMem _ (Generate _ _)      _        = 0
sharedMem _ (Replicate _ _ _)   _        = 0
sharedMem _ (Index _ _ _)       _        = 0
sharedMem _ (Map _ _)           _        = 0
sharedMem _ (ZipWith _ _ _)     _        = 0
sharedMem _ (Permute _ _ _ _)   _        = 0
sharedMem _ (Backpermute _ _ _) _        = 0
sharedMem _ (Stencil _ _ _)      _       = 0
sharedMem _ (Stencil2 _ _ _ _ _) _       = 0
sharedMem _ (Fold  _ _ a)       blockDim = sizeOf (accType a) * blockDim
sharedMem _ (Fold1 _ a)         blockDim = sizeOf (accType a) * blockDim
sharedMem _ (Scanl _ x _)       blockDim = sizeOf (expType x) * blockDim
sharedMem _ (Scanr _ x _)       blockDim = sizeOf (expType x) * blockDim
sharedMem _ (Scanl' _ x _)      blockDim = sizeOf (expType x) * blockDim
sharedMem _ (Scanr' _ x _)      blockDim = sizeOf (expType x) * blockDim
sharedMem _ (Scanl1 _ a)        blockDim = sizeOf (accType a) * blockDim
sharedMem _ (Scanr1 _ a)        blockDim = sizeOf (accType a) * blockDim
sharedMem p (FoldSeg _ _ a _)   blockDim =
  (blockDim `div` CUDA.warpSize p) * 4 * F.sizeOf (undefined::Int32) + blockDim * sizeOf (accType a)
sharedMem p (Fold1Seg _ a _) blockDim =
  (blockDim `div` CUDA.warpSize p) * 4 * F.sizeOf (undefined::Int32) + blockDim * sizeOf (accType a)