Not sure i understand what the use case is. As soon as you start doing something on the event loop , you need some kind of way to perform the operation in another "thread" ( or goroutine or whatever). And then you start to need some kind of concurrency mechanism, and pay the price. Stripping those mechanism to pretend the event handling is faster only works if you never intend to have some real computation performed.…
Not the OP but typically you resort to these tactics when you want to shave the last ms of the server's response time, and/or get that last 1000 requests/s/core performance. You have a "fast path" that is simple and event driven and hand off operation processing to regular threads for the (less frequent) more complex operations. So you're not missing anything.
Evio – Fast event-loop networking for Go
51–60 of 67 posts
Re: Evio – Fast event-loop networking for Go
#52One of my favorite things about Go is that it cuts through the "threads vs. events" debate by offering thread-style programming with event-style scaling using what you might call green threads (compiler assisted cooperative multitasking that has the programming semantics of preemptive multitasking). That is, I can write simple blocking code, and my server still scales. Using event loop programming in Go would take aw…
It doesn't really "cut through" the debate any more than any other implementation of threads does. The only difference between Go and plain old one-thread-per-connection is that regular threads run in the kernel, while Go threads run in userspace. That's not a semantic difference, only an implementation detail (a large detail, to be clear, but still an implementation detail). There were historical implementations of…
I agree with OP that golang seems to provide the best of both worlds in the “event” vs “thread” debate. We can get the performance benefits of an eventing model with a much simpler programming model of thread per request.
It’s all “semantically” similar but it’s the details that matter. And I think golang chose the correct trade offs here (and with their sub-ms GC as well). The JVM, as an opposing example, made all the wrong choices I think for the general use case. Slow GCs and 1:1 threading.
I always understood the overhead of kernel threads compared to user threads to be significant at large scale. It’s not just stacks either. It can be a lot cheaper to swap between user threads, depending on implementation, compared to the scheduler having to preempt and trap into kernel code and provide a general purpose context switch.
Re: Evio – Fast event-loop networking for Go
#53Earlier quoted context omitted.
I'd suggest that's not the right way to look at it. To a first approximation, "everything" is using an event loop nowadays, in that everything is using the same fundamental primitives to handle and dispatch events. In particular, this includes the Go runtime; run "strace" on a Go network program and you'll see these same calls pop up in the strace. What this does instead is give a Go program direct access to the even…
> It does seem to me that a lot of people are a bit bedazzled by the top-level stuff that various languages offer, and forget that under the hood, everyone's using the event-based interfaces. Yup. It's all very similar under the hood. The most important difference between I/O models is whether the paradigm involves explicit vs. implicit management of the event loop. Callback models like Node, async/await style models…
I often hear "context switching between threads is cheaper" but pthreads still have their own PID and everything, so is this really the case?
Is there really much advantage to pthreads over the way PostgreSQL does things with efficient CoW sharing between processes for the binary?
Re: Evio – Fast event-loop networking for Go
#54Earlier quoted context omitted.
Thanks for taking the time to write this response. The more I hear about Go's features, the more I seem to like it.
FYI, this is not unique to Go. A number of languages implement lightweight threads in the same way (roughly), like erlang or Haskell. It’s all epoll or other efficient polling primitives under the hood.
Re: Evio – Fast event-loop networking for Go
#55Earlier quoted context omitted.
Go uses an m:n thread model. Goroutines are multiplexed onto a smaller number of os-level threads. They're sort of like threads, but they have a simplified programming model (There is no thread-level storage for example).
Goroutines are threads. They're just not kernel-level threads. There have been implementations of pthreads that used an M:N model, for instance.
Re: Evio – Fast event-loop networking for Go
#56Re: Evio – Fast event-loop networking for Go
#57One of my favorite things about Go is that it cuts through the "threads vs. events" debate by offering thread-style programming with event-style scaling using what you might call green threads (compiler assisted cooperative multitasking that has the programming semantics of preemptive multitasking). That is, I can write simple blocking code, and my server still scales. Using event loop programming in Go would take aw…
Re: Evio – Fast event-loop networking for Go
#58Earlier quoted context omitted.
> It does seem to me that a lot of people are a bit bedazzled by the top-level stuff that various languages offer, and forget that under the hood, everyone's using the event-based interfaces. Yup. It's all very similar under the hood. The most important difference between I/O models is whether the paradigm involves explicit vs. implicit management of the event loop. Callback models like Node, async/await style models…
Patrick, on the 2.6+ Linux kernels, is there a significant difference between threads and processes? It seems like both threads and processes are created via clone and the only difference is memory access? I often hear "context switching between threads is cheaper" but pthreads still have their own PID and everything, so is this really the case? Is there really much advantage to pthreads over the way PostgreSQL does…
Yes, they’re both created with clone, but with different levels of sharing. A pthread will share the virtual address space of its parent, which makes shared memory simple to implement; use the same pointer and you’re done. CoW is not “sharing” really, because you can’t communicate over it, it just saves some creation overhead.
With CoW, technically nothing gets copied initially, but as soon as the new process starts executing, it’s going to start copying the stack frame and any other regions it’s using. With a pthread you can be certain it will just copy the stack.
Context switches are usually cheaper when you don’t need to throw out the old virtual address space (and invalidate the Translation Lookaside Buffer). Pthreads share virtual address space, so there is no need to flush the TLB.
In a use case like Postgres, you don’t necessarily need to optimise for context switches. If you have a lot of concurrent connections, each of which has one process, then you’ll only hit limits with context switching overhead if very few of those connections are fighting over any locks or spending much time in IO at all. This is atypical, so usually those other factors hit you first.
Re: Evio – Fast event-loop networking for Go
#59Earlier quoted context omitted.
FYI, this is not unique to Go. A number of languages implement lightweight threads in the same way (roughly), like erlang or Haskell. It’s all epoll or other efficient polling primitives under the hood.
I'm of the impression that there's an important difference between Go and Haskell's models--namely that Go is M:N threaded and Haskell is not; however, I don't entirely understand the significance of the difference, so hopefully someone else can comment and enlighten me.
Re: Evio – Fast event-loop networking for Go
#60Earlier quoted context omitted.
What you call a "Go thread" has a precise name (goroutine) and running in userspace is hardly the only difference between a goroutine and a kernel thread. Creating and destroying kernel threads is significantly more expensive. A kernel thread has a fixed stack and if you go beyond, you crash. Which means that you have to create kernel threads with worst-case-scenario stack sizes (and pray that you got it right). Goro…
> What you call a "Go thread" has a precise name (goroutine) I call goroutines threads because they are user-level threads. As an analogy, NVIDIA calls local threadgroups "warps", but that doesn't make them not local threadgroups. > Creating and destroying kernel threads is significantly more expensive. Because kernel threads usually have larger stacks. But they don't always have large stacks: that is configurable. O…