9

问题

我想在 Haskell 中模拟一个多值输出函数。Haskell 代码是生成的(不是手写的)——这是重要的信息,见下文:

这当然可以通过从函数返回一个元组来轻松完成,例如

f x y = (x+y, x-y)

但是当使用这样的函数时,我必须知道它返回什么样的元组:

...
(out_f_1, out_f_2)          = f a b
(out_g_1, out_g_2, out_g_3) = g out_f_1
...

依此类推...但是在生成代码时,我不知道可以说 f 的输出类型是什么,所以现在我正在使用该Data.List.Select包并通过以下方式模拟上述内容:

import Data.List.Select
...
out_f = f a b
out_g = g (sel1 outf)
...

问题是性能 - 在我的测试程序中,使用Data.List.Select的版本比手工编写的版本慢两倍。

这是非常明显的情况,因为Data.List.Select是使用classesand编写的instances,所以它使用某种运行时字典(如果我没记错的话)。(http://hackage.haskell.org/packages/archive/tuple/0.2.0.1/doc/html/src/Data-Tuple-Select.html#sel1

问题

我想问你是否有可能以某种方式将版本(使用Data.List.Select)编译为与手动制作的版本一样快?

我认为应该切换到编译器,这将告诉他为每次使用“实例化”类和接口(类似于 C++ 中的模板)。

基准

测试1.hs:

import qualified Data.Vector as V
import System.Environment
b :: Int -> Int
b x = x + 5
c x = b x + 1
d x = b x - 1
a x = c x + d x
main = do
   putStrLn "Starting..."
   args <- getArgs
   let iternum = read (head args) :: Int in do
      putStrLn $ show $ V.foldl' (+) 0 $ V.map (\i -> a (iternum-i))
         $ V.enumFromTo 1 iternum
      putStrLn "Done."

编译ghc -O3 Test1.hs

测试2.hs:

import qualified Data.Vector as V
import Data.Tuple.Select
import Data.Tuple.OneTuple

import System.Environment
b x = OneTuple $ x + 5
c x = OneTuple $ (sel1 $ b x) + 1
d x = OneTuple $ (sel1 $ b x) - 1
a x = OneTuple $ (sel1 $ c x) + (sel1 $ d x)
main = do
   putStrLn "Starting..."
   args <- getArgs
   let iternum = read (head args) :: Int in do
      putStrLn $ show $ V.foldl' (+) 0 $ V.map (\i -> sel1 $ a (iternum-i))
         $ V.enumFromTo 1 iternum
      putStrLn "Done."

编译ghc -O3 Test2.hs

结果

time ./Test1 10000000 = 5.54 s
time ./Test2 10000000 = 10.06 s
4

2 回答 2

3

我不确定,但可能值得尝试 http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/pragmas.html#specialize-pragma

于 2013-07-24T13:03:08.120 回答
0

好的,我发布的结果不准确 - 正如@sabauma 所说 - 如果您在启用优化的情况下编译它们,这两个代码会同时执行。

@tohava 的答案非常好,如果您想明确显示哪些功能需要专门化(请参阅上面的 @sabauma 评论)。

于 2013-07-25T10:23:35.630 回答