以下是代码:
{-# 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)
我使用ghc 7.6.2
和-O2
编译,运行时间为1.7秒。
我尝试了几个不同版本的f
:
f x = U.zipWith (+) x
f x = (U.zipWith (+) x) . id
f x y = U.zipWith (+) x y
版本1与原版相同,而版本2和版本3在0.09秒内运行(INLINING
f
不会改变任何内容)。
我还注意到,如果我使f
多态(上面有三个签名中的任何一个),即使使用“快速”定义(即2或3),它也会慢下来......到1.7秒。这让我想知道原始问题是否可能是由于(缺乏)类型推断,即使我明确给出了Vector类型和元素类型的类型。
我也有兴趣添加模q
的整数:
newtype Zq q i = Zq {unZq :: i}
当添加Int64
时,如果我编写一个指定每种类型的函数,
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中以任何方式影响性能的情况如何?对我来说似乎是一个错误(编码风格)会影响这样的性能。在Vector之外还有其他示例,其中部分应用函数或其他样式选择会影响性能吗?Num
类型,两者或幻像类型中)?我知道多态性会使代码变慢,但这很荒谬。周围有黑客吗?编辑1
我向Vector库页面提交了issue。我找到了与此问题有关的GHC issue。
EDIT2
在从@ kqr的回答中获得一些见解后,我重写了这个问题。 以下是原件供参考。
-------------- ORIGINAL QUESTION --------------------
以下是代码:
{-# 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)
我使用ghc 7.6.2
和-O2
编译,运行时间为1.7秒。
我尝试了几个不同版本的f
:
f x = U.zipWith (+) x
f x = (U.zipWith (+) x) . U.force
f x = (U.zipWith (+) x) . Control.DeepSeq.force)
f x = (U.zipWith (+) x) . (\z -> z `seq` z)
f x = (U.zipWith (+) x) . id
f x y = U.zipWith (+) x y
版本1与原版相同,版本2在0.111秒内运行,版本3-6在0.09秒内运行(INLINING
f
不会改变任何内容。)< / p>
因此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秒。这让我想知道原始问题是否可能是由于(缺乏)类型推断造成的,即使所有内容都应该很好地推断出来。
以下是我的问题:
force
撰写帮助,但使用严格的iterate
却没有? U.force
比DeepSeq.force
更差?我不知道U.force
应该做什么,但听起来很像DeepSeq.force
,似乎也有类似的效果。Num
类型中,或两者都有)?正如@kqr指出的那样,问题似乎并不严格。因此,我编写函数的方式导致使用泛型zipWith
而不是Unboxed特定版本。这只是GHC和Vector库之间的侥幸,还是有更普遍的东西可以在这里说出来?
答案 0 :(得分:13)
虽然我没有你想要的明确答案,但有两件事可以帮助你。
首先,x `seq` x
在语义和计算上都与x
完全相同。维基说seq
:
关于
seq
的一个常见误解是seq x
“评估”x
。好吧,有点。seq
仅仅根据源文件中的现有值来评估任何内容,它所做的只是将一个值的人工数据依赖性引入另一个值:当评估seq
的结果时,第一个参数还必须(有点;见下文)进行评估。例如,假设
x :: Integer
,则seq x b
的行为与if x == 0 then b else b
基本相同 - 无条件等于b
,但在此过程中强制x
。特别是,表达式x `seq` x
完全是多余的,并且始终与仅写x
具有完全相同的效果。
第一段所说的是写seq a b
并不意味着a
会立即神奇地评估,这意味着a
会在b
后立即进行评估需要进行评估。这可能会在程序的后期发生,也可能永远不会发生。当你从那个角度看待它时,显而易见的是seq x x
是一种冗余,因为它只是说,“只要x
需要评估就评估x
。”如果你刚刚写了x
,那当然会发生什么。
这对您有两个含义:
你的“严格” iterate'
函数实际上并不比没有seq
时更严格。事实上,我很难想象iterate
函数如何变得比现在更严格。你不能严格限制列表的尾部,因为它是无限的。你可以做的主要是强制“累加器”,f x
,但这样做并没有给我的系统带来任何显着的性能提升。[1]
抓一点。您的严格iterate'
与我的爆炸模式版本完全相同。见评论。
写(\z -> z `seq` z)
并没有给你一个严格的身份功能,我认为这是你想要的。事实上,共同的身份功能与您将获得的一样严格 - 它会在需要时立即评估其结果。
然而,我偷看了GHC生成的核心
U.zipWith (+) y
和
U.zipWith (+) y . id
我的未经训练的眼睛只能发现一个很大的区别。第一个只使用普通Data.Vector.Generic.zipWith
(这里你的多态性重合可能发挥作用 - 如果GHC选择泛型zipWith
,它当然会表现为好像代码是多态的!)而后者有将这个单一函数调用分解为近90行状态monad代码和解压缩的机器类型。
状态monad代码看起来几乎就像你用命令式语言编写的循环和破坏性更新,因此我认为它适用于它运行的机器。如果我不是那么匆忙,我会花更长的时间来看看它究竟是如何工作的,以及为什么GHC突然决定它需要一个紧密的循环。我和其他想要看一看的人一样,为自己添加了生成的核心。[2]
[1]:沿途强制累加器:(这就是你已经做过的,我误解了代码!)
{-# LANGUAGE BangPatterns #-}
iterate' f !x = x : iterate f (f x)