Asynchronous I/O vs resource loading thread.

Started by
8 comments, last by swiftcoder 11 years, 7 months ago
I've been messing around with asynchronous I/O (Win32 overlapped file I/O) and I was wondering. What are the advantages of asynchronous I/O over having a resource loading thread that just reads from the hard drive with regular synchronous I/O techniques in a different thread?
Advertisement
Depends on the architecture of your game.

If you load everything at start you won't see much difference. The bottleneck will still be loading the resources.

Some games support streaming of assets during play. In that situation a loading thread can mean much faster general-case because you can continue running the game without those assets, and swap them in as they arrive. The resource loading thread can prioritize one asset over another, which a simple overlapped I/O call wouldn't do.
Yeah, I was thinking mostly in the context of a Skyrim type of game with open world exploration.

Yeah, I was thinking mostly in the context of a Skyrim type of game with open world exploration.

Then it'd be best to use an I/O thread.

Otherwise, you'd see hiccups when you hit certain boundaries in the game. You can see that a lot in older games from the 80s.
The resource loading thread can prioritize one asset over another, which a simple overlapped I/O call wouldn't do.
That's apples and oranges.
You can implement prioritisation in both schemes:main thread loop:
push requests to loading thread

loading thread loop:
sort requests list by priority
blocking load top priority resource
main thread loop:
add requests to list
if( !load in progress )
sort requests list by priority
async load top priority resource

What are the advantages of asynchronous I/O over having a resource loading thread that just reads from the hard drive with regular synchronous I/O techniques in a different thread?
You should word it the other way around: what are the advantages of using an extra thread + blocking I/O, vs using no extra threads an async I/O.

The answer is that you've only added extra overhead, because the blocking I/O API is just a wrapper around the asynchronous I/O API.
e.g. say the async API looked like:
token StartAsyncRead( void* buffer, int size );
bool IsAsyncReadDone(token);
Then the blocking API is basically just:void BlockingRead( void* buffer, int size )
{
token id = StartAsyncRead( buffer, size );
while( !IsAsyncReadDone(id) ) { Sleep(); }
}
So by using an extra thread, you've taken a naturally asynchronous operation, then wrapped it in a blocking abstraction, and then wrapped that abstraction in a thread to again make it asynchronous!

One reason to use a blocking I/O API, is because the C blocking file API (fopen/fread etc) are portable across different OSs, whereas if you use the OS's optimal loading API (e.g. CreateFile/ReadFileEx/etc), then you've got to re-write your file loading code when you switch to another OS.

You should word it the other way around: what are the advantages of using an extra thread + blocking I/O, vs using no extra threads an async I/O.

Rumor has it that async IO (in windows) does block when there are X amount of requests in flight - making a separate thread can be of help when such rare hiccups are undesirable. Heard it some years ago, but never tested it out myself (afaicr it was sufficiently reputable source - so i did not bother). No idea whether it was some win version specific or what.

Anyone having some insights there? Is it bonkers nowadays? Was it true at some point at all?
I'm going to go with a resource loading thread. It would be more portable across platforms anyways (I've never heard of I/O completion ports on anything but Windows though I could be wrong), and I think loading asynchronously would more than make up for the call overhead on Windows. Obviously I'd need to benchmark both ways, but I'll try a seperate thread first.

[quote name='Hodgman' timestamp='1347683391' post='4980286']
You should word it the other way around: what are the advantages of using an extra thread + blocking I/O, vs using no extra threads an async I/O.

Rumor has it that async IO (in windows) does block when there are X amount of requests in flight - making a separate thread can be of help when such rare hiccups are undesirable. Heard it some years ago, but never tested it out myself (afaicr it was sufficiently reputable source - so i did not bother). No idea whether it was some win version specific or what.

Anyone having some insights there? Is it bonkers nowadays? Was it true at some point at all?
[/quote]I found the MS KB article about this issue -- http://support.microsoft.com/kb/156932 -- it mostly applies to NT/2K, but some applies to XP as well. It seems that putting your "async" file read commands onto a non-time critical thread might be a good idea.
It looks like ReadFileEx + SleepEx is generally preferred over ReadFile as for async reads -- it doesn't contain the warning about the possibility of synchronous behaviour, but can fail if the internal queue is full.
Thanks for the digging. Appreciated.

I've never heard of I/O completion ports on anything but Windows though I could be wrong

Sun OS has I/O completion ports, linux has epoll, Mac OS/BSD have kqueue. All fulfill roughly the same purpose.

Of course, you probably don't want to write separate back-ends for all those alternatives, which is why libevent exists.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

This topic is closed to new replies.

Advertisement