好吧,事实证明我在我的程序代码中定义了这个函数:
st_zipOp :: (a -> a -> a) -> Stream a -> Stream a -> Stream a
st_zipOp f xs ys = St.foldr (\x r -> st_map (f x) r) xs ys
它做它看起来做的事情。它使用 type 的内部运算符压缩(多次应用运算符,是的)两个 type 元素Stream a
,这是一个类似列表的类型a
。定义非常简单。
一旦我以这种方式定义了函数,我就尝试了另一个版本:
st_zipOp :: (a -> a -> a) -> Stream a -> Stream a -> Stream a
st_zipOp = St.foldr . (st_map .)
据我所知,这与上面的定义完全相同。它只是先前定义的无点版本。
但是,我想检查是否有任何性能变化,我发现,确实,无点版本使程序运行得稍差(无论是在内存上还是在时间上)。
为什么会这样?如果有必要,我可以编写一个重现此行为的测试程序。
如果这有所作为,我正在编译-O2
。
简单的测试用例
我编写了以下代码,试图重现上述行为。这次我使用了列表,性能的变化不太明显,但仍然存在。这是代码:
opEvery :: (a -> a -> a) -> [a] -> [a] -> [a]
opEvery f xs ys = foldr (\x r -> map (f x) r) xs ys
opEvery' :: (a -> a -> a) -> [a] -> [a] -> [a]
opEvery' = foldr . (map .)
main :: IO ()
main = print $ sum $ opEvery (+) [1..n] [1..n]
where
n :: Integer
n = 5000
使用opEvery
(显式参数版本)的分析结果:
total time = 2.91 secs (2906 ticks @ 1000 us, 1 processor)
total alloc = 1,300,813,124 bytes (excludes profiling overheads)
分析结果使用opEvery'
(无点版本):
total time = 3.24 secs (3242 ticks @ 1000 us, 1 processor)
total alloc = 1,300,933,160 bytes (excludes profiling overheads)
但是,我希望这两个版本是等效的(在所有意义上)。