Running this on a machine with one processor would lead to situations like:
import scala.concurrent._import ExecutionContext.Implicits.globalval session = socialNetwork.createSessionFor("user", credentials)val f: Future[List[Friend]] = future {session.getFriends()}
Assuming `session.getFriends()` is blocking, it seems to me that the global execution context, being backed by a ForkJoinPool, should not be used here.
I've been using scala.concurrent for a bit and I've been hitting some problems that I would like to share...
I've been reading those 2 pages
* SIP: http://docs.scala-lang.org/sips/completed/futures-promises.html
* Current doc: http://docs.scala-lang.org/overviews/core/futures.html
The SIP speaks about a 2nd SIP that would address ExecutionContext, but it apparently got never written. ExecutionContext are equally absent from the current doc.
This is really unfortunate in my opinion as ExecutionContexts are a crucial part of the future API.
From the current documentation:
import scala.concurrent._import ExecutionContext.Implicits.globalval session = socialNetwork.createSessionFor("user", credentials)val f: Future[List[Friend]] = future {session.getFriends()}
Assuming `session.getFriends()` is blocking, it seems to me that the global execution context, being backed by a ForkJoinPool, should not be used here.
Running this on a machine with one processor would lead to situations like:
implicit val ec = ExecutionContext.fromExecutor( new ForkJoinPool(1))
val f1 = Future {
println("I'm blocking")
Thread.sleep(99999)
}
val f2 = Future {
println("But not me")
}
val f3 = Future {
println("And me neither... too bad :(")
}
Await.ready(Future.sequence(Seq(f1, f2, f3)), 1.hour)
The scaladoc is also quite terse on the matter:
So that I can
f.map( someCheapNonBlockingOperation )(ConcurrencyUtil.currentThreadExecutionContext)
Like Paul Chiusano in https://issues.scala-lang.org/browse/SI-6932 I'm wondering why map, flatMap, recover and the like take another ExecutionContext.
Viktor Klang says "that we consider that to be a disadvantage, i.e. the programmer has to make choices about execution rather than flow." But if you don't make choices about execution you'll end up using the wrong execution context for the task.
In any case, that kind of decision should be documented somewhere, along with, why ExecutionContext is passed implicitly?
Maybe i've missed something, but this whole concurrent package has been written as if there is one ExecutionContext that rule them all. Don't we need different ExecutionContexts for different tasks?
Then there's the bit about ExecutionContext.prepare. The scaladoc is not helping so much:
/** Prepares for the execution of a task. Returns the prepared
* execution context. A valid implementation of `prepare` is one
* that simply returns `this`.
*/
def prepare(): ExecutionContext = this
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Great to see this email, we're currently curating the second part of that SIP which will address ExecutionContext in detail.Please watch the scala-sips ML.
Assuming `session.getFriends()` is blocking, it seems to me that the global execution context, being backed by a ForkJoinPool, should not be used here.Why?
use scala.concurrent.blocking { Thread.sleep(99999) }
Or even better, don't use Thread.sleep. Ever.
session.getFriends() in a blocking call.> If it is implicit, you can choose whether you want to pass it explicitly or not, if it is explicit you have to pass it explicitly at all time.
> I don't share that conclusion: Akka Dispatchers are ExecutionContexts, there are multiple possibilities for enhancing ExecutionContexts, several of those already mentioned in the thread linked earlier, but I will repeat them here for easy access:
Bruno
Hi Viktor!Great to see this email, we're currently curating the second part of that SIP which will address ExecutionContext in detail.Please watch the scala-sips ML.
Nice!Assuming `session.getFriends()` is blocking, it seems to me that the global execution context, being backed by a ForkJoinPool, should not be used here.Why?
use scala.concurrent.blocking { Thread.sleep(99999) }
Or even better, don't use Thread.sleep. Ever.
Thread.sleep was used here to simulate a blocking call. Of course I never Thread.sleep(99999) in a real program :)
That code snippet was just there to illustrate that future f2 and f3 won't run before f1 finishes, because blocking in a ForkJoinPool without notifying it doesn't make it to grow its workers.
So here come the blocking construct. With this you can notify the fork join pool of a blocking call. But I guess you wouldn't build an app that does a lot of IO on a FJP right?
I mean, if you don't know upfront how many blocking call you might end up with, you could create a lot of threads, up to the FJP limit (32767). That's a pretty high limit isn't it? I'd say way too high in many situations.
A word on the blocking construct. There's indeed a "Blocking" section in the current doc, but it's mainly about blocking on a future and no so much in a future. At least this distinction is not made clear.
As the documentation says almost nothing about what `blocking` does in effect, I looked up its code (scala 2.10.3) and it turns out that only the default ExecutionContext implementation implements it (by means of ExecutionContextImpl.DefaultThreadFactory).
If I use `ExecutionContext.fromExecutor(Executor)` the blocking construct will be made useless.
BTW I find weird the fact that the default implementation falls back on a ThreadPoolExecutor if building a FJP fails?! Also the TPE is then configured with a thread factory that builds ForkJoinWorkerThread with calls ForkJoinPool.managedBlock...
Overall I'd say that to me the biggest issue when I started using the scala.concurrent package was the lack of documentation:
* in the current documentation, the example should wrapsession.getFriends()in a blocking call.
* the global ExecutionContext should be way more documented. First of all that it is backed by a FJP with all the consequences, such as failing to wrap blocking calls in a blocking block can lead to deadlocks, or using too many blocking calls will kill your resources
> See the recent discussion on this ML regarding that: https://groups.google.com/forum/#!searchin/scala-user/ExecutionContext%7Csort:date/scala-user/lVFde4UyBvc/vbX4wIarhisJ> Because Futures are strict—they are not a lifted expression of operations. User code shall not ever run on the thread of the completer, as this introduces weird situations where it is impossible to figure out where things are running (non-determinisim due to the race between completion and attaching of action). For a longer discussion on this, see http://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/
Thanks for the links. The blog post was great!
Maybe it's a matter of taste, but to me if the documentation clearly states how `runNow` or `runInCurrentThread` behave then it's fine. If I understand, the problem is about locks: locking with `runNow` can make you deadlock.
But if all i'm doing is a simple non-blocking operation then I don't really care do I?
> I think you misunderstand me. By always having to specify where things are run, you are always in control of execution.Ok.
> If it is implicit, you can choose whether you want to pass it explicitly or not, if it is explicit you have to pass it explicitly at all time.> I don't share that conclusion: Akka Dispatchers are ExecutionContexts, there are multiple possibilities for enhancing ExecutionContexts, several of those already mentioned in the thread linked earlier, but I will repeat them here for easy access:
I'll write about this in another message.
Thanks for your time, it's really nice to write all that down :)
Bruno
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
> If it is implicit, you can choose whether you want to pass it explicitly or not, if it is explicit you have to pass it explicitly at all time.
The problem is that it let the beginner think that you can do concurrent programming without knowing much about it. Very much like GWT with the promise that you won't have to learn javascript, VisualBasic that makes you think you can build programs without knowing programming, Access that let you design databases without having a clue about normalization, etc...
Basically all you have to do is import the global execution context and you're done. The higher up the better. As a matter of fact there's only one import of ExecutionContext.global in the whole doc. And as it is written : "for now it is sufficient to know that you can import the default execution context as shown above".
My take is that it is necessary to have ExecutionContext passed implicitly to make futures usable in for-comprehensions.
And by making futures for-comprehension compatible, you end up with methods like foreach. Why would you need foreach if you have onSuccess?
Foreach let you write stuff like:
for( result <- Future { sys.error("damn") } ) {
println("And here is our result: " + result)
}
And your exception is swallowed.
You say you can decide to pass ExecutionContext explicitly if you wanted to, but you cannot do that in a for-comprehension, you have to rewrite your code with map, flatMap and foreach (which will probably be replaced by onSuccess).
To me using for-comprehension on a future is way too powerful and if I had to introduce futures in a team of developpers that are not acquainted with this API I would probably forbid its use with static analysis.
In a project where I want to have a fine control on the amount of threads created I've written a compiler plugin to spot uses of the global ExecutionContext. It issues warnings when used implicitly.
> I don't share that conclusion: Akka Dispatchers are ExecutionContexts, there are multiple possibilities for enhancing ExecutionContexts, several of those already mentioned in the thread linked earlier, but I will repeat them here for easy access:
Yeah but Akka Dispatchers don't come with the scala library and are not mentioned in the doc. It might be that having ExecutionContexts passed implicitly in Akka is the right decision though. In this case you might want to have a Future API for Akka and another one for the standard library (which is based on thread pools)?
Bruno
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
On Thu, Feb 6, 2014 at 4:53 PM, Bruno Bieth <bie...@gmail.com> wrote:
> If it is implicit, you can choose whether you want to pass it explicitly or not, if it is explicit you have to pass it explicitly at all time.
The problem is that it let the beginner think that you can do concurrent programming without knowing much about it. Very much like GWT with the promise that you won't have to learn javascript, VisualBasic that makes you think you can build programs without knowing programming, Access that let you design databases without having a clue about normalization, etc...
Basically all you have to do is import the global execution context and you're done. The higher up the better. As a matter of fact there's only one import of ExecutionContext.global in the whole doc. And as it is written : "for now it is sufficient to know that you can import the default execution context as shown above".
My take is that it is necessary to have ExecutionContext passed implicitly to make futures usable in for-comprehensions.
And by making futures for-comprehension compatible, you end up with methods like foreach. Why would you need foreach if you have onSuccess?
Foreach let you write stuff like:
for( result <- Future { sys.error("damn") } ) {
println("And here is our result: " + result)
}
And your exception is swallowed.
Of course. What else would happen?
You say you can decide to pass ExecutionContext explicitly if you wanted to, but you cannot do that in a for-comprehension, you have to rewrite your code with map, flatMap and foreach (which will probably be replaced by onSuccess).
Can you give a code example that demonstrates the problem?
To me using for-comprehension on a future is way too powerful and if I had to introduce futures in a team of developpers that are not acquainted with this API I would probably forbid its use with static analysis.
I tend to prefer to trust my colleagues, and use code reviews to give feedback.
In a project where I want to have a fine control on the amount of threads created I've written a compiler plugin to spot uses of the global ExecutionContext. It issues warnings when used implicitly.
You know that you can configure the global EC, right?
> I don't share that conclusion: Akka Dispatchers are ExecutionContexts, there are multiple possibilities for enhancing ExecutionContexts, several of those already mentioned in the thread linked earlier, but I will repeat them here for easy access:
Yeah but Akka Dispatchers don't come with the scala library and are not mentioned in the doc. It might be that having ExecutionContexts passed implicitly in Akka is the right decision though. In this case you might want to have a Future API for Akka and another one for the standard library (which is based on thread pools)?
No, it's about the possibility of isolation.
Good point, I'll try to add some extra documentation into 2.11 (there's still time to do a documentation PR so if you want to help out that'd be fantastic)
As the documentation says almost nothing about what `blocking` does in effect, I looked up its code (scala 2.10.3) and it turns out that only the default ExecutionContext implementation implements it (by means of ExecutionContextImpl.DefaultThreadFactory).
If I use `ExecutionContext.fromExecutor(Executor)` the blocking construct will be made useless.It's not useless. not only can you install your own BlockContext but it serves as "documentation" so that if someone changes the EC implementation, your code can be made to run better.
I think you need to revisit that piece of code.newThread(runnable)andnewThread(FJP)are different methods.
as long you don't use any blocking.
You say you can decide to pass ExecutionContext explicitly if you wanted to, but you cannot do that in a for-comprehension, you have to rewrite your code with map, flatMap and foreach (which will probably be replaced by onSuccess).
Can you give a code example that demonstrates the problem?
val f = Future { ... }(ec1).map( ... )(ec2).map( ... )(ec3)
f.onSuccess( ... )(ec4)
f.onFailure( ... )(ec5)
I might have missed something about the language, but can you write this in for-comprehension style?
On Saturday, February 8, 2014 3:03:15 PM UTC+1, √iktor Klang wrote:On Thu, Feb 6, 2014 at 4:53 PM, Bruno Bieth <bie...@gmail.com> wrote:
> If it is implicit, you can choose whether you want to pass it explicitly or not, if it is explicit you have to pass it explicitly at all time.
The problem is that it let the beginner think that you can do concurrent programming without knowing much about it. Very much like GWT with the promise that you won't have to learn javascript, VisualBasic that makes you think you can build programs without knowing programming, Access that let you design databases without having a clue about normalization, etc...
Basically all you have to do is import the global execution context and you're done. The higher up the better. As a matter of fact there's only one import of ExecutionContext.global in the whole doc. And as it is written : "for now it is sufficient to know that you can import the default execution context as shown above".
My take is that it is necessary to have ExecutionContext passed implicitly to make futures usable in for-comprehensions.
And by making futures for-comprehension compatible, you end up with methods like foreach. Why would you need foreach if you have onSuccess?
Foreach let you write stuff like:
for( result <- Future { sys.error("damn") } ) {
println("And here is our result: " + result)
}
And your exception is swallowed.
Of course. What else would happen?
This is equivalent to
Future { sys.error("damn") } onSuccess { case result =>Which is way more obvious (at least to me) that you are swallowing exceptions.
println("And here is our result:" + result)
}
I'm not saying `foreach` is broken per-se, but that its name and worse its use in a for-comprehension is obscure.
Asynchronously processes the value in the future once the value becomes available.
Will not be called if the future fails.
You say you can decide to pass ExecutionContext explicitly if you wanted to, but you cannot do that in a for-comprehension, you have to rewrite your code with map, flatMap and foreach (which will probably be replaced by onSuccess).
Can you give a code example that demonstrates the problem?
val f = Future { ... }(ec1).map( ... )(ec2).map( ... )(ec3)
f.onSuccess( ... )(ec4)
f.onFailure( ... )(ec5)
I might have missed something about the language, but can you write this in for-comprehension style?
To me using for-comprehension on a future is way too powerful and if I had to introduce futures in a team of developpers that are not acquainted with this API I would probably forbid its use with static analysis.
I tend to prefer to trust my colleagues, and use code reviews to give feedback.
I hope you have enough time to code review every single line of every single colleague that is not aware of those gotchas ;)
That might work in some projects, but definitely not in others.
In a project where I want to have a fine control on the amount of threads created I've written a compiler plugin to spot uses of the global ExecutionContext. It issues warnings when used implicitly.
You know that you can configure the global EC, right?
What do you mean? The global EC is a val in an object.
Maybe you're refering to
val desiredParallelism = range(getInt("scala.concurrent.context.minThreads", "1"),getInt("scala.concurrent.context.numThreads", "x1"),getInt("scala.concurrent.context.maxThreads", "x1"))
But that's really not helping the blocking story.
> I don't share that conclusion: Akka Dispatchers are ExecutionContexts, there are multiple possibilities for enhancing ExecutionContexts, several of those already mentioned in the thread linked earlier, but I will repeat them here for easy access:
Yeah but Akka Dispatchers don't come with the scala library and are not mentioned in the doc. It might be that having ExecutionContexts passed implicitly in Akka is the right decision though. In this case you might want to have a Future API for Akka and another one for the standard library (which is based on thread pools)?
No, it's about the possibility of isolation.
Sorry, I don't understand, could you expand a little?
Bruno
--To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
You received this message because you are subscribed to the Google Groups "scala-user" group.--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
You have the exact same situation with all collections and their CanBuildFrom instance.
You have the exact same situation with all collections and their CanBuildFrom instance.
I have never had the need to use a different CanBuildFrom from what the compiler chose. This is the exact opposite situation where the types tell you precisely which instance of CanBuildFrom is needed.
I'm not going to use a BitSet CanBuildFrom when turning a List of String to a Vector of String.
Like you said, we're still without a "perfect" thread pool implementation and that's why this pattern should be avoided in this situation. I do use it however in my application with a finer grain construct:
trait IoExecutionContext
implicit class IoFuture[A](val future: Future[A]) extends AnyVal {
/** map this future by executing `f` in an `IoExecutionContext` */
def mapIo[B](f : A => B)(implicit ioEc: IoExecutionContext) : Future[B] = future.map(f)(ioEc)
}
Of course this is in the context of my application, where `Io` means something specific.
Exactly, that's the DefaultBlockContext which does nothing. So imagine you've decorated your code with `blocking` wherever it's needed, you start your application and for some reason (although I can't see why?) the FJP cannot be instantiated. You get a warning on the console that it is falling back to a TPE and baam you end up deadlock everywhere because `blocking` isn't doing its job.
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
I think you're misunderstanding the intention of "blocking". It is to give a hint that there will be blocking ahead, so IF the pool can do something, it will attempt to do so. If you annotate your blocking code appropriately, at least you give the underlying execution mechanism a chance to deal with it.
import ExecutionContext.Implicits.globalval f: Future[List[Friend]] = future { blocking { session.getFriends() }}I think you're misunderstanding the intention of "blocking". It is to give a hint that there will be blocking ahead, so IF the pool can do something, it will attempt to do so. If you annotate your blocking code appropriately, at least you give the underlying execution mechanism a chance to deal with it.
Hmm, then I'm not sure about what should be written in the documentation. Maybe along those lines:
import ExecutionContext.Implicits.global
val f: Future[List[Friend]] = future {
blocking {session.getFriends()}}Note that we're using the global execution context here but you can be surprised that using `blocking` might not work as expected if the FJP cannot be instantiated and turns to be a TPE. This will show up as a warning in your logs, so be sure to check your logs if unexpected deadlock occurs.
Used to designate a piece of code which potentially blocks, allowing the current BlockContext to adjust the runtime's behavior. Properly marking blocking code may improve performance or avoid deadlocks.
Blocking on an Awaitable should be done using Await.result instead of blocking.
--To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
You received this message because you are subscribed to the Google Groups "scala-user" group.--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
That's my point, you have to find circumvoluted ways to accomodate the for-comprehension. By using flatMap instead of map you've created two more futures and spawned 2 more threads.
On Tuesday, February 11, 2014 12:05:00 PM UTC+1, Derek Williams wrote:On Tue, Feb 11, 2014 at 10:16 AM, Bruno Bieth <bie...@gmail.com> wrote:
You say you can decide to pass ExecutionContext explicitly if you wanted to, but you cannot do that in a for-comprehension, you have to rewrite your code with map, flatMap and foreach (which will probably be replaced by onSuccess).
Can you give a code example that demonstrates the problem?
val f = Future { ... }(ec1).map( ... )(ec2).map( ... )(ec3)
f.onSuccess( ... )(ec4)
f.onFailure( ... )(ec5)
I might have missed something about the language, but can you write this in for-comprehension style?The way I deal with this is to make sure that anything that requires running in it's own ExecutionContext is isolated, and use a for-comprehension to flatMap them together:
def getA: Future[A] = Future(...)(blockingEC1)def doA(a: A): Future[B] = Future(....)(blockingEC1)def doB(b: B): Future[C] = Future(....)(blockingEC2)for {a <- getAb <- doA(a)c <- doB(b)} yield cUse the default ExecutionContext for non blocking work, and isolate blocking work. Don't do blocking within flatMap/map, let those be done by the default ExecutionContext.--Derek Williams
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
On Tue, Feb 11, 2014 at 6:32 PM, Bruno Bieth <bie...@gmail.com> wrote:
That's my point, you have to find circumvoluted ways to accomodate the for-comprehension. By using flatMap instead of map you've created two more futures and spawned 2 more threads.The DefaultPromise is about as cheap as you can make things, so allocating one is trivial.As for the "spawned 2 more threads." I don't understand what you mean.
On Tuesday, February 11, 2014 6:41:00 PM UTC+1, √iktor Klang wrote:On Tue, Feb 11, 2014 at 6:32 PM, Bruno Bieth <bie...@gmail.com> wrote:
That's my point, you have to find circumvoluted ways to accomodate the for-comprehension. By using flatMap instead of map you've created two more futures and spawned 2 more threads.The DefaultPromise is about as cheap as you can make things, so allocating one is trivial.As for the "spawned 2 more threads." I don't understand what you mean.def doA(a: A): Future[B] = Future( reallyDoA )(blockingEC1)def doB(b: B): Future[C] = Future( reallyDoB )(blockingEC2)
getA().flatMap(doA(a)) // one thread for flat map
.flatMap(doB(b)) // one thread for flat map = 2 more threads
getA().map( reallyDoA )(blockingEC1).map( reallyDoB )(blockingEC2) // no overhead
The flapMap operation, even though cheap, has to run through an executor.
If you're using the global EC it means submitting a task to the FJP (= context switch overhead + many objects created on the way).
On Tue, Feb 11, 2014 at 7:19 PM, Bruno Bieth <bie...@gmail.com> wrote:
On Tuesday, February 11, 2014 6:41:00 PM UTC+1, √iktor Klang wrote:On Tue, Feb 11, 2014 at 6:32 PM, Bruno Bieth <bie...@gmail.com> wrote:
That's my point, you have to find circumvoluted ways to accomodate the for-comprehension. By using flatMap instead of map you've created two more futures and spawned 2 more threads.The DefaultPromise is about as cheap as you can make things, so allocating one is trivial.As for the "spawned 2 more threads." I don't understand what you mean.def doA(a: A): Future[B] = Future( reallyDoA )(blockingEC1)def doB(b: B): Future[C] = Future( reallyDoB )(blockingEC2)
getA().flatMap(doA(a)) // one thread for flat map
.flatMap(doB(b)) // one thread for flat map = 2 more threads
getA().map( reallyDoA )(blockingEC1).map( reallyDoB )(blockingEC2) // no overhead
The flapMap operation, even though cheap, has to run through an executor.Yes, that's the entire point. If you don't want to do things async, just do it directly.
If you're using the global EC it means submitting a task to the FJP (= context switch overhead + many objects created on the way).No absolutely not. It depends on the implementation and configuration of the global EC—which is exactly how it should be, as it means that it can be optimized separately from your code. Just as you noted with FJP vs. TPE for the Global.
Just to reiterate: The entire idea of EC is to abstract away _how_ things are executed.
Are you having a performance problem? If you have an example app that is running slower than you'd expect that could help. We use the pattern I previously posted in production (using Akka's default ExecutionContext, not the Scala global one) and we don't have problems caused by the extra flatMaps (compared to the blocking calls, the cost of the context switch is negligible).
On Tuesday, February 11, 2014 8:19:35 PM UTC+1, √iktor Klang wrote:On Tue, Feb 11, 2014 at 7:19 PM, Bruno Bieth <bie...@gmail.com> wrote:
On Tuesday, February 11, 2014 6:41:00 PM UTC+1, √iktor Klang wrote:On Tue, Feb 11, 2014 at 6:32 PM, Bruno Bieth <bie...@gmail.com> wrote:
That's my point, you have to find circumvoluted ways to accomodate the for-comprehension. By using flatMap instead of map you've created two more futures and spawned 2 more threads.The DefaultPromise is about as cheap as you can make things, so allocating one is trivial.As for the "spawned 2 more threads." I don't understand what you mean.def doA(a: A): Future[B] = Future( reallyDoA )(blockingEC1)def doB(b: B): Future[C] = Future( reallyDoB )(blockingEC2)
getA().flatMap(doA(a)) // one thread for flat map
.flatMap(doB(b)) // one thread for flat map = 2 more threads
getA().map( reallyDoA )(blockingEC1).map( reallyDoB )(blockingEC2) // no overhead
The flapMap operation, even though cheap, has to run through an executor.Yes, that's the entire point. If you don't want to do things async, just do it directly.
I think you missed the point.
Is async. It just uses 2 threads less.
getA().map( reallyDoA )(blockingEC1).map( reallyDoB )(blockingEC2) // no overhead
If you're using the global EC it means submitting a task to the FJP (= context switch overhead + many objects created on the way).No absolutely not. It depends on the implementation and configuration of the global EC—which is exactly how it should be, as it means that it can be optimized separately from your code. Just as you noted with FJP vs. TPE for the Global.
Just to reiterate: The entire idea of EC is to abstract away _how_ things are executed.
As we already discussed, the global EC implementation is written in stone
and its configuration almost inexistent (i'm referring to the "scala.concurrent.context.maxThreads" VM argument).
To me the entire idea of the global EC is to let the programmers use the Future library standalone (i.e without Akka). Look at Derek William answer, which explicitly recommends using the global EC for the flatMap operation.
Anyway, back to the original question, I'm going to rewrite the for-comprehension and inline the doX functions:
getA().flatMap(Future(reallyDoA)(blockingEC1))(globalEC).flatMap(Future(reallyDoB)(blockingEC2))(globalEC)
This wouldn't be so bad if globalEC was used everywhere (instead of blockingEC1 and blockingEC2), as the globalEC would be able to execute the flatMap wiring in the same thread as reallyDoA and reallyDoB, but as we are using blockingEC1 and blockingEC2 we can be sure that there will be a context switch just for that flatMap wiring.
Even if we replace globalEC by something else, it seems there will always be an overhead compared to the `map` version above. We exclude the `currentThreadExecutionContext` as we wouldn't want it to be imported implicitly.
On Wed, Feb 12, 2014 at 10:31 AM, Bruno Bieth <bie...@gmail.com> wrote:
On Tuesday, February 11, 2014 8:19:35 PM UTC+1, √iktor Klang wrote:On Tue, Feb 11, 2014 at 7:19 PM, Bruno Bieth <bie...@gmail.com> wrote:
On Tuesday, February 11, 2014 6:41:00 PM UTC+1, √iktor Klang wrote:On Tue, Feb 11, 2014 at 6:32 PM, Bruno Bieth <bie...@gmail.com> wrote:
That's my point, you have to find circumvoluted ways to accomodate the for-comprehension. By using flatMap instead of map you've created two more futures and spawned 2 more threads.The DefaultPromise is about as cheap as you can make things, so allocating one is trivial.As for the "spawned 2 more threads." I don't understand what you mean.def doA(a: A): Future[B] = Future( reallyDoA )(blockingEC1)def doB(b: B): Future[C] = Future( reallyDoB )(blockingEC2)
getA().flatMap(doA(a)) // one thread for flat map
.flatMap(doB(b)) // one thread for flat map = 2 more threads
getA().map( reallyDoA )(blockingEC1).map( reallyDoB )(blockingEC2) // no overhead
The flapMap operation, even though cheap, has to run through an executor.Yes, that's the entire point. If you don't want to do things async, just do it directly.
I think you missed the point.In the code above, there is exactly _nothing_ that dictate how many java.lang.Threads will be used–That is _completely_ at the discretion of the _implementations_ of the ECs—the ECs might just delegate to the same underlying Executor for all we knowIf you _decide_ to pass in ECs that spawn threads that is under _your_ control and responsibility. It is however not hardcoded into the semantics of Futures.That is my point.Is async. It just uses 2 threads less.
getA().map( reallyDoA )(blockingEC1).map( reallyDoB )(blockingEC2) // no overhead
Again–you, the programmer, _choose_ to use those ECs!
As we already discussed, the global EC implementation is written in stone and its configuration almost inexistent (i'm referring to the "scala.concurrent.context.maxThreads" VM argument).
To me the entire idea of the global EC is to let the programmers use the Future library standalone (i.e without Akka). Look at Derek William answer, which explicitly recommends using the global EC for the flatMap operation.
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
And you've made it clear that an EC that runs on the calling thread violates the EC contract, so there's no other implementation than something delegate to another thread, which is disproportionally expensive for the amount of work being done (in this example, a simple map function).
And you've made it clear that an EC that runs on the calling thread violates the EC contract,
so there's no other implementation than something delegate to another thread, which is disproportionally expensive for the amount of work being done (in this example, a simple map function).
Synchronous ECs are bad. (You are free to do bad things, if things break, you have only yourself to blame.)
For more background, see Havocs blogpost that I have referred to earlier.
Confusion cleared?
You're oversimplifying, the contract is that it cannot run synchronously on the current thread to be a generic ExecutionContext.
If the current thread is within the given EC and that EC decides to enqueue the runnable to run _after_ the current task finishes, then that is completely fine, see: https://github.com/scala/scala/blob/master/src/library/scala/concurrent/BatchingExecutor.scala
Also, if you know that the thing that uses the EC is safe to run synchronously then you can of course use a synchronous version for that, but naturally if things break it's your own fault. (It's your code after all)
Thank you,Rob
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Actually, I don’t want a synchronous EC, I want to use this EC, but with a way to block tasks and still keep liveness. I also was unsure how I could create an EC with one of these executors. It looks private to me.
If you want to be able to block (i.e stop) execution you can construct an Executor based around one of Java’s queues, like ArrayDeque or ArrayBlockingQueue. Take a look at the SerialExecutor in the javadocs at http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html, it should be trivial to add a method to turn on and off execution. You can create an ExecutionContext from a java Executor using ‘ExecutionContext.fromExecutor’.
Cheers
Actually, I don’t want a synchronous EC, I want to use this EC, but with a way to block tasks and still keep liveness. I also was unsure how I could create an EC with one of these executors. It looks private to me.If you want to be able to block (i.e stop) execution you can construct an Executor based around one of Java’s queues, like ArrayDeque or ArrayBlockingQueue. Take a look at the SerialExecutor in the javadocs at http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html, it should be trivial to add a method to turn on and off execution.
You can create an ExecutionContext from a java Executor using ‘ExecutionContext.fromExecutor’.
On Thu, Feb 13, 2014 at 5:02 AM, charlie robert <charlie...@icloud.com> wrote:
Whatever I can get. Ultimately if a java wait or entry into a synchronized block of code would be sweet, to allow any code to run. I figure its not possible to do this without changing the threading in the JVM, but still how could it be done, I am quite curious. Getting a scala Await would be nice, where the future goes off in batched mode, and the Await call doesn’t block the Executor.
Fibers are in general a language-level feature. Scala has something similar called Scala Async, based on macros and targeted for release in Scala 2.11, but you can use it as a library in Scala 2.10. It’s very similar to C#’s async support. I’ve been using it lately and haven’t had problems with it.
See the SIP here: http://docs.scala-lang.org/sips/pending/async.html
The repo is here: https://github.com/scala/async
You can then run code that looks like this (including type annotations for you to see what’s going on):
def slowCalcFunc: Future[Int] = ???
val result: Future[Int] = async {
val num1: Int = await(slowCalcFunc)
val num2: Int = await(slowCalcFunc)
num1 + num2
}
So await() is basically a function that takes a Future[T] and returns a T, very similar to what Await.result does, except that it does so in the context of an async block. The compiler, using the macros support in Scala 2.10, will then rewrite that code into a bunch of flatMap and map statements (don’t really know what it does actually, maybe it uses onComplete directly - but that’s of no consequence), the code above being equivalent to this:
for (num1 <- slowCalcFunc; num2 <- slowCalcFunc) yield num1 + num2
Basically Scala Async allows you to write code with asynchronous calls without having to deal with callbacks, maps and flatMaps. It’s pretty cool in practice.
Now, if you were thinking of somehow transforming things like Thread.sleep, Object.wait and so on, such that third-party code that blocks can behave in an asynchronous manner, well that doesn’t make sense and it would be pretty awful even if possible.
Cheers,