Haskell 如何将cuda DevicePtr用作加速阵列
我正在尝试使用从外部代码返回的(在CUDA land中称为a)作为with 我在下面编写的代码有些有效:Haskell 如何将cuda DevicePtr用作加速阵列,haskell,cuda,gpu,ffi,accelerate-haskell,Haskell,Cuda,Gpu,Ffi,Accelerate Haskell,我正在尝试使用从外部代码返回的(在CUDA land中称为a)作为with 我在下面编写的代码有些有效: import Data.Array.Accelerate (Acc, Array, DIM1, Z(Z), (:.)((:.)), use) import qualified Data.Array.Accelerate as Acc import Data.Array.Accelerate.Array.Data (GArrayData(AD_Float), uns
import Data.Array.Accelerate
(Acc, Array, DIM1, Z(Z), (:.)((:.)), use)
import qualified Data.Array.Accelerate as Acc
import Data.Array.Accelerate.Array.Data
(GArrayData(AD_Float), unsafeIndexArrayData)
import Data.Array.Accelerate.Array.Sugar
(Array(Array), fromElt, toElt)
import Data.Array.Accelerate.Array.Unique
(UniqueArray, newUniqueArray)
import Data.Array.Accelerate.LLVM.PTX (run)
import Foreign.C.Types (CULLong(CULLong))
import Foreign.CUDA.Driver (DevicePtr(DevicePtr))
import Foreign.ForeignPtr (newForeignPtr_)
import Foreign.Ptr (intPtrToPtr)
-- A foreign function that uses cuMemAlloc() and cuMemCpyHtoD() to
-- create data on the GPU. The CUdeviceptr (initialized by cuMemAlloc)
-- is returned from this function. It is a CULLong in Haskell.
--
-- The data on the GPU is just a list of the 10 floats
-- [0.0, 1.0, 2.0, ..., 8.0, 9.0]
foreign import ccall "mytest.h mytestcuda"
cmyTestCuda :: IO CULLong
-- | Convert a 'CULLong' to a 'DevicePtr'.
--
-- A 'CULLong' is the type of a CUDA @CUdeviceptr@. This function
-- converts a raw 'CULLong' into a proper 'DevicePtr' that can be
-- used with the cuda Haskell package.
cullongToDevicePtr :: CULLong -> DevicePtr a
cullongToDevicePtr = DevicePtr . intPtrToPtr . fromIntegral
-- | This function calls 'cmyTestCuda' to get the 'DevicePtr', and
-- wraps that up in an accelerate 'Array'. It then uses this 'Array'
-- in an accelerate computation.
accelerateWithDataFromC :: IO ()
accelerateWithDataFromC = do
res <- cmyTestCuda
let DevicePtr ptrToXs = cullongToDevicePtr res
foreignPtrToXs <- newForeignPtr_ ptrToXs
uniqueArrayXs <- newUniqueArray foreignPtrToXs :: IO (UniqueArray Float)
let arrayDataXs = AD_Float uniqueArrayXs :: GArrayData UniqueArray Float
let shape = Z :. 10 :: DIM1
xs = Array (fromElt shape) arrayDataXs :: Array DIM1 Float
ys = Acc.fromList shape [0,2..18] :: Array DIM1 Float
usedXs = use xs :: Acc (Array DIM1 Float)
usedYs = use ys :: Acc (Array DIM1 Float)
computation = Acc.zipWith (+) usedXs usedYs
zs = run computation
putStrLn $ "zs: " <> show z
然而,从阅读accelerate和accelerate llvm ptx源代码来看,这似乎不应该起作用
在大多数情况下,它似乎像一个加速数组
携带一个指向主机内存中数组数据的指针,以及一个唯一标识数组
的值。在执行计算时,accelerate将根据需要将阵列数据从主机内存加载到GPU内存中,并使用Unique
索引的HashMap
对其进行跟踪
在上面的代码中,我使用指向GPU数据的指针直接创建了一个数组。这似乎不应该起作用,但在上面的代码中似乎起作用
然而,有些事情不起作用。例如,试图打印出xs
(我的数组
,指针指向GPU数据)会因segfault而失败。这是有意义的,因为Array
的Show
实例只是尝试peek
主机指针中的数据。此操作失败,因为它不是主机指针,而是GPU指针:
-- Trying to print xs causes a segfault.
putStrLn $ "xs: " <> show xs
--尝试打印xs会导致SEG故障。
putStrLn$“xs:“显示xs”
有没有合适的方法可以使用CUDADevicePtr
并将其直接用作加速阵列
?事实上,我很惊讶上面的方法已经起到了同样的作用;我无法复制
这里的问题之一是设备内存与执行上下文隐式关联;一个上下文中的指针在不同的上下文中无效,即使在同一GPU上也是如此(除非在这些上下文之间显式启用对等内存访问)
因此,这个问题实际上有两个组成部分:
以其理解的方式将外来数据导入Accelerate;及
确保后续的加速计算在可访问此内存的上下文中执行
解决方案
下面是我们将用于在GPU上生成数据的C代码:
#包括
#包括
#包括
CUdeviceptr生成gpu数据()
{
CUresult状态=CUDA_成功;
CUdeviceptr d_arr;
常数int N=32;
浮动h_arr[N];
对于(int i=0;i
以及使用它的Haskell/Accelerate代码:
{-#语言外来函数接口#-}
导入Data.Array.Accelerate作为
将Data.Array.Accelerate.Array.Sugar导入为Sugar
将Data.Array.Accelerate.Array.Data作为AD导入
将Data.Array.Accelerate.Array.Remote.LRU导入为LRU
将Data.Array.Accelerate.LLVM.PTX导入为PTX
将Data.Array.Accelerate.LLVM.PTX.Foreign导入为PTX
将Foreign.CUDA.Driver作为CUDA导入
导入文本.Printf
main::IO()
main=do
--初始化CUDA并创建执行上下文。由此我们也创造了
--我们的加速程序将在其中运行。
--
CUDA.initialise[]
发展
-- Trying to print xs causes a segfault.
putStrLn $ "xs: " <> show xs