Использует ли Lwt зависимости данных для увеличения параллелизма? - PullRequest
3 голосов
/ 22 сентября 2019

Я пытаюсь понять, что конкретно lwt делает в нескольких примерах:

Если у меня есть:

let%lwt x = f () in
let%lwt y = g () in
return ()

Запускает ли это f тогда g, или так как yне полагается на x, будет ли он работать параллельно?

Ответы [ 2 ]

4 голосов
/ 22 сентября 2019

Этот конкретный пример последовательно запускает f () и g (), т. Е. g () не запускается до тех пор, пока не будет решено обещание, возвращаемое f ().

Способ увидеть,при взгляде на

let%lwt x = e in
e'

, чтобы понять, что e' становится телом обратного вызова, который будет выполняться только при наличии 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', эти функции возвращают и ожидают обещания:

let x' = f () in
let y' = g () in
let%lwt x = x' in
let%lwt y = y' in
return ()

(Иответьте на заголовок: Lwt не использует зависимости данных. Я не думаю, что библиотека могла бы иметь доступ к такого рода информации о зависимостях данных).

2 голосов
/ 24 сентября 2019

В вашем коде нет, потому что вы используете Lwt.t как монаду, а не как аппликатив.

Монады

Возможно, вы уже знакомы с асинхронным вводом-выводом и функциями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.

Монада - это универсальный тип 'a t, который имеет некоторую функцию bind : 'a t -> ('a -> 'b t) -> 'b t и некоторую функцию return : 'a -> 'a t.(Эти функции также должны следовать определенным законам, но я отвлекся.) Очевидно, что тип 'a Lwt.t с функциями Lwt.bind и Lwt.return образует монаду.

Монады - это общий шаблон функционального программирования, когда одинхочет представить какой-то «эффект» или «вычисление», в данном случае асинхронный ввод-вывод.Монады являются мощными, потому что функция 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 ().

Применимые

Функциональный шаблон программирования, связанный с монадой, является аппликативным.Аппликатив - это универсальный тип 'a t с функцией map : ('a -> 'b) -> 'a t -> 'b t, функцией return : 'a -> 'a t и функцией both : 'a t * 'b t -> ('a * 'b) t.Однако, в отличие от монад, аппликативам не нужно иметь bind : 'a t -> ('a -> 'b t) -> 'b t, что означает, что с одними аппликативами более поздние вычисления не могут зависеть от предыдущих.Все монады являются аппликативными, но не все аппликативные являются монадами.

Поскольку 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, вы можете просто написать перевод напрямую.

...