Vincent Gable’s Blog

May 31, 2008

Links: Less Code Is Better

Filed under: Design,Programming,Quotes
― Vincent Gable on May 31, 2008

I happen to hold a hard-won minority opinion about code bases. In particular I believe, quite staunchly I might add, that the worst thing that can happen to a code base is size.

–Steve Yegge

The fundamental nature of coding is that our task, as programmers, is to recognize that every decision we make is a trade-off. To be a master programmer is to understand the nature of these trade-offs, and be conscious of them in everything we write.

Now, remember, these dimensions are all in opposition to one another. You can spend a three days writing a routine which is really beautiful AND fast, so you’ve gotten two of your dimensions up, but you’ve spent THREE DAYS, so the “time spent coding” dimension is WAY down.

So, when is this worth it? How do we make these decisions?

The answer turns out to be very sane, very simple, and also the one nobody, ever, listens to:

“START WITH BREVITY. Increase the other dimensions AS REQUIRED BY TESTING.”

— Wil Shipley

Shared Code

Filed under: Programming,Quotes
― Vincent Gable on May 31, 2008

I’m very picky this time about what I consider “shared” — I have to actually USE code in two different projects to consider it shared, not just think “Hmm, someday somebody may want to re-use this.” Because, in truth, most of the crap people put into shared code directories is either too specific to really be shared, OR (more commonly) it’s written in a general way but the particular app it was written for only tested some of the pathways, so it is essentially a bunch of buggy code that’s not actually being used and is waiting to trip you up.

Wil Shipley

Messages to Nowhere

Filed under: Bug Bite,Cocoa,iPhone,MacOSX,Objective-C,Programming | , , ,
― Vincent Gable on May 31, 2008

If you send a message to (call a method of) an object that is nil (NULL) in Objective-C, nothing happens, and the result of the message is nil (aka 0, aka 0.0, aka NO aka false). At least most of the time. There is an important exception if sizeof(return_type) > sizeof(void*); then the return-value is undefined under PowerPC/iPhone, and thus all Macs for the next several years. So watch out if you are using a return value that is a struct, double, long double, or long long.

The fully story:

In Objective-C, it is valid to send a message to a nil object. The Objective-C runtime assumes that the return value of a message sent to a nil object is nil, as long as the message returns an object or any integer scalar of size less than or equal to sizeof(void*).
On Intel-based Macintosh computers, messages to a nil object always return 0.0 for methods whose return type is float, double, long double, or long long. Methods whose return value is a struct, as defined by the Mac OS X ABI Function Call Guide to be returned in registers, will return 0.0 for every field in the data structure. Other struct data types will not be filled with zeros. This is also true under Rosetta. On PowerPC Macintosh computers, the behavior is undefined.

I was recently bitten by this exceptional behavior. I was using an NSRange struct describing a substring; but the string was nil, so the substring was garbage. But only on a PPC machine! Even running under Rosetta wouldn’t have reproduced the bug on my MacBook Pro.Undefined values can be a hard bug to detect, because they may be reasonable values when tests are run.

Code running in the iPhone simulator will return all-zero stucts when messaging nil, but the same code running on an actual device will return undefined structs. Be aware that testing in the simulator isn’t enough to catch these bugs.

A few simple guidelines can help you avoid my misfortune:

  • Be especially careful using of any objective-C method that returns a double, struct, or long long
  • Don’t write methods that return a double, struct, orlong long. Return an object instead of a struct; an NSNumber* or float instead of a double or long long. If you must return a dangerous data type, then see if you can avoid it. There really isn’t a good reason to return a struct, except for efficiency. And when micro-optimizations like that matter, it makes more sense to write that procedure in straight-C, which avoids the overhead of Objective-C message-passing, and solves the undefined-return-value problem.
  • But if you absolutely must return a dangerous data type, then return it in a parameter.
    Bad:
    - (double) crazyMath;
    Good:
    - (void) crazyMathResult:(double*)result;.

