Haskell 使用向量的风格与性能
代码如下:Haskell 使用向量的风格与性能,haskell,lambda,pointfree,Haskell,Lambda,Pointfree,代码如下: {-# LANGUAGE FlexibleContexts #-} import Data.Int import qualified Data.Vector.Unboxed as U import qualified Data.Vector.Generic as V {-# NOINLINE f #-} -- Note the 'NO' --f :: (Num r, V.Vector v r) => v r -> v r -> v r --f :: (V.Vec
{-# LANGUAGE FlexibleContexts #-}
import Data.Int
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Generic as V
{-# NOINLINE f #-} -- Note the 'NO'
--f :: (Num r, V.Vector v r) => v r -> v r -> v r
--f :: (V.Vector v Int64) => v Int64 -> v Int64 -> v Int64
--f :: (U.Unbox r, Num r) => U.Vector r -> U.Vector r -> U.Vector r
f :: U.Vector Int64 -> U.Vector Int64 -> U.Vector Int64
f = V.zipWith (+) -- or U.zipWith, it doesn't make a difference
main = do
let iters = 100
dim = 221184
y = U.replicate dim 0 :: U.Vector Int64
let ans = iterate ((f y)) y !! iters
putStr $ (show $ U.sum ans)
{-# LANGUAGE FlexibleContexts #-}
import Control.DeepSeq
import Data.Int
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Generic as V
{-# NOINLINE f #-} -- Note the 'NO'
--f :: (Num r, V.Vector v r) => v r -> v r -> v r
--f :: (V.Vector v Int64) => v Int64 -> v Int64 -> v Int64
--f :: (U.Unbox r, Num r) => U.Vector r -> U.Vector r -> U.Vector r
f :: U.Vector Int64 -> U.Vector Int64 -> U.Vector Int64
f = V.zipWith (+)
main = do
let iters = 100
dim = 221184
y = U.replicate dim 0 :: U.Vector Int64
let ans = iterate ((f y)) y !! iters
putStr $ (show $ U.sum ans)
我用GHC7.6.2
和-O2
编译,运行了1.7秒
我尝试了几种不同版本的f
:
fx=U.zip带(+)x
fx=(U.zipWith(+)x)。id
f x y=U.zip带(+)x y
内联f
不会改变任何内容)
我还注意到,如果我使f
多态(使用上面三个签名中的任何一个),即使使用“快速”定义(即2或3),它也会减慢…精确到1.7秒。这让我想知道最初的问题是否是由于(缺乏)类型推断,即使我明确给出了向量类型和元素类型的类型
我还对以q为模添加整数感兴趣:
newtype Zq q i = Zq {unZq :: i}
与添加Int64
s时一样,如果我使用指定的每种类型编写函数
h :: U.Vector (Zq Q17 Int64) -> U.Vector (Zq Q17 Int64) -> U.Vector (Zq Q17 Int64)
与保留任何多态性相比,我获得了一个数量级的性能
h :: (Modulus q) => U.Vector (Zq q Int64) -> U.Vector (Zq q Int64) -> U.Vector (Zq q Int64)
但我至少应该能够删除特定的幻影类型!它应该被编译出来,因为我正在处理一个newtype
以下是我的问题:
经济放缓从何而来
在f
的第2版和第3版中发生了什么以任何方式影响性能的情况?对我来说,(相当于)编码风格可能会影响这样的性能,这似乎是一个bug。在Vector之外,是否有其他部分应用功能或其他风格选择会影响性能的示例
为什么多态性会使我的速度降低一个数量级,而与多态性的位置无关(即在向量类型中,在Num
类型中,两者都是,或幻影类型)?我知道多态性会使代码变慢,但这很荒谬。这附近有黑客吗
编辑1
我在矢量库页面上提交了一个文件。我发现了一个与这个问题有关的问题
编辑2
在从@kqr的回答中获得一些见解之后,我重写了这个问题。
以下是原件供参考
--------------原始问题--------------------
代码如下:
{-# LANGUAGE FlexibleContexts #-}
import Data.Int
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Generic as V
{-# NOINLINE f #-} -- Note the 'NO'
--f :: (Num r, V.Vector v r) => v r -> v r -> v r
--f :: (V.Vector v Int64) => v Int64 -> v Int64 -> v Int64
--f :: (U.Unbox r, Num r) => U.Vector r -> U.Vector r -> U.Vector r
f :: U.Vector Int64 -> U.Vector Int64 -> U.Vector Int64
f = V.zipWith (+) -- or U.zipWith, it doesn't make a difference
main = do
let iters = 100
dim = 221184
y = U.replicate dim 0 :: U.Vector Int64
let ans = iterate ((f y)) y !! iters
putStr $ (show $ U.sum ans)
{-# LANGUAGE FlexibleContexts #-}
import Control.DeepSeq
import Data.Int
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Generic as V
{-# NOINLINE f #-} -- Note the 'NO'
--f :: (Num r, V.Vector v r) => v r -> v r -> v r
--f :: (V.Vector v Int64) => v Int64 -> v Int64 -> v Int64
--f :: (U.Unbox r, Num r) => U.Vector r -> U.Vector r -> U.Vector r
f :: U.Vector Int64 -> U.Vector Int64 -> U.Vector Int64
f = V.zipWith (+)
main = do
let iters = 100
dim = 221184
y = U.replicate dim 0 :: U.Vector Int64
let ans = iterate ((f y)) y !! iters
putStr $ (show $ U.sum ans)
我用GHC7.6.2
和-O2
编译,运行了1.7秒
我尝试了几种不同版本的f
:
fx=U.zip带(+)x
fx=(U.zipWith(+)x)。美国部队
fx=(U.zipWith(+)x)。控制。深度顺序力)
fx=(U.zipWith(+)x)。(\z->z`seq`z)
fx=(U.zipWith(+)x)。id
f x y=U.zip带(+)x y
版本1与原始版本相同,版本2在0.111秒内运行,版本3-6在0.09秒内运行(并且内联f
不会改变任何内容)
因此,数量级的减速似乎是由于懒惰,因为force
起到了帮助作用,但我不确定懒惰是从哪里来的。未绑定的类型不允许懒惰,对吗
我试图编写一个严格版本的iterate
,认为向量本身一定是惰性的:
{-# INLINE iterate' #-}
iterate' :: (NFData a) => (a -> a) -> a -> [a]
iterate' f x = x `seq` x : iterate' f (f x)
但是对于无点版本的f
,这一点都没有帮助
我还注意到了其他一些事情,这可能只是巧合和转移注意力:
如果我使f
多态(使用上面三个签名中的任何一个),即使使用“快速”定义,它也会减慢速度…精确到1.7秒。这让我想知道,最初的问题是否是由于(缺乏)类型推断,即使一切都应该很好地推断出来
以下是我的问题:
经济放缓从何而来
为什么使用组合会有帮助,而使用严格的迭代则没有帮助李>
为什么U.force
比DeepSeq.force
更糟糕?我不知道U.force
应该做什么,但它听起来很像DeepSeq.force
,似乎也有类似的效果
为什么多态性会使我的速度降低一个数量级,而与多态性的位置无关(即在向量类型中,在Num
类型中,或者两者都有)
为什么版本5和版本6(两者都不应该有任何严格含义)与严格函数一样快
正如@kqr所指出的,问题似乎并不在于严格。因此,我编写函数的方式导致使用泛型zipWith
,而不是未绑定的特定版本。这仅仅是GHC和向量库之间的侥幸,还是这里有更一般的说法?虽然我没有你想要的确切答案,但有两件事可能会对你有所帮助
第一件事是x`seq`x
在语义和计算上与x
是一样的。维基上说的是关于seq
:
关于seq
的一个常见误解是seq x
“评估”x
。嗯,有点seq
不会仅仅凭借源文件中的存在来计算任何内容,它所做的只是引入一个值对另一个值的人工数据依赖关系:当计算seq
的结果时,还必须计算第一个参数(某种程度上;参见下文)
例如,假设x::Integer
,则seq x b
的行为本质上类似于如果x==0,则b else b
–无条件地等于b
,但一路上强制执行x
。特别是,表达式x`seq`x
是完全冗余的,并且始终具有与仅写入x
完全相同的效果
第一段说的是,写seqabb
并不意味着a
会变魔术
{-# LANGUAGE BangPatterns #-}
iterate' f !x = x : iterate f (f x)