Yet another infrequently-updated blog, this one about the daily excitement of working in the software industry.
Wednesday, January 09, 2008
XO Laptop, part 2
Friday, January 04, 2008
XO laptop mini-review
My first hours with the XO laptop
Back when the One Laptop Per Child Foundation was running their "give one, get one" promotion, I signed up to donate a laptop, and get one for myself. I figured I could do some good, and get a chance to see what it's all about.
Initial impressions:
Hardware:
It's small - really small. Seems more deserving of the "notebook" designation, rather than calling it a "laptop". It'd probably fit well on a child's lap, though.
The exterior case feels very solidly constructed, and has a built-in handle. It reminds of my original iBook. It does look a little like a toy, but that impression goes away pretty rapidly once you start using it.
The keyboard is a rubber dome "chiclet" keyboard of the sort you might have found on inexpensive home computers in the 1980's in the USA. It's not too hard to type on, with the exception of the "space bar", which seems to be made up of 10 or so individual switches, and my thumb keeps hitting it between the individual switches.
The screen is very clear and readable. I haven't used it in black & white mode very much yet, but it's readable in a (fairly bright) room with the backlight off. Not bad at all.
Software:
The user interface is a little weird, if you're used to a standard PC operating system interface. I think someone who's coming to it with no preconceptions would find it fairly easy to get started.
It's running Ubuntu Linux, but you'd never know it from looking at the UI. Everything uses just one mouse button - none of the right-click, middle-click crap from KDE.
The "window manager" doesn't so much manage windows as screens - most of the applications run in full-screen mode, to make better use of the low resolution screen.
The web browser is plain but functional. I'm using it to type this entry, as a matter of fact. So far the only problem is that the cursor disappears in some text boxes. That makes it a little harder to edit text than it should be. Might be a Blogger Javascript problem, I'll see if it come up elsewhere.
I'll report back with an update after I've tried out the other included applications.
Thursday, December 06, 2007
Programmer's purity test
It occurred to me that that'd make for an interesting variation on the classic "purity test". A number of "hacker" and "geek" purity tests are out there, but I haven't seen one specifically for programming. There are way too many extremely obscure languages on that Wikipedia list, though.
If we trimmed out the truly obscure languages, we'd get something like this:
Have you ever:
- Programmed a computer?
- In ADA?
- In ALGOL?
- In APL?
- In APPLESCRIPT?
- In Assembly?
- In AWK?
- In B, or BCPL?
- In BASIC?
- In brainf*ck?
- In Bourne Shell?
Nah - too boring, and we haven't even gotten out of the B's yet. Maybe we could organize it by generation:
Have you ever:
- Programmed a computer?
- With jumper wires?
- In machine code?
- ...without a coding sheet or other aid?
- ...with toggle switches?
- ...from a Hex keypad?
- In assembly language?
- On punched cards?
- In a language whose syntax assumes that you're still using punched cards (eg Fortran, RPG)?
- In COBOL?
- In C or Pascal?
- In Forth?
- In Lisp (Scheme, Logo)?
- In Smalltalk?
- In a 4GL?
- In C++?
- In Java or C#?
- With a scripting language?
- In a modern functional language (Haskell, etc)?
- In an object-oriented language without class-based inheritance?
That's a pretty good start, maybe we could add a few questions on how you used these various tools.
Have you ever...
- Written a program that directly controlled objects in the physical world?
- ...did you ever injure anyone with a bug?
- ...other than yourself?
- Written software for internal business use?
- Written software that was sold at retail?
- Written software that sends email?
- ...did it ever send thousands of messages due to a bug?
- ...outside the organization you were working at?
- Programmed in a language of your own design?
- ...did anyone else ever use your language?
- ...did it become a de-facto standard?
- ...or an ISO or ECMA standard?
- Written a compiler?
- ...not as an assignment for a class?
- ..."by hand" (without using lex/yacc or related tools)?
- Created self-modifying code?
- Written code that modifies some other program's binary?
- Written self-reproducing code?
- ...without it getting away from you?
- Changed the class of an object at runtime?
- ...in a language without dynamic dispatch?
- Created a program that took longer to run (once) than it did to write?
- ...while running on a cluster of computers?
- ...or a conventional supercomputer?
I seem to have run out of ideas. Suggestions for additional questions would be greatly appreciated. A traditional Purity Test would have 100 questions, so you could easily generate a percentage score.
For what it's worth, I scored 32/44, or about 27% pure. I think that probably indicates that the test is a little too focused on my own experiences. Send me your questions, and I'll work up a better list...
Saturday, November 24, 2007
Fear of teaching
Since I knew other people would want to read about it later, especially given that I gave the presentation on the Wednesday before Thanksgiving, I put my notes up on our Wiki. I have a bit of a love-hate relationship with the Wiki. While I love the idea of a single place to look for information, the usability of Wiki markup languages really stinks. It's a bit like Blogger's "plain text" format, in that if you don't care about what things come out looking like, it's alright, but I always spend more effort trying to work around the limitations of the format than I do actually writing the content.
I've learned over the years that there are about three major methods of preparing for giving a presentation. Some people actually go to the effort of writing out everything that they want to say, similar to a speech as given by politician. Some people write nothing down, and ad-lib the whole thing, and then there's the approach I've always used. I usually write an outline that contains all the topics I want to cover, and then I ad-lib the presentation around the outline, making whatever mid-course corrections might seem necessary based on the audience's reactions.
Because I wanted people to be able to get something out of my notes without having to be at the presentation, I filled in the outline with some additional explanatory text. That's very similar to the process I usually use when I write other things (for example, blog posts). I start with an outline, then I replace items in the outline with paragraphs and sentences. Even when I do something more free-form like this post, I have the outline in my head, at least. Revising an outline on the iPhone would have pretty painful...
When I finished the presentation, I got some very positive feedback from the audience, including at least one actual pat on the back, something I had thought of as a metaphor before. Afterwards, I thought a little bit about what I thought was good about that presentation, especially compared to other presentations I've seen lately, and I think it's all about not over-planning or under-planning it. The worst presentations I've been subjected to are of the "guy reading directly from his Powerpoint slides" style. Second worst are the "guy who is totally not prepared or sure what he wants to say" variety. Once again, the happy medium wins out.
Sunday, October 28, 2007
The C++ FQA (frequently questioned answers)
Via a discussion on Joel On Software, I got directed to this:
The C++ FQA
It's a response, of sorts, to the C++ FAQ. You can read moreabout it on the site, but he basically goes through the questions in the C++ FAQ, and explores what it is about C++ that makes those questions "frequently asked". There is some sarcasm, and some rather insightful commentary on why C++ is so very hard to develop real expertise in.
It would be neat to see something like this done for Java and C#. I think the idea of looking at a language from the standpoint of "why are these areas confusing to so many users?" is an interesting approach.
I have always felt that my resistance to really learning C++ was a failure on my part, but after reading "Effective C++", and with the backing of the C++ FQA, I feel a little better about taking the position that C++ is really far too complex for the good of the people who need to work with it.
Wednesday, October 24, 2007
You're probably using "unsigned" incorrectly
You're probably using "unsigned" incorrectly, and that makes me sad.
Chances are that if you write code in C (or related languages like Java, C#, or C++), then you've come across the "unsigned" type, and its relatives "unsigned long" and "unsigned short". If you've written code that uses unsigned types, it's also quite likely that you've used them incorrectly, at least by my standards.Misuse of "unsigned" in C is one of those things that I keep seeing over and over, with different developers, even folks who really ought to know better. I find it immensely frustrating. If I had to pick one aspect of C that was responsible for more stupid bugs than anything else, this'd be one of the top candidates. Probably not the top candidate - the string-handling functions in the standard library probably win that handily.
Here are my simple rules for the use of unsigned integer types:
- Don't use unsigned just because "that value should never be less than zero"
- Always compile your code with all warnings enabled
- Avoid mixing the use of signed and unsigned integers in the same calculation
- Do use unsigned when modelling hardware registers that hold unsigned values
- Do used unsigned when performing bit-wise arithmetic
Don't use unsigned just because "that value should never be less than zero"
This is by far the most common abuse of unsigned types that I see on a regular basis. It's not even a bad idea, as far as it goes. A majority of the values in a typical program are going to be non-negative by design - sizes, screen coordinates, loop counters, etc, etc. The problem really isn't unsigned values per se, it's how unsigned and signed values interact.Part of the problem is that constant values in C are signed by default, which means that signed values will creep into your program unless you make a concerted attempt to avoid them. When you compare signed and unsigned values, the results will often not be what you expect. For example:
unsigned four = 4;Looking at this code, it's pretty obvious what the programmer intended, but in fact the comparison "neg_one < four" evaluates to false in this case. This is because the signed value will be "promoted" to unsigned, turning it from a small negative number to a very large positive number, before the comparison is made.
int neg_one = -1;
if (neg_one < four)
{
printf("true\n");
}
else
{
printf("false\n");
}
In actual cases of this problem in the wild, the declarations will typically be a long way away from the comparison, and it won't be at all obvious what the cause of the problem actually is. I've seen experienced programmers stare at the debugger in disbelief when it seems to be showing them that their program thinks that -1 is greater than 4. An additional complication is that constants in C are signed by default, so you can replace the "neg_one" variable in the example with the constant "-1", and you'll get the same behavior.
A related problem comes with the handling of sizes and lengths. A size is typically going to bea non-zero value, so it "makes sense" to use unsigned variables. The problem is that sizes are often calculated by subtracting one value from another. If you accidentally subtract a larger value from a smaller one with signed variables, you get a negative size, which you can at least detect and handle (with an assert(), if nothing else). If you're using unsigned math, you just get a huge bogus "size", which may or may not be immediately obvious.
Always compile your code with all warnings enabled
Admittedly, this rule is more general, rather than specifically tied to problems with using "unsigned" correctly. Most C and C++ compilers have an option to warn on comparisons between signed and unsigned values, when there's a chance the comparison will be interpreted incorrectly. It's even more frustrating to debug one of these issues when compiling with warnings enabled would have produced a warning message that points to exactly where the problem is, but some yutz has that particular warning disabled.Of course, they have it disabled because enabling warnings on comparisons between signed and unsigned tends to generate zillions of bogus warnings. That's just a good reason to avoid using unsigned variable, where possible - it obscures the actual problem areas with bogus warnings.
Avoid mixing the use of signed and unsigned integers in the same calculation
Given the example above of a simple comparison going wrong, it ought to be obvious that anything more complex is at least as likely to go subtly wrong in some way. Again, the real problem arises because the declarations of the variables (and constants) will be far far away from the point of the errant calculation.So, when is it okay to used unsigned types?
Do use unsigned when modelling hardware registers that hold unsigned values
This is most likely how unsigned types got into C in the first place. If you're writing low-level OS or driver code that talks to the hardware, you'll often find that the unsigned int type exactly matches what the hardware is using. This also segues nicely into the next rule...Do used unsigned when performing bit-wise arithmetic
If you're doing something with fancy binary arithmetic, like an encryption algorithm, or something else where you're using an integer as a sollection of bits, unsigned types are probably what you want. I'd put something in here about using "unsigned" with bitfields, but the bitfield construct in C is pretty useless (and a topic for another rant), so I'll just mention that it's worth thinking about whether you want an unsigned or signed bitfield, if you ever use them.Unfortunately, you actually can't avoid "unsigned" values
As it turns out, there are types that the standard library uses that are usually unsigned, for example size_t. So, your inteactions with the standard library will occsionally force unsigned values to creep into your program. Still, that's no reason for you to make it any harder on yourself.Monday, May 28, 2007
New Blender!

Last week, we got a new blender - a Blendtec Total Blender. Since Yvette started her weight loss program, we've gone through 4 blenders or so. Making multiple shakes every morning wears out your typical bargain basement blender in 6 months or less. We're hoping that the new blender lasts us a whole lot longer. It had better last a long time, it was nearly 10 times as expensive as the last blender we bought.
The Total Blender is based on Blendtec's commercial blenders (my local Starbucks uses Blendtec blenders). It's got a 1500 watt motor, as compared to the 300-600 watt motors in a typical home blender. It's got a very solid square blender jar, and a scary-looking set of ultra-sharp blades. Instead of a set of speed buttons that are labeled with wacky labels like "frappe" and "fold", it's got buttons naming what food you're making - "milkshake", "soup", "smoothie". Each button initiates an automatic program which speeds up or slows down as necessary to perfectly mix whatever you're making. It even stops automatically at the end of the program. Nifty!
So how well does it work? It's incredibly powerful - it crushes ice without even slowing down. It's also very loud at full speed. The instructions that came with the blender were minimal, but it did come with a pretty big cookbook. Apparently, the motor is strong enough to actually grind flour, make peanut butter from whole nuts, etc. On the "soup" setting, the friction of the blending actually makes the soup hot! For simple stuff, like the aforementioned diet shakes, it does the job about twice as fast as our old blender. The jar is super easy to clean, and doesn't seem like it'll get food stuck under the blades like our old blender tended to.
There is one downside that I've identified so far. Have you ever had a blender accident, where an overloaded blender popped the top off and splashed stuff everywhere? Try to imagine what it looks like when that happens with a blender that's 4 times as powerful. This is particularly problematic with hot liquids - the fast start of the blender throws the hot liquid up in the jar, which causes the air in the jar to expand and jet out the top. I think of it as Mount Blendtec erupting. Cleaning shake mix off the ceiling isn't very much fun.
I think I've got the technique dialed in now. I do wish they'd implement a slightly softer start for the low-speed settings, though. All in all, it's definitely a massive improvement over our old, tired blender. I'm going to try some of the recipes from the cookbook. That should help familiarize me with the various cycles. Besides, it's just plain fun to use - it'll blend darn near anything. Speaking of which, if you haven't seen it, check out Blendtec's Will It Blend? for amusing demos of the blender in action.
Wednesday, May 02, 2007
They're running a contest over at Worse Than Failure
For those not familiar with the site, their theme is Curious Perversions in Information Technology. People submit particularly awful pieces of code, or database schemas, or business practices, and the readers of the site make various insightful, witty, outraged, or just plain misguided, comments on them.
The contest: Implement a four-function calculator program (for Windows, or UNIX/GTK). Your program must pass the specified test cases, have a GUI driveable using a mouse, and should cause people who read the code to shake their heads sadly in disgust.
There's much more detail on the contest website, but I thought the idea of the contest was interesting enough to mention. The contest discussion forum is hilarious, as well. Check out this comment:
Mine's too slow right now. It's taking about 45 minutes to add 9876 and 1234, when I was hoping for about three seconds. I knew it was O(n^2), but I was expecting the constant to be a bit smaller. I may need to replace the Mersenne twister with a faster random-number generator.
I have what I think is a pretty good set of ideas for a submission, I'm just not sure that I'll be able to finish in the time remaining (12 days left). I'll probably submit whatever I have by then, even though I almost certainly won't win. I'm looking forward to seeing the other entries, though.
Tuesday, April 10, 2007
Slashdot really irritates me sometimes...
This would allow manufacturers to fix design defects in already-manufactured chips, rather than fixing the defects in subsequent revisions of the chips, and leaving earlier customers with the buggy chips they bought (which is what they do now).
Unfortunately, the article that's linked in the Slashdot submission is a little light on details, and the summary is just plain misleading, so the Slashdot comments are completely swamped in responses like: "this is an old idea!", "what's so novel about an FPGA?", "FPGA's are expensive, slow, and inefficient!", and other nonsense.
I actually read the article and tried to understand what it was about. When I posted that it sounded like a good idea, and it'd be interesting to read more about the design, an unregistered Slashdot user provided me with a link to the original paper, which made it much more clear what the whole thing was about.
But nobody else can see that link, because Slashdot's moderation system assumes that unregistered users are less trustworthy, so the comment with the link to the original paper is invisible to the idiots that keep ranting back and forth to each other about what a dumb idea this is, without having any idea what they're talking about.
So, go here, and read the original paper, especially if you've ever had the experience of running into one of these errata before. Hopefully, the folks at AMD, Intel, and IBM will see this, and it'll make it's way into newer designs.
Monday, March 12, 2007
Daylight Saving Time is here...
Daylight Saving Time is here again in the good 'ole US of A. This year, the dates for the switch to and from summer time have changed. Naturally, despite the fact that this change has been known about for nearly two years, there was a last-minute scramble by various companies to get "patches" for their software out in time for the new Daylight Saving switchover.
And some of them didn't, in fact, get ready on time. Despite the fact that I (and the rest of my company) installed Microsoft's patches for Windows, this morning all of our meetings in our Outlook calendars are shifted by one hour. And we're not the only ones - I see in the news that this is causing problems all over. If someone's late for an appointment with you today, cut them some slack - it's probably Microsoft's fault.
Tuesday, February 27, 2007
What was that?
The project I was working on was the motion control system for a robot. Now, I should probably clarify that, so you don't get the wrong idea - we're not talking about C3PO or R2D2, here. This was an industrial robot, used for 3-d imaging. The robot itself was made out of slabs of cast iron bolted together, was probably 8 feet tall, 4 feet wide, and ten feet long, and weighed in the neighborhood of 4 tons.
The problem I was having was that the motion control was somewhat unresponsive - you'd move the joystick, and the translation table or the optical head would slowly start to move, and when you got to where you wanted to go, it'd keep on moving for a little while before coming to a stop.
As I was looking at the code, I found what I thought was the problem - I had simply put the wrong coefficient in for one of the control equations, so we weren't getting the proper exponential factor applied to the requested motion. A quick edit and recompile, and I was ready to test the new code.
I started the system up, and very slightly moved the joystick. The translation table started to creep forward. I then pushed the stick over a little farther, and the table accellerated. So far so good.
And then, something very bad happened. When I put the stick back to the rest position, the table didn't slow down. In fact, it kept speeding up. I tried pulling back on the stick, but that didn't seem to have any effect. I managed to turn off the power to the motors just before the table hit the hard stops at the end of its travel.
So this 600 pound cast-iron table slams into the rubber bumpers at the back of the machine, going something like 30 feet per second. The whole machine rings like a gong, and all work in the entire shop grinds to a halt as everybody looks over to see this multi-ton machine gently rocking back and forth. I was really worried that I'd wrecked at least part of a very expensive machine, but a later calibration run showed that the mechanical parts of the robot were just fine.
It turns out that there were two problems in the system - the incorrect exponential on the input side that I'd corrected, and an additional incorrect damping factor on the output side. The upshot of all this is that once the system was up to speed, it took a very long time to slow down, but the bug on the input side ensured that it never got up to more than a tiny fraction of the maximum speed.
The next time I needed to make a change in those calculations, I did a "dry run" with the motors disconnected first...
Monday, February 12, 2007
Just barely better than no backup at all...
That kind of sucks, but at least I have relatively recent backups to restore from. Or, maybe I actually don't. I've been using the .Mac Backup program to do backups for the last year or so - prior to that, I was just bulk-copying stuff by hand to an external hard drive. The Backup program is a lot more convenient, and makes much better use of the space on the external drive.
I figured that surely, now that Backup is at version 3.1, it'll be rock-solid reliable, right? I mean, once they fixed that awful crashing bug I reported back in the 1.0 days, I hadn't noticed any problems, so everything is OK, right? Well, as it turns out, Backup 3.1 is no more reliable than the old Backup - it just has different bugs. Now, instead of crashing on backing up large numbers of files, it crashes when trying to restore them. If I was given a choice between these two behaviors, which do you think I would have chosen?
The one saving grace is that inside the broken Backup file package is a more-or-less standard Mac OS X disk image file. So, I can mount those files (one from the last full backup, and one from each of the incrementals), and hand-copy the files over from them. Let's hear it for unreliable backup software...
Tuesday, January 16, 2007
Language matters
Why are there are so many different computer languages?
My co-workers sometimes ask me:
Why are there so many reference books for obscure languages on your bookshelf?
The answer in both cases turns out to be the same: Language matters. The language you use affects how you think about a problem. Psychologists and Linguists (and Politicians and Salesmen) have known about this for years as it applies to human languages, and it turns out to be true for computer languages as well, of course.
I was reminded of this fact just today, as I was explaning some concepts in Object Oriented Programming to a co-worker who's just coming up to speed on C#, after using mostly C and Perl for several years.
My own first exposure to Object Oriented Programming was back in the (very) early 90's, with Borland and Microsoft's C++ compilers. I'm not sure I ever really "got" OOP in C++, and neither did most (all?) of the people I worked with. We mostly just used C++ as a better version of C.
Fortunately for me, I managed to snag a copy of Digitalk's Smalltalk for Windows not long after that. And suddenly, it all made sense!. Because the Object-Oriented nature of Smalltalk is so "in your face", you can't help but start thinking about things in a new way. The version I got came with pretty decent tutorial info as well, which was also a big help.
I never actually produced any large-scale software in Smalltalk (it was impractical for work projects in terms of performance), but the new way of looking at things stuck with me as I continued to write low-level bit-banging code in C. When I came out to California and worked for NeXT, my Smalltalk experience translated, more or less directly, to Objective-C.
Anyway, back to the discussion with my co-worker. There are a couple of different ways of thinking about "using objects" that I'm familiar with, and I tend to think of them as "The C++ way" and "The Smalltalk way". In some sense, this isn't entirely a fair characterization, but in my experience, the two views are more-or-less endemic in their respective programmer communities, so that's how I think of them.
The C++ view:
An object is a combination of some data, and functions that operate on that data. To perform some action, you call a member function of the object instance that has the data you need to work on.
The Smalltalk view:
An object is a model of some part of your problem domain, and each object has its own set of responsibilities and capabilities. To cause something to happen in your program, you send a message to an object, which may in turn send messages to other objects.
Now, it turns out that these two definitions are actually equivalent, or at least compatible, despite the fact that the C++ definition is entirely focused on the implementation detail, and the Smalltalk definition is entirely focused on the semantics.
When you get right down to the low-level implementation details, "sending a messsage" and "calling a member function" are really the same thing. A couple of CPU registers get loaded with a couple of addresses, and then you jump to a subroutine. Yes, I know it's more complicated than that in the real world, where you've got vTables, and dynamically-compiled methods, etc, etc. Work with me here...
The C++ programmers that I've met usually come to C++ from a background writing software in C. Because C is such a very low-level language, it encourages (or maybe I should say requires) that you understand the low-level details of how stuff works under the hood - memory management, how structures are laid out in memory, that sort of thing. When these folks start using C++, they apply the same low-level filter to things, and they see a class as just a data structure with some functions attached to it. This is in fact technically true, but kind of misses the point.
As I was trying to explain to my co-worker what I didn't like about some code of his that I was reviewing, I ran into a bit of a wall. I knew that something wasn't quite right, but I wasn't able to articulate the problems well enough. I think I finally figured out that it was at least partly a result of the difference in perspective due to our different backgrounds. Once I figured that out, I was able to take the discussion out of the tactical questions like "should I use a Regular Expression here?", and into the more theoretical territory of "is this an appropriate way to model the problem you're trying to solve?", and I think we made better progress after that.
It'll be interesting to see whether my co-worker has the same kind of epiphany that I did, or if he'll pick stuff up more gradually. Given that we're mostly using C# and C++ at ZING, I suspect it'll be the latter. You can write code in C# that looks just like C, with the minimal amount of "classiness" wrapped around it to compile. I suspect it would be easier for most folks to learn a new concept in a language where their old habits are obviously not going to do the trick.
Which reminds me of something else I wanted to write...
Monday, January 15, 2007
So, how was CES, anyway?
ZING did really well at CES this year. In addition to the usual behind the scenes deal-making and partner development, we had a couple of high-profile announcements, and one of the products we're helping to bring to market won an award!
SanDisk's Sansa Connect got a "best of show" award from CNET.
What does that have to do with ZING, you ask? We designed the hardware, software, and service infrastructure that makes the Sansa Connect work.
ZING also announced a deal with FON that will allow ZING-enabled players to connect to the FON network. This is great because FON is the fastest-growing WiFi hotspot network out there. They've got an interesting business model, too - share your WiFi bandwidth at home, and get free access to WiFi when you're not at home. Check them out at http://www.fon.com.
For a while there, an at-CES interview with our CEO Tim Bucher was linked from the front page of CBS.com. It's a little harder to find today, but here's a link.
Friday, January 12, 2007
So that's what it feels like...
The iPhone is very expensive by my standards, but that's always true for Apple hardware. When I worked there, I never really felt like I belonged, in some sense. I never would have bought an iPod when they came out, but the market certainly ate them up. I'll most likely take a pass on the iPhone as well, not least because it's not a clamshell phone. I'm not really a fan of phones that are designed such that I can't sit down with them in my pocket.
The more surprising thing (and frankly worrying, as a competitor) is Jobs' claim that the iPhone is running Mac OS X. Now, we all know that that's not *quite* true - presumably it's been stripped down a bit for the environment. But the key thing is that it's actually got a rational software architecture. One of the things that was a constant drain on the iPod team when I was there was that there wasn't any OS as such on the iPods, and they weren't really built on any kind of common platform.
This made adding new features to the product line, or even fixing minor bugs, a major hassle - between making the changes in 3 or 4 gradually diverging code bases, and retesting absolutely everything anytime we changed anything (no memory protection or preemptive multitasking on those old iPods), we had a hell of a time just getting anything done.
When Apple needs to add a new feature to the iPhone, they'll just have any one of Apple's developers crank out a widget in DashCode, and they can then just make it available for download. If you're any kind of Consumer Electronics company, you really ought to see this as a shot across the bow. Maybe you're not in the mobile phone business, and you don't see how a $500 cell phone is relevant to you.
First, don't expect that the price is going to stay that high for long. Second, think about what's the actual difference between an iPhone and a new wide-screen iPod, or an Apple portable game machine, or whatever? That's right - a couple of minor board changes, and some new application software, and it's whatever Apple wants it to be. They've got an actual PLATFORM for high-end (currently) Consumer Electronics, and the game as we've known it is changing.
Wednesday, November 08, 2006
Job postings...
I spent some time trying to come up with something short and sweet that at least answers the basic set of questions somebody might have about us, and about the jobs I have available. This was in the context of emailing a possible candidate that one of my co-workers referred to me.
Here's what I came up with in about 5 minutes between lunch and a meeting -
What we do:The Platform QA team is responsible for:
- Writing automated tests
- Developing testing frameworks
- Executing automated and semi-automated tests, and reporting the results
- Participating in design and code reviews with the development team
- Creating utilities to improve the Development and QA processes
- Evaluating and implementing tools (Static Analysis, Code Coverage, etc) to enhance the testing process
- Experience in software testing and software development, either as a white-box tester or software developer
- Familiarity with one or more of: C++, C#, or Java
- Someone who likes to debug complicated problems
- Some experience with API testing is useful, but not required
- We’re a Consumer Electronics technology company – we license our hardware and software designs to companies that sell them under their own brand
- We’re a pre-IPO startup
- Located in Mountain View, California
- The first product based on our technology just recently went on the market – the Sirius Stiletto 100 portable satellite radio
- Website: www.zing.net (a little short on details, but it’s getting better)
- You’ll get to work with a wide variety of cutting-edge technologies
- We’ve got a great working relationship between QA and Development
- You’ll actually get to write code, report bugs, and see them get fixed quickly
What do you think? Is that a reasonable job description, and what sorts of things strike you as missing? Should I include more information about what the actual job duties are? Or more information about the company?
Any suggestions gratefully accepted. And, should you happen to know someone who's an ace White Box tester looking for a job, send them my way, OK?
Wednesday, September 27, 2006
A couple of quick links...
Second, at the Intel Developer Forum, Intel showed off a prototype of an 80-core processor, which they expect to have commercially available in 5 years or less. It's an amusing bit of synchronicity that they announced this a day after my blog post discussing the inevitable adoption of massively-parallel processor designs for the desktop market.
Tuesday, September 26, 2006
Another thread on . . . threads
Thanks for reading...
First, I want to thank everybody who read Part I, especially those of you who made comments on it. I'm going to address a couple of those comments and questions first, then proceed to my philosophy of How not to shoot yourself in the foot when writing multi-threaded code in C-like languages.In a completely non-technical aside, one of my previous articles somehow got listed on both digg and reddit, and now random people on the Internet are making cogent, well-reasoned responses to it, and to my previous posts. I feel like a "real blogger" now. Thanks, and I'll try not to let it go to my head. It's a bit ironic, in that the original purpose of this blog was to help me get over my fear of writing, and now that I know that I have an audience, it's even harder...
Okay, back to threads...
Graham Lee pointed out that Mach threads can in fact be configured to conform to something like the no-state shared model. All you have to do is create a new task, use vm_inherit() to disallow any sharing of memory regions with the old task, and Bob's your Uncle. That's a good point, and something that I might have glossed over. In many cases, you can get a separation of state between threads by doing a little additional work outside the pthreads-style interface.
Reimer Mellin mentioned that the CSP model had been around for quite some time before Occam was invented. That's true - the initial paper describing CSP was apparently published in 1978, whereas Occam didn't hit the scene until 1983 or so, when the Transputer first started to become available. Apparently, Tony Hoare (the inventor of CSP) wrote a book on a more formalized version of CSP in 1985. It's available online, but if you're not a mathematician, it might be rough going. Personally, I find that the more funky symbols used in a piece of writing, the harder it is to read. Hoare's book uses lots of symbols - there's even a six page long "glossary of symbols".
Some Dos and Don'ts
These are in no particular order, and simply represent some different ways of slicing the multi-programming pie. One or more of them may apply to your next project...Do consider whether you need to use threads at all
Sometimes what you actually want is a separate process, in the heavy-weight, OS-level process sense. If you think about it, one program doing two things at once isn't fundamentally all that different from two programs doing one thing each. Yeah, I know, all that overhead, spawning a whole new process, setting up IPC with named pipes or whatever... But have you ever actually measured the overhead of creating a process, or transferring a few megabytes of data between two processes on the same machine?I've done a couple of simple, two-process (GUI and background server) applications on both Mac OS and Windows, and you might well be surprised by how well this design works in practice. Of course, if your 'background' process just ends up spinning its wheels inside some hideously-complex calculation, or you actually need to send a lot of data between the GUI and the calculation engine, then you haven't actually solved your problem, and you'll have to do something more sophisticated.
Don't use threads to avoid blocking on I/O
Unless you're programming on some seriously old, backwater OS, you should have other options for your file and network I/O that don't involve waiting for the I/O to complete. This is very dependent on what platform you're using. Try hitting your favorite search engine with the terms "async I/O" or "nonblocking I/O" to read about the various options available. The complexity of these async I/O approaches can seem a little daunting, until you realize that in the simple-seeming "create a thread for background I/O" model, the complexity is all still there, it's just not as easy to see.Do know what each thread in your program is for
You need to have an identified set of responsibilities for each thread in your system. Without a clear idea of what each thread is responsible for, you'll never be able to figure out what your data-sharing strategy needs to be. If you use UML or CRC cards to model your system, or even if your "design" is a bunch of clouds and arrows on a whiteboard, you need to be able to determine which parts of the system can run concurrently, and what information they need to share. Otherwise, you're doomed.Don't reinvent the wheel
It's harder than you might think to write code that's truly thread-safe. You'd be well advised to see what's been done already for your language & environment of choice. If someone has already gone to the effort of creating thread-safe data structures for you to use, then use them, don't create your own.For example, if you're already running your "main" GUI thread in an event-processing loop, consider using that message queue as your communication channel between threads. The .NET 2.0 framework provides a class called BackgroundWorker specifically to address the "trivial background calculation in a GUI app". The design of BackgroundWorker is worth reading about (Google it), even if you're on another platform. It's a nice, simple way to manage a second thread for background processing in a GUI application.
Do consider developing a strategy for detecting and/or avoiding deadlocks
Let's get this out of the way - in any non-trivial shared-memory system with conventional locking semantics, you'll never be able to predict ahead of time whether on not a deadlock will occur. I'm told there's a proof that in the general case, predicting deadlocks is equivalent to the infamous Halting Problem, which you've perhaps heard of before. If you have a reference to a research paper on this, let me know - I'd like to beat some people over the head with it. Despite all that, it's relatively easy to detect when the system is deadlocked.Don't spawn threads in response to external events
This is really just a special case of know what each thread in your program is for. It's hard enough to coordinate all the concurrency in your program with a static set of threads. Adding in the additional complication of unknown numbers of active threads at any given time is sheer insanity.Also, given that there's some amount of overhead involved for each thread that you create or have active, scaling up the number of threads as load increases will often have the perverse effect of decreasing throughput by attempting to improve it..
Do consider a message-passing design
I mentioned this in Part I, but you might want to consider using the message passing model, even if you're working in a shared-memory world. The basic rule here is to avoid modifying any global state from within more than one thread. When you send a message from one thread to another, you pass in all the data it'll need to access in order to complete its job. Then, you don't touch those data structures from anywhere else until the other thread is done working with them.The only real hurdle in implementing this strategy is in keeping up the separation between threads, despite not having any language-level support for the desired partitioning. You need to be really careful to not accidentally start sharing data between threads without intending to (and without having a plan).
Don't hold a lock or semaphore any longer than actually necessary
In particular, never hold a lock across a function call. Now, this might seem a bit extreme, but remember, we're trying to manage complexity here. If you can see all the places where a lock can be acquired and released all at once, it's easier to verify that it's actually acquired and released in the right places. Holding locks for the shortest time practical also shortens the window in which you can experience a deadlock, if you've made some other mistake in your locking strategy.Do stay on the well-trodden path
The producer-consumer model, thread pools and work queues all exist for a reason. There's a solid theoretical underpinning for these designs, and you can find robust, well tested implementations for most any environment you might be working in. Find out what's been done, and understand how it was done, before you go off half-cocked, inventing you own inter-thread communication and locking mechanisms. If you don't understand the very low-level details of how (and when) to use the "volatile" qualifier on a variable, or you haven't heard of a memory barrier, then you shouldn't be trying to implement your own unique thread-safe data structures.Do use multiple threads to get better performance on multi-processor systems
If your program is running on a multi-processor or multi-core computer (and chances are that it will be, eventually) you'll want to use multiple threads to get the best possible performance.Moore's Law, and what the future holds
Welcome to the multi-core era
I can't find the excellent blog post I was reading on this subject just yesterday, but here's an article by Herb Sutter that hits the high points. The bottom line is that you're not going to see much improvement in the performance of single-threaded code on microprocessors in the near future. In order to make any kind of performance headway with the next couple generations of processors, your code needs to be able to distribute load over multiple processes or threads.The future is now
Desktop PCs with 4 processors are already readily available. Sun's UltraSparc T1 has 8 cores on one chip, and can execute 32 threads "simultaneously", under ideal conditions. Even Intel's Itanium is going multi-core, a dramatic departure from the instruction-level parallelism that was supposed to be the hallmark of the EPIC architecture (but that's a story for another time).Some time in the very near future, the programs that you're writing will be executing on systems with 8, 16, or more processors. If you want to get anything near the peak level of performance the hardware is capable of, you're going to need to be comfortable with multi-processor programming.
Everything old is NUMA again
It's perhaps a trite observation that yesterday's supercomputer is tomorrow's desktop processor. Actually, I think it's more like there is a tide in processor design, that hits the supercomputer world, then hits the mainstream a couple decades or so later, when the high-performance folks have moved on to something else.In the 1980's, supercomputers were all about high clock-speed vector (SIMD) processing, which is where the current generation of desktop chips have stalled out. Clock speeds aren't going to massively increase, and the vector capabilities of the Pentium and PowerPC processors, while impressive, are still limited in the kinds of calculations they can accelerate. And the processor designs are so very complex, that it's hard to imagine that there are many more tricks available to get more performance-pre-clock out of the existing designs.
When the supercomputer folks hit their own megahertz and design complexity wall, they went through their own muti-core CPU era, then in rapid succession to massively parallel MIMD systems, then to the super-cluster computers we see these days. It seems reasonable to expect an explosion of processors in desktop systems too, and for much the same reason - the standard SMP shared memory model doesn't scale well.
In particular, cache coherency becomes a major performance issue in shared-memory multi-processor systems as the number of processors increases. The conventional wisdom says that a design where all memory is shared can scale to 4-8 processors. This is obviously dependent on memory performance, cache architecture, and a number of other factors. Perhaps worryingly, this means we're not only at the start of the multi-core era in the desktop world, we're also about one processor generation away from the end of it. Gee, that went by pretty fast, didn't it?
So, what's next?
Going by the "20 years behind supercomputers" model, the Next Big Thing in desktop processors would be massively-parallel architectures, with locally-attached memory. You'd expect to see something like the Connection Machine, or the Transputer-based systems of the 90's. Given the advances in process technology, you might even be able to fit hundreds of simple processors on a single chip (actually, some folks have already done that for the DSp market).However, the desktop computer market has shown a remarkable reluctance to embrace new instruction sets. So a design using hundreds or thousands of very simple processors with fast locally-attached memory isn't likely to succeed the currently ascendant IA32/IA64 Intel architecture. So where do we go from here? I think Intel is going to keep trying to wring as much performance out of their now-standard two chip, multiple cores per chip design. They can certainly do some more clever work with the processor caches, and with a little help from the OS, they can try to minimize thread migration.
Ultimately that approach is going to run out of steam though, and when that happens, there's going to be a major shift in the way these systems are designed and programmed. Through the multi-core era, and even into the beginning of the massively parallel era which will inevitably follow, you ought to be able to get away with following the pthreads model. You might need to think about processor affinity and cache sharing in ways you don't have to now, but it'll at least be familiar territory.
When really massively-parallel systems start to become more common, the programming model will have to change. The simplicity of implementation of the shared-memory model will inevitably give way to more explicitly compartmentalized models. What languages you'll likely use to program these beasts is an interesting question - most likely, it'll be a functional language, something like Haskell, or Erlang. I've been lax in getting up to speed on functional programming, and I'm going to make an effort to do better. I recommend that you do the same.
Saturday, September 02, 2006
Hell is a multi-threaded C++ program.
What are threads?
Every modern operating system has support for threads, and most programming environments provide some level of support for threading. What threads give you is the ability for your program to do more than one thing at once. The problem with threads is the way that they can dramatically increase the complexity of your program.First, a little background, so we're all on the same page. In Computer Science, as in the physical sciences, using a simplified model makes it easier to discuss complex phenomena without getting bogged down in insignificant details. The trick of course, is in knowing where your simplifications deviate from reality in a way that affects the validity of the results. While spherical cows on an infinite frictionless plane do make the calculations easier, sometimes the details matter.
When Real Computer Scientists (tm) are discussing problems in concurrent programming (like the Dining Philosophers), they'll sometimes refer to a Process, which is kind of abstract ideal of a computer program. Multiple Processes can be running at the same time in the same system, and can also interact and communicate in various ways.
The threads provided by your favorite operating system and programming language are something basically similar to this theoretical concept of a Process, with a few unfortunate details of implementation.
The New Jersey approach
I couldn't find a definitive reference to the history of the development of threads as we know them today, but the model most people are familiar with arose out of POSIX, which was largely an attempt to formalize existing practice in UNIX implementations.It turns out that POSIX Threads, Mach Threads, Windows Threads, Java Threads, and C# Threads all work very much the same, since they're all implemented in more or less the same way. The object-oriented environments wrap a thin veneer of objects around a group of extremely low-level functions, but you've got your basic operations of create(), join(), and exit(), as well as operations on condition variables and mutexes. For the rest of this rant, I'll refer to these as "Pthreads", for convenience.
Pthreads are an example of the Worse is better philosophy of software design, as applied to the problems of concurrent programming. The POSIX threading model is just about the simplest possible implementation of multi-threading you could have. When you want to create a new thread, you call pthread_create(), and a new thread is created, starting execution with some function you provide. The newly-created thread is created by allocating some memory for a stack for the new thread, loading up a couple of machine registers, and jumping to an address.
Shared state - two models
In the Pthreads model, all of your threads share the same address space. This makes sharing data between threads very simple and efficient. On the other hand, the fact that all of the state in the program is accessible and changeable from every thread can make it very difficult to ensure that access to all this shared state is managed correctly. Race conditions, where one thread attempts to update a data structure at the same time that another thread is trying to access or change that same structure, are common.The problem with the all state is shared model is that it doesn't match up very well with what you're generally trying to accomplish when you spawn a thread. You'll normally create a new thread because you want that thread to do something different than what the main thread is already doing. This implies that not all of the state in the parent thread needs to be available to be modified in the second thread. But because of the way threads are created in this model, it's easier (for the OS or language implementor) to share everything rather than a well-defined subset, so that's what you get.
The other major model for multi-threading is known as message-passing multiprocessing. Unless you're familiar with the Occam or Erlang programming languages, you might not have encountered this model for concurrency before.
There are a number of variations on the message-passing model, but they all have one thing in common: In the message-passing model, your threads don't share any state by default. If you want some information to go from one thread to another, you need to do it by having one thread send a message to the other thread, typically by calling a function provided by the system for just this purpose. Two popular variants of the message-passing model are "Communicating Sequential Processes" and the "Actor model".
You can get a nice introduction to the message-passing model by reading the first couple chapters of the Occam Reference Manual, which is apparently available online these days (I got mine by digging around in a pile of unwanted technical books at a former employer). Occam is of course the native language of the Transputer, a very inventive but commercially unsuccessful parallel processor architecture from the UK which made a big splash in the mid-80's before vanishing without a trace.
Why would you want to learn about this alternative model, when Pthreads have clearly won the battle for the hearts and minds of the programming public? Well, besides the sheer joy of learning something new, you might develop a different way of looking at problems, that'll help you top make better use of the tools that you do use regularly. In addition, as I'll explain in Part II of this rant, there's good reason to believe that message-passing concurrency is going to be coming back in a big way in the near future.
Enough rope to hang yourself with
As I mentioned earlier, the Pthreads model implies that all of your program's address space is shared between all threads. Most (all?) implementations allow you to allocate some amount of thread-local storage, but in general, the vast majority of your program's state is shared by every thread. This implies that every thread has the ability to modify the value of any variable, and call any arbitrary function, at any time. This is a really powerful tool, but like all powerful tools, it can be dangerous if misused.It's extremely difficult to predict what the behavior of even fairly simple code will be, when multiple threads can run it simultaneously. For more complex code, the problem rapidly becomes intractable. In a low-level language like C, you need to know the intimate details of how the compiler will optimize your code, which operations are guaranteed to be completed atomically, what the register allocation policy is, etc, etc. In a JIT-compiled language like Java or C#, it's impossible to even know what machine code will be used at runtime, so analyzing runtime behavior in detail just isn't possible.
An unlocked gun cabinet
I think one of the major problems with Pthreads is that it's too easy to make something that almost works. This then leads to an unwarranted belief that multi-threaded programming is simple. For example, say you've got a simple interactive GUI application, and you think that the application takes too long to calculate something after the user presses a button. The "obvious" solution to this problem is to have your button-press handler spawn off a new thread to perform the long-running operation.So you try this, and it works perfectly on the first try - the child thread launches, and then calculates away while the main thread goes back to handling the UI. You still need to notify the main thread when the calculation is complete, but there any number of easy ways to do this, and you probably won't have much trouble figuring that out. Gee, that wasn't so difficult, I wonder why people say that multi-threaded programming is difficult?
It's this sort of ad-hoc approach to creating threads that gets people into trouble. They create a new thread to solve one problem, and then another, and then they suddenly realize that thread A and thread M are interacting in a bad way. So they protect some critical data structures with mutexes, and before they know it, they're trying to debug a deadlock situation where they don't even understand how those two pieces of code could interact.
You need to really think about why you're creating a thread, before you spawn it. I'm not going to go so far as to say that creating threads while your program is running (rather than at startup) is de-facto proof that you're doing something wrong, but it's definitely a strong indication that you're not thinking about what your threads are for with any great rigor.