Replies: 38 comments 91 replies
-
Implementing coroutines (similar to Go approach) has always been the plan and is in the roadmap. See https://vlang.io/docs#concurrency |
Beta Was this translation helpful? Give feedback.
-
@nnsgmsone what does 'more energy can be injected' mean in this context? |
Beta Was this translation helpful? Give feedback.
-
@spytheman make the routine more useful |
Beta Was this translation helpful? Give feedback.
-
As far as I know this was the intention all along, they were Implemented that way to begin with to have something working. |
Beta Was this translation helpful? Give feedback.
-
I think the use of subroutines is very limited in contrast to what threads can offer (as long as implented e.g. with the "actor" paradigm, without any semaphores/locks)... so threads shall be still supported. See this statement:
|
Beta Was this translation helpful? Give feedback.
-
@gslicer don't worry, there will always be the Thus I think this topic can be closed as it got fully superseded by #1868 . |
Beta Was this translation helpful? Give feedback.
-
As long it's "unsafe" I'm clearly worrying :) |
Beta Was this translation helpful? Give feedback.
-
@gslicer I think it's easy to achieve the effect of an actor with the routine and channel.For example, a library I wrote myself is such an effect. |
Beta Was this translation helpful? Give feedback.
-
Will there be a compiler flag to make |
Beta Was this translation helpful? Give feedback.
-
When this is implemented in V, would it be possible to implement as pre-emptive (like erlang) instaed of cooperative (like go)? |
Beta Was this translation helpful? Give feedback.
-
@atomkirk so far V has built-in "go routines" which are fully preemptive (and I think the consensus is, that it should stay so). This GitHub issue seems to be about a different thing - namely about standard library offering simple pure coroutines (which are by definition non-preemptive). |
Beta Was this translation helpful? Give feedback.
-
@dumblob they can be. Erlang processes are user-level AND preemptive. They are very robust. its preemptive now because it uses kernel threads which are bulky and expensive. |
Beta Was this translation helpful? Give feedback.
-
That depends on how the Erlang VM is being executed. If it runs on bare hardware and no non-Erlang SW is being called, then you're right. In any other case Erlang processes are only partially preemptive (i.e. one Erlang process can starve indefinitely leading to stopping the whole Erlang VM). But I digress. My point was different. V community seems to incline to have built-in support (in the form of V's go routines) for fully preemptive execution while offering non-preemptive alternative (refered to as coroutines) in the standard library (i.e. not built into the language). |
Beta Was this translation helpful? Give feedback.
-
This is not true since Go 1.14: |
Beta Was this translation helpful? Give feedback.
-
Let me reiterate - please read the whole thread #1868 incl. all links recursively (depth 3 should be enough).
Partially yes - IMHO yielding under the hood will be done less aggressively than Go lang does, because there'll be the full preemptiveness, so presumably the inserted yields will be put only on critical places chosen based on true performance profiling analysis of representative apps (unlike in Go lang where they have no choice and have to put them really everywhere to make the language kind of work). |
Beta Was this translation helpful? Give feedback.
-
what is "agressive" about cooperative yielding? its simply efficient because it will yield at exactly the points the thread would end up sleeping anyway |
Beta Was this translation helpful? Give feedback.
-
also isnt the fact that just about any other language (even c) now has cooperative coroutines an indication that they are a good idea? |
Beta Was this translation helpful? Give feedback.
-
Well, this supposes that programmers are dumb and will use 1 go routine per 1 request (be it a network request or any other sample from a high-rate stream). Which is one of the dumbest things one can do. Nobody from the Go lang nor Erlang world does this because Go routines in Go (and Erlang processes as well) are still extremely expensive (you can have only smaller millions of them which is by far not enough for a scalable app). Therefore I'd say V shall actually not make the scheduler this smart. But let's see, maybe someone will provide some measurements and data and in V 1.1 (which is years ahead IMHO) there'll be such a smart scheduler. But definitely not now for V 1.0 because it's a nonsense from my point of view.
At some point (if you have too many yields) it becomes less efficient than less frequent preemptying (and it has also other downsides - it increases code size a bit, it disallows good CPU-bound performance optimization, etc.). Please just finally find few hours to read the thread #1868 recursively (and maybe wait one more day to let the brain calmly absorb it all before proposing other concepts which actually are quite aging already). We'll be here, we won't run away 😉. This topic is not urgent and very old and many people smarter than me have put their thoughts in it - most of it documented in the #1868 thread and recursive links.
Again - there is a plan for coroutines (as part of the standard library, maybe even with some intrinsics). But IMHO it's lower priority. Feel free to make a PR with a potential API (some people already worked on that, but I can't find the links now quickly - just search for them yourself and ask e.g. on Discord). |
Beta Was this translation helpful? Give feedback.
-
I am not convinced. How can it be more efficient to let a routine sleep (on a network select or waiting on a mutex or...) than yield it? The thread will idle and eventually be swapped out by the os kernel. Unless you are talking about spin locking. But even that you can model with proper co-routines. Also see this talk where Gor Nishanov applies the new co-routines to micro-optimizations to mask cache line latencies, so I beleive co-routines, at least the new ones that have compiler support, have been driven "all the way down". Also bear in mind that with the new compiler support for co-routines in later c compiler the compiler can even inline across and through yield points. |
Beta Was this translation helpful? Give feedback.
-
Please really devote several hours to reading the whole thread #1868 incl. links. You'll learn among other things about Weave which nicely shows what the performance differences are - but don't forget we're talking about maxing out performance of multiple processing units, not just a single core. And btw. as I said, V will insert yields internally on important places (preferably according to measurements and not human guesses) like selects, mutexes, etc. But I suppose it'll be on a (much) lower number of places than the recent Go versions started to do (you can read about this also in one of the linked resources from the #1868 thread).
Thanks for the link. Gor explains a cool idea how to implement very lightweight coroutines which highly efficiently leverage modern CPU cache hierarchy. These nano-coroutines are unfortunately something V can't care about. Simply because they don't support scheduling across multiple processing units. In other words they do support only a single processing unit (i.e. only one thread) and based on Gor's explanation this can't be changed without losing some of their benefits. I'd even guess that e.g. Weave (which offers tasks which is just a slightly different abstraction for the very same coroutine concept) is about as fast as Gor's nano coroutines even if run only on a single core (despite Weave being designed to max out performance of many processing units with different processing powers). Feel free to test it and post your results here to let us reproduce them on more machines.
Inlining micro-optimization sound like a patch to a wrong abstraction. But yes, thanks to this I'd guess it'll catch up with "normal function calls" when it comes overhead on one core. |
Beta Was this translation helpful? Give feedback.
-
I had a quick look at weave, i dont see how it applies to async operations like networking where you have to keep on juggling tasks because most of them simply cannro make progress at any given time due to waiting for io. I think you are fundamentally mixing up concurreny with parallelism. |
Beta Was this translation helpful? Give feedback.
-
Hi. I am not sure what exactly you mean with |
Beta Was this translation helpful? Give feedback.
-
I am not sure, arguing with you feels like trying to chase a rabbit to me, and I think I am about to give up. I don't really unerstand what you intend to build. RIght now in v I have a synchronous networking library and a server would basically built using the good old fork approach, only that I would be using threads instead of exec. And you seem to want to automatically reduce the huge amount of threads this can amount to in something really large scale by some magic automatic pre-emptive runtime which I dont iunderstand and honestly I am to lazy to read all those references. but good luck with that. I like c++ coroutines because they make things more readable and are effectively a negative overhead abstraction as they give the compiler the chance to inline through what would traditionally have been a context switch. and I like the reactor pattern as it avoids huge amounts of threads which feel ineficcient (i have a hard time beleiving there is no downside to having 1000 threads running), but maybe I just grew used to that. Once your scheme works I might have a look at it. Right now v doesnt seem usable for my purposes though. I would have offered to help out, but since I dont even understand the direction you are going in I dont feel in a position to do though. May be I am just too old. Hmm, I just went through it again, and end up confused. I mean, if I can use your clone of the go-routine syntax (that is the idea, right?) and it will pre-empt "cleverly", the cleverest to me being to pre-empt at obvious bloking points such as select and lock, then the result somehow seems the same as go-routines (or equivalently c++ coroutines with some reactor implementaion such as asio). what else will your pre-emption scheme acheive? some kind of better loadbalancing? |
Beta Was this translation helpful? Give feedback.
-
https://github.com/sustrik/libmill |
Beta Was this translation helpful? Give feedback.
-
quick heads up: I seem to be able to implement synchronization primitives now. I basically use a combined lock/condition variable as my lowest primitive. With that I implemented a concurrent generator/consumer pattern (i.e. push/pull from a fifo queue) successfully in the presence of a multithreaded scheduler. as a side note I seem to be able to do about 500k context switches/s/thread whereas even creating and detaching 1M threads takes >10s. next up will be playing with async i/o. |
Beta Was this translation helpful? Give feedback.
-
I'll just throw this here as I can't devote much time to this now. Coroutines upside down as "native async" in VIt seems (some) people expect V to be usable also for "special" things like hard real-time guarantees or low-power embedded systems or systems with poor threading support running on single-core CPUs or systems with extreme I/O pressure (i.e. processing of shortest imaginable data lengths in the highest frequency the given HW permits). For such cases it'd be inefficient to use any synchronization and thus to use multiple cores. This is important to realize and stress. Therefore from now on I'll assume in this write up only single-core processing (of course, this can be seamlessly combined with One idea how this could be achieved is to create and/or use coroutines as sort of a "type". Thus basically offering seamless See https://qed-lang.org/article/2019/06/27/coroutines.html which explains the ideas with detailed code samples how to implement (or emulate) the desired behavior using an imperative language with interfaces. I hope this can be modeled using existing V mechanisms (as a module in standard library). But if that wouldn't be convenient enough, then contemplate some minimal extension of current semantics or in the worst case even some tiny syntax addition. A crazy related question is whether this "smart coroutines" functionality could "blend" with the functionality of go routines somehow? Basically "telling the compiler more information" than current |
Beta Was this translation helpful? Give feedback.
-
small heads up: I managed to fix the networking code and now have a fully concurrent webserver example running. now I still have to fixx some issues around synchronization primitives. |
Beta Was this translation helpful? Give feedback.
-
The first version of coroutines is live! Check out the examples/simple_coroutines.v example. |
Beta Was this translation helpful? Give feedback.
-
Hi, coming from Go and having read (and probably not fully understood) the whole thread: I always missed in Go some more control over the goroutines. // pseudocode
// have a constant telling us the available cpu cores
th1 := thread.New() or {panic('no threads left') }
th2 := thread.New() or {panic('no threads left') }
cr1 := th1.NewCoroutine()
cr2 := th1.NewCoroutine()
cr3 := th2.NewCoroutine()
ch := chan(int, 3)
fn doStuff(c chan bool, cr *CoRoutine) {
defer cr.Close()
// have other defers to release resources
c <- cr.Num
}
// we can only run coroutines, no threads
go cr1.run(doStuff)
go cr2.run(doStuff)
go cr3.run(doStuff)
for {
i := <- ch or { break}
println(i)
}
th1.WaitToFinish()
th2.WaitToFinish() The implications being:
Then on top of this abstractions could be schedulers, queues etc. build for several scenarios and use for io etc. within the standard library. Why is such a way not discussed? It should be simple to implement (you don't need to have heuristics at that point) and all the optimizations can be done later on top of the fundamentals... |
Beta Was this translation helpful? Give feedback.
-
I found that the coroutine of v is directly called pthread_create. I think it is possible to add user-level coroutines, so that more energy can be injected. Perhaps can learn from golang's approach and add a runtime. . .
Beta Was this translation helpful? Give feedback.
All reactions