我正在使用Haskell的加速库做一个有趣的副项目。我有一个我需要编写的函数,在纯Haskell中看起来像这样:
oddfac :: Int -> Int
oddfac n = product [1,3...n]
即。类似于阶乘函数,但只乘以奇数。我想在加速后端执行此功能,所以如果我理解正确,它需要成为Exp Int -> Exp Int
类型。但是,出于性能原因,库不允许在Exp
中评估任意表达式。幸运的是,我只需要为小值评估此函数,例如N'LT; = 7。我有想法定义一个预先计算的返回值的列表(或数组),以便简单地索引它将返回适当的值,并且每个评估将花费相同的时间量,这不是天真版本的情况。但是,我还没有找到办法做到这一点。我现在有两个问题:
1)有没有办法做到这一点,即定义一个硬编码数组,然后对其进行索引以在Exp a -> Exp b
类型的函数中检索适当的值?
2)我是否以有效的方式处理事情?我如何思考这个问题是否有任何明显的缺陷?
更新
以下作品基于@ ErikR的回答及后续评论:
module Test where
import Data.Array.Accelerate as A
import Prelude as P
oddfac :: Exp Int -> Exp Int
oddfac n = (use $ A.fromList (Z :. 6) [1, 1, 3, 3, 15, 15]) A.! (index1 n)
alloddfac :: Acc (Vector Int)
alloddfac = A.map oddfac $ use $ A.fromList (Z :. 3) [1, 3, 5]
答案 0 :(得分:2)
在我看来,您可以使用多种方法之一创建Acc (Array DIM1 Int)
,然后使用带有(!)
函数的Accelerate index1
来索引数组。
请注意,Vector a
是Array DIM1 a
的别名。
以下是如何创建Acc (Vector Int)
奇数阶乘(7个元素):
oddfacts :: Acc (Vector Int)
-- analogous to: take 7 $ scanl (*) 1 [3,5..]
oddfacts = A.scanl (*) (constant (1::Int)) $ A.enumFromStepN (A.index1 (constant (7::Int))) (constant (3::Int)) (constant (2::Int))
以及如何索引它:
foo :: Exp Int -> Exp Int
foo n = oddfacts A.! (A.index1 n)
以及如何使用条件:
bar :: Exp Int-> Exp Int
bar n = (n <=* (constant (7::Int))) ?
( oddfacts A.! (A.index1 n)
, constant (0::Int) -- return 0 if n > 7
)
警告 - 我实际上并没有运行此代码,但它会进行类型检查。
accelerate-examples包中的示例包含许多使用数组生成函数(A.generate,A.scanl等)和索引函数(A.index1,A.index2等)的代码。)
答案 1 :(得分:1)
你可以从Haskell“扔过篱笆”进入Exp
任意阵列到
(Shape sh, Elt e) => Lift Acc (Array sh e)
个实例。因此,您可以在Haskell中创建查找表,然后只需lift
:
import Data.Array.Accelerate as A hiding (product)
oddfac :: Int -> Int
oddfac n = product [1,3..n]
oddfacs :: Vector Int
oddfacs = A.fromFunction (Z :. 7) (\(Z :. i) -> oddfac i)
lut :: Acc (Vector Int)
lut = A.lift oddfacs
然后通过索引到查找表lut
来完成其余的@ ErikR答案。