重新创建并加快API相似性

Haskell repa库用于在CPU上自动进行并行数组计算。加速库是GPU上的自动数据并行处理。 API非常相似,具有相同的N维数组表示形式。甚至可以使用fromRepa中的toRepaData.Array.Accelerate.IO在加速和REPA数组之间切换:

fromRepa :: (Shapes sh sh', Elt e) => Array A sh e -> Array sh' e
toRepa   :: Shapes sh sh'          => Array sh' e  -> Array A sh e

有多个后端可以加速,包括LLVM,CUDA和FPGA(请参见http://www.cse.unsw.edu.au/~keller/Papers/acc-cuda.pdf的图2)。我发现repa backend可以加速,尽管该库似乎未得到维护。鉴于repa和Acceleration编程模型相似,我希望有一种优雅的方式在它们之间进行切换,即一次编写的函数可以使用repa的R.computeP或用Accelerator的后端之一执行。使用CUDA run函数。

两个非常相似的功能:在南瓜上加速和加速

采取简单的图像处理阈值功能。如果灰度像素值小于50,则将其设置为0,否则将保留其值。这是对南瓜的作用:




以下代码展示了repa并加速了实现:

module Main where

import qualified Data.Array.Repa as R
import qualified Data.Array.Repa.IO.BMP as R
import qualified Data.Array.Accelerate as A
import qualified Data.Array.Accelerate.IO as A
import qualified Data.Array.Accelerate.Interpreter as A

import Data.Word

-- Apply threshold over image using accelerate (interpreter)
thresholdAccelerate :: IO ()
thresholdAccelerate = do
  img <- either (error . show) id `fmap` A.readImageFromBMP "pumpkin-in.bmp"
  let newImg = A.run $ A.map evalPixel (A.use img)
  A.writeImageToBMP "pumpkin-out.bmp" newImg
    where
      -- *** Exception: Prelude.Ord.compare applied to EDSL types
      evalPixel :: A.Exp A.Word32 -> A.Exp A.Word32
      evalPixel p = if p > 50 then p else 0

-- Apply threshold over image using repa
thresholdRepa :: IO ()
thresholdRepa = do
  let arr :: IO (R.Array R.U R.DIM2 (Word8,Word8,Word8))
      arr = either (error . show) id `fmap` R.readImageFromBMP "pumpkin-in.bmp"
  img <- arr
  newImg <- R.computeP (R.map applyAtPoint img)
  R.writeImageToBMP "pumpkin-out.bmp" newImg
  where
    applyAtPoint :: (Word8,Word8,Word8) -> (Word8,Word8,Word8)
    applyAtPoint (r,g,b) =
        let [r',g',b'] = map applyThresholdOnPixel [r,g,b]
        in (r',g',b')
    applyThresholdOnPixel x = if x > 50 then x else 0

data BackendChoice = Repa | Accelerate

main :: IO ()
main = do
  let userChoice = Repa -- pretend this command line flag
  case userChoice of
    Repa       -> thresholdRepa
    Accelerate -> thresholdAccelerate

问题:我只能写一次吗?
thresholdAcceleratethresholdRepa的实现非常相似。有没有一种精巧的方法来编写一次数组处理功能,然后以编程方式在交换机中选择多核CPU(repa)或GPU(加速)?我可以考虑根据要CPU还是GPU选择导入,即导入Data.Array.Accelerate.CUDAData.Array.Repa以执行Acc a类型的操作,方法是:
run :: Arrays a => Acc a -> a

或者,使用类型类,例如大概是这样的:

main :: IO ()
main = do
  let userChoice = Repa -- pretend this is a command line flag
  action <- case userChoice of
    Repa       -> applyThreshold :: RepaBackend ()
    Accelerate -> applyThreshold :: CudaBackend ()
  action

还是对于我想为CPU和GPU表示的每个并行数组函数,我必须两次实现它-一次是通过repa库,一次是是使用加速库?

最佳答案

简短的答案是,目前,您不幸需要同时编写两个版本。

但是,我们正在致力于CPU对Accelerate的支持,这将消除对Repa版本代码的需要。特别是,Accelerate最近获得了针对GPU和CPU的基于LLVM的新后端:https://github.com/AccelerateHS/accelerate-llvm

这个新的后端仍然不完整,有故障且处于试验阶段,但是我们计划使其成为当前CUDA后端的可行替代方案。

关于arrays - 编写一次并行数组Haskell表达式,以repa在CPU和GPU上运行并加速,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23202145/

10-10 19:11