Como criar dados para os benchmarks do Criterion?

10

Estou usando o critério para avaliar meu código Haskell. Eu estou fazendo alguns cálculos pesados para os quais eu preciso de dados aleatórios. Eu escrevi meu arquivo de benchmark principal assim:

main :: IO ()
main = newStdGen >>= defaultMain . benchmarks

benchmarks :: RandomGen g => g -> [Benchmark]
benchmarks gen =
   [
     bgroup "Group"
     [
       bench "MyFun" $ nf benchFun (dataFun gen)
     ]
   ]

Mantenho benchmarks e geradores de dados para eles em diferentes módulos:

benchFun :: ([Double], [Double]) -> [Double]
benchFun (ls, sig) = fun ls sig

dataFun :: RandomGen g => g -> ([Double], [Double])
dataFun gen = (take 5 $ randoms gen, take 1024 $ randoms gen)

Isso funciona, mas tenho duas preocupações. Primeiro, o tempo necessário para gerar dados aleatórios incluídos no benchmark? Eu encontrei uma pergunta que toca nesse assunto mas sinceramente falando, não posso aplicá-lo ao meu código. Para verificar se isso acontece, escrevi uma versão alternativa do meu gerador de dados dentro da IO mônada. Coloquei a lista de benchmarks com main, chamada de generator, extrai o resultado com < - e depois passei para a função de benchmarking. Não vi diferença no desempenho.

Minha segunda preocupação está relacionada à geração de dados aleatórios. No momento, o gerador, uma vez criado, não é atualizado, o que gera os mesmos dados em uma única execução. Este não é um grande problema, mas, no entanto, seria bom fazê-lo corretamente. Existe uma maneira legal de gerar diferentes dados aleatórios dentro de cada função * de dados? "Limpo" significa "sem fazer funções de dados adquirir StdGen dentro de IO"?

EDIT: Como observado no comentário abaixo, eu realmente não me importo com a aleatoriedade dos dados. O que é importante para mim é que o tempo necessário para gerar os dados não seja incluído no benchmark.

    
por Jan Stolarek 15.10.2012 в 15:05
fonte

2 respostas

6
  

Isso funciona, mas tenho duas preocupações. Primeiro, o tempo necessário para gerar dados aleatórios incluídos no benchmark?

Sim, seria. Toda a geração aleatória deve estar acontecendo preguiçosamente.

  

Para verificar se isso acontece, eu escrevi uma versão alternativa do meu gerador de dados dentro da IO mônada. Coloquei a lista de benchmarks com main, chamada de generator, extrai o resultado com < - e depois passei para a função de benchmarking. Não vi diferença no desempenho.

Isso é esperado (se eu entendi o que você quis dizer); os valores aleatórios de randoms gen não serão gerados até que sejam necessários (ou seja, dentro do loop de referência).

  

Existe uma maneira legal de gerar diferentes dados aleatórios dentro de cada função de dados *? "Limpo" significa "sem fazer funções de dados adquirir StdGen dentro de IO"?

Você precisa estar em IO ou criar um StdGen com uma semente inteira fornecida, com mkStdGen .

sua principal questão de como você deve tirar o material do pRNG de seus benchmarks, você deve ser capaz de avaliar a entrada aleatória totalmente antes seu defaultMain (benchmarks g) stuff, com evaluate e force como:

import Control.DeepSeq(force)
import Control.Exception(evaluate)
myBench g = do randInputEvaled <- evaluate $ force $ dataFun g
               defaultMain [
                    bench "MyFun" $ nf benchFun randInputEvaled
                    ...

onde force avalia seu argumento para a forma normal, mas isso ainda acontecerá preguiçosamente. Então, para que seja avaliado fora de bench , usamos evaluate para alavancar o sequenciamento monádico. Você também pode fazer coisas como chamar seq na cauda de cada uma das listas em sua tupla, etc., se quiser evitar as importações.

Esse tipo de coisa deve funcionar bem, a menos que você precise armazenar uma quantidade enorme de dados de teste na memória.

EDITAR : esse método também é uma boa ideia se você quiser obter seus dados de E / S, como ler no disco e não quiser mesclar seus valores de referência.

    
por jberryman 15.10.2012 / 15:37
fonte
0

Você pode tentar ler os dados aleatórios de um arquivo de disco. (Na verdade, se você estiver em algum sistema operacional semelhante ao Unix, você pode usar /dev/urandom .)

No entanto, dependendo da quantidade de dados que você precisa, o tempo de E / S pode diminuir o tempo de computação. Depende de quantos dados aleatórios você precisa.

(Por exemplo, se o seu benchmark ler números aleatórios e calcular sua soma, ele será limitado por E / S. Se o seu benchmark ler um número aleatório e fizer algum cálculo enorme baseado apenas nesse número, o I / O adiciona quase nenhuma sobrecarga em tudo.)

    
por MathematicalOrchid 15.10.2012 / 21:11
fonte