2
votes

As we all know, use of Future in Scala requires declaration of an execution context, which creates a thread pool that is used to run the code that appears in Future. My question is if use of Future.successful or Future.failed skips this request to the thread pool. The performance implications are important for correct use of Future.

To provide some background, I have been told by a coworker with years of Scala experience that direct use of Future() to wrap some block of code is discouraged because, to be meaningful, the code in the Future call has to have the potential to throw an exception, and this is undesirable. In our codebase, directly throwing an exception is considered un-functional style and also has performance implications, since throwing an exception is an operation that is relatively slow, with performance costs, and is therefore to be avoided. My question has two parts:

1) I want to know if there are other potential implications of avoiding direct calls to Future(): does use of Future.successful and Future.failed save computing resources by not scheduling work on a thread from the thread pool? By creating an already completed Future, do these two programming constructs avoid requesting a thread from the thread pool?

To be concrete, let us compare two blocks of code:

a)

val fut = Future(1+1)

and

b)

val fut = Future.successful(1+1)

Does b) save computing resources by not going to the thread pool?

2) In addition, I would also like to know if a thread is requested from the thread pool when mapping or flatmapping a Future. For example, I've noticed that any code which involves a flatmap on a Future requires an execution context to be defined. Does that mean that the flatmap operation itself is scheduled on a thread from the thread pool? Is it the case that every map or flatmap operation on a Future occurs on a thread from the thread pool? In that case, is requesting a thread from the thread pool inevitable as long as we are mapping or flatmapping a Future, even if it is already completed due to use of Future.successful and Future.failed?

To be concrete, let us look at an example:

val fut = Future.successful(1+1).map(x => x+1)

Does the map operation occur inside a thread requisitioned from the thread pool managed by the execution context?

This is just for me to better understand why use of Future.successful and Future.failed is considered better style than directly wrapping some code with Future(). To repeat what I said above, I already know that throwing exceptions is a performance bottleneck, but does use of Future.successful and Future.failed avoid requesting a thread from the thread pool completely and thereby reduce the performance overhead? Is this completely irrelevant since mapping or flatmapping a Future has to be scheduled on a thread from the thread pool anyway?

1

1 Answers

3
votes

Future.successful and Future.failed do not require an execution context, therefore no thread is consumed by them. However calling map/flatMap/foreach on them does require an execution context for the callback passed in, therefore a thread is indeed consumed. More than performance considerations, calling Future.successful(v) rather than Future(v) is appropriate to semantically indicate that we already have the value v and we are just lifting it into a Future. Performance gain from Future.successful(1+1) as opposed to Future(1+1) is likely negligible in a real-world system where true bottleneck will be slow I/O such as remote API calls, database lookups, etc.