Ocaml表现vs去

问题描述 投票:0回答:2

我正在尝试在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代码这么慢?

performance go ocaml
2个回答
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。

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


5
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.