-
Notifications
You must be signed in to change notification settings - Fork 188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate with the new Windows IO manager #364
Comments
@kazu-yamamoto Registered I/O Networking Extensions, RIO, is a "new" API that has been added to Winsock to support high-speed networking for increased networking performance with lower latency and jitter. (It's been out for a few years now). Essentially though, in traditional Winsock code, when you e.g. send data, the buffer in user mode is copied and is locked into physical memory by the kernel, once the request completes the buffer is unlocked and data copied down to user mode buffer. This operation is expensive and so RIO offers a way to lock a pre-allocated buffer into physical memory, so the application and the kernel can read/write directly to it. greatly reducing your CPU overhead and latency. To get the most out of this we would likely need a new API targeted towards such applications requiring high throughput. There are similar technologies on Linux so it wouldn't necessarily be Windows only. Anyway more detailed summary here [1] and interesting read on experimentation done on ASP.NET and Azure [2] [1] https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh997032(v=ws.11) @winterland1989 The new I/O manager supports the event based system just fine. That's one of the reasons we did it inside base as this allows us to integrate with the scheduler. Because of this integration it can also support things such as setting NUMA affinity based on what the RTS wants using e.g. |
|
Or you just associate multiple sockets with each event, then loop through them and query the individual sockets when an event is triggered. Which sounds bad but in practice works quite well. This is how some torrent clients (like uTorrent) handle their connections. This assumes a "correct" distribution of work.
Read should work fine, it's write that may be an issue. |
and to be clear, I'm only talking about ways to maintain a working reasonably performant interface with what network has, not the only interface. I'd want a different interface for high performance I/O anyway for |
@Mistuke If you want to revise the |
@Mistuke @winterland1989 Thank you for your explanations. Many things are now clear to me. |
It's time to start discussing this more, The I/O first version of the I/O manager will be in GHC 8.12 if all goes well https://gitlab.haskell.org/ghc/ghc/-/merge_requests/1224. That version will be feature complete enough to start work on network (will need some tweaks to support multiple worker threads, not sure I will get that in for 8.12 but 8.14 certainly). So a couple of things, GHC will no longer contain any so to support The new I/O manager can be detected at runtime and compile time by checking the CPP macro Concretely for Windows I'd like to accomplish the following if possible
In a short description, the new I/O manager has these requirements: As an example, the lowest level file operation -- For this to actually block, the file handle must have
-- been created with FILE_FLAG_OVERLAPPED not set. As an implementation note I
-- am choosing never to let this block. But this can be easily accomplished by
-- a getOverlappedResult call with True
hwndRead :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
hwndRead hwnd ptr offset bytes
= fmap fromIntegral $ Mgr.withException "hwndRead" $
withOverlapped "hwndRead" (toHANDLE hwnd) offset (startCB ptr) completionCB
where
startCB outBuf lpOverlapped = do
debugIO ":: hwndRead"
-- See Note [ReadFile/WriteFile].
ret <- c_ReadFile (toHANDLE hwnd) (castPtr outBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == #{const ERROR_SUCCESS} = Mgr.ioSuccess $ fromIntegral dwBytes
| err == #{const ERROR_HANDLE_EOF} = Mgr.ioSuccess 0
| err == #{const STATUS_END_OF_FILE} = Mgr.ioSuccess 0
| err == #{const ERROR_BROKEN_PIPE} = Mgr.ioSuccess 0
| err == #{const STATUS_PIPE_BROKEN} = Mgr.ioSuccess 0
| err == #{const ERROR_MORE_DATA} = Mgr.ioSuccess $ fromIntegral dwBytes
| otherwise = Mgr.ioFailed err the If the blocking isn't desired then network doesn't have to use this method at all. It's free to (and probably should) do it's own thing for efficiency reasons. The call back routine
This would greatly increase the scalability of network on Windows while dramatically lowering CPU usage.
|
I have cc'd @coot who has been using an I/O library based on IOCP on Windows at https://github.com/input-output-hk/ouroboros-network/tree/5c253f7533901d2daf528388def065a54947a122/Win32-network We should ensure we cover their uses as they provide real world use-cases. |
@Mistuke Good news! What can I do for you? |
I'd like to take you up on the offer of creating a new API that doesn't use |
Ideally also something that fits with https://github.com/simonmar/async |
@Mistuke I am glad to see the changes are moving forward. The requirements for us were
We have tests that ensure those for our implementation (plus real users, and no complains :)) . Since we use the same high level api as the |
WINIO has officially been merged in GHC 8.12. now the fun begins.. |
@kazu-yamamoto @eborden So the main thing I need some help with is how an asynchronous interface should look like. The synchronous interface we won't change of course, but what happens on scenarios where you are handling thousands of connections, in these cases while the blocking interface requires thousands of Haskell threads, the bigger issue is the thousands of locks. In such cases I think a callback interface would be more efficient. Also one thing I really want to get rid off is needing configure on Windows. @phadej perhaps you have an idea how to do that while keeping it for non-Windows? AFAIK you can't change package types on sub-packages? |
This is a stub issues for discussing issues around the new Windows IO manager.
Discussion kicked off in: #357
So far:
@Mistuke
Just FYI, I'm finishing up the final bits of a new I/O manager for Windows in GHC. Which will require completely new code in network using the native async APIs. I'll create a ticket soon to start discussing how this should all look.
@eborden
@Mistuke Oh baby, that sounds like a whopper. Will that require any API changes? That will force us to bump the lower bound of base and drop support for many GHC versions. We should probably open an issue specifically for discussing that.
@Mistuke:
@eborden Yeah, it's years of work, the GHC patch itself clocks in at around 7k lines of code atm, without comments.. lol. On the bright side, it will fix almost all of the warts/quirks on Windows.
I haven't thought about the API in network yet, my hope is that since Winsock was originally based on BSD sockets anyway the native interface is close enough to maintain the same interface for most things. At least for IOCP, Still need to look into RIO support.
Yes I'll create a new ticket to discuss it through before I start any work on it. In terms of base GHC will (at least for a period) support both I/O managers and provides hooks and helpers to switch between the two for library code (the same helpers base uses). The new I/O manager won't be the default for a while, not the least until core libraries catch up.
My intention for this is to use Win32 to abstract that functionality, such that you will have to bump the minimum version of Win32 but not base. Win32 still maintains backwards compatibility back to GHC 7.6 which is a reasonably ways back.
@hvr
@eborden
That will force us to bump the lower bound of base and drop support for many GHC versions.
...but only if os(windows), no?
@kazu-yamamoto
@Mistuke Sound exciting!
What does RIO mean?
@kazu-yamamoto
@winterland1989 @Mistuke I would like to know whether of not Mio, libuv based IO manager and the new I/O manager for Windows can coexist if we create a proper layer to GHC.
@winterland1989
I'm not sure if IOCP based IO manager can be easily combined with current event-based interface, namely threadWaitRead/threadWaitWrite. I gave a talk on this on Haskell symposium 2018 and i don't think it's an easy job, but maybe @Mistuke can tell more on this.
On the other hand libuv based IO manager is a one-stop solution since libuv already take care of system call encapsulation, it's not only an IO manager but also a substitution to all the system packages, e.g. network, directory, etc. It should be able to coexist with whatever IO solution base provides, as a third-package.
The text was updated successfully, but these errors were encountered: