packages feed

hhlo-0.11.0.0: test/Test/Runtime/BufferGPU.hs

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

module Test.Runtime.BufferGPU (tests) where

import Control.Concurrent (threadDelay)
import Data.IORef (newIORef, readIORef, writeIORef)
import qualified Foreign.Concurrent as Conc (addForeignPtrFinalizer)
import qualified Data.Vector.Storable as V
import System.Mem (performMajorGC)
import Test.Tasty
import Test.Tasty.HUnit

import HHLO.Core.Types
import HHLO.Runtime.PJRT.Types
import HHLO.Runtime.Buffer
import HHLO.Runtime.Device (addressableDevices)
import HHLO.Runtime.PJRT.Plugin (withPJRTGPU)
import Test.Runtime.GPUResource (GPUResource(..))

tests :: IO GPUResource -> TestTree
tests getGPU = testGroup "Runtime.BufferGPU"
    [ testCase "gpu buffer round-trip f32" $ do
        GPUResource api client dev <- getGPU
        let input = V.fromList [1, 2, 3, 4, 5, 6] :: V.Vector Float
        buf <- toDeviceOn api client dev input [2, 3] bufferTypeF32
        result <- fromDeviceF32 api buf 6
        result @?= input

    , testCase "gpu buffer metadata" $ do
        GPUResource api client dev <- getGPU
        let input = V.fromList [1..12] :: V.Vector Float
        buf <- toDeviceOn api client dev input [3, 4] bufferTypeF32
        dims <- bufferDimensions api buf
        dims @?= [3, 4]
        et <- bufferElementType api buf
        et @?= bufferTypeF32
        sz <- bufferOnDeviceSize api buf
        sz @?= (12 * 4)

    -- Regression test for the post-session buffer finalizer segfault.
    -- Without the registry fix, this crashes because the GPU buffer's
    -- internal CUDA references become dangling after PJRT_Client_Destroy.
    , testCase "buffer finalizer safe after GPU session closes" $ do
        ref <- newIORef False
        _ <- withPJRTGPU $ \api client -> do
            devs <- addressableDevices api client
            let dev = head devs
            let inp = V.fromList [1.0, 2.0] :: V.Vector Float
            buf <- toDeviceOn api client dev inp [2] bufferTypeF32
            let PJRTBuffer fp = buf
            Conc.addForeignPtrFinalizer fp $ writeIORef ref True
            return buf
        performMajorGC
        -- Poll until the witness finalizer fires (max ~1s).
        let wait n = do
                done <- readIORef ref
                if done
                    then return ()
                    else if n > 100
                        then assertFailure "buffer finalizer did not run"
                        else threadDelay 10000 >> wait (n + 1)
        wait 0
    ]