packages feed

cuda-0.13.0.0: examples/src/deviceQueryDrv/DeviceQuery.hs

{-# LANGUAGE CPP             #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

module Main where

import Control.Monad
import Foreign.Marshal.Utils                            ( toBool )
import Numeric
import Prelude                                          hiding ( (<>) )
import Text.PrettyPrint
import Text.Printf

import Foreign.CUDA.Driver.Device                       ( Device(..) )
import Foreign.CUDA.Analysis                            as CUDA
import qualified Foreign.CUDA.Driver                    as CUDA


main :: IO ()
main = do
  version    <- CUDA.driverVersion
  printf "CUDA device query (Driver API, statically linked)\n"
  printf "CUDA driver version %d.%d\n" (version`div`1000) ((version`mod`100)`div`10)
  printf "CUDA API version %d.%d\n" (CUDA.libraryVersion`div`1000) ((CUDA.libraryVersion`mod`100)`div`10)

  CUDA.initialise []
  numDevices <- CUDA.count

  if numDevices == 0
     then printf "There are no available devices that support CUDA\n"
     else printf "Detected %d CUDA capable device%s\n" numDevices (if numDevices > 1 then "s" else "")

  infos   <- forM [0 .. numDevices-1] $ \n -> do
    dev <- CUDA.device n
    prp <- CUDA.props dev
    return (n, dev, prp)

  forM_ infos $ \(n, dev, prp) -> do
    p2p <- statP2P dev prp infos
    printf "\nDevice %d: %s\n%s\n" n (deviceName prp) (statDevice prp)
    unless (null p2p) $ printf "%s\n" p2p


statDevice :: DeviceProperties -> String
statDevice dev@DeviceProperties{..} =
  let
      DeviceResources{..} = deviceResources dev

      grid (x,y)          = int x <+> char 'x' <+> int y
      cube (x,y,z)        = int x <+> char 'x' <+> int y <+> char 'x' <+> int z

      props =
        [("CUDA capability:",                           text $ show computeCapability)
        ,("CUDA cores:",                                text $ printf "%d cores in %d multiprocessors (%d cores/MP)" (coresPerMP * multiProcessorCount) multiProcessorCount coresPerMP)
        ,("Global memory:",                             text $ showBytes totalGlobalMem)
        ,("Constant memory:",                           text $ showBytes totalConstMem)
        ,("Shared memory per block:",                   text $ showBytes sharedMemPerBlock)
        ,("Registers per block:",                       int regsPerBlock)
        ,("Warp size:",                                 int warpSize)
        ,("Maximum threads per multiprocessor:",        int maxThreadsPerMultiProcessor)
        ,("Maximum threads per block:",                 int maxThreadsPerBlock)
        ,("Maximum grid dimensions:",                   cube maxGridSize)
        ,("Maximum block dimensions:",                  cube maxBlockSize)
        ,("GPU clock rate:",                            text . showFreq $ clockRate * 1000)
        ,("Memory clock rate:",                         text . showFreq $ memClockRate * 1000)
        ,("Memory bus width:",                          int memBusWidth <> text "-bit")
        ,("L2 cache size:",                             text $ showBytes cacheMemL2)
        ,("Maximum texture dimensions",                 empty)
        ,("  1D:",                                      int  maxTextureDim1D)
        ,("  2D:",                                      grid maxTextureDim2D)
        ,("  3D:",                                      cube maxTextureDim3D)
        ,("Texture alignment:",                         text $ showBytes textureAlignment)
        ,("Maximum memory pitch:",                      text $ showBytes memPitch)]++

        $(if CUDA.libraryVersion >= 13000 then [|
        [("Concurrent copy and kernel execution:",      bool concurrentKernels <> text (printf " with %d copy engine%s" asyncEngineCount (if asyncEngineCount > 1 then "s" else "")))]
        |] else [|
        [("Concurrent kernel execution:",               bool concurrentKernels)
        ,("Concurrent copy and execution:",             bool deviceOverlap <> text (printf ", with %d copy engine%s" asyncEngineCount (if asyncEngineCount > 1 then "s" else "")))]
        |])++

        [("Runtime limit on kernel execution:",         bool kernelExecTimeoutEnabled)
        ,("Integrated GPU sharing host memory:",        bool integrated)
        ,("Host page-locked memory mapping:",           bool canMapHostMemory)
        ,("ECC memory support:",                        bool eccEnabled)
        ,("Unified addressing (UVA):",                  bool unifiedAddressing)]++

        $(if CUDA.libraryVersion >= 8000 then [|
        [("Single to double precision performance:",    text $ printf "%d : 1" singleToDoublePerfRatio)
        ,("Supports compute pre-emption:",              bool preemption)]
        |] else [|[]|])++

        $(if CUDA.libraryVersion >= 9000 then [|
        [("Supports cooperative launch:",               bool cooperativeLaunch)]
        |] else [|[]|])++

        $(if CUDA.libraryVersion >= 9000 && CUDA.libraryVersion < 13000 then [|
        [("Supports multi-device cooperative launch:",  bool cooperativeLaunchMultiDevice)]
        |] else [|[]|])++

        [("PCI bus/location:",                          int (busID pciInfo) <> char '/' <> int (deviceID pciInfo))
        ,("Compute mode:",                              text (show computeMode))
        ]
  in
  render
    $ hang (table props) 4
    $ text (describe computeMode)


statP2P :: Device -> DeviceProperties -> [(Int, Device, DeviceProperties)] -> IO String
statP2P dev prp infos
  | CUDA.libraryVersion < 4000          = return []

  -- Only Fermi and later can support P2P
  | computeCapability prp < Compute 2 0 = return []
  | otherwise
  = let
        go []                 = return []
        go ((m, peer, pp):is) =
          if dev == peer
            then go is
            else do
              access <- CUDA.accessible dev peer
              rest   <- go is
              return $ (printf "  Device %d (%s):" m (deviceName pp), bool access) : rest
    in do
      access <- go infos
      return
        $ if null access
            then []
            else render
               $ table (("Peer-to-Peer access to:", empty) : access)


table :: [(String, Doc)] -> Doc
table kvs
  = nest 2
  $ vcat
  $ map (\(k,v) -> text (pad k) <+> v) kvs
  where
    pad v = take width $ v ++ repeat ' '
    width = maximum $ map (length . fst) kvs

bool :: Bool -> Doc
bool True  = text "Yes"
bool False = text "No"

showFreq :: Integral i => i -> String
showFreq x = showFFloatSIBase Nothing 1000 (fromIntegral x :: Double) "Hz"

showBytes :: Integral i => i -> String
showBytes x = showFFloatSIBase (Just 0) 1024 (fromIntegral x :: Double) "B"

showFFloatSIBase :: RealFloat a => Maybe Int -> a -> a -> ShowS
showFFloatSIBase p b n
  = showString
  $ showFFloat p n' (' ':si_unit)
  where
    n'          = n / (b ^^ pow)
    pow         = (-4) `max` floor (logBase b n) `min` 4        :: Int
    si_unit     = case pow of
                       -4 -> "p"
                       -3 -> "n"
                       -2 -> "µ"
                       -1 -> "m"
                       0  -> ""
                       1  -> "k"
                       2  -> "M"
                       3  -> "G"
                       4  -> "T"
                       _  -> error "out of range!"