Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-ordering statements for more optimal parallel future execution #138

Open
spockz opened this issue Aug 16, 2015 · 3 comments
Open

Re-ordering statements for more optimal parallel future execution #138

spockz opened this issue Aug 16, 2015 · 3 comments
Milestone

Comments

@spockz
Copy link

spockz commented Aug 16, 2015

It is a well known issue with for-comprehensions where the futures in the code block below are not executed as parallel as possible:

for {
  val1 <- getSomeFuture1()
  val2 <- getSomeFuture2()
} yield ...

We can manually solve this by rewriting the code as the code block:

val future1 = getSomeFuture1()
val future2 = getSomeFuture2()
for {
  val1 <- future1
  val2 <- future2
} yield ...

This transformation can be done only when the following two criteria are met:

  1. The creation of future2 doesn't depend on the result of future1; and,
  2. The evaluation of the value of future1 doesn't influence the evaluation of getSomeFuture2 and future2.

This manual transformation is quite tedious. This transformation is pretty mechanical and therefore ideally suited for a macro or compiler optimisation. Also, we need to a way to verify the two conditions hold, unless we leave that to the developer.

The reason why I suggest performing the transformation on the level of futures instead of await/async is so it can be used in more scenarios.

@suriyanto
Copy link

👍

@retronym
Copy link
Member

Also, we need to a way to verify the two conditions hold, unless we leave that to the developer.

The compiler won't be able to help out here, we don't have any framework for purity analysis.

But I think it would be okay to have an Async.asyncAggressive alternative entry point to let the developer opt in to the reordering. (Or maybe AsyncAggressive.async).

To start experimenting with this, you could create AsyncAggressive in the same way that Async is constructed, customizing postAnfTransform to perform the reordering.

@retronym
Copy link
Member

Here's an example of the transform that would be needed:

Original program:

def f: Future[Int] = ...
async {
  await(f) + await(f)
}

ANF tree:

[async] ANF transform expands to:
 {
  ();
  val awaitable$macro$2 = f;
  val await$macro$3 = scala.async.Async.await[Int](awaitable$macro$2);
  val awaitable$macro$4 = f;
  val await$macro$5 = scala.async.Async.await[Int](awaitable$macro$4);
  val x$macro$6 = await$macro$5;
  await$macro$3.+(x$macro$6)
}

The new transform would then kick in and result in:

[async] Reordering transform expands to:
 {
  ();
  val awaitable$macro$2 = f;
  val awaitable$macro$4 = f;
  val await$macro$3 = scala.async.Async.await[Int](awaitable$macro$2);
  val await$macro$5 = scala.async.Async.await[Int](awaitable$macro$4);
  val x$macro$6 = await$macro$5;
  await$macro$3.+(x$macro$6)
}

@retronym retronym added this to the 1.1 milestone Jun 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants