Portal 2

For anyone that hasn’t noticed, Portal 2 was released a few days ago. I’ve already played through the single-player portion of the game (the only portion that I’m interested in, to be honest), and thought I would write up a review.

Long story short, my feelings toward Portal 2 are a bit mixed. Viewed as a game in its own right, Portal 2 does not disappoint. It’s really hard to find much to fault it for in this context. But viewed as a continuation of the original Portal, the game just doesn’t quite live up to the sky-high expectations that I (and probably other fans of the original game) had for it. GLaDOS is back, and just as sarcastic, humorous, and passive-aggressive as ever. At least for the first half of the game. Something seems to happen to the writing about halfway through; it loses its clever wit, that special something-or-other that left you anticipating the next catty remark from GLaDOS and laughing-out-loud when it finally arrived.

Much of the dialog towards the end of the game becomes just matter-of-fact moving-the-story-along chatter, with one AI acting loopy and another AI trying to provide a plausible explanation for why the first one is acting loopy. After a certain point, it just doesn’t feel as clever. At first I thought that it might be just because your antagonist changes, but that’s not really it. Wheatley’s early game dialog is on par with anything GLaDOS ever said, but as the game progresses his lines become less and less inspired. By the time you reach the end some of them are so bland that you pretty much have to force yourself to listen.

In the end, I think the developers erred in putting too much effort into trying to explain and justify their game world. Why should I care about history when there is a sarcastic, witty, and antagonistic AI practically goading me into dismantling it? Such was the case in Portal, but unfortunately not so in Portal 2. Instead the game distracts itself in trying to explain the origins of its world and the players within it, and the result is that some of the drive to keep pushing forward is lost.

Moving along, Portal had its fair share of fiendishly difficult levels. Levels which would leave you genuinely stumped and probably kill you once or twice before you puzzled your way out of them. Portal 2 has a couple of moderately challenging levels, but for the most part the puzzles are straightforward and easy to solve. I’m glad that for the most part the developers fixed it so that going through a portal won’t arbitrarily rotate your character’s orientation (and/or they made it easier to avoid placing portals such that they force you to rotate), but apart from that I think the game was nerfed a bit too much.

Portal put you on a conveyor belt heading towards an incinerator and left you with very little time to work out an escape plan. The situation felt very tense the first time through, and if you did not react very quickly to locate the way out then the game had no qualms about killing you for your shortcoming. Portal 2 has a similar scenario, but the way out of it is much too obvious and the element of danger just isn’t there. Rather than being compelled to react, it feels like there’s plenty of time to just stand their and listen to the AI gloat about its triumph. The sense of imminent danger just isn’t there like it was in the original game.

That said, there’s very little to complain about in terms of graphics, sound or gameplay. Any reviewer who gripes about Portal 2 (PC version, obviously) suffering from being a “port of the console version” is just being a fool. Portal 2 was implemented using the Source engine, an engine which began its life first and foremost as a PC game engine. It cannot be a “port” of anything if its implementation engine natively supports the PC as a target platform.

Yes the graphics in Portal 2 are a bit simple/dated-looking, but such has been the case with every Source-engine game released (even the very first games to use the engine). Valve has always favored attaining playable framerates on lower-end hardware over throwing in lots of hardware-crushing eye-candy, and I’m not about to fault them for their decision. If you need hardware-crushing eye-candy, play Crysis.

On top of its solid graphics and gameplay, Portal 2 gives you quite a few new puzzles to play through. In fact, there are almost certainly more puzzles in the Portal 2 single-player game than in Portal’s single-player game. But, because of the decreased level of difficulty, most of these new puzzles can be breezed through in a handful of minutes, even on your first play-through. It took me less then 7 hours to complete the single-player game, and I wasn’t rushing or trying to solve puzzles as quickly as possible.

I spent time exploring the environment and looking around, trying to knock off as many achievements as I could, and still I only needed 7 hours to complete the game. That’s a bit short, but the problem isn’t that the game itself is too short, rather it’s that the level of difficulty has been reduced too much. Once you know the solutions, Portal can be completed in far less than 7 hours. Portal 2’s real problem is that too many of the solutions are a bit too obvious.

Portal 2 does introduce a number of new gameplay elements, from the propulsion/repulsion/conversion gels, to mirrored “discouragement redirection cubes”, to hard-light bridges, excursion beams, and “aerial faith-plates”. The last of these I find a bit questionable, as their real purpose seems to be replacing some of Portal’s more challenging jumps (the ones where you have to work out some way to build sufficient momentum to bridge an impassable chasm of some sort or another) with a prepackaged solution that “just works”.

Unlike the gels, mirrors, bridges, and beams, the aerial faith-plates function less as a tool for solving the current puzzle and more like a quick way of hopping between Point A and Point B without using a portal and without having to worry about where you land. I think the game could have been better without them, but apart from that I also think that the rest of the new gameplay elements function very well within the context of the game, and I’m surprised that Valve was able to come up with so many new physics things to add to the game.

One last thing that leaves me a little confused is the decision to replace the guided missiles of Portal with generic unpropelled exploding boxes in Portal 2. It doesn’t really change the gameplay that much, but personally I found the missiles to be a lot more threatening and fun than some nondescript red boxes that just happen to explode when they hit something. Perhaps Valve decided that they didn’t want to renew their missile targeting AI license?

