On 11/14/13 4:32 AM, Daniel Micay wrote:
The same Rust code won't work with both 1:1 threading and M:N
threading though. It's nearly impossible to expose safe bindings to a
library like NSS with a heavy dependency on thread-local storage. With
task pinning, the libraries could pin the task to the thread they were
initialized on, but the interface is going to be much lower level than
C. It will need a context object with all the functions as methods for
the application to shove into task-local storage.

I don't understand this. If a library uses native TLS, then I think a reasonable implementation strategy in an M:N scheduler is to just perform the task pinning, and then the implementation will be the same as the 1:1 scheduler since TLS will be available.

I don't think it's possible to build completely lossless abstractions
over the differences in TLS and I/O. A library supporting both will
have an inferior API to a library with only 1:1 threading.

Without specifics I'm not convinced. I/O is, at its core, about these things:

* A `Reader` and `Writer` trait, along with a couple of supporting traits.

* Opening files, network streams, and other handles, and RAII-based destructors to close them.

TLS is, at its core, about these things:

* Declaring and initializing a TLS slot.

* Writing and reading data to and from that TLS slot.

These interfaces remain the same regardless of whether M:N or 1:1 is used. Now it may well be the case that they're so different that no implementations can reasonably be shared for performance reasons: you don't even want a runtime check to see whether we're in M:N or 1:1 scheduling mode. But it seems to me that that is what `#[cfg]` is for: if we decide that 1:1 scheduling is sufficiently mature on the host OS we can just not even compile M:N scheduling and hardwire in 1:1, eliminating the runtime tax.

It may also be the case that there are some APIs that we can only surface in 1:1 and M:N (Built-in TLS variables come to mind for the former; scheduling modes come to mind for the latter.) That's fine; along the edges we can surface those APIs without compromising the core set of abstractions.

Moving from one to another may not be as simple as "flip a switch and recompile your app", but it should be reasonable to have an app that works in both with minor `#[cfg]` changes. And this is an important use case: consider a game that does heavy computation and wants maximum performance on Windows 7 and iOS, for example.

There's an expectation that a language will work with a profiler like
callgrind or provide these tools itself. Go comes with an
implementation of CPU/memory profiling and analysis of M:N threading.
Where are Rust's versions of these tools?

We haven't written them yet, but we can. But perf and Instruments work reasonably well with the M:N threads already. Enough to find the hot spots in the call graph rooted at a particular function, which is 90% of what you want profilers for.

The robust, proven solution is 1:1 threading and many operating
systems used M:N threading before dropping it due to the significant
drawbacks. The move to more expensive fair CPU scheduling didn't
happen by historical accident.

I find the argument that 1:1 threading is where things are going persuasive. But we still need to support systems that are in place today, as well as systems in which M:N currently performs better and may continue to in the future. It sounds like Windows is in good shape, but Linux doesn't have the user-mode scheduling patches yet, and I don't know what Apple's plans are for Mac OS X. Those are all important platforms for us, and for others.

I'm personally fine with looking into migrating away from M:N scheduling on some platforms, but I think it would be a shame to fracture the community to do it by forking the standard library. More on that below.

An M:N threading implementation is nothing but a performance
optimization for the HPC and socket server use cases to avoid context
switches. I'm not willing to make compromises in semantics or
performance for other use cases to support this.

We're developing a language for many use cases. M:N is useful for some cases and on some platforms, while 1:1 is useful for others. Part of what a general-purpose systems language should strive to do is to provide abstractions that allow for portability to the greatest extent possible.

I want Rust to be a viable replacement for C, C++, Java, C# and other
languages but I don't see it happening with the standard library, so
I'll just put my full effort behind developing an alternative.

I hope you reconsider. As I said before, I think that, even if the implementation needs to be totally different, there is little point in duplicating traits like `Reader` and `Writer` and `TcpStream`. Divergence is just going to fragment the nascent community. One of the main reasons that those languages have been successful is that, in all of them, the core set of APIs are consistent and available everywhere. I don't want a situation whereby half of the Rust community will be writing libraries that depend on `libstd` and half will be writing libraries that depend on `rust-core`, when most of the libraries work just fine in either. For example, `lmath` is just linear algebra; it would be a shame if it had to choose a camp over needless API differences.

If we need to split out the Rust standard library into a core set of interfaces that remain the same across different implementations, then I think it would be much more productive to talk about doing that. I've reviewed rust-core and I don't really see any fundamental differences that prevent compatibility with the standard library--in fact, I'd really like to try to just merge them: pulling in the I/O as the "native I/O" module, eliminating redundant traits from libstd, eliminating conditions, taking whichever algorithms are faster, and so on. We can find and shake out bugs as we go.

Patrick

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

Reply via email to