lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

On Fri, Mar 21, 2014 at 03:47:22PM -0700, Coroutines wrote:
> Just popping in to say IOCP on Windows is like POSIX AIO in that you
> request an operation be completed and are notified when it has been.
> With things like select() or epoll() you are notified when a socket is
> ready for reading, but with IOCP or AIO you are notified when the
> buffer you pointed the OS at has been filled with the data you wanted
> to read.  IOCP/AIO should mean less waiting for events to occur
> because they can handle the two-step process of reading data from and
> filling a buffer, where usually your program does that.  (This is just
> an example -- the other side would be telling IOCP/AIO to write data
> from a buffer to a socket when it can.)  Usually you would
> select()/poll()/epoll()/kqueue() to be notified when you can write
> from the buffer out, but the OS can do this faster (which is why
> people want to like IOCP/AIO).  The only one nobody seems to like is
> real-time signal IO.  Dude, fuck SIGIO.

IOCP isn't fundamentally any faster. All it does is hide the thread
synchronization. IOCP has to do the same number of logical operations as you
would do with epoll + pthreads or with AIO.

IOCP does have some magic regarding how it manages your thread pool by
controlling the number of outstanding threads doing user-space work, but in
practice it doesn't buy you anything. If you look at benchmarks people tend
to get the same throughput with IOCP and they do with epoll, presuming you
multi-thread your epoll usage.

The _huge_ downside to IOCP (which IMHO far outweights the small
optimizations) is that it's very difficult to build simple consumer/producer
frameworks atop it without using callbacks. And callbacks are bug factories,
especially in combination with threads, because they completely trash code
locality, among other issues. libevent 2.x continues to fix bugs related to
callbacks and threading.

IOCP (and AIO, for that matter) lack software composability:

It's not just as matter of signaling in an inner loop. Linux, for example,
allows you to poll an AIO handle. Callbacks hinder composable designs. You
can't _hide_ a callback, because it obstructs execution flow.

With a non-blocking design, you can implement state machines at the core of
your consumer/producer object. And you can stack these objects. When you
switch from a purely threaded design to a non-blocking design, the only
necessary semantic condition you need to teach your code is "try again".
With callbacks, you have to _also_ manage interfaces for managing callbacks,
and all that entails--ownership problems are more difficult, request
cancellation is problematic, etc.

Example: I have a small socket library in C that does DNS and SSL
internally. It was trivial to add the features because both my own DNS
library and OpenSSL handles I/O in a non-blocking manner and _without_
callbacks. So my so_read() routine can behave _exactly_ like Unix read()
does. And I can use it everywhere, regardless of the event loop or threading
framework of the project.

Imagine implementing (and debugging!) the same feature set within an IOCP
framework. I'd sooner quit my job and work at McDonalds.