修复并加速 API 相似度
Haskell repa 库用于在 CPU 上自动并行数组计算。加速库是 GPU 上的自动数据并行化。这些 API 非常相似,具有相同的 N 维数组表示。人们甚至可以在加速和修复阵列之间切换fromRepa
and toRepa
in Data.Array.Accelerate.IO
:
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(参见图2)http://www.cse.unsw.edu.au/~keller/Papers/acc-cuda.pdf http://www.cse.unsw.edu.au/~keller/Papers/acc-cuda.pdf)。我发现了一个修复后端 https://github.com/blambo/accelerate-repa为了加速,尽管该库似乎没有得到维护。鉴于 repa 和加速编程模型相似,我希望有一种优雅的方式在它们之间切换,即编写一次的函数可以使用 repa 的 R.computeP 或加速的后端之一执行,例如与 CUDArun https://github.com/AccelerateHS/accelerate-cuda/blob/master/Data/Array/Accelerate/CUDA.hs#L232功能。
两个非常相似的函数:Repa 和 Accelerate on a Pumpkin
采取一个简单的图像处理阈值函数。如果灰度像素值小于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
问:我可以只写一次吗?
的实施thresholdAccelerate
and thresholdRepa
非常相似。是否有一种优雅的方法可以编写一次数组处理函数,然后以编程方式在交换机中选择多核 CPU(repa)或 GPU(加速)?我可以考虑根据我想要 CPU 还是 GPU 来选择导入,即导入其中之一Data.Array.Accelerate.CUDA
or Data.Array.Repa
执行类型的动作Acc a
with:
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 库,另一次使用加速库?