I love Objective-C’s “nil messaging” behavior, even though it is rough around the edges. It’s usefulness is beyond the scope of this article, but it can simplify your code if you don’t return a data-type that is larger then sizeof(void*). With time, when the intel-style return behavior can be universally relied on, things will be even better.

May 28, 2008

PayPal Reliability Problems

Filed under: Uncategorized | , ,
― Vincent Gable on May 28, 2008

It is rather worrying that:

a) PayPal broke something so fundamental as subscription payments. Don’t they have proper testing before they roll out changes?

b) It still wasn’t fixed 12 days later.

c) PayPal seem completely unresponsive to requests for information from developers when problems occur.

According to Andy Brice

UPDATE: 2009-12-17: A terrifying and Kafkaesq story of Paypal shutting down an indie developer’s account, because of a spike in sales due to a bundle sale.

The Minimum Screen Size You Must Support for Mac OS X Is 800×600

Filed under: Accessibility,Bug Bite,Design,MacOSX,Programming,Quotes,Usability |
― Vincent Gable on May 28, 2008

Mac OS X can run on systems with a screen size as small as 800 x 600 … Unless you know that your users will be using a specific display size, it is best to optimize your applications for display at 1024 x 768 pixels. … Design your user interface for a resolution of at least 800 x 600.

According to Apple’s Human Interface Guidelines (retrieved 2010-04-21).

May 27, 2008

Readable Colors

Filed under: Accessibility,Design,Usability | , ,
― Vincent Gable on May 27, 2008


…the most readable color combination is black text on white background; overall, there is a stronger preference for any combination containing black. The two least readable combinations were red on green and fuchsia on blue.

Also, in every color combination surveyed, the darker text on a lighter background was rated more readable than its inverse (e.g. blue text on white background ranked higher then white text on blue background).

I wish Columbia Blue and Red were tested in the survey. They were my high school’s colors; and they were terrible. The worst permutation was blue text on a red background.

Question for all you who prefer light source-code on a dark background: why do you like it?

Bad Design = Bargain?

Filed under: Design,Quotes
― Vincent Gable on May 27, 2008

..the eBay design model. My theory is that eBay was a success due to its crappy design because it gave it that flea market feel and when you went there you felt like you getting a deal. Go to Tiffany & Co. and you don’t get the feeling that you are getting a bargain because you shouldn’t.

Paul Scrivens

May 26, 2008

People Prefer Sure (but small) Gains; Avoidable (but possibly large) Losses

Filed under: Quotes,Research,Usability | , ,
― Vincent Gable on May 26, 2008

Bruce Schneier has a new essay, How to Sell Security. As usual, it’s well worth reading.

The most interesting tidbit, to me, is that people have a bias to choose a small certain gain over an uncertain but possibly larger gain. But with loss, it’s the opposite. People avoid certain losses; preferring to “play double or nothing” — risking a larger loss for the chance of not sustaining a loss.

Here’s an experiment that illustrates Prospect Theory. Take a roomful of subjects and divide them into two groups. Ask one group to choose between these two alternatives: a sure gain of $500 and 50 percent chance of gaining $1,000. Ask the other group to choose between these two alternatives: a sure loss of $500 and a 50 percent chance of losing $1,000.

These two trade-offs are very similar, and traditional economics predicts that the whether you’re contemplating a gain or a loss doesn’t make a difference: People make trade-offs based on a straightforward calculation of the relative outcome. Some people prefer sure things and others prefer to take chances. Whether the outcome is a gain or a loss doesn’t affect the mathematics and therefore shouldn’t affect the results. This is traditional economics, and it’s called Utility Theory.

But Kahneman’s and Tversky’s experiments contradicted Utility Theory. When faced with a gain, about 85 percent of people chose the sure smaller gain over the risky larger gain. But when faced with a loss, about 70 percent chose the risky larger loss over the sure smaller loss.