Anyways, though I have picked the game apart over a few minor issues, I still think that overall Portal 2 is a very fun game and worth a play-through or two. Play it and enjoy it as a game in its own right, as its biggest flaws only become apparent when you hold it to the candle that is Portal the first. It doesn’t quite satisfy as a sequel to that original magical title, but as a first-person puzzle game with a completely unique mode of gameplay there’s still nothing else quite like it.

Overall score: 8.5 / 10.0

P.S. The Portal Song is an order of magnitude better than The Portal 2 Song. So don’t get your hopes up expecting an epic musical ending like in the first game. You still get a musical ending, but it’s not quite as epic.

Posted in gaming, software | Tagged , , | Leave a comment

[Status] Site Downtime

My apologies for the recent bit of downtime. It seems like the router decided to hang while we were out on vacation, hence the long delay in rectifying the problem. By my estimates there were approximately 4 days worth of down-time as a result.

But in any case, the router is now unstuck and normal operations have resumed.

Posted in status | Leave a comment

[Objective-C + Cocoa] Runtime Performance Profiling

The iPhone SDK and XCode provide some very useful tools for application profiling, particularly with respect to tracking memory consumption and pinpointing memory leaks and other similar issues, but one thing which I have found lacking in the default toolset is the ability to track the execution time of this-or-that specific bit of code, be it a function call, a loop within a function, or some arbitrary series of API calls made in quick succession.

Typically I would deal with such cases by adding some one-off code around the section that I was interested in profiling to measure and log its execution time. But this approach quickly becomes unwieldy when there are several different places that you are interested in profiling, and particularly when it comes time to track down and remove all of these bits of code so that they stop spamming the log with timing data that is no longer needed.

In any case, here is my solution to this minor annoyance, patterned after a similar utility I implemented in ActionScript some time ago:

//PerformanceTimer.h
#import <Foundation/Foundation.h>


@interface PerformanceTimer : NSObject {
    NSMutableArray* startTimes;
    NSMutableArray* names;
}

+ (void) startTimerWithName: (NSString*) name;
+ (clock_t) stopLastTimer;
+ (clock_t) stopTimerWithName: (NSString*) name;

@end


//PerformanceTimer.m
#import "PerformanceTimer.h"

static PerformanceTimer* timerInstance = nil;

@implementation PerformanceTimer

- (id) init {
    self = [super init];
    if (self) {
        startTimes = [[NSMutableArray alloc] initWithCapacity:16];
        names = [[NSMutableArray alloc] initWithCapacity:16];
    }
    
    return self;
}

- (void) dealloc {
    [startTimes release];
    startTimes = nil;
    
    [names release];
    names = nil;
    
    [super dealloc];
}

- (void) startTimer: (NSString*) name {
    @synchronized(startTimes) {
        clock_t start = clock();
        NSNumber* startNum = [NSNumber numberWithUnsignedLong:start];
        [startTimes addObject:startNum];
        [names addObject:name];
    }
}

- (clock_t) stopTimerNamed: (NSString*) name {
    @synchronized(startTimes) {
        int index = 0;
        for (NSString* timerName in names) {
            if ([timerName isEqualToString:name]) {
                break;
            }
            index++;
        }
        if (index >= [names count]) {
            //couldn't find it
            return 0;
        }
        clock_t start = [[startTimes objectAtIndex:index] unsignedLongValue];
        clock_t end = (clock() - start) / (CLOCKS_PER_SEC / 1000.0);
        
        #ifdef DEBUG
        //if debugging, always print, otherwise let the caller decide what to do
        NSLog(@"PerformanceTimer:  Total execution time for task named '%@':  %lu ms", name, end);
        #endif
        
        [startTimes removeObjectAtIndex:index];
        [names removeObjectAtIndex:index];
        
        return end;
    }
}

- (clock_t) stopTimer {
    @synchronized(startTimes) {
        if ([names count] < 1) {
            //no timer is running
            return 0;
        }
        
        NSString* name = [names objectAtIndex:[names count] - 1];
        return [self stopTimerNamed:name];
    }
}

+ (void) startTimerWithName: (NSString*) name {
    if (! timerInstance) {
        timerInstance = [[PerformanceTimer alloc] init];
    }
    [timerInstance startTimer:name];
}

+ (clock_t) stopLastTimer {
    if (! timerInstance) {
        timerInstance = [[PerformanceTimer alloc] init];
    }
    return [timerInstance stopTimer];
}

+ (clock_t) stopTimerWithName: (NSString*) name {
    if (! timerInstance) {
        timerInstance = [[PerformanceTimer alloc] init];
    }
    return [timerInstance stopTimerNamed:name];
}

@end

This adds a static utility class that can be used to spawn multiple independent named timers. It is used like:

[PerformanceTimer startTimer:@"myTimerName"];
//do some things that you want to time
clock_t elapsedTimeMillis = [PerformanceTimer stopTimerWithName: @"myTimerName"];
//print or log the elapsed time

The default behavior of this class when run in debug mode is to print out the elapsed time automatically when ‘stopTimer’ is called, so it’s not generally necessary to capture or inspect the value returned from ‘stopTimer’ unless you are either running in release mode or if you want to do something more involved than simply logging the timing data to the console.

