Reputation: 3565
Let's say I have a code that does a blocking/long operation that returns a future, and then I need do a bunch of transformations on the results. A naive example is
longOperation().map(_ * 2).map(_.toString).map(_ + "bla").
Each of the maps introduces a context switch. Is there a simple way to avoid the context switches? I know about the trampoline execution context, and Scalaz tasks, but I am looking for something simpler that can be applied in very specific places where I know for a fact I don't need the context switch. (kinda similar to view on collections).
A more real example is I have a function that logs the execution time of a future - there is no reason to do a context switch just to record the time (not to mention that now I am measuring the execution time + the time it took the 'onComplete' to get picked up by the executor)
def timedFuture[T](metric: Histogram)(futureBlock: => Future[T])(implicit ec: ExecutionContext): Future[T] = {
val startTime = System.nanoTime()
val result: Future[T] = futureBlock
result onComplete (_ => metric.record((System.nanoTime - startTime) / 1000000))
result
}
Upvotes: 0
Views: 759
Reputation: 40500
As long as you have more than one thread in the system, a context switch my happen at any time, it's up to the OS. While using .map
somewhat increases the probability it'll happen at that exact moment, that doesn't really matter for anything, because the exact moment of the switch is insignificant (and outside of your control anyway). So, I wouldn't worry about that.
Upvotes: 0
Reputation: 5763
Actually there are some trampoline execution context in cats-effect or play framework. You can just copy such things to your project.
Note, don't do blocking call inside the flatMap/map body if you use such an execution context
Upvotes: 0
Reputation: 26579
If you have strict/synchronous transformations which you want to execute as a single thing, perform the transformation on the Try instance instead:
longOperation().transform(_.map(_ * 2).map(_.toString).map(_ + "bla"))
Source: https://viktorklang.com/blog/Futures-in-Scala-protips-5.html
Upvotes: 2
Reputation: 5275
Use a ExecutionContext that doesn't switch, like this one:
val currentThreadExecutionContext = ExecutionContext.fromExecutor(
new Executor {
def execute(runnable: Runnable) { runnable.run() }
})
From scala future document, there is also some arguments that suggesting not using this one, as the runnable (callback) maybe called in the unexpected thread. But you can use it if you fully understand how async future/promise works.
Upvotes: 0