This experiment, repeated again and again by many researchers, across ages, genders, cultures and even species, rocked economics, yielded the same result. Directly contradicting the traditional idea of “economic man,” Prospect Theory recognizes that people have subjective values for gains and losses. We have evolved a cognitive bias: a pair of heuristics. One, a sure gain is better than a chance at a greater gain, or “A bird in the hand is worth two in the bush.” And two, a sure loss is worse than a chance at a greater loss, or “Run away and live to fight another day.” Of course, these are not rigid rules. Only a fool would take a sure $100 over a 50 percent chance at $1,000,000. But all things being equal, we tend to be risk-adverse when it comes to gains and risk-seeking when it comes to losses.

This cognitive bias is so powerful that it can lead to logically inconsistent results. Google the “Asian Disease Experiment” for an almost surreal example. Describing the same policy choice in different ways–either as “200 lives saved out of 600” or “400 lives lost out of 600”– yields wildly different risk reactions.

Evolutionarily, the bias makes sense. It’s a better survival strategy to accept small gains rather than risk them for larger ones, and to risk larger losses rather than accept smaller losses. Lions, for example, chase young or wounded wildebeests because the investment needed to kill them is lower. Mature and healthy prey would probably be more nutritious, but there’s a risk of missing lunch entirely if it gets away. And a small meal will tide the lion over until another day. Getting through today is more important than the possibility of having food tomorrow. Similarly, it is better to risk a larger loss than to accept a smaller loss. Because animals tend to live on the razor’s edge between starvation and reproduction, any loss of food — whether small or large — can be equally bad. Because both can result in death, and the best option is to risk everything for the chance at no loss at all.

May 25, 2008

Objects that Won’t Hide

Filed under: Bug Bite,Cocoa,Interface Builder,MacOSX,Objective-C,Programming |
― Vincent Gable on May 25, 2008

NOTE: Although this specific Bug Bite is about NSTextView, and the “hidden” property, the same underlying issue applies to other interface-objects (NSTableView, etc.), and different properties, like size.

Problem

If you send a setHidden:YES message to an NSTextView, and it’s text disappears, but the view itself (white box) stays visible here’s the problem, and the solution.

It turns out that if you created the NSTextView by dragging it off the pallet in Interface Builder, then it’s not an NSTextView. It’s an NSTextView wrapped inside an NSClipView inside an NSScrollView. The NSScrollView is what puts up the scroll-bars if the NSTextView gets really big; the NSClipView helps make the scrolling work.

So if text is your IBOutlet to your NSTextView, then when you say [text setHidden:YES];, the NSTextView is hidden, but the the total package won’t disappear, unless you hide the NSScrollView as well.

Solutions

You can send the message to NSScrollView containing text, like so:
   [[text enclosingScrollView] setHidden:YES];.
This will hide everything inside the NSScrollView, including text.

Another solution is to create just an NSTextView in Interface Builder. To do this, put an NSView in your interface (it’s called a “Custom View”,in the Interface Builder objects pallet). Then select it, bring up the object inspector (cmd-shift-i), choose the “custom class” from the category menu at the top, and select NSTextView from the list of subclasses. This puts an NSTextView in your interface, without the surrounding clip and scroll views. Unfortunately, it also means you can’t configure it in Interface Builder, beyond resizing it. That’s why I’m not partial to this approach, although I have used it.

Thanks to ZachR for suggesting enclosingScrollView.

May 24, 2008

memcopy, memmove, and Speed over Safety

Filed under: Design,Programming | , ,
― Vincent Gable on May 24, 2008

“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.”

–Knuth, Donald. Structured Programming with go to Statements, ACM Journal Computing Surveys, Vol 6, No. 4, Dec. 1974. p.268.

If you’ve programmed in C, you’ve seen memcpy (pronounced “mem-copy”). It’s the preferred way to copy a block of memory somewhere. There is a safer and equally easy to use alternative, memmove. But it has never gained traction, in spite of it’s advantages. As we will see, this is unfortunate, and may be an example of a programming culture that values (superficial) speed over safety.

The Difference

The difference between memcpy and memmove is simple: when the source and destination blocks of memory overlap (for example, if they are the same), memmove works, but memcpy‘s behavior is undefined. It might work. It might crash. It might corrupt data. It might behave differently during debugging.

memmove is safer.

