2008-02-26

A Tip on Performing Geographic Price Discrimination

From: JP
To: Apple
Subject: Pro Tip

When doing geographic price discrimination, try not to spam the same email address with both mailings on the same day.

FAIL

TomTom Improves the Wake-from-Standby Process

In February 2007, I left the following comment on a post about combined play/pause controls on the excellent History of the Button blog:

Jean-Philippe Daigle Says:
February 1st, 2007 at 8:51 pm

The problem with a combined start/stop button is that many devices fail to provide proper feedback that the keypress has registered.

If there’s significant lag between the keypress and the action it is supposed to initiate, and no immediate feedback, the frustrated user pushes again, thus reverting the state to whatever it was before the interaction began. Discrete start/stop buttons, on the other hand, are idempotent and can afford multiple presses.

Concrete examples (since you asked for some): This happens to me several times a week with my TomTom Go car-mounted GPS and the Motorola RAZR phone. Each of these devices has a single button for on/off, and requires holding it to invert the state of the device, but in both cases, there’s no feedback whatsoever for a few seconds.

Happily, more than a year after the TomTom Go 700 has been superceded by better models, TomTom is still showing commitment to its customers with updates! With the latest software upgrade, they've fixed this little issue. Now, instead of just sitting there silently during boot, the screen's backlight is powered on immediately while the unit wakes up. Granted, it's a subtle piece of feedback, but it works - I no longer power it off with an unnecessary second press.

2008-02-23

Performance Overhead of Non-Final Method Calls in Java

Working with developers who care deeply about application performance, you get around to having frequent interesting discussions about the subject. We end up reviewing every frequent temporary object allocation, scrutinizing every usage of a mutex in the fast-path for possible elimination, etc. Recently the topic was something really simple on the surface: people are afraid to call non-final instance methods, especially in situations where the method being called needs to be resolved to a particular superclass. Is this fear justified?

There is evidence and wisdom out there pointing to a non-trivial performance penalty of non-final method calls: articles like this one (PDF), my boss' repeated assertions, and the Java Platform Performance book, if I recall correctly.

Unfortunately (or fortunately?) I couldn't find, in a simple test, evidence to support these fears. I designed a test that performed a warm-up (to allow HotSpot to compile what it could after seeing how often I was calling specific methods), then used a System.nanoTime() call right before and after a ten-million iteration loop that just calls an "increment a counter" instance method on an object.

Test Cases

java_perf 

  • There is a warmup period before any timing loop (it's also 10,000,000 calls).
  • I'm not doing any object allocation in the timing loop, so the GC won't run and screw up our results.
  • I ran the whole test suite 25 times and averaged the results.
  • Just to be extra safe, I'm getting the counter value at the end of the 10,000,000-call loop to prevent any 'cleverness' from the compiler determining the end result is useless and not doing the call at all.
  • The first two tests deal with trying a final counter incrementing method in SingleClass, as well as a non-final counter incrementing method. SingleClass is declared as final and cannot be subclassed.
  • The second two test cases do the same as the first two, but calling all methods on SubClass.

The Results

CropperCapture[213]

The conclusion seems to be: it doesn't matter at all what you do. I see a few explanations:

  • The common wisdom of there being a significant overhead to non-final method invocations may have been true in previous versions of the JVM, and modern JVMs may have levelled the playing field.
  • My test was flawed in some way. (Doing a counter++ was so simple it got inlined?)

2008-02-19

Towards an Efficient Pancake-Serving Strategy in Office Environments

Background

I was invited to sit in on a "Social Committee Meeting" at work [Solace Systems - ze cool message routing company that allows me to pay the bills]. I provided some constructive criticism of last year's pancake breakfast by pointing out that although a delightful time was had by all, surely the quality of the food would have been even better if instead of thick store-bought pancake mix, we had prepared homemade batter for all the crêpes [if I recall correctly, it ended up being a mix of store / homemade, or perhaps it was all-store, I'm not 100% sure, but the dimensions of the final pancakes indicated the batter was likely a bit thick].

I was subsequently assigned the task of making the pancakes for all the engineering staff this year. Yay me and my big mouth.

Problem

