Pages

.

Do-less go-fast, revisited

Yesterday, someone I follow on Twitter (@johnstack) said he enjoyed my post on writing faster code and asked if I'd refer to some examples of "do less, go fast."

Canonical examples from Algorithms 101 abound. I won't attempt to list them. Quicksort versus bubble-sort is an example that comes to mind (although worst-case performance is actually the same for both of those algorithms -- as is often true of divide-and-conquer approaches).

One of the most outstanding do-less algorithms of all time would have to be the Fast Fourier Transform, anticipated in 1805 by Carl Friedrich Gauss but made popular by Cooley and Tukey in 1962.

"Do less" doesn't always mean choosing a different algorithm. Sometimes you just need to parameterize the problem properly. I don't mean "parameterize" in a rigorous mathematical sense. What I'm talking about is rethinking the problem so you can define it (and attack it) in some fundamentally new way. You can find plenty of examples of this sort of thing in graphics programming, particularly 3D graphics . The Graphics Gems series of books abounds with examples of "doing less to go fast." All the source code (mostly C) from these books can be downloaded here, by the way.

I wrote an article for MacTech in 1999 on fast graphics-rendering strategies for the Mac, back when Apple was using PowerPC processors. That article contains a lot of do-stuff-faster tips and tricks, some of which can certainly be adapted to non-Mac systems. (If you like the article, or even if you don't, you might also want to look into dope vectors, sometimes called Iliffe arrays.)

My son (who is 14 years old) showed me a particularly egregious example of code-in-need-of-optimization the other day. Justin is a big fan of Runescape (the massive online adventure game), and he obtained Java source code to one of many knock-offs of the Runescape server that are floating around on the web. I looked at the main loop and sat there stunned for a few minutes. It contained easily the largest continuous sequence of if-elses I've ever seen. The if-elses were piled on so thick that when Justin tried to insert one extra if-else expression of his own, the class would no longer compile! "Code is too big" (or something like that) was the error message. It turns out Java has a hard-coded max size limit, per method, of 64K (in source code). Sun was way too lenient here, though. I think the limit should be something closer to 8K.

This is the kind of thing that, if it were in C++ or JavaScript (or a language that supports pointers-to-functions), would be a prime candidate for jump-table conflation. The natural syntax for long runs of if-elses in C, Java, or JavaScript is, of course, the switch statement, which the compiler (under the covers) implements as a jump table, usually. But you can also implement it yourself, directly. In JavaScript:
// horrible Runescape hacker way:
function updateUser( user ) {

if ( user.something == State.GOOD )
handleGood( user );
else if ( user.something == State.BAD )
handleBad( user );
else if ( user.something == State.TERRIBLE )
handleTerrible( user );
else if ( user.something == State.MESSED_UP )
handleMessedUp( user );
else if [ ... ] // 64Kbytes more of this
}

// jump-table way (error checking omitted):
function updateUser( user ) {

var table = {
State.GOOD : handleGood,
State.BAD : handleBad,
State.TERRIBLE : handleTerrible,
State.MESSED_UP : handleMessedUp
};

table[ user.something ]( user ); // dispatch
}
Ideally, of course, you'd initialize the (static) table once and keep a permanent copy somewhere so you don't always have to create it each time you enter the updateUser() method, but even if you create it every time, it's still cheaper than crunching through a ridiculously long list of if-elses.

The moral, in this case, is that whenever you see a big, long run of if-elses, consider that someone has handed you an optimization opportunity.

In tomorrow's post, I'm going to run through some code for doing fast set manipulation in JavaScript. Just for fun, I'll throw in some console AJAX, and we'll have a quick look at Twitter's social-graph REST API. The snippets I want to show you won't win any prizes for elegance, but hey, this is console code we're talking about. Prettiness isn't on the requirements list.

Later.
reade more... Résuméabuiyad

How to write fast code

There was a time, early in my programming career, when I needed to rewrite a particular program (a very small one) to make it run faster. I was quite new to programming and thought that the way to get something to run faster was to rewrite it in assembly. In those days, you could unroll a loop in assembly and pretty much count on getting a worthwhile speedup, if it was a tight loop to begin with.

Fortunately, I had a fabulous mentor in those days, a coder with wisdom and experience far beyond his years. The person in question was a first-class code ninja and a master circuit designer, a genius of Woz-like proportions. Silicon obeyed him the way marble obeyed Michelangelo.

When it came to code, John could do astounding things. He could optimize (and did optimize) virtually any algorithm for any situation, and do it in so little code that you'd sit there studying the printout, wondering where the heck the algorithm went! I remember John had this peculiar way of making loops vanish, for example. They'd turn into table-lookups or recursion or self-modifying code, or some combination of the three.

One day my mentor asked me what I was working on and I told him. I mentioned that I was frantically searching for a way to speed up my little program. I described a few of the things I'd tried so far. He listened intently.

When I was done talking, John gave me some of the most profound advice any programming expert has ever given me. (It was profound for me, at the time. Maybe it'll be stupid-sounding to you.)

"The CPU," he said, "runs at a certain speed. It can execute a fixed number of instructions per second, and no more. There is a finite limit to how many instructions per second it can execute. Right?"

"Right," I said.

"So there is no way, really, to make code go faster, because there is no way to make instructions execute faster. There is only such a thing as making the machine do less."

He paused for emphasis.

"To go fast," he said slowly, "do less."

To go fast, do less. Do less; go fast. Yes, of course. It makes perfect sense. There's no other way to make a program run faster except to make it do less. (Here, when I say "program," I'm not talking about complex, orchestrated web apps or anything with fancy dependencies, just standalone executables in which there's a "main loop.")

Key takeaway: Don't think in terms of making a slow piece of code run faster. Instead, think in terms of making it do less.

In many cases, doing less means using a different algorithm. Then again, it may be as simple as inserting a few if-elses to check for a few trivial (but frequently encountered) "special cases" and return early, before entering a fully-generalized loop.

It may mean canonicalizing your data in some way before passing it to the main routine, so that the main routine doesn't have to include code that checks for corner cases.

The tricks are endless, but they end up with the CPU doing less, not more; and that's the key.

The "go fast do less" mantra has been a valuable one for me, paying off in many ways, in many situations, over the years. It has helped me understand performance issues in a different kind of way. I'm grateful to have been exposed to that concept early in my career. So I provide it here for you to use (or not use) as you see fit.

Maybe you received a similarly influential piece of advice early in your career? If so, please leave a comment. I'd love to hear about it.
reade more... Résuméabuiyad

Who bears responsibility for layoffs?

In these less-than-buoyant economic times, many technology companies are struggling to remain profitable. Some are struggling for their lives. When earnings turn negative, talk turns to cost-cutting. And since the single biggest cost of running a company is people (or should I say, the salaries, benefits, and miscellaneous costs associated with employees), inevitably the meat cleaver comes out. The result: Headcount reduction. Downsizing. Rightsizing. "Smartsizing." Redeployment. Restructuring. Reduction in force (RIF). Force shaping.

In non-Dilbertian terms: Layoffs.

Paradoxically, it costs a lot of money to let people go (in the U.S., at least). Time-Warner recently announced it would cut 1200 jobs. According to the company, the RIF will cost an estimated $100 million.

The true costs of a RIF to a company go well beyond the termination pay and other costs associated with getting rid of someone. The real costs include:
  • Brand equity costs: How much does it hurt a company's reputation as a "good place to work" when it announces layoffs?

  • Morale costs: Employees who survive the cut are often demoralized and fearful (with good reason).

  • Rehiring costs: Eventually, when (and if) things turn around, some positions will need tobe refilled.

  • Sales-disruption costs: Deals may fail to close, or a company may be eliminated from consideration during a procurement effort, if the company in question is perceived as "in trouble" due to a painful cost-cutting exercise involving layoffs. These days, procurement teams are justifiably "skittish." They want to avoid going with a company that might be perceived as a risky choice.
But where does the responsibility lie for the poor financial performance that gave rise to a RIF in the first place? In the current economic environment, it's easy to put the blame on the recession itself. And certainly, there's some truth in that. Demand is down. Credit is tight. Layoffs are, in fact, inevitable during economic downturns of the sort we're in now. But what if a company was already performing poorly before the recession? What if it has been in a negative-earnings posture for two, three, four years running?

What if you're Sun? Or Novell? Or Vignette?

The answer is pretty simple. Responsibility for a company performance ultimately rests with executive leadership: the folks at the top.

A company's success is a team effort, obviously. No one person causes a company to succeed or fail. But strategy -- and its execution -- begin at the top. It all cascades down from there. This is why the folks with CxO after their names are paid the Big Bucks. Isn't it?

Therefore, when I see a company like Sun (which is overflowing with good technology and good people) do poorly over a protracted period of time -- going back before the current recession -- I can't help but wonder why folks like Jonathan Schwartz get to keep their jobs.

Mind you, I have nothing against Schwartz. I know someone who has worked closely with him, and by all accounts he's a really nice guy. But guess what? He has failed at his job.

The same can be said for Ron Hovsepian at Novell. I know him to be a heck of a nice guy. He's smart, he's dynamic, and his heart is in the right place. But he, too, has failed miserably at his job.

The list is long.

I don't know about you, but I think people who fail in their jobs (no matter how smart they are or how personable or how great a job they did in prior lives at other companies) need to be put on probation, then ultimately (as conditions warrant) replaced. This is how it works at the bottom. This is how it works at the middle. It needs to work that way at the top.

Sure, it happens. But it's not happening fast enough.
reade more... Résuméabuiyad

Developers should not be allowed to work overtime

I have a very simple tip for software companies and engineering managers who want to make their developers happier and more productive (and less likely to produce buggy code). Forbid overtime.

I give this advice based primarily on what I've observed personally (and anecdotally via friends in the industry). What I've witnessed is that people who are flogged like mules either burn out, quit their jobs unexpectedly, or (worse) stay with the company, carrying a new, sour attitude.

Conversely, I've seen that developers who are encouraged to go home at quitting time tend to do a better overall job, because they know what's expected of them (i.e., finish what you're doing by 5:00) and come to work in the morning well-rested (or at least with a reasonably positive outlook compared to their counterparts at other companies who are doing 60- or 80-hour weeks).

People who know they have to leave at 5:00 (or 6:00, etc.) tend to go to extra lengths to finish whatever it is they were working on before the clock runs out. They ramp up their productivity as necessary to get work done in the time allotted. This is what you want.

Productive employees become more productive when they have to work within time constraints, because they learn time-management skills they wouldn't otherwise learn. That sounds like a tautology, but it's true.

It works in reverse as well. You find out quickly who your less-productive people are when they're under time constraints. This is valuable info if you're a manager.

The no-overtime rule tends to enforce good project-management discipline. People become realistic about how much progress can be achieved in a given length of time and set milestone dates accordingly. If delivery goals aren't met, new dates are set (assuming there's a firm no-overtime rule) and project managers assume responsibility for the initial misjudgment. (Of course, the project managers get their time estimates from the various engineering managers, so the responsibility for missing a goal actually gets cascaded down through management.)

Without a no-overtime rule, people are expected to adjust their work day as needed in order to meet milestones, and failure to meet the goal is blamed on employees rather than management (because the underlying assumption is that if you put in enough hours, you could have made the goal). Putting the onus for lateness on regular employees rather than managers only demoralizes workers and makes them less apt to deliver on future deadlines. The right thing to do (the productive thing to do, in the long run) is let managers bear responsibility for lateness -- as they should.

When I see or hear tales of people sleeping under their desk and drinking energy drinks while they bang out code at 2:00 in the morning, I know that the company in question is poorly run and will ultimately suffer (in any number of possible ways) for making (or letting) people work crazy shifts. "But," you may be saying, "what if people are putting in those hours because they truly want to?" In my experience, people with families like to be with their families. Some people take classes at night (or need to change a baby's diapers in the middle of the night), or have ailing relatives to take care of, or have any number of other outside responsibilities. Working till dawn is not an option for some people, and in my experience most people do not choose it voluntarily. There are exceptions, of course (such as with short-staffed entrepreneurs who are trying to bootstrap a new business), but as a general rule, working till 2:00 or sleeping under your desk not only reflects bad company policy but poor personal judgment as well. (Again, though, if you're a founder of a new business, you do what you have to do. But if you know what's good for you, you won't make others work that way.)

I'm sure there are some who would say that in these troubled economic times, special measures are called for. Many companies, after all, are fighting for their very lives right now. Surely workers should expect to work overtime some of the time, until the economic storm passes?

To which I say: If your situation is so desperate that you think making people work a few extra hours is going to save the company, you're in more trouble than you think. Way more.
reade more... Résuméabuiyad

Making one line of code do the work of 47

Every year or two, I go back and re-read certain papers, presentations, and book chapters that have inspired me or been the source of priceless "Aha moments." One paper that I periodically revisit is John K. Ousterhout's Scripting: Higher Level Programming for the 21st Century, written when Ousterhout was affiliated with Interwoven. (He's currently a research professor at Stanford.)

If you're not familiar with John K. Ousterhout, it might be because you don't use Tcl (Tool Command Language, the scripting language created by Ousterhout). Sun Microsystems hired Ousterhout in 1994 specifically to accelerate the development of Tcl. It turns out that Sun's CTO at the time (the person who hired Ousterhout) was Eric Schmidt. (Yes, that Eric Schmidt.) The whole story of the creation of Tcl (an interesting tale in its own right) is told by Ousterhout here.

In any case, if you haven't yet encountered Ousterhout's excellent "Scripting" paper (originally published in IEEE Computing), I recommend that you check it out. Don't be misled by the 1998 date. It's still a very relevant paper.

Rather than argue for or against scripting languages, Ousterhout lays out the philosophy (and relative benefits) of various kinds of languages and explains, often with recourse to real-world data, why certain languages are advantageous in certain situations and others are not.

Ousterhout talks about the productivity-multiplier effect of high-level languages:
On average, each line of code in a system programming language translates to about five machine instructions, compared to one instruction per line in assembly language (in an informal analysis of eight C files written by five different people, I found that the ratio ranged from about 3 to 7 instructions per line[7]; in a study of numerous languages Capers Jones found that for a given task, assembly languages require about 3-6 times as many lines of code as system programming languages[3]). Programmers can write roughly the same number of lines of code per year regardless of language[1], so system programming languages allow applications to be written much more quickly than assembly language.
The same effect applies when going from a high-level compiled language to a scripting language, except that the multiplier effect is even greater. As Ousterhout says: "A typical statement in a scripting language executes hundreds or thousands of machine instructions." The net result is summarized in the following graph.



This graph produced a kind of "Aha!" moment for me, because I realized (in a way I somehow hadn't, before) that scripting languages were all about code reuse; that if I could reduce the number of lines of code I write, I can (almost by definition) reduce the number of bugs I write; and that if one can accomplish an operation by calling a library method (such as the String "replace" method in JavaScript), scripted code can run at compiled-language speed, because a scripting language's built-in methods are implemented in C++ (Spidermonkey) or Java (Rhino).

If you haven't read Ousterhout's paper before, I don't want to spoil the suspense here. Suffice it to say, the paper gives a balanced account of the strengths and weaknesses of languages of all kinds; there's no language-bigotry, no theological diatribes. It's a concise and eloquent treatment of a tricky topic, and considering the year in which it was written, it's quite a prescient piece in many ways. On top of everything else, it's just plain entertaining to read -- a rarity these days, online or off-.
reade more... Résuméabuiyad

Java as Legacy Language

Bruce Eckel (Thinking in Java) has an interesting post on The Positive Legacy of C++ and Java. I'm a sucker for articles that talk about Java and C++ as legacy languages, and this one is notable not only for the fact that it comes from a true authority (Eckel was on the C++ Standards Committee for 8 years, and saw the language design decisions take place first-hand) but for the 70-odd comments (mostly lengthy, mostly astute and non-emotional) that his post provoked.

Eckel's post is akin to a very short eulogy. I wish it had been much longer. But the fact is, Eckel has written numerous article, books, and blogs over the past five or six years outlining the weaknesses (large and small) of Java and C++, so it's not hard to find more to read on this subject.

Java is far from dead, of course. Much like COBOL, it will live on and on, eventually becoming something of an arcane specialty language mostly taught in universities.

What were (are) some of Java's biggest failings? Maybe it's more important (and constructive) to ask what its biggest successes were. To me, Java's major disruptive effect (when it arrived in the mid-1990s) was and is due to its game-changing notion of running on a virtualized machine. The idea of cleanly separating a language from underlying machine dependencies was a huge advance. In sum, the importance of Java is that it was/is not just a language but a platform.

A second pivotally important aspect of Java is that by its very design, it facilitated programming-in-the-large. The package mechanism, the orientation toward safe reuse, and the assurance of a common, stable, secure, platform-independent process space in which to run (the JVM), allowed teams of programmers to work independently to produce large applications that could take advantage of the huge advances in machine design (per Moore's Law) that occurred throughout the 1990s (without which, frankly, Java would have failed to be anything but a curiosity).

By supporting programming-in-the-large, Java made possible the huge middleware industry we see around us today. Certainly we would have had a middleware industry without Java, but it would have looked much different and come with a much different set of problems. (It would have had even more of a Rube Goldbergish quality to it than it has now.) Java popularized the notion of an application server, which by itself was a landmark development that changed the software industry.

Another outcome of Java (and C++): The notion of design patterns. The very complexity of the large systems made possible by Java and C++ demanded a new type of "best practices" knowledge around the construction of "orchestrated systems" (tm).

We owe a lot to Java, clearly, but the sheer cost of using Java for smaller-scale projects is driving the creation of new, lighter-weight languages and frameworks that offer a better impedance match with agile working styles. Going forward, it simply won't be economical to use Java for most kinds of development.

The industry hasn't quite figured out yet what the best technologies are for replacing Java in various types of development, but that's what makes things so interesting right now. Economic downturns like the one we're in tend to drive intense competition, and intense competition breeds innovation. Hence, whatever the next Big Disruption in programming turns out to be, it will probably come sooner rather than later.

One thing is for sure: If you're in the software development business, don't cling to old ways of doing development. And also, don't get too carried away thinking that something like Scrum is going to be the Bandaid that fixes your agility problems, because it may turn out that your main problem is Java itself. Keep an open mind. Try new things. Be ready when the next disruption arrives, or you may find yourself without a chair when the music stops.
reade more... Résuméabuiyad