I concur with other posters that M:N is a better foundation for runtime performance if you want to scale to large number of tasks. Some "data points" (more like anecdotes):

1. In my own personal experience, I implemented a coroutine system that did M:N with expandable stacks precisely as Graydon outlined (linked list of chunks at fixed positions in memory). At one point we did some micro-benchmarks which suggested 1:1 threading wouldn't be so painful, so we ported the system over---and ended up regretting that decision for a long time, since it was very hard to "unport", and the overall system was definitely slower and scaled much more poorly, despite the micro-benchmarks.

2. Java may have a 1:1 model, but---in practice---there is a lot of movement towards M:N. It's just that this M:N mapping is being done in libraries, such as Doug Lea's Fork-Join scheduler work, as well as projects like Kilim and others (not to mention the ever present thread pools and executors, which are basically a primitive M:N setup). These projects have the disadvantage, however, of lacking access to low-level operations, which gives them an inherent efficiency gap. They can't implement a chunked stack, for example.

3. In addition to Haskell, I believe that Erlang uses M:N scheduling.

The main downside of M:N as I see it is interaction with C code. This IS a significant downside, to be sure. It's the reason that we tried to port over to 1:1, as described above. The techniques Graydon described (make a big chunk of space, limit callbacks when possible) are basically what we were using before. They do address the problem, though they can sometimes be a pain. There is also the challenge of implementing O/S concepts like high vs low priority threads and so forth, if those prove necessary.

Regarding the efficient implementation of yield points and the like, the Capriccio project [1] discussed a number of techniques in a paper some time back. The project seems to be dead at this point, unfortunately. Still I think (as many others have said) that these costs are quite minimal, especially in exchange for the scalability and control that you get.


Niko

[1] http://capriccio.cs.berkeley.edu/

Sebastian Sylvan wrote:
2011/5/25 Rafael Ávila de Espíndola<respind...@mozilla.com>:
Of arguing that the absence of data motivates a particular assumption.
In this case it has to do with yield checking (which, incidentally, we
do have some data on from the work in other JITs, and it's seldom a cost
center).

Elsewhere you seem to be frustrated that in the absence of data to the
contrary, I'm promoting the assumption that M:N (even if it runs 1:1
much of the time) is a safer default than mandatory-1:1. I can't say for
certain which of those scenarios *will* need M:N and how badly it'll
hurt to be without; but I feel like mandatory-1:1 will paint ourselves
into a bunch of potential corners, as I tried to point out in the last
email. So I'm arguing the M:N stance is safer to start.

Or maybe you're not frustrated about that, or hadn't previously
considered those scenarios, and I am just reading frustration into the
argument. Did any of the points I raised in defense of *permitting* M:N
operation sound reasonable?
I still don't see where I made an accusation. If I did, sorry, it was not
the intention.

I am a bit frustrated that in the absence of data we are selecting the more
expensive option which is also the more expensive to backtrack and following
the examples of the least successful languages.


I'm somewhat unconvinced that spawning an OS thread for every task
should be considered the less expensive option. Easier to do, yes, but
that pariticular performance claim goes directly against my
performance intution, and could use some substantiation, IMO.

E.g. Haskell does M:N threads and it's much more efficient than OS
threads. I'd link  you some microbenchmarks from the shootout game but
that website seems broken at the moment - IIRC on the benchmarks
measuring concurrency overhead, Haskell's M:N threading beat pretty
much everything else including C without breaking much of a sweat.

So, my current "gut instinct" (in the absence of data) is to favour
light weight tasks that map to (few) OS tasks, for performance
reasons. You seem to oppose that on performance grounds. I'm not sure
why our intuitions differ, but I don't think there's enough data to,
at this point, position the 1:1 option as the more higher performance
option. Did I miss some elaboration on why you think it is?


_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to