Let 是否利用数据依赖性来增加并行性
Does Lwt utilise data dependencies to increase paralleism
我试图通过几个例子来弄清楚 lwt 具体在做什么:
如果我有:
let%lwt x = f () in
let%lwt y = g () in
return ()
这个 运行 f 然后 g,或者因为 y 不依赖 x 会 运行 两者并行吗?
那个特定的例子 运行s f ()
和 g ()
按顺序,即 g ()
直到承诺 return 之后才开始 return =14=]已解决。
看到这个的方法是,当看到
let%lwt x = e in
e'
实现 e'
成为回调的 body,只有当 x
可用时才会 运行。所以,在题中的代码中,Lwt首先看到:
(* Do this first. *)
let%lwt x = f () in
(* Do this once is available. *)
let%lwt y = g () in
return ()
并且,一旦 x
可用,就剩下
(* Do this next. *)
let%lwt y = g () in
(* Do this once y is available. *)
return ()
为了避免这种序列化,首先调用f ()
和g ()
,没有任何干预let%lwt
,将变量绑定到承诺x'
,y'
这些函数 return,并等待承诺:
let x' = f () in
let y' = g () in
let%lwt x = x' in
let%lwt y = y' in
return ()
(回答标题,Lwt 不使用数据依赖。我不认为图书馆可以访问这种数据依赖信息)。
在您的代码中,不,因为您将 Lwt.t
用作 monad,而不是应用程序。
单子
您可能已经熟悉异步 IO 以及函数 Lwt.bind : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t
和 Lwt.return : 'a -> 'a Lwt.t
。不过,为了以防万一,我将简要回顾一下:
Lwt.bind promise callback
等待 promise
,并根据结果调用 callback
,取回另一个承诺。
Lwt.return data
创建一个解析为 data
. 的承诺
monad 是一种泛型类型 'a t
,它具有某些功能 bind : 'a t -> ('a -> 'b t) -> 'b t
和某些功能 return : 'a -> 'a t
。 (这些函数也必须遵循一定的规律,但我跑题了。)显然,类型 'a Lwt.t
与函数 Lwt.bind
和 Lwt.return
构成了一个 monad。
Monad 是一种常见的函数式编程模式,当人们想要表示某种 "effect" 或 "computation," 时,在这种情况下是异步 IO。 Monad 之所以强大,是因为 bind
函数让后面的计算依赖于前面的计算结果。如果 m : 'a t
表示导致 'a
的某些计算,并且 f : 'a -> 'b t
是使用 'a
执行导致 'b
的计算的函数,则bind m f
使得 f
依赖于 m
.
的结果
在Lwt.bind promise callback
的情况下,callback
取决于promise
的结果。 callback
中的代码不能 运行 直到 promise
被解析。
写的时候
let%lwt x = f () in
let%lwt y = g () in
return ()
你真的在写Lwt.bind (f ()) (fun x -> Lwt.bind (g ()) (fun y -> return ()))
。因为 g ()
在回调中,所以在 f ()
解决之前它不是 运行。
应用程序
与 monad 相关的函数式编程模式是 applicative。应用程序是一个泛型类型 'a t
,具有函数 map : ('a -> 'b) -> 'a t -> 'b t
、函数 return : 'a -> 'a t
和函数 both : 'a t * 'b t -> ('a * 'b) t
。然而,与 monad 不同的是,applicatives 不需要 bind : 'a t -> ('a -> 'b t) -> 'b t
,这意味着单独使用 applicatives,以后的计算不能依赖于以前的计算。所有 monad 都是应用程序,但并非所有应用程序都是 monad。
因为 g ()
不依赖于 f ()
的结果,您的代码可以重写为使用 both
:
let (let*) = bind
let (and*) = both
let* x = f ()
and* y = g () in
return ()
此代码转换为 bind (fun (x, y) -> return ()) (both (f ()) (g ()))
。 f ()
和 g ()
出现在 bind
的回调之外,这意味着它们立即 运行 并且可以并行等待。 both
将 f ()
和 g ()
合并为一个承诺。
(let*)
和 (and*)
运算符是 OCaml 4.08 的新增功能。如果你使用的是较早版本的OCaml,直接写翻译即可。
我试图通过几个例子来弄清楚 lwt 具体在做什么:
如果我有:
let%lwt x = f () in
let%lwt y = g () in
return ()
这个 运行 f 然后 g,或者因为 y 不依赖 x 会 运行 两者并行吗?
那个特定的例子 运行s f ()
和 g ()
按顺序,即 g ()
直到承诺 return 之后才开始 return =14=]已解决。
看到这个的方法是,当看到
let%lwt x = e in
e'
实现 e'
成为回调的 body,只有当 x
可用时才会 运行。所以,在题中的代码中,Lwt首先看到:
(* Do this first. *)
let%lwt x = f () in
(* Do this once is available. *)
let%lwt y = g () in
return ()
并且,一旦 x
可用,就剩下
(* Do this next. *)
let%lwt y = g () in
(* Do this once y is available. *)
return ()
为了避免这种序列化,首先调用f ()
和g ()
,没有任何干预let%lwt
,将变量绑定到承诺x'
,y'
这些函数 return,并等待承诺:
let x' = f () in
let y' = g () in
let%lwt x = x' in
let%lwt y = y' in
return ()
(回答标题,Lwt 不使用数据依赖。我不认为图书馆可以访问这种数据依赖信息)。
在您的代码中,不,因为您将 Lwt.t
用作 monad,而不是应用程序。
单子
您可能已经熟悉异步 IO 以及函数 Lwt.bind : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t
和 Lwt.return : 'a -> 'a Lwt.t
。不过,为了以防万一,我将简要回顾一下:
Lwt.bind promise callback
等待promise
,并根据结果调用callback
,取回另一个承诺。Lwt.return data
创建一个解析为data
. 的承诺
monad 是一种泛型类型 'a t
,它具有某些功能 bind : 'a t -> ('a -> 'b t) -> 'b t
和某些功能 return : 'a -> 'a t
。 (这些函数也必须遵循一定的规律,但我跑题了。)显然,类型 'a Lwt.t
与函数 Lwt.bind
和 Lwt.return
构成了一个 monad。
Monad 是一种常见的函数式编程模式,当人们想要表示某种 "effect" 或 "computation," 时,在这种情况下是异步 IO。 Monad 之所以强大,是因为 bind
函数让后面的计算依赖于前面的计算结果。如果 m : 'a t
表示导致 'a
的某些计算,并且 f : 'a -> 'b t
是使用 'a
执行导致 'b
的计算的函数,则bind m f
使得 f
依赖于 m
.
在Lwt.bind promise callback
的情况下,callback
取决于promise
的结果。 callback
中的代码不能 运行 直到 promise
被解析。
写的时候
let%lwt x = f () in
let%lwt y = g () in
return ()
你真的在写Lwt.bind (f ()) (fun x -> Lwt.bind (g ()) (fun y -> return ()))
。因为 g ()
在回调中,所以在 f ()
解决之前它不是 运行。
应用程序
与 monad 相关的函数式编程模式是 applicative。应用程序是一个泛型类型 'a t
,具有函数 map : ('a -> 'b) -> 'a t -> 'b t
、函数 return : 'a -> 'a t
和函数 both : 'a t * 'b t -> ('a * 'b) t
。然而,与 monad 不同的是,applicatives 不需要 bind : 'a t -> ('a -> 'b t) -> 'b t
,这意味着单独使用 applicatives,以后的计算不能依赖于以前的计算。所有 monad 都是应用程序,但并非所有应用程序都是 monad。
因为 g ()
不依赖于 f ()
的结果,您的代码可以重写为使用 both
:
let (let*) = bind
let (and*) = both
let* x = f ()
and* y = g () in
return ()
此代码转换为 bind (fun (x, y) -> return ()) (both (f ()) (g ()))
。 f ()
和 g ()
出现在 bind
的回调之外,这意味着它们立即 运行 并且可以并行等待。 both
将 f ()
和 g ()
合并为一个承诺。
(let*)
和 (and*)
运算符是 OCaml 4.08 的新增功能。如果你使用的是较早版本的OCaml,直接写翻译即可。