splash-damage / future-extensions

Unreal Engine plugin for async task programming
BSD 3-Clause "New" or "Revised" License
175 stars 26 forks source link

Question: Performance with chained promises? #38

Closed impguard closed 1 year ago

impguard commented 1 year ago

Curious for someone a little more experienced with Unreal, C++, and this library - how are chained promised handled in terms of performance?

If I have something like:

SD:Async([]{
  Blob b = CreateLargeBlob();
  return b;
})
.Then([](const Blob& b)
{
  // Do something with the blob
  return b;
})
.Then([](const Blob& b)
{
  // Do something with the blob
  return b;
})
.Then([](const Blob& b)
{
  // Do something with the blob
  return b;
})
...

Am I unnecessarily creating copies of Blob each time. Scanning the source code, it seems like the answer is possibly no, since from reading the source code, copy elision and the ability to construct a TExpected via a move "should" result in the initial blob being stored directedly in the TExpected wrapper. After this, a similar thing must be happening as blob is passed through the chain?

This is more for curiosity. Is it more performant to have chained handlers take the entire TExpected value from the previous future and pass it through or is unwrapping to a const ref and back enough?

digibob commented 1 year ago

Hi Kevin,

Using the above approach will result in making a new copy of Blob for each stage, yes. There's no way for you to move the contents out of the existing wrapper, so that isn't possible to avoid directly.

What you can do, however, is change the type from Blob to TSharedRef<const Blob>, that way it's only the TSharedRef that's being copied, which would be much more lightweight (assuming Blob is very expensive to copy).

TSharedRef<Blob> is also an option, if you want to mutate the result, just keep in mind that if there are multiple users of the future, they'd see the changes made by each other.

Hope that helps!

impguard commented 1 year ago

Yea, I figured as much. Thanks so much!