gpt4 book ai didi

ocaml - 为什么这种基于Lwt的看似并发的代码如此不一致

转载 作者:行者123 更新时间:2023-12-04 16:25:47 25 4
gpt4 key购买 nike

我正在尝试创建Lwt的并发示例,并提出了这个小样本

let () =
Lwt_main.run (
let start = Unix.time () in
Lwt_io.open_file Lwt_io.Input "/dev/urandom" >>= fun data_source ->
Lwt_unix.mkdir "serial" 0o777 >>= fun () ->
Lwt_list.iter_p
(fun count ->
let count = string_of_int count in
Lwt_io.open_file
~flags:[Unix.O_RDWR; Unix.O_CREAT]
~perm:0o777
~mode:Lwt_io.Output ("serial/file"^ count ^ ".txt") >>= fun h ->
Lwt_io.read ~count:52428800
data_source >>= Lwt_io.write_line h)
[0;1;2;3;4;5;6;7;8;9] >>= fun () ->
let finished = Unix.time () in
Lwt_io.printlf "Execution time took %f seconds" (finished -. start))

编辑:要求50GB它是:
“但是,这非常慢,而且基本上没有用。
是否需要以某种方式强制内部约束?”

编辑:我最初写的是要50 GB的请求,但从未完成,现在我有一个不同的要求50 MB的问题,执行几乎是即时执行的,du -sh报告的目录大小仅为80k。

编辑:我也尝试过用明确关闭文件句柄的代码,结果也一样。

我使用的是 OS X最新版本,并使用
ocamlfind ocamlopt -package lwt.unix main.ml -linkpkg -o Test
(我也尝试过 /dev/random,是的,我正在使用挂钟时间。)

最佳答案

因此,您的代码有一些问题。

第1期

主要问题是您对Lwt_io.read函数的理解不正确(没有人能责怪您!)。

val read : ?count : int -> input_channel -> string Lwt.t
(** [read ?count ic] reads at most [len] characters from [ic]. It
returns [""] if the end of input is reached. If [count] is not
specified, it reads all bytes until the end of input. *)

如果指定了 ~count:len,它将最多读取 len字符。最多意味着它可以读更少。但是,如果省略 count选项,则它将读取所有数据。我个人认为这种行为不是很直觉,即使不是很奇怪。因此,这最多意味着最多 len或更少,即,不能保证它将准确地读取 len字节。实际上,如果将支票添加到程序中:
 Lwt_io.read ~count:52428800 data_source >>= fun data ->
Lwt_io.printlf "Read %d bytes" (String.length data) >>= fun () ->
Lwt_io.write h data >>= fun () ->

您将看到,每次尝试它只会读取 4096字节:
Read 4096 bytes
Read 4096 bytes
Read 4096 bytes
Read 4096 bytes
Read 4096 bytes
Read 4096 bytes
Read 4096 bytes
Read 4096 bytes
Read 4096 bytes
Read 4096 bytes

为什么是 4096?因为这是默认的缓冲区大小。但这实际上并不重要。

第2期
Lwt_io模块实现了一个缓冲的IO。这意味着您的所有写入和读取操作都不会直接进入文件,而是会缓存在内存中。这意味着,您应该记住 flushclose。您的代码并没有完全关闭描述符,因此您可能会遇到这样的情况:程序终止后,某些缓冲区没有被清除。特别是 Lwt_io,在程序退出之前刷新所有缓冲区。但是,您不应该依赖此未记录的功能(将来,当您尝试其他任何缓冲的io(例如来自标准C库的fstream)时,它可能会打击您)。因此,请始终关闭文件(另一个问题是,今天的文件描述符是最宝贵的资源,而且很难发现它们的泄漏)。

第3期

不要使用 /dev/urandom/dev/random来测量io。对于前者,您将测量随机数生成器的性能,对于后者,您将测量机器中的熵流。两者都很慢。根据您的CPU速度,您很少会获得超过16 Mb/s的速度,而实际速度却远远低于16 Mb/s,因此 Lwt可以吞吐。从 /dev/zero读取并写入 /dev/null实际上将执行内存中的所有传输,并显示实际速度,这可以通过程序来实现。编写良好的程序仍将受到内核速度的限制。在下面提供的示例程序中,这将显示700 MB/s的平均速度。

第4期

如果您确实在追求性能,请不要使用缓冲的io。您将永远无法获得最大 yield 。例如, Lwt_io.read将首先在缓冲区读取,然后将创建 string并将数据复制到该字符串。如果确实需要一些性能,则应提供自己的缓冲。在大多数情况下,不需要这样做,因为 Lwt_io的性能很高。但是,如果您需要每秒处理数十兆字节,或者需要一些特殊的缓冲策略(非线性的东西),则可能需要考虑提供自己的缓冲。好消息是 Lwt_io允许您执行此操作。您可以看一下 example程序,该程序将测量 Lwt输入/输出的性能。它模仿了一个著名的 pv程序。

第5期

您期望通过并行运行线程来获得一些性能。问题是在您的测试中并没有地方。 /dev/random(以及 /dev/zero)是仅受CPU限制的一种设备。这与调用 random函数相同。它将始终为 available,因此不会在其上阻止任何系统调用。写入常规文件也不是实现并发的好地方。首先,通常只有一个硬盘驱动器,其中有一个写磁头。即使系统调用将阻塞并放弃对另一个线程的控制,这也会导致性能下降,因为两个线程现在将争夺头位置。如果您有SSD,那么标题将不会有任何竞争,但是性能会变差,因为您会破坏缓存。但幸运的是,通常不会在常规文件上进行写操作。因此,您的线程将因此运行,即它们将被序列化。

关于ocaml - 为什么这种基于Lwt的看似并发的代码如此不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31153693/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com