News for hackers. Stuff that matters.
Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @12:51PM (#21250243)
Author here. I'll be watching the comments and can answer any questions!

I do want to make clear that most of the optimizations discussed are implemented in the Go scheduler, which is where I discovered them. I wrote the article to call them out as they were not easy to discover.

Re:Making the Tokio scheduler 10x faster by MarkSweep on Monday October 14, 2019 @04:19PM (#21252412)
It looks like the number of threads in the threadpool is fixed. If have 1 one thread in the thread pool, is it possible to dead lock it by queuing a Task A the blocks waiting for Task B to do something, where A starts executing before B?

Do you think it would make sense to dynamically adjust the number of threads in the pool? An example of a thread pool that dynamically adjusts the number of threads is .NET. The algorithm is described by Matt Warren[1] and a paper[2]. In addition to the C++ version Matt links to, there is a C#[3] implementation.

[1]: https://mattwarren.org/2017/04/13/The-CLR-Thread-Pool-Thread... [2]: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.487... [3]: https://github.com/dotnet/corert/blob/master/src/System.Priv...

Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @04:27PM (#21252501)
The expectation is that all code running on the Tokio scheduler does not block and does not do anything CPU intensive.

There is follow up work planned to allow annotating blocking / CPU intensive bits of code so that the scheduler can do something smarter (things like you linked).

The old scheduler already had such an annotation, but in the interest of shipping, this has punted in the new scheduler.

Re:Making the Tokio scheduler 10x faster by ulber on Monday October 14, 2019 @06:15PM (#21253454)
Is that expectation wrt. responsiveness guarantees? In other words, if I'm just interested in throughput are CPU intensive tasks a problem? For example, my expectation is that it would be appropriate to use the new Tokio scheduler to parallelize a compute intensive simulation workload (where inside a "frame" responsiveness is not a problem).
Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @11:31PM (#21255324)
The Tokio scheduler uses cooperative multitasking (non-preemptive). So, if your task runs without yielding it can prevent other runnable tasks from executing.

So, if that is acceptable, then it is fine.

Re:Making the Tokio scheduler 10x faster by weavie on Tuesday October 15, 2019 @03:54AM (#21256400)
Is there a recommended approach to dealing with the occassional cpu intensive task?

Supposing you were running an http server and most responses were very quick - but occassionally a request came along that required calculating pi to the n billionth digit.. (for example).. Would you need to farm that request to a separate process - or could you keep it in process but in a separate thread outside of Tokio?

Re:Making the Tokio scheduler 10x faster by carllerche on Tuesday October 15, 2019 @08:15AM (#21257623)
Right now, CPU intensive code blocks or code blocks that block need to be annotated. This way, the scheduler can respond accordingly (cooperative).
Re:Making the Tokio scheduler 10x faster by nicoburns on Tuesday October 15, 2019 @04:44AM (#21256648)
I believe tokio-threadpool is designes for this scebario.
Re:Making the Tokio scheduler 10x faster by networkimprov on Monday October 14, 2019 @01:35PM (#21250723)
The Go team is working on an update of its scheduler to make it preemptive. Will Tokio follow suit?

https://github.com/golang/go/issues/24543

Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @01:41PM (#21250794)
My understanding of that PR is it relates to how the Go compiler does code generation. Rust takes a different approach and is about to ship `async / await` which is a different strategy.

Preemption is out of scope for Tokio as we are focusing on a runtime to power Rust async functions. So, for the foreseeable, Tokio will using "cooperative preemption" via `await` points.

Re:Making the Tokio scheduler 10x faster by 15155 on Monday October 14, 2019 @01:44PM (#21250830)
IIRC: one of the issues with the original Rust stdlib greenthread implementation was that it's extremely difficult to preempt execution in a systems language. It's also quite difficult to add userland execution preemption without incurring significant performance penalties.

How do you preempt syscalls or FFI calls? (Go will almost certainly also have this issue with cgo modules)

I would be very surprised if preemptible Futures are something Tokio can or would implement.

Re:Making the Tokio scheduler 10x faster by wbl on Monday October 14, 2019 @04:48PM (#21252719)
What does systems mean here? There is a dirty trick with a NOP and self rewriting code that's quite cheap when not triggered. I think java does it that way.
Re:Making the Tokio scheduler 10x faster by sansnomme on Monday October 14, 2019 @05:24PM (#21253062)
Elaborate?
Re:Making the Tokio scheduler 10x faster by wbl on Monday October 14, 2019 @06:16PM (#21253469)
So you put in a NOP before each backward jump. To preempt you overwrite it with a jump to a handler that saves the state of the thread.
Re:Making the Tokio scheduler 10x faster by jedisct1 on Monday October 14, 2019 @06:51PM (#21253708)
How multitasking used to work on Atari :)

But this requires code pages to be writable. Which, from a security perspective is awful.

Re:Making the Tokio scheduler 10x faster by nine_k on Monday October 14, 2019 @07:56PM (#21254175)
This presumes writable code segments, which many OSes shun.
Re:Making the Tokio scheduler 10x faster by hyperman1 on Tuesday October 15, 2019 @07:09AM (#21257337)
It's java. Dynamic code generated by a jit.
Re:Making the Tokio scheduler 10x faster by adwn on Tuesday October 15, 2019 @12:15AM (#21255516)
Which NOP do you overwrite? Do you have to do this for all backward-jump-NOPs in all loaded executable code, or do you have to determine, which loop the thread in question is executing right now?
Re:Making the Tokio scheduler 10x faster by heavenlyblue on Monday October 14, 2019 @07:17PM (#21253914)
How does that resolve syscall question?
Re:Making the Tokio scheduler 10x faster by wbl on Monday October 14, 2019 @09:56PM (#21254886)
Syscall you have to do with a signal
Re:Making the Tokio scheduler 10x faster by pcwalton on Monday October 14, 2019 @01:49PM (#21250883)
Preemption isn't a concept that applies to a task scheduler like Tokio. You can't just inject more states into a finite state machine at runtime.
Re:Making the Tokio scheduler 10x faster by Exuma on Monday October 14, 2019 @03:42PM (#21251981)
When does that get released into Go? That sounds cool
Re:Making the Tokio scheduler 10x faster by jstrong on Monday October 14, 2019 @01:35PM (#21250725)
I noticed that you call `unsync_load` on the `AtomicU32`, which confused me, and I later saw that there is a custom `AtomicU32` in the loom library. Can you explain what `unsync_load` is and whether it will be provided by the atomics library in `std`?
Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @01:53PM (#21250920)
`unsync_load` reads the memory as if it were a normal field (no atomic operation). The `std` atomic types already provide `get_mut` which gives access to the "raw" field when the caller has a mutable reference to the atomic. The mutable access guarantees there are no concurrent threads that can access the field, so it is safe.

The `unsync_load` is used because, at that point, two things are guaranteed: - The only thread that mutates the field is the `unsync_load` caller - All other threads will only read from that field.

Because of this, we can just do a "plain old" access of the field w/o the atomic overhead.

As for whether or not it should be in `std`, I don't know. It probably could, but it doesn't have to. It's a pretty "advanced" access strategy.

Re:Making the Tokio scheduler 10x faster by likeliv on Monday October 14, 2019 @03:23PM (#21251778)
Is that not an undefined behaviour?

Can't you get some values "out of thin air" (partial updates of some bits.) Or some intermediate values?

(For example, can the compiler decide to use this memory location to store some unrelated temporary result, as it knows it will erase it soon with a final value and that no other threads is supposed to access this)

Re:Making the Tokio scheduler 10x faster by lilyball on Monday October 14, 2019 @07:34PM (#21254030)
It's not undefined because the thread doing the reading is the same one that wrote the value in the first place, meaning there is no cross-thread behavior at the moment and therefore the memory model collapses down to the single-threaded model (where all atomic accesses are irrelevant).
Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @03:55PM (#21252146)
The compiler cannot use the contents of an `UnsafeCell` for temporary storage. In general, `UnsafeCell` is how data can be shared across threads without synchronization.
Re:Making the Tokio scheduler 10x faster by CodesInChaos on Tuesday October 15, 2019 @07:44AM (#21257486)
I thought `UnsafeCell` is only exempt from the aliasing rules, not the memory model? I.e. the compiler can use it as it wants, if it can prove that it's not accessed in the meantime (which can be difficult without relying aliasing rules, but possible if the code is simple enough)
Re:Making the Tokio scheduler 10x faster by carllerche on Tuesday October 15, 2019 @08:22AM (#21257654)
To be honest, I'm not entirely following what you see as the danger. The compiler can't generate code that randomly goes and writes at pointer locations.
Re:Making the Tokio scheduler 10x faster by BubRoss on Monday October 14, 2019 @04:42PM (#21252641)
In x86 at least, 32 bit loads and stores are done atomically already.
Re:Making the Tokio scheduler 10x faster by likeliv on Monday October 14, 2019 @04:59PM (#21252827)
I know rust is not C or C++, but we are not programming in x86 assembly, but for the language "abstract machine". And if the compiler infer that this memory location is not used by other threads because we don't use an atomic operation, it can perform optimizations that could result in subtle bugs. That's what undefined behaviour is.

Although in this case, I guess this is probably fine since the non-atomic read can't race with a write.

Re:Making the Tokio scheduler 10x faster by gpderetta on Monday October 14, 2019 @07:15PM (#21253897)
The code is correct both practically and formally. All unordeeed reads are reading values written by the same thread. Even if the writes are atomic there is no violation of the no data races rule.
Re:Making the Tokio scheduler 10x faster by charliesome on Monday October 14, 2019 @09:12PM (#21254642)
x86 does do out-of-order execution however, so while it's true that you'll never read a half-written value, it's still important to use the atomic types to ensure that reads/writes across threads are performed in the right order.
Re:Making the Tokio scheduler 10x faster by BubRoss on Tuesday October 15, 2019 @11:24AM (#21259423)
That's true but not what is being discussed here.
Re:Making the Tokio scheduler 10x faster by dunkelheit on Monday October 14, 2019 @02:04PM (#21251038)
What is the difference with Ordering::Relaxed?
Re:Making the Tokio scheduler 10x faster by ComputerGuru on Monday October 14, 2019 @02:10PM (#21251104)
Ordering::Relaxed guarantees atomicity (but not coherence). On 32-bit or 64-bit platforms, a (aligned) 32-bit read or write will always be atomic anyway.
Re:Making the Tokio scheduler 10x faster by comex on Monday October 14, 2019 @10:05PM (#21254940)
An assembly-level read or write will always be atomic anyway, and in fact a relaxed atomic read/write typically compiles down to the exact same assembly as a normal read/write. However, there are compiler optimizations that can break atomicity, such as splitting accesses into multiple parts; using (relaxed) atomics instructs the compiler not to perform those optimizations.
Re:Making the Tokio scheduler 10x faster by dunkelheit on Monday October 14, 2019 @02:22PM (#21251221)
What do you mean by coherence? If it is the property that all updates to a single memory location are seen in a consistent order by all threads then Relaxed guarantees that.
Re:Making the Tokio scheduler 10x faster by wilsonthewhale on Monday October 14, 2019 @03:00PM (#21251564)
it doesn't. From cppreference (whose model Rust roughly follows): "Relaxed operation: there are no synchronization or ordering constraints imposed on other reads or writes, only this operation's atomicity is guaranteed"
Re:Making the Tokio scheduler 10x faster by dunkelheit on Monday October 14, 2019 @03:21PM (#21251768)
Still I think I am correct. The docs (https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html...) point to https://llvm.org/docs/Atomics.html#monotonic which states that "It essentially guarantees that if you take all the operations affecting a specific address, a consistent ordering exists." which is what I've said.
Re:Making the Tokio scheduler 10x faster by ComputerGuru on Monday October 14, 2019 @03:34PM (#21251892)
Yes, a consistent ordering exists but that ordering isn’t necessarily what you think or want it to be. The consistency is present based off the order the reads and writes were executed in; you’ll never read back a value that wasn’t at some point stored there. Which can happen with non-atomic reads/writes, where you’ll read part of one write and part of the next. Reads can still be reordered before writes, as no memory barriers are issued.
Re:Making the Tokio scheduler 10x faster by dunkelheit on Monday October 14, 2019 @04:20PM (#21252418)
Well that doesn't contradict anything that I have said :) My question remains - what do you mean by coherence? I argue that it is precisely the property that there is a consistent order of operations a single memory memory location so you can't say that Relaxed operations don't satisfy coherence.
Re:Making the Tokio scheduler 10x faster by gpderetta on Monday October 14, 2019 @07:02PM (#21253794)
You are correct. IIRC Coherency is the only property of relaxed atomic operations, as opposed of plain load/stores where concurrent modification and access is just UB.
Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @02:16PM (#21251164)
Relaxed ordering impacts the compiler. There are a number of optimizations it cannot make. I am not sure exactly what those are, but it is measurable.
Re:Making the Tokio scheduler 10x faster by CodesInChaos on Tuesday October 15, 2019 @07:34AM (#21257443)
The only forbidden optimization I could come up with is:

    x = load-relaxed(a)
    ...
    f(x)
cannot be turned into

    x0 = load-relaxed(a)
    ...
    x1 = load-relaxed(a)
    f(x1)
while this would be possible with normal memory access.

so under register pressure the compiler would be required to spill something onto the stack instead of just reloading `x` from the original location.

Re:Making the Tokio scheduler 10x faster by carllerche on Tuesday October 15, 2019 @08:24AM (#21257663)
Last time I spoke about this topic w/ the rust compiler devs, they mentioned there were some LLVM "bugs" with regards to optimizations and Relaxed, as in LLVM is too conservative.
Re:Making the Tokio scheduler 10x faster by dunkelheit on Monday October 14, 2019 @02:54PM (#21251496)
Probably some reordering? Would be interesting to compare generated assembly.
Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @11:32PM (#21255331)
I believe there are some LLVM "bugs" with regards to Relaxed ordering. I don't know the specifics.
Re:Making the Tokio scheduler 10x faster by petschge on Monday October 14, 2019 @02:05PM (#21251041)
Tiny comment: I think the sentence "However, in practice the overhead needed to correctly avoid locks is greater than just using a mutex." should be part of footnote 1, not the main text.
Re:Making the Tokio scheduler 10x faster by jmakov on Monday October 14, 2019 @02:54PM (#21251509)
Too bad FFBuffer is not implemented from the FastFlow project. They are using queues without memory fences.
Re:Making the Tokio scheduler 10x faster by carllerche on Monday October 14, 2019 @03:02PM (#21251584)
Assuming [this](https://github.com/fastflow/fastflow/blob/master/ff/buffer.h...) is the FFBuffer in question, it looks like it is spsc (which would not support stealing). Also, it would need some kind of synchronization to ensure consistency between the consumer & producer.
Re:Making the Tokio scheduler 10x faster by jmakov on Monday October 14, 2019 @03:10PM (#21251661)
Yes, it's an implementation of the paper they're referencing - "An Efficient Unbounded Lock-Free Queue for Multi-core Systems". And they've build a framework around it (all queues implemented using this structure). Their benchmarks shows improvement (as shown in their paper). Might be interesting to use that in Rust. But what I miss is a benchmark for various approaches (in Rust, C++ etc.) with trying to use multi core effectively. What I found is that some of the papers use https://parsec.cs.princeton.edu/overview.htm. Probably still better that what we have now - "X nanosec per push/pop".
Re:Making the Tokio scheduler 10x faster by gpderetta on Monday October 14, 2019 @07:11PM (#21253872)
The advantage of FF is that it is unbounded, but if you add the required mutual exclusion between pop and steal, it will be as expensive (if not more due to the extra complexity).