After I expressed the opinion that the batter should be made fresh and cooked on the spot (just-in-time cooking), concerns were raised that it would be impossible to produce pancakes at an acceptable rate to feed all 30 or so expected attendees. Two options present themselves:

  1. Find a way to make pancakes fast enough with the single heating element available (let's say we are shooting for 60 pancakes in 20 minutes or so). This is hard because there is no possible parallelism.
  2. Find an acceptable way to preserve pre-cooked pancakes and reheat them in situ once at the office, without destroying their texture or flavour.

DPP_0001

Test Procedure

jpdaigle@vitis:~$ make pancakes
make: *** No rule to make target `pancakes'. Stop.

This weekend, I endeavoured to measure the rate at which I could cook pancakes (evaluating the feasibility of option 1), and, should an acceptable rate prove unattainable, I would experiment with pancake reheating.

I used my usual batter recipe and a single well-seasoned pan at medium-high heat, starting to cook the first pancake at 13:15 EST on Sunday. I logged the time at which each pancake was completed and transferred to a holding plate.

CropperCapture[10]

I completed seven (7) pancakes in 13 minutes, for an average of 111 seconds per pancake. The graph above demonstrates that the output rate remained mostly constant over the course of the experiment, which I interpret to mean the pan had attained its nominal temperature before I started cooking the first pancake, and there were no noticeable speed gains to be had as time wore on.

At this rate, it would take nearly two hours to feed 30 engineers, assuming each desired two pancakes. Clearly, we had to consider Option 2.

I decided to eat three (3) pancakes on Sunday to establish the "fresh pancake" baseline, and refrigerate the other four (4) for consumption on Monday, to determine what they'd taste like after 24 hours, and a chill / reheat cycle. [Note: I am well aware that a better approach would have been to bake a fresh batch on the second day, so that it may be compared directly to the 24-hour-old batch instead of relying on memory, but I ran out of flour and could not purchase any more on Monday, as it was a holiday in Ontario and all the shops were closed. Even if this had worked, we'd still be comparing different batches of batter, with possible variations in the egg/milk/flour ratios that are hard to control for.]

Results and Conclusion

Day 2: Of the four (4) remaining pancakes, I reheated the first two (2) in a microwave to test the naive approach. The result, however, was disappointing: the pancakes came out warm, but too damp and mushy. They did not meet the high standards for what I'd feel comfortable serving to my coworkers.

The remaining two (2) pancakes were reheated in a very hot seasoned pan, about 15 seconds per side. I am pleased to report that although the final result is a pancake that is a bit drier than the original, it retained its taste and texture acceptably, and this method cuts the 111 seconds needed to cook a pancake down to 30 seconds to simply reheat it. It would allow us to feed 30 people in about a half hour, which points to a possible approach for the Solace pancake breakfast.

DELICIOUS

Future Work

Find how to accompany the food with good espresso; I don't have any proposals here.

2008-02-18

The Best Things in Life Are Free

I decided to go out and have some fun this weekend. I wanted to be active a bit, but didn't feel like driving all the way out to Kanata (KRP, ugh) just to get to the gym.

Earlier this week, a coworker was talking about snowshoeing, so the bug bit me - I hadn't gone snowshoeing yet this winter, and I resolved not to let the season end without going out at least once. On Saturday, the weather report showed -11C for the afternoon, so I figured it would be safe to go out on the river a bit and enjoy nature, as well as a bit of peace and quiet outdoors.

Most amazing weather all week:

I packed up my camera, some water, snowshoes (des raquettes, pour les francophones), and some warm clothing, and left home with no idea where exactly I was heading. I decided to start in Westboro and drive west on Richmond / Carling until I found an access to the river. Luckily, it didn't take long - I ended up parking in Britannia Park, and continued westward from there in the snow, on foot.

Ended up staying out until the sun started setting. I tested the ice a bit before venturing out, but it was thick, so no worries. Didn't cross paths with a whole lot of people, but the few I did meet were out hiking or skiing too, and quite friendly.

The weird thing about Ottawa is that the demographics really don't seem to be in my favour for meeting new people my age - Saturday was no exception; friendly people all around, but all in their 30s or 40s. I don't know where all the mid-twentysomethings are in this town.

2008-02-12

Reusable shopping bags are taking over the world!

(and that's a good thing)

The Globe and Mail on the worldwide phasing out of disposable plastic bags, praising the remarkable grassroots, bottom-up effort.

I went to Costco today (hence this post) to stock up on snacks for the office, and must say they deserve praise for coming around in the past year.

About a year ago, I was in a Costco warehouse, shopping with one of those tough plastic/nylon bags sold by Loblaws. A short while later, I was accosted by an employee from store security, who informed me it was prohibited to use these bags while shopping, and that I'd have to get a cart. (Ironically, Costco actually sells reusable nylon shopping bags.) None too pleased, I wrote to customer service, identifying myself as someone who often went in to pick up just a few items, and for whom using a huge cart really makes no sense. I asked if the distribution of transaction sizes might reveal more like myself, for whom it might be justified to provide an alternative to the large carts. Happily, they took me seriously and replied that the average transaction size justified offering only large carts:

Thank you for your recent inquiry through Costco.ca, in response, the
daily average transaction at Costco ranges from $130 to $150 which is
why we only have the large carts.  We do appreciate the suggestion and
it has been sent to the appropriate management team for review and,
thank you again for taking the time to send this to us.

[...]

The part about "been sent to the appropriate management team for review" sounds like typical corp-speak, but it was apparently true, for a year later...

Ahh, how nice it was today, when I decided to risk it and try again to shop with a Loblaws bag. I got to the register, declined the cashier's offer of a box, and she smiled warmly and said: "Ah! You've got a bag, even better!" Similar friendly attitude from the cart-inspection clerk at the exit.

Thanks, Costco, you got it right. Impressive.

2008-02-09

Westboro Market just died

In local Ottawa news, the Westboro Community Association's blog is reporting that Westboro Market has closed down.

(I took the picture a week before they announced the closure - I had no idea at the time)

This is a real loss for the community. Last year, Westboro Bakery, the lone bake shop in the neighbourhood, had to close its doors after seeing its rent raised to 3500$ a month. This year the still-young Westboro Market, which was the only source in the area for Art-Is-In bread, followed suit. Too bad, I really hope we don't get another sporting goods store.

2008-02-07

Signal to Noise: the tale of how the statsvn.org domain expired

It was really my fault, but I still blame Go Daddy :) Read on...

Go Daddy, probably the only domain registrar weird enough to make you think twice before opening one of their ads at work, tried to warn me by email that the statsvn.org domain was about to expire. In fact, they sent me 5 emails about it. Unfortunately, for reasons explained below, I had set a rule in Gmail to have emails from Go Daddy skip the inbox and get archived to a tag.

CropperCapture[9]

A few facts about the email I've received from them over the past year:

  • Emails received from Go Daddy between April and December 2007: 74
  • Legitimate emails warning of expiration or confirming a purchase: 3
  • Quasi-"Spam" and promotional emails: 71
  • Ratio of important emails to promotional ones: 0.0423

I think a ratio that incredibly low justifies having a rule to at least move those messages out of the way and defer reading to some later time. The statsvn.org domain expired on January 17, 2008, but kept resolving just fine for me, so I never noticed anything was wrong until I finally glanced at the 684 emails tagged "commercial" in my GMail account, sometime this afternoon. In a panic, I logged into the godaddy.com site to renew, only to discover that since my registration had expired, it had been removed from my admin panel. Crap.

I called up their customer service, and I'm happy to say this is where things started looking up: no long wait times, no Indian call centers with impossible-to-understand operators! After five minutes I spoke to José, who politely informed me Verisign had the domain in some sort of grace period pool, and I could get it back by paying an 80$ penalty, on top of the registration fee. He accepted a DIGG coupon code for 10% off too :)

Ka-ching. Renewed for two years, everything should be fixed in 72 hours. In this instance, due to the fees, it looks like the spam was profitable even if I never read a single line of it.


statsvn.org is the domain name for the statsvn project. The developer wiki is at svn.statsvn.org/statsvnwiki (DNS issues notwithstanding).


EDIT (2008-02-12): The domain and all the subdomain configuration has been restored, and the wiki is back up. (Even used it myself to copy-paste some command-line options when doing a StatSVN run yesterday.)

2008-02-05

Ant Tricks: how to find the SVN revision of a directory with Ant

Here's a neat little recipe I wrote when trying to get the current SVN revision of a checked-out project into an Ant property.

Why would you need to do this?

Well, for example, you might want Ant to get the revision number of the source going into the build it's making and use it to name the output directory it creates when deploying your load. (ie: deploy the built library to /projects/myproject/build_svnXXXX/) You might also want to echo that revision number into some resource file to implement a "--version" type of command line option in your application, etc.

Sample Ant Target

<target name="load-svn-revinfo" depends="init-stage1">
    <property name="tmpfilename" value="tmpout.txt" />
    <delete file="${tmpfilename}" failonerror="false" />
    <exec executable="svn" dir="${basedir}/../" output="${tmpfilename}">
    <arg line="info --xml --username ${svn.username} --password ${svn.password} ." />
    </exec>
    <xmlproperty file="${tmpfilename}" prefix="svnprops"/>
    <delete file="${tmpfilename}" failonerror="false" />
    <echo>REV IS: ${svnprops.info.entry(revision)}</echo>
</target>

How it Works

There are basically three steps:

  1. Run an "svn info --xml" on your project sandbox and store the result to a temp file.
  2. Load the temp file into an Ant <xmlproperty/>, with the prefix "svnprops", so we can refer to the revision later in our build script.
  3. Clean up the temp file.

Note that running "svn info --xml ." on your checkout directory will give you output like this:



C:\dev\eclipse_workspace\HEAD_solsuite>svn info --xml .
<?xml version="1.0"?>
<info>
<entry
kind="dir"
path="."
revision="8617">
<url>svn://server/svn/repo/trunk</url>
<repository>
<root>svn://server/svn/repo</root>
<uuid>6e0d6cc3-672d-0410-8be7-bcd0fe73158e</uuid>
</repository>
<wc-info>
<schedule>normal</schedule>
</wc-info>
<commit
revision="8617">
<author>jpdaigle</author>
<date>2008-02-05T21:08:30.585592Z</date>
</commit>
</entry>
</info>



 



Meaning that once that's loaded with <xmlproperty/>, we can refer to the SVN revision as ${svnprops.info.entry(revision)}. Pretty cool, huh?