new i/o library

Duncan Coutts duncan.coutts at worc.ox.ac.uk
Sat Jan 28 07:08:04 EST 2006


On Fri, 2006-01-27 at 18:01 +0300, Bulat Ziganshin wrote:
> Hello Duncan,
> 
> Friday, January 27, 2006, 4:00:28 PM, you wrote:
> 
> >> moreover, i have an idea how to implement async i/o without complex
> >> burecreacy: use mmapped files, may be together with miltiple buffers.
> >> for example, we can allocate four 16kb buffers. when one buffer is
> >> filled with written data, the program unmaps it and switches to use
> >> the next buffer. i don't tested it, but OS can guess that unmapped
> >> buffer now should be asynchronously written to disk. the same for
> >> reading - when we completely read one buffer, we can unmap it, switch
> >> to the second buffer and map the third so that the OS can
> >> asynchronously fill the third buffer while we are reading second.
> >> should this work, at least on the main desktop OSes?
> 
> DC> On Linux an probably other unix-like OSes I don't think this would be
> DC> any different from using read/write.
> 
> DC> On Linux, read and mmap use the same underlying mechanism - the page
> DC> cache. The only difference is that with mmap you get zero-copy access to
> DC> the page cache. However frequent mapping and unmapping may eliminate
> DC> that advantage. Either way there is no difference in how asynchronous
> DC> the operations are.
> 
> yes, i want to save exactly this bit of performance - after i
> optimized all other expenses on the path of text i/o

There is a trade off, using mmap gives you zero-copy access to the page
cache however there is a not-insignificant performance overhead in
setting up and tearing down memory mappings. This is true on unix and
win32. So for small writes (eg 4k blocks) it is likely to be cheaper to
just use read()/write() on page aligned buffers rather than use mmap.

You would need to do benchmarks on each platform to see which method is
quicker. Given the code complexity that other people have mentioned I do
not think it would be worth it.

Using page aligned and sized buffers can help read()/write() performance
on some OSes like some of the BSDs.

> in other words, i interested in having zero-wait operation both for
> reading and writing,

As I said that is not possible with either read() or mmaped read.
Conversely it works automatically with write() and mmaped writes.

Zero-copy and zero-wait are not the same thing.

>  i.e. that in sequence of getChar or putChar
> actions there were no waits on any action - of course, if the disk is
> fast enough. in other words, speed of such i/o programs should be the
> same as if we just write these data to memory
> 
> current GHC's Handle implementation uses rather complex machinery for
> async reading and writing, and i can even say that most part of
> Handle's implementation complexity is due to this async machinery. so
> i wanna know what exactly accomplished by this implementation and can
> we implement async operation much easier by using mmap?

> the word "async" is overloaded here - i'm most interested in having
> zero-overhead in single-threaded operation, while GHC's optimization,
> afair, is more about overlapping I/O in one thread with computations
> in another. so i'm searching for fastest and easiest-to-implement
> scheme. what you propose?

An important factor for optimising IO performance is using sufficiently
large block sizes to avoid making frequent kernel calls. That includes
read()/write() calls and mmap()/unmap() calls.

Perhaps it is possible to move the complexity needed for the lazy
hPutStr case into the hPutStr implementation rather than the Handle
implementation. For example perhaps it'd be possible for the Handle to
just have one buffer but to have a method for writing out an external
buffer that is passed to it. Then hPutStr would allocate it's own
buffer, evaluate the string, copying it into the buffer. Then it would
call on the Handle to write out the buffer. The Handle would flush its
existing internal buffer and write out the extra buffer.

Perhaps a better solution for your single-threaded operation case is to
have a handle type that is a bit specialised and does not have to deal
with the general case. If we're going to get a I/O system that supports
various layers and implementations then perhaps you could have an one
that implements only the minimal possible I/O class. That could not use
any thread locks (ie it'd not work predictably for multiple Haskell
threads) and use mmap on the entire file. So you wouldn't get the normal
feature that a file extends at the end as it's written to, it'd need a
method for determining the size at the beginning or extending it in
large chunks. On the other hand it would not need to manage any buffers
since reads and writes would just be reads to/from memory.

So it'd depend on what the API of the low level layers of the new I/O
system are like as to whether such a simple and limited implementation
would be possible.

Duncan



More information about the Glasgow-haskell-users mailing list