memcpy can be faster, and usually is. There are less restrictions on it’s implementation, so more can be done to optimize it. But not necessarily a lot more — in fact, it could even be slower then memmove, and sometimes this is the case. On some systems, memcpy may just be memmove.

Faster

So how much faster can memcpy be then memmove? They both take O(N) time to copy N bytes of data. So in some computer-science circles, memcpy wouldn’t be considered faster then memmove.

Algorithmically, memmove needs one extra if-statement before it starts to copy; to determine if it needs to copy front-to-back, or back-to-front. (See this reference implementation for an example.) The only other advantage memcpy may have are esoteric processor-specific instructions that assume restricted pointers. So unless there is a “memcpy instruction”, we can expect the difference in speed to be pretty small.

But the real proof is in the pudding and … memmove is faster then memcpy! At least on my laptop, with this test; basically copying 4MB of memory 100 times. See for yourself:

$ gcc -O0 memcpy_memove_lab.c && ./a.out
   Surprise!
   memmove is 1.404409 times faster then memcpy

gcc -O3 memcpy_memove_lab.c && ./a.out
   Surprise!
   memmove is 1.054571 times faster then memcpy

“This must be an unfair test!”, you’re probably thinking. Yes, it is. Or at least it’s a dangerously narrow test. But it is also an honest and simple test. By that I mean, it is the first code I hammered out to just get some numbers for this article. Proper benchmarking is very difficult. I’m not going to attempt it.

The real lesson from this naive benchmark is that you must measure your code before concluding that an optimization is really faster. I would never have guessed that memmove would be up to 40% faster, at copying 4MB. But it was — in this particular instance.

On a related note, a significantly faster memcpy (say 2x) won’t have an appreciable impact on an application’s performance, unless the application spends a surprisingly large portion of it’s time copying memory (Amdahl’s law). For example, let’s say that 1 out of every 20 seconds is spent copying memory with memmove. (That’s a lot of time just moving bits around! Programs should do something with bits, not just move them.) So we replace memmove with memcpy, and this memcpy is a full 2x faster then memmove (which is optimistic). Surprisingly, we only get a 2.5% speedup! Remember that 1 in 20 is only 5% of the program’s total time. Cutting this time in half eliminates 2.5% of the program’s total execution time. If we can find an optimization that speeds up the other 95% of the program by just 2.7%, we get better performance overall.

So memcpy is unlikely to make a large difference to program performance, in general. Switching memcpy implementations is a “local optimization”, that has much less value then changing the algorithm that’s requiring all that duplication. It may even suddenly become slower when hardware is upgraded.

Safer

How much safer is memmove? This is a hard dimension to quantify, and I don’t have a satisfying answer. My instinct tells me that it isn’t dramatically safer. I don’t have any data to support this, but I believe it’s very rare to be copying memory into itself; compared to other memory-management errors, like a double-free().

But the bottom line is that, there are less ways your program can fail if you use memmove over memcpy. Period. Since memcpy‘s behavior is undefined when the source and destination overlap, it can be a vicious bitch to debug.

Speed over Safety

memcpy is preferred by a significant majority of C programmers. I don’t know exactly how many. But a google fight shows that memcpy is almost 6x more talked about then memmove (as of 2008-04-11). Anecdotally, memmove is mostly unheard of in my experience. It seems like the call of “faster” really is a siren’s-song for developers; luring them into dangerous code.

I think this is very unfortunate. Especially, because the performance advantage of memcpy just isn’t that big in general! (Sometimes it’s even harmful). Given the unreliability of software, anything that elements bugs is a Very Good Thing.

I wish I knew the full story of memcpy winning the popularity contest with memmove. By accident or design, it has left us with a programming culture that values superficial speed over safety.

For Further Reading:

Optimizing memcpy — includes some graphs showing the tradeoffs between optimizing for large chunks of memory (say copying pictures), and small data structures.

Why aren’t my optimizations optimizing? — “Optimizing code is a tricky business.”

memcpy_memove_lab.c — The naive benchmark from this article, plus a reference implementation of memcpy and memmove.

Older Posts »

Powered by WordPress