设计基本的Haskell基准测试代码

时间:2016-12-23 17:09:16

标签: haskell

我想在Haskell中编写一些基本的基准测试代码,即设计一个函数:

benchmark :: M a -> String -> Int -> M ()

给出action(相对于我自己的monad M的monadic值),用于消息传递目的的函数name和一些iterations,执行该操作与iterations一样多次,并在stdout上显示有关执行代码所花费时间的消息。出于这个问题的目的,我假设monad M是类MonadIO的一个实例。特别是我有一些功能:

 liftIO :: IO a -> M a 

花了很多时间在'真实世界Haskell'上,我被警告说“非严格的评估可以将性能测量和分析变成某个雷区”。但是,我并不是在寻找一个明确的答案,而是寻求关于如何最好地解决这个问题的建议和指导。

我天真的方法是分开时间测量代码:

benchmark action name iterations = do
  start <- liftIO getPOSIXTime
  runAction action iterations
  end <- liftIO getPOSIXTime
  let time = realToFrac $ (end - start) :: Double 
  liftIO $ printf "Benchmark: %s, %d iterations ran in %.3f seconds\n" 
    name iterations time

来自问题的真正危机,即将action作为循环运行:

runAction :: Monad m => m a -> Int -> m ()
runAction action iterations
  | iterations <= 0   = return ()
  | otherwise         = do
    action
    runAction action (iterations -1) 

我对我的解决方案的问题是,似乎代码花费更多时间在'样板'而不是运行action:如果我尝试对最简单的可能操作return ()进行基准测试,我对于100万次迭代,可能会得到1000毫秒的时间。我可以看到monad M的细节发挥了重要作用(将M替换为IO会将时间缩短到250毫秒而不是1000毫秒)。一百万个循环无效也可能在解释的pythonscheme中大约需要250毫秒(因此IO monad并不是那么糟糕),但它的速度要快得多C。通常Haskell速度非常快(距离C不远)。我尝试了一个涉及forM[1..1000000]但没有改进的解决方案。

有没有办法解决这个问题,因此'样板'代码不会压倒被测试的代码?

编辑:当使用@luqui建议的-O2编译器优化选项时,这个问题似乎消失了(即一百万个monadic循环无效,相对于其他语言而言,性能非常好,至少在我的情况下)。因此,就我所见,这个问题已经结束。

1 个答案:

答案 0 :(得分:2)

您可以尝试这种方式:

import React, { Component } from 'react';
import { getData } from './Utils';

class BoardContainer extends React.Component {
  constructor(props){
    super(props);
    this.state = { positions: [] };
  }

  componentWillMount(){
    var x = getData('positions');  //simplified code for debugging and example
    console.log(x);  //ISSUE: x is undefined
  }

  render() {
    return(
      <div>Testing Rendering Board Container
        //rendering code would be here (child component call)
      </div>
    )
  }
} 

或者,您可以重写runAction :: MonadIO m => m a -> Int -> m NominalDiffTime runAction action = fmap sum . flip replicateM action' where action' = do start <- liftIO getPOSIXTime action end <- liftIO getPOSIXTime let !delta = end - start return delta benchmark action name iterations = do time <- realToFrac <$> runAction action iterations liftIO $ printf "Benchmark: %s, %d iterations ran in %.3f seconds\n" name iterations (time :: Double)

runAction

或者,只需修复开销:

runAction = flip replicateM_