-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Thread safety tracker #10421
Comments
Don't we have a spreadsheet of this somewhere? Maybe make it public? |
Don't know if it is public - but I thought it would be easier to track the work here in the issue. |
I fancied up the spreadsheet a bit so that it's easier to organize and sort. |
i've tried to help knock a few off the list based off existing issues and upcoming changes |
That's true, #9986 helps with this. |
It would be nice to get basic printing to work:
|
Are these changes being made in the master, or a branch? I see items checked off for |
I think this is for the threading branch, right, @JeffBezanson? |
would also be interesting which is the current threading branch, "threading" or "threads" |
"threading" is newer than "threads"; not sure why we changed branches. |
that why I asked :-) I have seen changes being made to both branches at a time and thus was not sure about it. Would be also interesting to have an issue what the blockers are for merging into master. IIUC at some point llvm svn was required but this might be solved if we switch to llvm3.6? Further there is still the pthreads dependency. |
Moving to C11/C++11 threads would remove the pthreads dependency. Using C11/C++11 would also give us portable atomic ops with good memory consistency controls. LLVM is moving aggressively to depend on C++11 anyway. |
I am not 100% sure why the pthread dependency was added but it seems to be required to set thread affinity. Is this possible with C++11? For atomic ops we have written some macros that use the compiler dependent intrinsics. |
@kpamnany had suggested that I use the |
As far as I know, C++11 has no notion of thread affinity. So we still have to write platform-specific code for that. There is a hook in C++11 for getting at the platform-specific thread handle, so I think it's possible to write most of the threading in C++11/C11 and interface to platform-specific stuff for affinity. |
Not sure what exactly we gain when using C++11 threads. The Julia code is currently largely C based and only uses C++ where necessary (i.e. when interfacing with LLVM). This is a design decision. One could simplify quite some code when using C++ and could e.g. use STL containers instead of the self-written ones in libsupport. But the C vs C++ topic can be quite subjective. Its up to Jeff to give a direction here. |
C11 threading would work too. It's supposed to interoperate with C++ threads, i.e. in principle it's just difference spellings of the key operations. Though we'd need to check on Windows. Microsoft has been good about implementing the latest versions of C++, but has shown indifference to implementing even C99. |
Another route is to figure out the threading model first, and then write platform specific implementations of the core operations. That's the way the Intel Cilk Plus run-time is written, since it requires stack-switching capabilities beyond PThreads. |
To handle thread affinity I recommend using the hwloc library, already nicely wrapped in https://github.com/JuliaParallel/hwloc.jl. hwloc provides a platform-independent API to query number of cores, sockets, caches, etc., and to define the threads' affinity to them. I find that C++11 threading implementations are based on pthreads, and thus slow. It would e.g. not be efficient to run each Julia task on a new C++11 thread. This makes C++11 threading facilities (the @ArchRobison: What stack-switching capabilities does Cilk Plus have? I had the impression that there are certain limitations. I'm currently using Qthreads https://github.com/Qthreads/qthreads, since there each thread has its own stack, and threads can switch arbitrarily. |
Yes, direct use of C11/C++11 threading for tasks would be rather useless, but it at least provides portable access to OS-level threads. I would imagine the Julia run-time would have several layers for threading:
Cilk implementations (including Cilk Plus) implement a cactus stack. This paper gives a good overview of different ways to implement cactus stacks. The limitation to cactus stacks and the "busy leaves" property (each leaf has a thread running on it) is essential to Cilk's strong space and time guarantees, which may or may not be worth the limitations depending on perspective. For level 2 above, the internals of the Intel implementation use an abstraction similar to Windows fibers, essentially arbitrary stack switching. The file @eschnett How does QThreads deal with stack overflow? I recall that Rust gave up on segmented stacks. Go copies the entire stack, which requires some care with code generation and stack maps, and takes on a little overhead in function prologs. Though overall I like its design since users don't have to worry about stack overflow and are not tied to cactus stacks. |
@ArchRobison QThread stack overflows are handled via good, old signal 11, if you enable stack overflow detection. Otherwise they remain unhandled. |
@ArchRobison: Regarding Cilk Plus: I have the impression -- please correct me if I am wrong -- that Cilk offers only a limited kind of parallelism: A routine can spawn children, but all children need to exit before the routine itself exits. This is less powerful than e.g. C++11, where the spawned thread's state is typically captured in a future that can be returned, stored, and passed around. Thread execution in C++11 thus doesn't form a tree structure, and cactus stacks (as described in the paper you mention) are not relevant. Is this what you meant by "busy leaves property"? |
@ArchRobison: You said "I recall that Rust gave up on segmented stacks." Do you have a pointer to what Rust tried, or how they failed? |
To clarify on the |
The threading code uses pthreads to start threads, set thread affinities, and for a mutex and condition variable pair to let threads sleep rather than spin when they aren't working. The other platform dependency is atomics, which would also be used in the runtime for spin locks and other synchronization constructs. I've begun looking at C11 for threads and atomics, but if platform-dependent code is required anyways (for thread affinitization), and moving to C11 doesn't give us Windows, then I'm not sure it's worth the effort. To maximize parallel performance, at least on HSW-EP and KNL (i.e. 72 to >250 threads), plenty of platform-specific code will be needed anyway (e.g. the "best" barrier algorithm itself is different). |
libuv also provides threading primitives: https://github.com/joyent/node/blob/a995a6a776f1b2d01946702190c6ff837c338577/deps/uv/include/uv.h#L1331-L1378 |
@vtjnash this is what is used but libuv misses the thread affinity code. I would say extending libuv would be a Good appoach |
@eschnett: Here is a link to Rust giving up on segmented stacks. "Busy leaves" property says that all leaves in the cactus stack have a thread actively running. E.g., on a P-thread system there will be no more than P leaves, and each leaf will have a thread running it. Each path from a leaf to the root corresponds to a stack that would have occurred in the sequential version of the program, hence the total space is bounded by P*(space for sequential execution). An example of a system without the busy leave property is TBB, where a leaf can stall because of the way TBB maps tasks to threads internally. (Alas a trade off we made for easy of portability.) It looks like libuv's threading support is essentially the same as pthreads. Atomic operations and user-level scheduling are missing. So libuv seems like a good way to break dependence on pthreads, but we'll still need at least macros for atomics and something for decoupling stack<-->thread bindings. |
I'll start a checklist of steps needed before we can turn threading on by default:
Bonus items:
|
That's a good list for things that need to be done before threading can be used but I think far less needs to be done before it can be enabled but not used. In fact I think that none of those are necessary for threading support to be enambled by default. |
@yuyichao's opinion was that at least the GC item should be done before enabling it by default. Does the current setup when threads are enabled result in worse performance in the single threaded case? |
I do agree that some performance testing would be great to have and we turn on threading by default carefully. @jrevels Is this something that your perf testing setup you are planning, be used to track? |
Ok, I agree not all of those are necessary. I lowered the priority of some. I don't think there should be an intermediate state where threading is enabled by default, but shouldn't be used. To me, enabling it by default sends a signal that everybody should feel free to use it. |
Why does the GC need to be changed before compiling thread support by default? My understanding of the GC situation is that it can only cause problems in cases where threading is actually used. The main issue with turning threading support on currently is that on LLVM 3.7 there's a massive regression in compile time, but @Keno's hard work on LLVM 3.7.1 should fix that once we switch LLVM versions. |
The point of enabling it by default as a strictly experimental features is so that we can make sure it actually compiles everywhere and can start getting bug reports on crashes from people trying it out but not relying on it as a stable feature. We are not going fix all the potentially crashes in a vacuum. |
The only performance regression now is probably the tls access for every GC frame. One main challenge for #14190 is to make sure I don't insert too many GC safepoint / transitions so a performance benchmark would be good.
If this is what we want, I'm fine with enabling threading for now. I just don't want to encourage people to use threading yet.
Threading works fine on LLVM 3.3 now. |
I thought threading worked on LLVM 3.3 as well as 3.7 now with @yuyichao's work |
No shortage of those! As it is, it crashes almost constantly. We should at least get to a state where we can say "seems to work ok". |
Also the stop-the-world is also not the only thing that needs to be fixed in the GC. Many GC internal datastructures are still not thread safe (mostly importantly the remset (for write barrier) and the allocation counter). |
@yuyichao The point is to enable threading so that we detect various stability issues in the rest of the codebase, and not necessarily encourage people to use threading. In fact @ArchRobison's PR helps discourage some of the current thread APIs. |
Yes - at least all sequential tests need to pass and CI needs to be set up before we can enable threading by default. Ideally, also no performance regressions, but as long as those are identified, I guess it would be ok. |
I vehemently disagree. We can fix compilation issues and other problems concurrently with making it more stable. I do agree that our test suite needs to pass with threading enabled, but AFAIK, that's already true. If there are platforms where that's not the case, we want to know about them so we can fix them. |
@yuyichao Now that things work with LLVM 3.3, I guess we don't need to worry about requiring any updates to the CI system (which was the case before), and threading can get tested. Is that correct? If so, we can at least have a PR with threading enabled, and see if we can get a green. |
Yep. As long as we make it clear that it will almost certainly crash for non-trival interaction with the runtime, I'm totally fine with enabling it by default. And we can certainly start by seeing how does Travis and Appveyor like it. |
I don't see the point of shipping something that crashes so much, and where we know we have simple library functions like |
Perhaps a good intermediate state is to enable everything else that JULIA_THREADS=1 enables, but fix nthreads==1 by default. I would be fine with that. |
People want to try out experimental features without having to compile a special version of Julia. That said, I would be fine with |
Ok, then this will be fine. All you'll need to do is set an environment variable. What I'm worried about is the appearance of "shipping crap". We need to balance releasing new features ASAP with maintaining rigorous quality standards. |
It's dev master, we're already telling people not to use it for real work. It isn't shipping until it's a stable release branch, and we're still a ways away from that on array and other stdlib work. |
True, but this is a different level of brokenness. Let's at least see if we can fix some of the more flagrant crashes over the next few days. |
The "testable unit" of the perf tracking framework is just a function call, so as long as a benchmark can be coerced into that form, then the framework should be able to measure its execution. I'd have to explicitly test out a multithreaded function call to be sure that it doesn't induce any weird breakage, but I can't think of any reason why it shouldn't work in theory. |
Should we close this issue for now, or update it for 0.5.0? I am tagging 0.5.0 to make sure this gets reviewed for release, but perhaps 0.5.x is the more suitable tag. |
this issue doesn't really list anything |
One of the first things we need to do is make the runtime thread safe. This work is on the
threads
branch, and this tracker predates the new GC. I thought it is worth capturing a tracker that @StefanKarpinski prepared earlier in an issue to ease the thread safety work.This list is organized as
Variable; Approach
builtins.c
ccall.cpp
static char *temp_arg_area; thread-local (will be deleted very soon)static const uint32_t arg_area_sz = 4196; constant (will be deleted very soon)static uint32_t arg_area_loc; thread-local (will be deleted very soon)static void *temp_arg_blocks[N_TEMP_ARG_BLOCKS]; thread-local (will be deleted very soon)static uint32_t arg_block_n = 0; thread-local (will be deleted very soon)static Function *save_arg_area_loc_func; constant (will be deleted very soon)static Function *restore_arg_area_loc_func; constant (will be deleted very soon)cgutils.cpp
codegen.cpp
debuginfo.cpp
dump.c
gc.c
init.c
jltypes.c
llvm-simdloop.cpp
module.c
profile.c
sys.c
task.c
toplevel.c
The text was updated successfully, but these errors were encountered: