Ocaml表现vs去

时间:2017-12-14 10:02:34

标签: performance go ocaml

我正在尝试在Ocaml中实现utf8解码作为学习项目。为了检查性能,我正在对go标准库进行基准测试。

这是go代码:

package main

import (
    "fmt"
    "time"
    "unicode/utf8"
)

func main() {
    start := time.Now()

    for i := 0; i < 1000000000; i++ {
        utf8.ValidRune(23450)
    }

    elapsed := time.Since(start)
    fmt.Println(elapsed)
}

当我运行它时,我得到:

go build b.go
./b
344.979492ms

我决定在ocaml中写一个等价物:

let min = 0x0000
let max = 0x10FFFF

let surrogateMin = 0xD800
let surrogateMax = 0xDFFF

let validUchar c =
  if (0 <= c && c < surrogateMin) then
    true
  else if (surrogateMax < c && c <= max) then
    true
  else
    false

let time f x =
    let t = Sys.time () in
    let _ = f x in
    let t2 = Sys.time () in
    let diff = (t2 -. t) *. 1000. in
    print_endline ((string_of_float diff) ^ "ms")


let test () =
  for i = 0 to 1000000000 do
    let _ = validUchar 23450 in
    ()
  done

let () = time test ()

输出:

ocamlopt bMl.ml -o bMl
./bMl
2041.075ms

ocaml等价物基本上复制了来自https://golang.org/src/unicode/utf8/utf8.go#L517

的go stdlib的实现

为什么ocaml代码这么慢?

2 个答案:

答案 0 :(得分:8)

如您所见,您应该使用Unix.gettimeofday来衡量挂钟时间。但是,您可以使用Sys.opaque_identity来阻止OCaml优化无用操作,并且可以使用ignore来“返回单位”而不是通常的表达式值。共:

let time f x =
  let t = Unix.gettimeofday () in
  ignore (Sys.opaque_identity (f x));
  let t2 = Unix.gettimeofday () in
  ...

let test () =
  for i = 1 to 1_000_000_000 do
    ignore (Sys.opaque_identity (validUchar 23450));            
  done

注意i = 1,如果你想要十亿次迭代,你需要它(在添加下划线之前我无法分辨的数字是10亿,OCaml允许)。以前,您正在测量10亿加1次迭代。并非那就是差异。

validUchar的详细定义并没有使其表现受益。请写一个微基准并确认。

最后,在进行了上面建议的更改并以更自然的方式编写validUchar之后,我得到一个与Go运行时相同的OCaml运行时...在ocamlopt参数中添加-O3之后。并且很容易确认这不是由于编译器“优化操作” - 在f x中注释掉time调用导致运行时为0或接近0的值,如1.19e -06

不要因为你对这个问题的回答而气馁。但期望任何一种“为什么这个基准有这个结果?”对编程论坛的问题也会得到类似的回答。

答案 1 :(得分:5)

Sys.time不应该用于时间测量,因为它会返回处理器时间,而不是实时时间。 Unix.gettimeofday函数是一个更好的候选者。或者,您可以使用time命令从shell计算程序。

作为旁注,基准测试很难,而且很容易产生误导性的结果。在您的特定情况下,如果您打开优化,两个编译器都将删除计算,因为它们未被使用并将生成无效的代码,因此相当快:)