libtorch-ffi 2.0.0.1 → 2.0.1.0
raw patch · 11 files changed
+146/−12 lines, 11 filesdep +text
Dependencies added: text
Files
- libtorch-ffi.cabal +3/−1
- src/Torch/Internal/Const.hs +17/−0
- src/Torch/Internal/GC.hs +21/−8
- src/Torch/Internal/Managed/Type/Context.hs +4/−0
- src/Torch/Internal/Managed/Type/Generator.hs +9/−0
- src/Torch/Internal/Managed/Type/Tensor/Tensor0.hs +10/−0
- src/Torch/Internal/Unmanaged/Type/Context.hs +7/−0
- src/Torch/Internal/Unmanaged/Type/Generator.hs +16/−2
- src/Torch/Internal/Unmanaged/Type/Tensor/Tensor0.hs +15/−0
- test/MpsSpec.hs +40/−0
- test/Spec.hs +4/−1
libtorch-ffi.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: libtorch-ffi-version: 2.0.0.1+version: 2.0.1.0 -- The prefix(2.0) of this version("2.0.0.0") is the same as libtorch's one. synopsis: Haskell bindings for PyTorch description: This package provides Haskell bindings to libtorch, the C++ library underlying PyTorch, specifically designed for the Hasktorch ecosystem.@@ -148,6 +148,7 @@ , sysinfo >= 0.1.1 && < 0.2 , async >= 2.2.5 && < 2.3 , libtorch-ffi-helper >= 2.0.0 && < 2.1+ , text >= 2.0.2 && < 2.1 ghc-options: -fplugin GHC.NotExport.Plugin extra-libraries: c10 , torch@@ -194,6 +195,7 @@ -- libtorch-1.3 does not work this grad-function. But grad of higher level api works. -- , BackwardSpec , CudaSpec+ , MpsSpec , GeneratorSpec default-language: Haskell2010 build-depends: base
src/Torch/Internal/Const.hs view
@@ -26,9 +26,20 @@ C.include "<ATen/ScalarType.h>" C.include "<ATen/core/Reduction.h>" C.include "<c10/core/Layout.h>"+C.include "<torch/version.h>" +torchVersionMajor :: CInt+torchVersionMajor = [C.pure| int { TORCH_VERSION_MAJOR } |] +torchVersionMinor :: CInt+torchVersionMinor = [C.pure| int { TORCH_VERSION_MINOR } |] +torchVersionPatch :: CInt+torchVersionPatch = [C.pure| int { TORCH_VERSION_PATCH } |]++torchVersion :: String+torchVersion = show torchVersionMajor ++ "." ++ show torchVersionMinor ++ "." ++ show torchVersionPatch+ kByte :: ScalarType kByte = [C.pure| int8_t { (int8_t) at::ScalarType::Byte } |] @@ -86,6 +97,9 @@ kCUDA :: DeviceType kCUDA = [C.pure| int16_t { (int16_t) at::DeviceType::CUDA } |] +kMPS :: DeviceType+kMPS = [C.pure| int16_t { (int16_t) at::DeviceType::MPS } |]+ kMKLDNN :: DeviceType kMKLDNN = [C.pure| int16_t { (int16_t) at::DeviceType::MKLDNN } |] @@ -129,6 +143,9 @@ bCUDA :: Backend bCUDA = [C.pure| int { (int) at::Backend::CUDA } |]++bMPS :: Backend+bMPS = [C.pure| int { (int) at::Backend::MPS } |] bHIP :: Backend bHIP = [C.pure| int { (int) at::Backend::HIP } |]
src/Torch/Internal/GC.hs view
@@ -9,6 +9,7 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE OverloadedStrings #-} module Torch.Internal.GC where @@ -19,13 +20,19 @@ import Data.List (isPrefixOf) import Foreign.C.Types import GHC.ExecutionStack-import Language.C.Inline.Cpp.Exceptions+import Language.C.Inline.Cpp.Exception import System.Environment (lookupEnv) import System.IO (hPutStrLn, stderr) import System.IO.Unsafe (unsafePerformIO) import System.Mem (performGC) import System.SysInfo+import qualified Data.Text.Encoding as T+import qualified Data.Text.Encoding.Error as T+import qualified Data.Text as T+import Data.ByteString (ByteString)+import qualified Data.ByteString as B + foreign import ccall unsafe "hasktorch_finalizer.h showWeakPtrList" c_showWeakPtrList :: CInt -> IO () @@ -53,29 +60,32 @@ instance Exception HasktorchException +bsToChars :: ByteString -> String+bsToChars = T.unpack . T.decodeUtf8With T.lenientDecode+ unsafeThrowableIO :: forall a m. MonadThrow m => IO a -> m a-unsafeThrowableIO a = unsafePerformIO $ (pure <$> a) `catch` (\(CppStdException msg) -> pure . throwM $ HasktorchException msg)+unsafeThrowableIO a = unsafePerformIO $ (pure <$> a) `catch` (\(CppStdException _ msg _) -> pure . throwM $ HasktorchException ("Exception: " <> bsToChars msg)) prettyException :: IO a -> IO a prettyException func =- func `catch` \a@(CppStdException message) -> do+ func `catch` \a@(CppStdException _ message _) -> do flag <- lookupEnv "HASKTORCH_DEBUG" when (flag /= Just "0") $ do mst <- showStackTrace case mst of Just st -> hPutStrLn stderr st Nothing -> hPutStrLn stderr "Cannot show stacktrace"- hPutStrLn stderr message+ B.hPutStr stderr message throwIO a {-# INLINE prettyException #-} retryWithGC' :: Int -> IO a -> IO a retryWithGC' count func =- func `catch` \a@(CppStdException message) ->- if isPrefixOf msgOutOfMemory message+ func `catch` \a@(CppStdException _ message _) ->+ if B.isPrefixOf msgOutOfMemory message then if count <= 0- then throwIO $ userError $ "Too many calls to performGC, " ++ message+ then throwIO $ userError $ bsToChars $ "Too many calls to performGC, " <> message else do performGC mallocTrim 0@@ -83,8 +93,11 @@ retryWithGC' (count -1) func else throwIO a where- msgOutOfMemory :: String+#ifdef darwin_HOST_OS+ msgOutOfMemory = "MPS backend out of memory"+#else msgOutOfMemory = "Exception: CUDA out of memory."+#endif {-# INLINE retryWithGC' #-} retryWithGC :: IO a -> IO a
src/Torch/Internal/Managed/Type/Context.hs view
@@ -58,6 +58,10 @@ :: IO (CBool) hasMKL = _cast0 Unmanaged.hasMKL +hasMPS+ :: IO (CBool)+hasMPS = _cast0 Unmanaged.hasMPS+ hasLAPACK :: IO (CBool) hasLAPACK = _cast0 Unmanaged.hasLAPACK
src/Torch/Internal/Managed/Type/Generator.hs view
@@ -28,6 +28,10 @@ -> IO (ForeignPtr Generator) newCUDAGenerator _device_index = _cast1 Unmanaged.newCUDAGenerator _device_index +newMPSGenerator+ :: IO (ForeignPtr Generator)+newMPSGenerator = _cast0 Unmanaged.newMPSGenerator+ newCPUGenerator :: Word64 -> IO (ForeignPtr Generator)@@ -77,3 +81,8 @@ :: ForeignPtr Generator -> IO CBool generator_is_hip _obj = _cast1 Unmanaged.generator_is_hip _obj++generator_is_mps+ :: ForeignPtr Generator+ -> IO CBool+generator_is_mps _obj = _cast1 Unmanaged.generator_is_mps _obj
src/Torch/Internal/Managed/Type/Tensor/Tensor0.hs view
@@ -61,6 +61,11 @@ -> IO (ForeignPtr Tensor) tensor_cuda = cast1 Unmanaged.tensor_cuda +tensor_mps+ :: ForeignPtr Tensor+ -> IO (ForeignPtr Tensor)+tensor_mps = cast1 Unmanaged.tensor_mps+ tensor_data_ptr :: ForeignPtr Tensor -> IO (Ptr ())@@ -116,6 +121,11 @@ :: ForeignPtr Tensor -> IO (CBool) tensor_is_cuda = cast1 Unmanaged.tensor_is_cuda++tensor_is_mps+ :: ForeignPtr Tensor+ -> IO (CBool)+tensor_is_mps = cast1 Unmanaged.tensor_is_mps tensor_is_hip :: ForeignPtr Tensor
src/Torch/Internal/Unmanaged/Type/Context.hs view
@@ -79,6 +79,13 @@ )); }|] +hasMPS+ :: IO (CBool)+hasMPS =+ [C.throwBlock| bool { return (at::hasMPS(+ ));+ }|]+ hasLAPACK :: IO (CBool) hasLAPACK =
src/Torch/Internal/Unmanaged/Type/Generator.hs view
@@ -25,6 +25,7 @@ C.context $ C.cppCtx <> mempty { C.ctxTypesTable = typeTable } C.include "<ATen/detail/CUDAHooksInterface.h>"+C.include "<ATen/detail/MPSHooksInterface.h>" C.include "<ATen/CPUGeneratorImpl.h>" C.include "<ATen/core/Generator.h>" @@ -33,6 +34,8 @@ newCUDAGenerator _device_index = getDefaultCUDAGenerator _device_index >>= generator_clone +newMPSGenerator = getDefaultMPSGenerator >>= generator_clone+ newCPUGenerator :: Word64 -> IO (Ptr Generator)@@ -41,8 +44,6 @@ $(uint64_t _seed_in))); }|] -- generator_set_current_seed :: Ptr Generator -> Word64@@ -104,12 +105,25 @@ [C.throwBlock| bool { return ((*$(at::Generator* _obj)).device().is_hip()); }|] +generator_is_mps+ :: Ptr Generator+ -> IO CBool+generator_is_mps _obj =+ [C.throwBlock| bool { return ((*$(at::Generator* _obj)).device().is_mps());+ }|]+ getDefaultCUDAGenerator :: Word16 -> IO (Ptr Generator) getDefaultCUDAGenerator _device_index = [C.throwBlock| at::Generator* { return new at::Generator(at::detail::getCUDAHooks().getDefaultCUDAGenerator( $(uint16_t _device_index)));+ }|]++getDefaultMPSGenerator+ :: IO (Ptr Generator)+getDefaultMPSGenerator =+ [C.throwBlock| at::Generator* { return new at::Generator(at::detail::getMPSHooks().getDefaultMPSGenerator()); }|] getDefaultCPUGenerator
src/Torch/Internal/Unmanaged/Type/Tensor/Tensor0.hs view
@@ -92,6 +92,13 @@ )); }|] +tensor_mps+ :: Ptr Tensor+ -> IO (Ptr Tensor)+tensor_mps _obj =+ [C.throwBlock| at::Tensor* { return new at::Tensor((*$(at::Tensor* _obj)).to("mps"));+ }|]+ tensor_data_ptr :: Ptr Tensor -> IO (Ptr ())@@ -178,6 +185,14 @@ -> IO (CBool) tensor_is_cuda _obj = [C.throwBlock| bool { return (*$(at::Tensor* _obj)).is_cuda(+ );+ }|]++tensor_is_mps+ :: Ptr Tensor+ -> IO (CBool)+tensor_is_mps _obj =+ [C.throwBlock| bool { return (*$(at::Tensor* _obj)).is_mps( ); }|]
+ test/MpsSpec.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+module MpsSpec (main, spec) where++import Test.Hspec+import Control.Exception.Safe (bracket,catch,throwIO)+import Control.Monad (forM_,forM)+import Data.Int+import Foreign+import Torch.Internal.Const+import Torch.Internal.Type+import Torch.Internal.Managed.Type.TensorOptions+import Torch.Internal.Managed.Type.Tensor+import Torch.Internal.Managed.Type.IntArray+import Torch.Internal.Managed.Type.Context+import Torch.Internal.Managed.Native+import Torch.Internal.GC++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "MpsSpec" $ do+ it "When MPS is out of memory, do GC and retry" $ do+ flag <- hasMPS+ monitorMemory $ do+ forM_ [0..1000] $ \i -> do -- 80MByte x 1000 = 80GByte+ dims <- fromList [1000,1000,10] -- 8 byte x 10M = 80MByte+ to <- device_D $ if flag == 0 then kCPU else kMPS+ tod <- tensorOptions_dtype_s to kFloat+ zeros_lo dims tod+ return ()++fromList :: [Int64] -> IO (ForeignPtr IntArray)+fromList dims = do+ ary <- newIntArray+ forM_ dims $ intArray_push_back_l ary+ return ary
test/Spec.hs view
@@ -4,13 +4,16 @@ import Test.Hspec (hspec) import qualified BasicSpec import qualified CudaSpec+import qualified MpsSpec import qualified GeneratorSpec import qualified MemorySpec main :: IO () main = hspec $ do BasicSpec.spec-#ifndef darwin_HOST_OS+#ifdef darwin_HOST_OS+ MpsSpec.spec+#else CudaSpec.spec #endif GeneratorSpec.spec