-2

I have few questions on the go's answer to c10K problem. How is an event loop different from the network poller described in this blog?

I see a striking similarity between waiting threads and waiting goroutines. And, non-blocking IO.

I understand that it happens all under hood and transparently. But, at the sockets level, they are still the same?

does it matter whether network data is dispatched to the waiting goroutine or a thread from the pool? apart from the memory footprint and scheduling overhead of threads.

What difference would it make if new goroutines are spun off, on arrival of network data? or if the network data is dispatched to a goroutine from a pool?

2
  • 2
    Goroutines are much, much cheaper than OS threads. An OS thread requires multiple MB of memory and switching is controlled by the OS scheduler, which requires crossing the syscall boundary (expensive!). A Goroutine is roughly as cheap as an object, switching is as easy as a function call. Event-driven IO makes it possible to wait for many events with a single syscall, or even makes data directly available in userspace. That scales better than one blocking IO syscall per thread.
    – amon
    Commented Nov 27, 2023 at 7:10
  • 1
    You're asking whether implementations intended specifically to be functionally equivalent from outside the process, but more efficient, are "similar" or whether they "make a difference"? Of course the answer is that they are similar in that they accomplish the same thing, and different in that they accomplish it more efficiently.
    – Useless
    Commented Nov 29, 2023 at 0:57

1 Answer 1

6

There are two primary ways of dealing with socket connections, Blocking and Non-Blocking IO. Blocking IO typically allocates (at least) one thread to read from a socket, so that when activity occurs that thread wakes up to handle the request.

If you try to read from a non blocking socket (that doesn't have any data available), the call returns immediately (doesn't block), in simple apps especially those that have something for the thread to do, it may be sufficient just to poll the socket occasionally. However as the complexity of the app and number of connections increases it becomes impractical/inefficient to poll each thread individually, so the OS provides a call such as select() to check multiple sockets/handles at the same time.

However as the number of connections get very large (10K) even these mechanisms become inefficient so Mac provides KQueue and Linux provides epoll - which are event queues/notifications to let user space programs know when kernel events (IO in this case) occur.

Goroutines are serviced by native threads. When a goroutines needs to wait for data it is de-coupled from the native thread, so that only the goroutine is "blocked" - the native thread can be used to service a different goroutine. By doing this Go can use a much smaller number of native threads (relative to the number of socket connections it is servicing). Once data is available a native thread is assigned to service the goroutine with data.

Current versions of Go use epoll and KQueue to know when a "blocked" goroutines needs to be serviced by a native thread.

TL:DR; - goroutines are lighter than native threads, hence Go provides the benefits of a simplified programming model (logical code flow) whilst not incurring the cost of a large number (relative to the number of connections) of native threads to provide that model.

PS: I quoted "blocked" with respect to Goroutines as I may be misusing the word - its more like "de-scheduled" than blocked - since there is no active thread actually doing work/blocked.

How is an event loop different from the network poller described in this blog?

See the section of the blog: "Go’s answer to c10k"

Short version: An event loop is typically just servicing a queue - it takes the next item from the end and handles it. The network poller directly activates the specific goroutine (which also allows for different priorities in scheduling).

2
  • to quote the blog, "In older versions of Go, the network poller was a single goroutine that was responsible for polling for readiness notification using kqueue or epoll." would you say the older implementation of network poller had similar implementation as event loop?
    – ProgramCpp
    Commented Nov 28, 2023 at 22:24
  • 1
    The blog says "used a generalised wakeup mechanism of channel sends" - thats the piece that sounds like an event loop to me.
    – DavidT
    Commented Nov 28, 2023 at 23:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.