Windows Vista

I/O Improvements in Windows Vista

My tips for ef­fi­cient I/O are rel­e­vant all the way back to cod­ing for Win­dows 2000. A lot of time has passed since then though, and for all the crit­i­cism it got, Win­dows Vista ac­tu­ally brought in a few new ways to make I/O even more per­for­mant than be­fore.

This will prob­a­bly be my last post on user-mode I/O until some­thing new and in­ter­est­ing comes along, com­plet­ing what started a cou­ple weeks ago with High Per­for­mance I/O on Win­dows.

Synchronous completion

In the past, non-block­ing I/O was a great way to re­duce the stress on a com­ple­tion port. An un­for­tu­nate side-ef­fect of this was an in­creased amount of syscalls -- the last non-block­ing call you make will do noth­ing, only re­turn­ing WSAE­WOULD­BLOCK. You would still need to call an asyn­chro­nous ver­sion to wait for more data.

Win­dows Vista solved this el­e­gantly with Set­File­Com­ple­tion­No­ti­fi­ca­tion­Modes. This func­tion lets you tell a file or socket that you don't want a com­ple­tion packet queued up when an op­er­a­tion com­pletes syn­chro­nously (that is, a func­tion re­turned suc­cess im­me­di­ately and not ER­ROR_IO_PEND­ING). Using this, the last I/O call will al­ways be of some use -- ei­ther it com­pletes im­me­di­ately and you can con­tinue pro­cess­ing, or it starts an asyn­chro­nous op­er­a­tion and a com­ple­tion packet will be queued up when it fin­ishes.

Like the non-block­ing I/O trick, con­tin­u­ally call­ing this can starve other op­er­a­tions in a com­ple­tion port if a socket or file feeds data faster than you can process it. Care should be taken to limit the num­ber of times you con­tinue pro­cess­ing syn­chro­nous com­ple­tions.

Reuse memory with file handles

If you want to op­ti­mize even more for through­put, you can as­so­ci­ate a range of mem­ory with an un­buffered file han­dle using Set­FileIoOver­lappe­dRange. This tells the OS that a block of mem­ory will be re-used, and should be kept locked in mem­ory until the file han­dle is closed. Of course if you won't be per­form­ing I/O with a han­dle very often, it might just waste mem­ory.

Dequeue multiple completion packets at once

A new fea­ture to fur­ther re­duce the stress on a com­ple­tion port is GetQueued­Com­ple­tion­Sta­tu­sEx, which lets you de­queue mul­ti­ple com­ple­tion pack­ets in one call.

If you read the docs for it, you'll even­tu­ally re­al­ize it only re­turns error in­for­ma­tion if the func­tion it­self fails—not if an async op­er­a­tion fails. Un­for­tu­nately this im­por­tant in­for­ma­tion is miss­ing from all the of­fi­cial docs I could find, and search­ing gives noth­ing. So how do you get error in­for­ma­tion out of GetQueued­Com­ple­tion­Sta­tu­sEx? Well, after play­ing around a bit I dis­cov­ered that you can call GetOver­lappe­dResult or WSAGe­tOver­lappe­dResult to get it, so not a prob­lem.

This func­tion should only be used if your ap­pli­ca­tion has a sin­gle thread or han­dles a high amount of con­cur­rent I/O op­er­a­tions, or you might end up de­feat­ing the mul­ti­thread­ing baked in to com­ple­tion ports by not let­ting it spread com­ple­tion no­ti­fi­ca­tions around. If you can use it, it's a nice and easy way to boost the per­for­mance of your code by low­er­ing con­tention on a com­ple­tion port.

Bandwidth reservation

One large change in Win­dows Vista was I/O sched­ul­ing and pri­or­i­ti­za­tion. If you have I/O that is de­pen­dant on steady stream­ing like audio or video, you can now use Set­File­Band­widthReser­va­tion to help en­sure it will never be in­ter­rupted by some­thing else hog­ging a disk.

Cancel specific I/O requests

A big pain pre-Vista was the in­abil­ity to can­cel in­di­vid­ual I/O op­er­a­tions. The only op­tion was to can­cel all op­er­a­tions for a han­dle, and only from the thread which ini­ti­ated them.

If it turns out some I/O op­er­a­tion is no longer re­quired, it is now pos­si­ble to can­cel in­di­vid­ual I/Os using Can­ce­lIoEx. This much needed func­tion re­places the al­most use­less Can­ce­lIo, and opens the doors to shar­ing file han­dles be­tween sep­a­rate op­er­a­tions.

My Windows Vista/7/8 Wishlist

These are some changes I’ve been try­ing to get made since Vista en­tered beta. Now 7’s beta has begun and still chances look bleak. Maybe I’ll have more luck in 8?

Enabling IPv6 and PNRP in Windows Vista

Win­dows Vista is the first ver­sion of Win­dows to sup­port IPv6 out of the box. Even those of us with an IPv4 con­nec­tion can make use of this, using a tech­nol­ogy called Teredo to get IPv6 con­nec­tiv­ity over IPv4. With Google fi­nally get­ting IPv6, now seems like a good time for oth­ers to start too.

The steps to en­able IPv6 are sim­ple:

  1. Open up a com­mand prompt with ad­min­is­tra­tor priv­i­leges. Start->All Pro­grams->Ac­ces­sories, right click on Com­mand Prompt and se­lect Run as ad­min­is­tra­tor.
  2. If you aren’t on a router, or if your router sup­ports UPNP, enter netsh interface teredo set state client.
  3. If you want to man­u­ally for­ward a port or your router doesn’t sup­port UPNP, enter netsh interface teredo set state client clientport=12345, sub­sti­tut­ing 12345 with the port you want to use. You will have to for­ward UDP over this port to your com­puter.
  4. Now wait for a minute or so and run netsh interface teredo show state. It should show “qual­i­fied” under State.
  5. Now if you run ip­con­fig, it should come up with a Tun­nel adapter Local Area Con­nec­tion with an IPv6 ad­dress start­ing with 2001:0.
  6. You can test if it’s work­ing by vis­it­ing Google IPv6, or the KAME pro­ject’s fa­mous danc­ing kame.

Now for the sec­ond part of the post. PNRP (Peer Name Res­o­lu­tion Pro­to­col) ver­sion 4.0 was also in­tro­duced in Win­dows Vista. With PNRP, every com­puter can have a host­name point­ing to it that al­lows any XP SP2, Vista, and Server 2008 com­puter to con­nect to it via the in­ter­net. This can be in­cred­i­bly use­ful if you’re on the go and wish to re­mote in to your com­puter. An­other use I’ve found for it is to en­able it on rel­a­tive’s PCs for those in­evitable tech sup­port calls that we geeks de­spise so much.

PNRP func­tions solely over IPv6, so you will need to have a valid IPv6 ad­dress to make it work. The above Teredo in­struc­tions should work fine if you don’t. Here’s how you en­able it:

  1. Open up a com­mand prompt with ad­min­is­tra­tor priv­i­leges.
  2. Run the com­mand netsh p2p pnrp peer set machinename publish=start autopublish=enable.
  3. Now if you run netsh p2p pnrp peer show machinename, it should show you a host­name to use in the for­mat p.<ran­dom hex here>.​pnrp.​net. Record this name, and you can use it to talk to your ma­chine re­motely just like any other host­name.

De­vel­op­ers aren’t left out ei­ther: Win­dows comes with an ex­ten­sive P2P frame­work, and PNRP is only one of the things built on it. WCF for in­stance has full in­te­gra­tion with P2P.