更具体地说,我有以下看似无害的小修复3程序:
{-# LANGUAGE QuasiQuotes #-}
import Prelude hiding (map, zipWith)
import System.Environment (getArgs)
import Data.Word (Word8)
import Data.Array.Repa
import Data.Array.Repa.IO.DevIL
import Data.Array.Repa.Stencil
import Data.Array.Repa.Stencil.Dim2
main = do
[s] <- getArgs
img <- runIL $ readImage s
let out = output x where RGB x = img
runIL . writeImage "out.bmp" . Grey =<< computeP out
output img = map cast . blur . blur $ blur grey
where
grey = traverse img to2D luminance
cast n = floor n :: Word8
to2D (Z:.i:.j:._) = Z:.i:.j
---------------------------------------------------------------
luminance f (Z:.i:.j) = 0.21*r + 0.71*g + 0.07*b :: Float
where
(r,g,b) = rgb (fromIntegral . f) i j
blur = map (/ 9) . convolve kernel
where
kernel = [stencil2| 1 1 1
1 1 1
1 1 1 |]
convolve = mapStencil2 BoundClamp
rgb f i j = (r,g,b)
where
r = f $ Z:.i:.j:.0
g = f $ Z:.i:.j:.1
b = f $ Z:.i:.j:.2
在我的2Ghz核心2双核笔记本电脑上处理640x420图像花了这么多时间:
real 2m32.572s
user 4m57.324s
sys 0m1.870s
我知道一些事情肯定是错误的,因为我在使用Repa 2的更复杂的算法上获得了更好的性能。在该API下,我发现的重大改进来自于在每次数组转换之前添加对'force'的调用(我理解这意味着每次调用地图,卷积,遍历等)。我无法弄清楚在维修3中做的类似事情 - 实际上我认为新的表现形式类型参数应该确保不存在关于何时需要强制数组的歧义?新的monadic接口如何适应这个方案?我已经阅读了Don S的精彩教程,但在Repair 2和3 API之间存在一些关键差距,这些差距在网上很少讨论AFAIK。
更简单的说,是否有一种最低效的方法来解决上述程序的效率?
答案 0 :(得分:10)
新的表示类型参数在需要时不会自动强制执行(这可能是一个很难解决的问题) - 您仍然需要手动强制执行。在Repa 3中,这是通过computeP函数完成的:
computeP
:: (Monad m, Repr r2 e, Fill r1 r2 sh e)
=> Array r1 sh e -> m (Array r2 sh e)
我个人真的不明白为什么它是monadic,因为你也可以使用Monad身份:
import Control.Monad.Identity (runIdentity)
force
:: (Repr r2 e, Fill r1 r2 sh e)
=> Array r1 sh e -> Array r2 sh e
force = runIdentity . computeP
所以,现在你的output
函数可以用适当的强制重写:
output img = map cast . f . blur . f . blur . f . blur . f $ grey
where ...
使用缩写f
使用辅助函数u
来辅助类型推断:
u :: Array U sh e -> Array U sh e
u = id
f = u . force
通过这些更改,加速非常显着 - 这是可以预期的,因为没有中间强制每个输出像素最终评估的结果远远超过必要(中间值不共享)。
您的原始代码:
real 0m25.339s
user 1m35.354s
sys 0m1.760s
强迫:
real 0m0.130s
user 0m0.320s
sys 0m0.028s
使用600x400 png测试,输出文件完全相同。
答案 1 :(得分:7)
computeP
是新force
。
在Repa 3中,您需要在Repair 2中使用computeP
的任何地方使用force
。
来自repa-examples的Laplace示例与您正在执行的操作类似。您还应该在cmap
函数中使用map
代替普通blur
。将在下周初在我的主页上发表一篇论文解释原因。