@Suiseiseki @marcelschmall

> Is it really that impressive [...]

Yes. Because spawning a thread is much heavier than waiting for `POLLIN | POLLOUT`.

Spawning a thread just to handle a single fd is definitely not resource-wise; you need a dedicated stack, a tid, scheduling parameters, I/O prio, a dedicated PCB, and so many other scheduling-related resources.

@Suiseiseki @marcelschmall

Before epoll was invented, there were already select() and poll() to handle multiple fds with a single process, but poll()'s interface wasn't very efficient because you had to traverse all the `struct pollfd` array members for each sysret.

If you have 10K connections and one connection sends you data, with poll(), you need to traverse up to 10K `struct pollfds` to find which connection sends you data.

select() is even worse; it can only handle up to `FD_SETSIZE` number of fds which is defined as 1024. The traversal is pretty much the same as poll().

epoll solves the problem by avoiding wasted iterations in the caller for iterating over connections.

@ammarfaizi2 @Suiseiseki @marcelschmall the retval from poll can help optimize the traversal as it indicates the number of ready file descriptors.

I know it is still not as efficient as epoll, but if you find all ready fds earlier, you can break the iteration earlier too:

ready_fds = poll(fds, 10000, -1); if (ready_fds < 0) { handle_error(ready_fds); return; } for (i = 0; i < 10000; i++) { if (ready_fds == 0) { // All ready fds have been handled. // Break out of the loop early to // avoid unnecessary iterations. break; } if (fds[i].revents & (POLLIN|POLLOUT)) { handle_events(i, fds[i]); ready_fds--; } }

If you are lucky: that one client is not in the last index, you can break earlier.

@yuka @Suiseiseki @marcelschmall

Yeah, that makes sense. That kind of optimization should be applied to poll() and select() based event loop.

I said up to 10K. It doesn't necessarily mean 10K strictly. You can obviously break early. The worst case is if you have a ready fd in the last index.