Note that although a ‘stopLastTimer’ method is provided for convenience, its use is generally not recommended unless you are absolutely sure that nobody else has started a timer somewhere else in the code. Otherwise you can end up inadvertently stopping the wrong timer. Inside of a small, simple, single-threaded application there is little to worry about. But for any more complex/multi-threaded projects it is much safer to always specify a timer by name.

Posted in coding, objective-c | Tagged , , | 1 Comment

[Objective-C + Cocoa] Screen Capture, Multiplexing, and More

It’s been a bit quiet on the coding-front lately, so I thought I’d share a small handful of various utilities that I’ve developed over the past few weeks. None of them are particularly revolutionary, but they can certainly prove useful in the right situations. Anyways, in no particular order, I give you:

ScreenCaptureView

This bit of code allows you to easily capture the contents of any arbitrary UIView:

//  ScreenCaptureView.h
#import <UIKit/UIKit.h>

@interface ScreenCaptureView : UIView {
}

@property(retain) UIImage* currentScreen;
@property(assign) float frameRate;

@end


//  ScreenCaptureView.m
#import "ScreenCaptureView.h"
#import <QuartzCore/QuartzCore.h>

@implementation ScreenCaptureView

@synthesize currentScreen, frameRate;

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.contentMode = UIViewContentModeRedraw;
        self.frameRate = 20.0f;//20 frames per seconds
    }
    return self;
}

- (void) drawRect:(CGRect)rect {
    CGContextRef context =  UIGraphicsGetCurrentContext();
    [self.layer renderInContext:context];
    CGImageRef cgImage = CGBitmapContextCreateImage(context);
    UIImage* background = [UIImage imageWithCGImage: cgImage];
    CGImageRelease(cgImage);
    self.currentScreen = background;
    
    [self performSelector:@selector(setNeedsDisplay) withObject:nil afterDelay:(1.0 / self.frameRate)];   //redraw at the specified framerate
}

- (void)dealloc {
    [super dealloc];
}
@end

The ‘ScreenCaptureView‘ class automatically captures its current contents (including any nested subviews) to a UIImage which it exposes through the ‘currentScreen‘ property. You can use this functionality to do things like generate screenshots of your application for use in help/tutorial (or promotional) content, and you can even pass the data as frames to an `AVCaptureSession` to create a live video of your app in action.

Update: An enhanced version of this class that includes the ability to record video is now available.

MultiplexingDelegate

For those rare cases when you want multiple delegate objects to be notified of a change/event in some object:

//  MultiplexingDelegate.h
#import <Foundation/Foundation.h>

@interface MultiplexingDelegate : NSObject {
    NSMutableArray* delegates;
}

- (void) addDelegate: (id) theDelegate;
- (void) removeDelegate: (id) theDelegate;
- (void) sendSelectorToDelegates: (SEL)selector withObject: (id)param1 andSecondObject: (id)param2
@end

//  MultiplexingDelegate.m
@implementation MultiplexingDelegate
- (id) init {
    if ((self = [super init])) {
        delegates = [[NSMutableArray alloc] initWithCapacity: 16];
    }
}

- (void) dealloc {
    [delegates release];
    [super dealloc]
}

- (void) addDelegate: (id) theDelegate {
    @synchronized(delegates) {
        if (theDelegate && ! [delegates containsObject: theDelegate]) {
            [delegates addObject: theDelegate];
        }
    }
}

- (void) removeDelegate: (id) theDelegate {
    @synchronized(delegates) {
        if (theDelegate && [delegates containsObject: theDelegate]) {
            [delegates removeObject: theDelegate];
        }
    }
}

- (void) sendSelectorToDelegates: (SEL)selector withObject: (id)param1 andSecondObject: (id)param2 {
    @synchronized(delegates) {
        for (id theDelegate in delegates) {
            if (param2) {
                [theDelegate performSelector:selector withObject:param1 withObject:param2];
            }
            else if (param1) {
                [theDelegate performSelector:selector withObject:param1];
            }
            else {
                [theDelegate performSelector:selector];
            }
        }
    }
}
@end

