“I might be a little late for a question about your recent post, but just in case I’m not: Do you have any strategies for, or stories about, dealing with an external library that you couldn’t get rid of that violated some (or all) of these API design guidelines? It’s a vague question, but I’m really just asking about any past experience as a user of an API that really sticks out in your mind.”
This reminded me that I’d always wanted to go write down the steps necessary to use a bad API, just to highlight how terrible it can be for the programmer. I don’t think people who make APIs really appreciate how important it is to get them right, and how much unnecessary work their mistakes can cause for hundreds, thousands, sometimes even millions of other programmers. So I felt like it was important to spend an article walking through an API and showing just how much unnecessary work an API can manufacture.
It’d probably be a nice column on its own — a weekly dissection of a bad API. But since I don’t have time for something like that, if I was only going to dissect one API, the most important question was, which API should I choose?
It’s a great time in the history of computing to be writing an article about bad APIs (which is another way of saying it’s a terrible time to actually have to program for a living). There’s so many bad APIs out there, I could have picked one at random and been very likely to find enough problems to fill a 3000-word article. But if I was only going to pick apart one specific operation in one API, it seemed only right to try to pick the worst API I’d ever actually used.
Now there are a lot of APIs out there that routinely turn in top-ranking efforts for the “worst API” leaderboard. CSS, for example, can probably claim half the spots on the top 10 for any year in which there’s a new version. DirectShow, while it was still a going concern, certainly dominated the rankings for its era. And in the modern age, newcomers like the Android SDK are showing real potential with development environments so convoluted that the quality of the APIs when called from actual C++ code are the last thing you’ll worry about when trying to ship something with them.
But when I thought long and hard about who the all-time heavyweight bad API champion was, there was one clear winner: Event Tracing for Windows.
Event Tracing for Windows is an API that does something very simple: it allows any component of the system (including end-user software) to announce “events” which any other component can then “consume”. It is a logging system, and it is used to record performance and debugging information by everything from the kernel upwards.
Now, normally, a game developer would have no reason to use the Event Tracing for Windows API directly. You can use tools like PerfMon to view logged information about your game, like how much working set it was using or how much disk I/O it did. But there is one specific thing that directly accessing Event Tracing gives you that you can’t get anywhere else: context switch timing.
Yes, if you have any relatively recent version of Windows (like 7 or 8), the kernel will log all thread context switches, and using the CPU timestamp included in those events, you can actually correlate them with your own in-game profiling. This is incredibly useful information to have, and is the kind of thing you often only get from console hardware. It’s the reason tools like
RAD’s
Telemetry can show you when your running threads were interrupted and had to wait for system threads to do work, something that can often be critical to debugging weird performance problems.
So far, the API is sounding pretty good. I mean, context switch timing is very vaulable information, so even if the API was a little janky, it’d still be pretty great, right?
Right?
Before we take a look at the actual Event Tracing for Windows API, I want to walk the walk here and do exactly what I said to do in last week’s lecture: write the usage code first. Whenever you evaluate an API, or create a new one, you must always, always, ALWAYS start by writing some code as if you were a user trying to do the thing that the API is supposed to do. This is the only way to get a nice, clean perspective on how the API would work if it had no constraints on it whatsoever. If it was “magical”, as it were. And then, once you have that, you can move forward and start thinking about the practical problems, and what the best way is for you to get to something implementable.
So, if I were a programmer, with no knowledge of the Event Tracing for Windows API, how would I want to get a list of context switches? Well, two methods come to mind.
The most straightforward approach would be something like this: