Performance of green threads and why it matters
250bpm.com
250bpm.com
Have to agree on this, and often wonder the same thing.
Parallelizing a goroutine is not safe if the goroutine shares memory. Obviously that's not the default Go style, but sometimes sharing state is unavoidable.
So that would be one point in favor of different syntax.
Without hardware executing code in parallel, it's not parallelism, BTW. It's not semantics; it's specific jargon that means something.
(I've also seen definitions of concurrency that do not imply the above, but I think the above definition is more in line with the distinction people try to make...)
https://lobste.rs/s/c8pxgu/performance_green_threads_why_it_...
Are people running into performance issues with green threads in practice? It seems like they're already pretty performant, particularly if you're using them more as an abstraction for concurrency than as a way to get parallelism. There are lots of problems that are trivial to solve with green threads/coroutines but that are very difficult without them. If you really care about performance you probably shouldn't be using green threads at all but should instead be running separate processes on dedicated cores. Green threads also have the advantage over kernel threads in that they don't require the overhead of a kernel context switch, since the context switch can be done in userspace, which requires saving much less context and also affects your caches less.
Name at least one that cannot be solved better without them.
Regarding green threads and state machines I would however add that while green threads allow to ditch some state machines (e.g. ones that wait until a complete packet was received) you won't get rid of all of them, e.g. you will still need state machines for lots of protocol implementations. E.g. if you are implementing the websocket protocol or HTTP/2, you have some interaction between incoming data, data that must be sent out and requests from the [library] user. The state of the connection depends on all of these, so you need some kind of connection state machine which would run most likely in it's own green thread (in addition to a sending thread and a receiving thread and a user thread). And now you have to implement clean synchronization between all of those threads. The lack of necessary synchronization (and the ability to observe a huge amount of state at once without danger) can make the implementation a little bit more easy in my experience. But here I also have to agree with strmpnk that a good design is necessary for both approaches, and accessing lots of shared mutable data in multiple callbacks is probably also not the cleanest approach.
So from there the benefits are around being able to use the sequential features of the language in combination (error handling, flow control, &c). Stackless transformations are a middle ground but there are plenty of things that become inconvenient without green thread stacks which make composition much easier.
No, it's not. See my other reply.
> there are plenty of things that become inconvenient without green thread stacks
There are no such things. Because you can have all of the sequential code you want, but without any of the downsides of threads.
I've spent plenty of time with both green threaded languages and non-green threaded languages. There is a most definitely a difference. A lot of the critiques you've given apply to event loops as well.
For example, data access, sure you can't write half a value but interleaving callbacks for work broken up into multiple stages is just as complex in terms of interleaved flows and would require the same careful treatments you would need in threaded code. The solution there is not to share data, even with events. I'm sure you implicitly do this yourself but there is nothing that makes either better or worse at it.
What you've failed to address are all of the trade-offs event loops have, like forgetful stack traces (and any effective fix keeps a stack of event flow contexts on the side, ironically).
In reality neither green threads or event loops are all that amazing on their own. Great runtimes need a whole lot more, so it seems kind of silly to sit on one side of the fence shouting that there are no downsides on your side of things because there obviously are plenty of problems worth solving better in both camps.
I think you didn't have a pleasure to work with event loops in languages with first-class functions, that's why you think they are on the same boat with green thread. They are not, of course. There are no trade-offs. You have multiple scopes to share memory from, one for each handler, that can execute only atomically, one for all handlers created by that function, that may execute out of order, and a global scope of course. You can compose functions, give them names and what not. You can even have a console, like Erlang does.
I've tried like a dozen of concurrency models, you cannot really convince me, that threads are any good for that.
completions create all sorts of concurrency problems because they are delivered to the program without any concurrency protection, and writing such protection correctly turns out to be non-trivial.
By delivering completions into coroutines, you gain all the concurrency protection that is offered by the green threads, without having to write it from scratch.
This is actually what the most basic event loop handles for you. OS signals, timers and asynchronous events are not delivered into the middle of your program, but into the point, where all of the event handlers are not running. That's the beauty of it. You can then react to the event and cancel whatever you are waiting to be running, or schedule something new.
With coroutines you can only safely handle those things each in a separate coroutine. But you don't need them isolated, you need your code to actually do something on those signals and timer events. So, you end up with a lot of flags spread all over other coroutines, protected by mutexes/channels. But then it's still not responsive, because you cannot unblock a currently blocking call, so many concurrency patterns do not apply and even bad things happen, like leaks and deadlocks. You rethink it again and move blocking calls each into a separate coroutine, so you would be able to construct a loop that waits for messages from other coroutines and reacts right away, abandoning blocking coroutines, if required. Hopefully you eventually realize, that you are emulating an event loop, but a very clumsy one, and go back to working with event handlers and higher-order functions.
If that is the case then processes could be faster than OS threads because you'd avoid context switches altogether. However, if your system supports pinning OS threads to processors and you limit the number of threads to the number of available processors, I can't think why that wouldn't be just as good.
In lthread I preallocate fixed size 1mb stack but madvise after a coroutine yields if the stack shrunk. No copies are needed but it does consume the vm address space.
Downvote all you want.
Please don't break the HN guidelines: https://news.ycombinator.com/newsguidelines.html.
We detached this subthread from https://news.ycombinator.com/item?id=11654767 and marked it off-topic.
The problem with comments like this is that they degrade the discourse even if they're true.