The ‘MultiplexingDelegate` class provides a generic starting point for creating delegates that conform to various protocols and support the multiplexing of events to any number of attached listeners. To use it you just need to create a new subclass of ‘MultiplexingDelegate‘ that implements whatever protocol you are interested in. For instance, here is a partial example of a multiplexing UISearchBarDelegate implementation:

//  MultiplexingSearchBarDelegate.h
#import <UIKit/UIKit.h>
#import "MultiplexingDelegate.h"

@interface MultiplexingSearchBarDelegate : MultiplexingDelegate<UISearchBarDelegate> {
}
@end

//  MultiplexingSearchBarDelegate.m
@implementation MultiplexingSearchBarDelegate
//add UISearchBarDelegate methods here, following a pattern like this:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
    [self sendSelectorToDelegates: @selector(searchBar:textDidChange:) withObject: searchBar andSecondObject: searchText];
}

//or alternately/equivalently, a pattern like this:
- (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    @synchronized(delegates) {
        for (id<UISearchBarDelegate> theDelegate in delegates) {
            [theDelegate searchBar:searchBar shouldChangeTextInRange: range replacementText: text];
        }
    }
}
@end

To use the ‘MultiplexingSearchBarDelegate‘ class you would create a single instance and add it as the delegate of your UISearchBar. Then you just register every other object that you want to receive UISearchBar events with the ‘MultiplexingSearchBarDelegate‘ (by using the ‘addDelegate:‘ method), and they will all receive event notifications from the UISearchBar. Simple, but powerful.

NSString+JavaAPI

There’s no getting around it, the standard NSString API just kind of sucks. Wouldn’t it be great if NSString supported an API that was more like what Java provides through its String class? Well now it can:

//  NSString+JavaAPI.h
#import <Foundation/Foundation.h>

@interface NSString (NSString_JavaAPI)
- (int) compareTo: (NSString*) comp;
- (int) compareToIgnoreCase: (NSString*) comp;
- (bool) contains: (NSString*) substring;
- (bool) endsWith: (NSString*) substring;
- (bool) startsWith: (NSString*) substring;
- (int) indexOf: (NSString*) substring;
- (int) indexOf:(NSString *)substring startingFrom: (int) index;
- (int) lastIndexOf: (NSString*) substring;
- (int) lastIndexOf:(NSString *)substring startingFrom: (int) index;
- (NSString*) substringFromIndex:(int)from toIndex: (int) to;
- (NSString*) trim;
- (NSArray*) split: (NSString*) token;
- (NSString*) replace: (NSString*) target withString: (NSString*) replacement;
- (NSArray*) split: (NSString*) token limit: (int) maxResults;
@end

//  NSString+JavaAPI.m
#import "NSString+JavaAPI.h"

@implementation NSString (NSString_JavaAPI)
- (int) compareTo: (NSString*) comp {
    NSComparisonResult result = [self compare:comp];
    if (result == NSOrderedSame) {
        return 0;
    }
    return result == NSOrderedAscending ? -1 : 1;
}

- (int) compareToIgnoreCase: (NSString*) comp {
    return [[self lowercaseString] compareTo:[comp lowercaseString]];
}

- (bool) contains: (NSString*) substring {
    NSRange range = [self rangeOfString:substring];
    return range.location != NSNotFound;
}

- (bool) endsWith: (NSString*) substring {
    NSRange range = [self rangeOfString:substring];
    return range.location == [self length] - [substring length];
}

- (bool) startsWith: (NSString*) substring {
    NSRange range = [self rangeOfString:substring];
    return range.location == 0;
}

- (int) indexOf: (NSString*) substring {
    NSRange range = [self rangeOfString:substring];
    return range.location == NSNotFound ? -1 : range.location;
}

- (int) indexOf:(NSString *)substring startingFrom: (int) index {
    NSString* test = [self substringFromIndex:index];
    return [test indexOf:substring];
}

- (int) lastIndexOf: (NSString*) substring {
    if (! [self contains:substring]) {
        return -1;
    }
    int matchIndex = 0;
    NSString* test = self;
    while ([test contains:substring]) {
        if (matchIndex > 0) {
            matchIndex += [substring length];
        }
        matchIndex += [test indexOf:substring];
        test = [test substringFromIndex: [test indexOf:substring] + [substring length]];
    }
    
    return matchIndex;
}

- (int) lastIndexOf:(NSString *)substring startingFrom: (int) index {
    NSString* test = [self substringFromIndex:index];
    return [test lastIndexOf:substring];
}

- (NSString*) substringFromIndex:(int)from toIndex: (int) to {
    NSRange range;
    range.location = from;
    range.length = to - from;
    return [self substringWithRange: range];
}

- (NSString*) trim {
    return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

- (NSArray*) split: (NSString*) token {
    return [self split:token limit:0];
}

- (NSArray*) split: (NSString*) token limit: (int) maxResults {
    NSMutableArray* result = [NSMutableArray arrayWithCapacity: 8];
    NSString* buffer = self;
    while ([buffer contains:token]) {
        if (maxResults > 0 && [result count] == maxResults - 1) {
            break;
        }
        int matchIndex = [buffer indexOf:token];
        NSString* nextPart = [buffer substringFromIndex:0 toIndex:matchIndex];
        buffer = [buffer substringFromIndex:matchIndex + [token length]];
        if (nextPart) {
            [result addObject:nextPart];
        }
    }
    if ([buffer length] > 0) {
        [result addObject:buffer];
    }
    
    return result;
}

- (NSString*) replace: (NSString*) target withString: (NSString*) replacement {
    return [self stringByReplacingOccurrencesOfString:target withString:replacement];
}
@end

Note that this isn’t the complete Java String API (and also that `split()` does not support regular-expressions as it does in Java). It is just a subset consisting of the most useful Java API functions that do not have convenient analogs in the standard NSString API. Still, it makes a lot of common string manipulations a lot more straightforward than they otherwise would be.

Posted in coding, objective-c | Tagged , , , | 16 Comments

Introducing Webcomix

I wanted to take a brief moment to invite anyone who stumbles across this post to beta-test my latest personal project; Webcomix. Webcomix is a simple web-comic aggregation service that allows you to read a number of different comics on a single page. Here is a screenshot of it in action:

Webcomix screenshot

Each comic in Webcomix is periodically checked for updates, so if like me you keep tabs on several different web-comics that all update at different intervals then Webcomix will save you from having to remember to refresh half a dozen different websites at various times just to keep up to date. Simply visit your Webcomix dashboard and you’re set!

Underneath the covers there’s a fair bit that I’m proud of with this app. First off the entire system is designed to be as lightweight and efficient (in terms of server compute-time and bandwidth) as reasonably possible. Virtually all of the actual work is handled by the client-side JavaScript. The handful of tasks that must be performed server-side are optimized with the help of a custom caching layer which ensures that any given computation (or fetch of an external resource) only needs to be performed once. There is also an additional caching layer in the client used to prevent it from making any given request to the server more than once per session.

The server itself is client-agnostic and provides a simple JSON-based API, meaning that while the initial incarnation of Webcomix is in the form of a web-app it should be just as easy to package the same functionality in a mobile, desktop, or other flavor of application. But that’s a project for another day.

Anyhow, please note that Webcomix should be considered to be beta software at the moment. So if you find any bugs or have any suggestions for new features or improved functionality then please don’t hesitate to say so in the comments section.

Also, the list of comics currently available in Webcomix is not set in stone and is only based upon my personal preferences at the moment. Webcomix is designed in a way that makes adding additional comics a trivial task, so if your favorite comic is currently not in the list then I encourage you to post a request in the comments section so that I can add it.

Lastly, if anyone has any good ideas for how to extend the comic selection UI to handle a large number of comics that they’d like to share, then please do. The current interface with its basic rows of checkboxes is obviously not sufficient for handling hundreds (or even dozens) of comics. It needs to be replaced with something better (or at least, collapsible) soon.

Posted in banter, javascript, software | Tagged , , | Leave a comment

[Hardware] Intel Atom D510 Server Build

As mentioned awhile back, the aging server box that was being used to host this blog started to develop some stability issues and I decided to replace it with an Intel Atom based machine. After nearly a month spent waiting for some back-ordered parts to arrive the build is finally complete. So how does it compare to the system it replaced? Well first off, let’s talk specs. The Atom D510 build used the following components:

Here’s a shot of everything unboxed and ready to be assembled:

Atom D510 Build Components

And here’s one of the build with everything installed and ready to be shut away inside of the case:

Completed Build

All told the total cost of this build was just slightly under AU$400, including GST and shipping fees. Nearly half of this was allocated to the Corsair SSD, so a comparable build may be attainable for closer to $300 if you’re willing to settle for a traditional HDD. As this system was intended to do duty as a server box, however, I felt that the performance benefit provided by the SSD was more than worth the extra cost.

Anyways, the system being replaced by the Atom build is a retail Hewlett-Packard box that was purchased way back in 2001. It has the following specs:

  • 1.3 GHz Intel Pentium 4 (no HyperThreading)
  • 256 MB PC-800 (400MHz) RDRAM
  • 40 GB Western Digital HDD (IDE)
  • Stock motherboard, case, audio, etc.

This system is all but worthless today, but back in 2001 it retailed for close to $1100 (with bundled monitor and printer). In its defense this old P4 box delivered a solid decade’s worth of performance and proved itself to be entirely capable of running a number of different servers, albeit under very light workloads. But all things must come to an end, and it’s time for this dinosaur to be replaced.

So how does the Atom processor stack up against this 10-year-old beast? Here’s a screenshot from Super PI comparing the two (the Atom is on the left, and the P4 is on the right):

SuperPI:  Atom D510 (left) vs. 1.3 GHz P4 (right)

Now it’s worth noting that this is not and cannot be an apples-to-apples comparison between the two CPU’s, as all the other variables (RAM capacity and speed, disk architecture, etc.) have been changed as well. But I think Super PI probably does a fairly good job of isolating the CPU, and at the 1M setting the task should be small enough that the difference in RAM capacity between the two systems doesn’t come into play.

Assuming that to be the case, the Atom D510 makes a fairly strong showing for itself. It comes in at roughly 3 times faster than the 1.3 GHz P4, and since the Atom D510 is a dual-core CPU one should expect it to be up to 6 times faster under a well-threaded (i.e. server) workload. That’s quite an improvement over the old system, although it’s worth noting that my desktop system (an Intel Core 2 Quad based machine clocked at 3.5 GHz) can breeze through the same benchmark in just under 15 seconds. So by modern standards the Atom CPU is quite slow.

But sheer performance is only part of the story. The other reason why I opted for the Atom based server is the Atom platform’s low power consumption. If I wanted the fastest server possible I could simply use my desktop for the task, and leave it running 24/7. And then I would cringe every time my utility bill was due. With the Atom my goal was to attain reasonable performance using as little power as possible, and after looking at the numbers I can only conclude that it delivers as advertised.

Using a Kill-A-Watt device I measured the power consumption of each system at the wall, and worked out that the 10-year-old P4 system draws 90 watts of power while sitting idle, and around 110 watts under full CPU load. The Atom system, on the other hand, weighs in at 17 watts while idle, and a massive 21 watts under full CPU load.

So 6 times the performance, for less than 1/5th the power consumption (and before I forget, the Atom system is virtually silent as well). With those numbers the Atom system starts to look like 100% win. I can run one box and get much better performance than the old system could deliver at a fraction of the energy consumption, or I could use the same amount of power to run a cluster of 5 Atom servers for 30 times the performance.

But in any case, that’s my Atom server build, and overall I’m quite pleased with the results. The Atom D510 provides a nice bump in speed over the (admittedly decrepit) server box that it replaced, while also using much less power. If you need a cheap, basic, quiet server box for low-volume workloads then an Atom-based solution is an option worth considering.

Posted in configuration, hardware | Tagged , | 3 Comments

[JavaScript + CSS] Fun with Google Maps

Here are a couple of quick Google Maps hacks that I came up with quite awhile ago; several years ago, in fact. While not hugely useful or profound, they do expose some fairly large holes in the Google Maps API. Holes that I would have expected a company like Google to have patched up by now.

First, let’s say that for whatever reason you need to remove the “Powered by Google” logo and the other copyright and similar boilerplate that normally appears along the bottom of the map. Why would you want to do this? Perhaps because you are displaying your map inside of a small area of the page where space is constrained to the point that the logo and other things are adding too much noise. Or perhaps because you feel (quite justifiably) that Google Maps have become iconic to the point that nobody needs to see a “Powered by Google” logo in the corner to know that the map is being powered by Google.

Whatever your reason for wanting to unclutter the map’s footer section, the task can be accomplished using a smidgen of CSS:

		.terms-of-use-link {
		    display: none;
		}

		#myMapContainerId div span {
			display: none;
		}

…and a handful of JavaScript:

			//call this after setting up your GMap2 instance
			window.unclutter = function() {
				var executed = false;
				var images = document.getElementsByTagName('img');
				for (var index = 0; index < images.length; index++) {
					var node = images[index];
					if (node.src && node.src.indexOf("mapfiles/poweredby.png") > 0) {
						node.style.display = 'none';
						executed = true;
					}
				}

				if (! executed) {
					setTimeout(window.unclutter, 250);
				}
			};

It’s pretty straightforward, really. The CSS gets rid of the “Terms of Use” link, as well as the spans that are used to display copyright text (note that you should replace the ‘myMapContainerId‘ with whatever your actual map div-id is). The JavaScript is used to get rid of the “Powered by Google” logo, which is added to the map after it loads. To use this code, you just need to call ‘unclutter()‘ after setting up your Google Map instance.

Of course, doing this is a blatant violation of the Google Maps Terms of Service. You know, the terms Google made you agree to when you generated your API key. Whether or not there is any merit in having Terms of Service mandate things which do nothing whatsoever to benefit end users such as the display of logos and copyright gibberish is a topic for another day. The topic for today is, if Google Maps can be used without an API key, and hence without agreeing to the Terms of Service, what does it matter what the terms are anyways?

That’s right, the Google Maps API can be used, at least for basic tasks, completely without an API key. It’s quite simple really, just a tiny bit more JavaScript:

			//be sure to do this *before* you source in the Google Maps javascript
			window._alert = window.alert;
			window.alert = function() {return;};

…and then you can include the Google Maps JavaScript without specifying an API key, like so:

<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=true" type="text/javascript"></script>

The added JavaScript simply moves the browser’s built-in ‘alert()‘ function to ‘_alert()‘ so that the map can’t use it to display a message complaining about the missing API key. For whatever reason, apart from this alert message Google does nothing to prevent their API from being used without a valid key. So by simply suppressing the error message you can get a fully functional Google map without registering for a key or agreeing to the Terms of Service.

Put all of this together and you can get an anonymous, unbranded, uncluttered map the works just as well as the original version. The source code (verified in current versions of IE, Firefox, and Chrome) might look something like this:

<html>
	<head>
		<title>Google Maps API Example Hack</title>
		<style>
			.terms-of-use-link {
		    		display: none;
			}

			#myMapContainerId div span {
			     display: none;
			}
		</style>
		<script type="text/javascript">
			window._alert = window.alert;
			window.alert = function() {return;};

			window.setupMap = function() {
				window.map = new GMap2(document.getElementById("myMapContainerId"));
				map.setCenter(new GLatLng(37.4419, -122.1419), 13);
				map.addControl(new GLargeMapControl3D());
				map.addControl(new GMapTypeControl());
			};

			window.unclutter = function() {
				var executed = false;
				var images = document.getElementsByTagName('img');
				for (var index = 0; index < images.length; index++) {
					var node = images[index];
					if (node.src && node.src.indexOf("mapfiles/poweredby.png") > 0) {
						node.style.display = 'none';
						executed = true;
					}
				}

				if (! executed) {
					setTimeout(window.unclutter, 250);
				}
			};

			window.onload = function() {setupMap(); unclutter();};
		</script>
		<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=true&amp;" type="text/javascript"></script>
	</head>
	<body>
		Note the lack of branding/logos, terms of use links, and copyright footer text in the map below.<br />
		Also note the lack of any Google Maps API key in the document source.<br />
		And the lack of any annoying <a href="#" onclick="_alert('I can still create alerts, but the Google Map can\'t.'); return false;">alert</a> popups complaining about the API key.<br /><br />
		<div id="myMapContainerId" style="width: 600px; height: 400px;"></div>
	</body>
</html>

…which gives you a page like this one. It’s enough to make one wonder why anyone would bother signing up for a Google Maps API key the proper way, when the entire thing can be subverted so easily.

Posted in coding, javascript | Tagged , , | 2 Comments

[Status] Upgrade Complete

Finally all the parts arrived, and the build and migration process was completed without incident. This site is now hosted on a shiny new energy-efficient Atom D510 based server box. More details on the hardware and the build and configuration process later.

Posted in status | Leave a comment

[Cocoa + iPhone] Avoiding ‘_handleMemoryWarning:’ Crashes

Spend enough time developing iPhone applications (particularly if you go to the trouble of collecting crash logs from your apps), and eventually you are bound to come across something like the following:

2011-03-04 14:17:48.872 myApp[15129:307] -[UIImageView _handleMemoryWarning:]: unrecognized selector sent to instance 0x2b2060
[Switching to process 11523 thread 0x0]
2011-03-04 14:17:57.034 myApp[15129:307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIImageView _handleMemoryWarning:]: unrecognized selector sent to instance 0x2b2060'
*** Call stack at first throw:
(
	0   CoreFoundation                      0x3759dc7b __exceptionPreprocess + 114
	1   libobjc.A.dylib                     0x32d9bee8 objc_exception_throw + 40
	2   CoreFoundation                      0x3759f3e3 -[NSObject(NSObject) doesNotRecognizeSelector:] + 98
	3   CoreFoundation                      0x37544467 ___forwarding___ + 506
	4   CoreFoundation                      0x37544220 _CF_forwarding_prep_0 + 48
	5   Foundation                          0x351663b5 _nsnote_callback + 156
	6   CoreFoundation                      0x37520971 __CFXNotificationPost_old + 396
	7   CoreFoundation                      0x37520611 _CFXNotificationPostNotification + 128
	8   Foundation                          0x351556a3 -[NSNotificationCenter postNotificationName:object:userInfo:] + 70
	9   Foundation                          0x3515edbd -[NSNotificationCenter postNotificationName:object:] + 20
	10  UIKit                               0x35a32354 -[UIApplication _performMemoryWarning] + 68
	11  UIKit                               0x35a33450 -[UIApplication _receivedMemoryNotification] + 184
	12  UIKit                               0x35a2ef24 _memoryStatusChanged + 64
	13  CoreFoundation                      0x37552333 __CFNotificationCenterDarwinCallBack + 26
	14  CoreFoundation                      0x37537e37 __CFMachPortPerform + 218
	15  CoreFoundation                      0x3752f5cb __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 28
	16  CoreFoundation                      0x3752f589 __CFRunLoopDoSource1 + 164
	17  CoreFoundation                      0x37521835 __CFRunLoopRun + 580
	18  CoreFoundation                      0x3752150b CFRunLoopRunSpecific + 226
	19  CoreFoundation                      0x37521419 CFRunLoopRunInMode + 60
	20  GraphicsServices                    0x33e76d24 GSEventRunModal + 196
	21  UIKit                               0x3591d57c -[UIApplication _run] + 588
	22  UIKit                               0x3591a558 UIApplicationMain + 972
	23  myApp                             0x00002649 main + 96
	24  myApp                             0x000025b0 start + 52
)
terminate called after throwing an instance of 'NSException'

This error occurs most frequently when suspending an app to the background, but it can also happen randomly at other times as well. Of course, the problem here should be obvious; the crash happens entirely outside of my application code. After the call to ‘main()‘ the stack-trace that leads to the crash lies entirely within framework code. The problem appears to be that some CoreFoundation class is attempting to invoke a ‘_handleMemoryWarning:‘ selector on objects that do not support it.

So how to prevent a crash that happens completely outside of your application code? You might say “use less memory”, but that is not a reliable answer here. The ‘_performMemoryWarning‘ selector may still be invoked no matter how streamlined your memory usage is. The answer, or at least the only answer I’ve found that works reliably, is to use a custom UIApplication subclass and excise the functionality that causes this error, like so:

//
//  SafeUIApplication.h
//  myApp
//
#import <Foundation/Foundation.h>

@interface SafeUIApplication : UIApplication {
}

@end


//
//  SafeUIApplication.m
//  myApp
//
#import "SafeUIApplication.h"

@implementation SafeUIApplication

//these selectors cause crashes in random Foundation classes when the default implementation is called, so instead of crashing our app 
//we ignore these messages, hold on to the memory that has already been allocated to us, and let some other less-clever app crash instead
- (void) _performMemoryWarning {
    NSLog(@"Received a '_performMemoryWarning' call, consider using less memory...");
    return;
}
- (void) _receivedMemoryNotification {
    NSLog(@"Received a '_receivedMemoryNotification' call, consider using less memory...");
    return;
}

@end

Now before the purists and the pedants go jumping down my throat: Yes, this is a hack. Yes, it disables built-in memory-management functionality. Yes, it can mask legitimate problems if your app does in fact have a memory leak (or if it just makes inefficient use of memory). But having built-in memory-management functionality that crashes the app when called is completely unacceptable in my book, and so disabling it is fair game. Further, even with this bit of code disabled, the application will still continue to receive low memory warnings (‘didReceiveMemoryWarning‘ calls) as normal. Hanging on to memory that is already allocated to the app cannot cause the app to crash or behave incorrectly, it just means that less memory is available for other apps that are also running. That seems fair enough to me, at least so long as the default implementation of these methods causes apps to crash.

Anyways, once you’ve added the SafeUIApplication class to your project, using it is as simple as modifying your application’s ‘main.m’ file like so:

//
//  main.m
//  myApp
//
#import <UIKit/UIKit.h>

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, @"SafeUIApplication", @"MyAppDelegateClassName");
    [pool release];
    return retVal;
}

Just specify the SafeUIApplication classname and your UIApplicationDelegate classname, and you no longer need to worry about obscure ‘_handleMemoryWarning:‘ crashes. As noted above, be aware that this will do little to nothing to help applications that have legitimate memory usage issues. But if you are confident that your application’s memory usage is correct and you are still experiencing crashes like the one shown above then this little hack will go a very long way towards keeping your application running and stable.

Posted in coding, objective-c | Tagged , , | 1 Comment

Reunion

And now for something completely different; some light fiction:

Slowly and with much deliberation he approached, taking care to avoid her eyes and the fierce and beautiful and terrifying intensity that he knew must still burn there even after so many years. She possessed that kind of penetrating gaze which can pierce right through you and with a glance expose you to the very core, and if he was sure of anything it was that time had done nothing to bolster his ability to withstand such a thing, that it must be avoided at all costs.

But also he remembered, with more than a touch of fondness, those long-lost gossamer days where she would dare him to meet her eyes with his and wait patiently as he tried, awkwardly and unsuccessfully, to rise to the task and accept that which was being freely offered. With such a staggering clarity did he remember those days of long ago that it was as if he could reach out and touch them if he so dared. Always would he try to muster the courage and the confidence to hold her gaze, to shatter the sudden paralysis which seemed to take hold of him whenever he felt the presence of her eyes upon him, and to do whatever it was that she expected him to do (though he was never sure exactly what that might be). And always would he fail. And always would he long for his next opportunity to try and to fail.

She was close now, and had undoubtedly noticed his approach.

“Hello”, he said meekly, giving an awkward kind of half-wave as he did so.

Instantly her gaze was on him, and involuntarily he found himself bracing as if in anticipation of a mighty blow, a force of old habits long dormant but not forgotten. But this time there was no challenge, no request, no invitation framed within her bright eyes. If anything she seemed uncomfortable and awkward, mildly unsettled by his presence. And he noted this with a quiet disappointment, being careful to keep his outward demeanor unchanged. What else could he have expected, he mused inwardly, after all that had passed between then and now. What else could there be, after his failures of inaction, his failures of ineffectual action, and his failures of inappropriate action? He held her gaze, and this time found no terror there, just quiet sorrow and a dreadful still emptiness where once something vital and important had lived.

“Hi, long time no see”, she replied with an air of cordiality, stopping just short of friendliness.

And, he reflected, now at last I see what it was that so scared me all those years ago, and understand why that fear will never return to my life. In her eyes, in that beautiful and terrible gaze of hers, had lived the future. A future that could be his had he dared to just reach out and touch it. But also a future that branched out upon itself like some crazed fractal, expanding outwards through the realm of possibility and past the foreseeable horizon. A future so immeasurably vast and so full of possibility that he could not countenance the idea of grasping for it, of trying to contain it all in his feeble palm.

He had not understood that it’s not necessary or beneficial to hold the entire thing in your hand at once, that it’s entirely sufficient to grasp just a single thread and be willing to follow it to wherever it leads. The unknown had scared him then, the idea of not being able to know or to completely control what the future might hold. And he had worked hard to break himself of this childish fear, and he had done so, and in the process learned that only in the unknown can one chance upon the majestic or the sublime. Only by exploring the intricacies of the pattern firsthand does one come to understand it and gain access to those unique shared experiences that are so priceless and rare.

But that pattern from his past, that future branching out into infinite possibility that he remembered with such unshakeable clarity, was dead now. Both of them had moved on and grown up, separated at most times by thousands of miles. They had their own lives, their own places that they were accustomed to, their own independent notions of home. Never again would he or could he see the same mystifying and terrifying realm of possibilities within her eyes, or be tested with a challenge or an open invitation to come plumb its depths. All that existed there now was a simple pattern; mundane conversation, awkwardness begetting awkwardness, a few trivial branches here and there, each punctuated by a scheduled departure a few days hence. He had missed his chance, would have no more opportunity to try again, to fail again.

Too late it was when he had finally recognized himself as the explorer that he was. Too late to go back as he longed to do, and explore that myriad future the mere glance of which had so paralyzed him in his youth. That door was closed to him forever now. So he took solace in the same way that anyone who has ever touched upon something so precious and rare that it occurs once in a lifetime and then allowed it through sheer carelessness to slip through their fingers does.

“What did I know back then,” he asked himself quietly, as he slowly and somewhat dazedly wandered past the greeting area and on to the reception area where more acquaintances from years past remained.

The night passed uneventfully, and not another word passed between the two of them. A few days later, he returned home.

Posted in banter, fiction, writing | Tagged | Leave a comment