packages feed

hhlo-0.1.0.0: examples/28-gpu-matmul-bench.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

module Main where

import qualified Data.Vector.Storable as V
import Data.Int (Int64)
import Data.Time.Clock (diffUTCTime, getCurrentTime)

import HHLO.Core.Types
import HHLO.EDSL.Ops
import HHLO.IR.AST (FuncArg(..), TensorType(..))
import HHLO.IR.Builder
import HHLO.IR.Pretty
import HHLO.Runtime.PJRT.Plugin
import HHLO.Runtime.PJRT.Types
import HHLO.Runtime.Device
import HHLO.Runtime.Compile
import HHLO.Runtime.Execute
import HHLO.Runtime.Buffer

matmulSize :: Int
matmulSize = 4096

main :: IO ()
main = withPJRTGPU $ \api client -> do
    putStrLn $ "Matrix multiply benchmark: " ++ show matmulSize ++ "x" ++ show matmulSize

    mDev <- defaultGPUDevice api client
    dev <- maybe (error "No GPU found") return mDev

    let modu = moduleFromBuilder @'[4096, 4096] @'F32 "main"
            [ FuncArg "arg0" (TensorType [4096, 4096] F32)
            , FuncArg "arg1" (TensorType [4096, 4096] F32)
            ]
            $ do
                x <- arg @'[4096, 4096] @'F32
                y <- arg @'[4096, 4096] @'F32
                z <- matmul x y
                return z

    putStrLn "Compiling..."
    exec <- compile api client (render modu)

    let n = matmulSize * matmulSize
        inputA = V.replicate n (1.0 :: Float)
        inputB = V.replicate n (1.0 :: Float)
        dims = [fromIntegral matmulSize, fromIntegral matmulSize] :: [Int64]

    putStrLn "Uploading to GPU..."
    bufA <- toDeviceOn api client dev inputA dims bufferTypeF32
    bufB <- toDeviceOn api client dev inputB dims bufferTypeF32

    putStrLn "Executing on GPU..."
    t0 <- getCurrentTime
    [bufOut] <- executeOn api exec dev [bufA, bufB]
    result <- fromDeviceF32 api bufOut n
    t1 <- getCurrentTime

    let gpuTime = realToFrac (diffUTCTime t1 t0) :: Double
    putStrLn $ "GPU time (including D2H): " ++ show gpuTime ++ "s"

    -- Verify a corner element: each output element is sum of 4096 * 1.0 * 1.0 = 4096.0
    let corner = result V.! 0
    putStrLn $ "Corner element: " ++ show corner ++ " (expected 4096.0)"

    if abs (corner - 4096.0) < 0.1
        then putStrLn "Verification PASSED"
        else putStrLn "Verification FAILED"