A Vesper Performance Enhancement

Vesper 2.003 came out earlier this week — and it includes a syncing performance enhancement which I thought I’d write up.

Performance enhancements aren’t always as straightforward as the one I’m about to describe. Often they require the hard work of revising the data model, adding caching, or doing your drawing the old-fashioned way (as opposed to just setting a property on a layer, for instance).

This one happens to be easy to write about, so I will.

But first I’ll say that Vesper is already fast and gets plenty of praise for its performance. I’m a speed freak with zero patience — except for the considerable patience required to make sure my software works for people like me.

So this performance enhancement isn’t something that any current users are likely to notice, but it will become important in the future as people create more and more notes.

* * *

Here’s what we noticed: the initial sync on a new device, with a large number of notes (more than almost anybody has; more than I have), seemed unexpectedly slow.

My first thought was that the server was having trouble handling this. It wasn’t — it was returning all the data quite quickly with no complaint. And the amount of data was around the same as a typical image file. A lot, sure, but not an insane amount for a first sync.

So I ruled out the server, networking, and JSON translation as issues. Next I did some poor-man’s profiling — I hit the pause button in the debugger a few times as the app was syncing.

And the same function always appeared: getUniqueID. It’s a little C function that calls SecRandomCopyBytes to generate a random unique ID for a note (VSNote object).

The answer was clear: that function either needs to get faster, or we need to not call it so often. Or both.

Not Call It So Often

The syncing system creates a VSNote object for each JSON note pulled from the server that does not exist locally. On first sync, that’s every single note.

The problem: VSNote’s init method generates a unique ID by calling getUniqueID. This is superfluous in the case of notes coming from the server — those notes already have a unique ID.

So I did the obvious thing: I created an -initWithUniqueID: method that allows the creator to specify a unique ID, which means I could avoid all those calls to getUniqueID.

Awesome. Problem solved. Done.

I could have stopped there, but I didn’t.

Make the Function Faster

It still bothered me that that function was so slow. It didn’t really matter, at this point. But why would SecRandomCopyBytes be so slow? Something like that could be a little slower than some other system APIs, but still the numbers I was getting seemed weirdly super slow.

So I did a straightforward timing test, and SecRandomCopyBytes itself is plenty fast enough. What gives?

I thought it might be the collision check. There’s an NSMutableSet of all note unique IDs, and we check the returned value of getUniqueID to make sure it’s not in that set. Profiling told me that that’s not the slowdown. (As expected, since the collision check happens in getUniqueID’s caller, not in the function itself.)

What I found was that it was the limits on unique IDs that were causing the problem.

The limits are this: it has to be a positive 53-bit integer (instead of 64-bit), and the integer has to be greater than the constant VSTutorialNoteMaxID. (Which is 100.)

The body of getUniqueID is actually a loop. It calls SecRandomCopyBytes repeatedly until it gets a uniqueID that fits within the limits.

I had thought, naively, that it would typically take one to three calls to get a suitable uniqueID — but I was wrong. Ten passes through the loop wasn’t that unusual, and it could be more.

The solution here was pretty simple: if the number is outside the range, use some arithmetic to get it inside the range.

If it’s negative, subtract it from zero to make it positive.

If it’s greater than the 53-bit limit, divide in half. (In a loop until it fits within the limit.)

If it’s less than VSTutorialNoteMaxID, add VSTutorialNoteMaxID.

This made it so that getting a unique ID that fits within the limits takes exactly one call to SecRandomCopyBytes instead of potentially many calls.

There is still the possibility of collision with an existing ID, but that would be so rare (most likely never), and the consequences are just a second call to SecRandomCopyBytes, so I didn’t worry about that.

But, again — most performance issues I run into don’t have nice straightforward solutions like this one. When they do, I don’t mind. It used to be that I’d beat myself up for not doing this better the first time, but these days I don’t. I’m just glad that I learned something and made the software better.

Tuples All the Way Down

David Owens suggests using named tuples instead of structs in some cases.

(“It’s tuples all the way down” is a new joke to iOS and Mac programmers. I know you’re groaning, but let us enjoy it and feel clever for a few minutes.)

Matt On Swift and iOS 8 Evolution

Old pal Matt Neuburg is interviewed on MacVoices.

There’s a video version and an audio version — I’m playing the audio version in the background right now.

Matt’s a dynamo. He should be on podcasts more often.

Sponsorships Available

We’re still running a summer sale for inessential.com sponsorships — $500 for a week ($400 for each of two or more weeks), which is about one-third off the normal price.

If you’d like to support this blog and talk about your thing (app, conference, service) to inessential.com readers — who are, to a person, successful, intelligent, and curious — then get in touch with me.

The week starting Sept. 1 (this Monday) is available — as are later weeks. See the Sponsorship page for more info.

Web Services and Dependencies

Tim Schmitz asked me on Twitter:

Curious on your take on dev services re: social network post. Should devs avoid svcs like Azure because an app may outlast it?

(He’s referring to my post from yesterday.)

You can’t escape dependencies — even if you’re running Linux, Apache, MySQL, and PHP on a virtual machine — and so you need to evaluate everything.

Some questions to ask:

How long will this service be around? How difficult would it be to move? How much of this service’s unique features do I use? How much benefit do I get from those?

This extends to software, too. What is a given package’s reputation for security? Is it likely to be maintained in the future? Will upgrading to get a security fix also mean revising some of my code?

You have to plan for scale. Will this service and and those software packages allow room for growth? (Sharding, running multiple instances, etc.)

And you have to balance developer time. The point is to do less housekeeping and more bug fixes and features.

With Vesper we chose Azure Mobile Services on the grounds that it’s likely to be around a very long time and it’s based on Node (which is well-supported and runs in many places). The folks there were extremely helpful as we were making our decision, and that helped us decide. (You want to go where you’re wanted, for one thing.)

That said, we still have contingency plans, because anything could happen. There are no cases where you wouldn’t want to plan to be able to move. (In fact, we have two: one for moving from Mobile Services to another Node provider and one for moving to another service running Sinatra, in case we have reason to get off Node.)

Our contingency plans aren’t specified to the smallest detail, but that wouldn’t be more than a day of work, which is acceptable. (One reason not to get too detailed: the options will look different in six months, one year, three years, etc.)

I have no expectation that we’ll ever need to move. Azure is a big bet for Microsoft (the new CEO comes from the Azure group). We’ve found that the system performs wonderfully (we get praise for efficient syncing) and there’s a ton of room for growth — we’ve barely scratched the surface so far. (We run with just one instance, and that’s well more than enough.) We’re entirely happy with our choice.

That’s what works for us. But every app and every developer is unique, and there’s no way out of evaluating all the dependencies and making the best decision. I don’t think there are easy answers — it takes diligent research and thinking.

Developer Blogs

Before there was Twitter, I used to say that if you want to be a developer, you need a blog.

I still say that.

Would you trade any of these blogs for their Twitter, Facebook, or Instagram accounts? I wouldn’t.

Gus Mueller
John Gruber
Dave Wiskus
Daniel Jalkut
Justin Williams
Manton Reece
Marco Arment
Mattt Thompson
Ole Begemann
David Owens
Kyle Sluder
David Smith
Craig Hockenberry
Dave Winer
Guy English
Michael Tsai
Nick Bradbury
Graham Lee
Keith Harrison
Waffle
Mark Bernstein
Matt Drance
Mike Abdullah
Brett Terpstra
Wil Shipley
Casey Liss

My most sincere apologies to everybody who’s not on the above list who should be. This was made via a quick glance through my RSS reader, and I just stopped when the list felt big.

Notice that the domains are all custom. Some of these may be hosted on Tumblr or WordPress.com (I didn’t check and don’t know), but since they have their own domain name they can move their blogs.

Stingray

It’s not just military equipment for the police that’s bothersome. It’s also surveillance equipment that the Tacoma police (for one) uses:

Known as Stingray, the device — small enough to be carried in a car — tricks cellphones into thinking it’s a cell tower and draws in their information.

According to the article, the Tacoma police has had this capability since 2008. It’s not a new thing.

Apparently the police have to be the army and the NSA.

A Rant About Stack Traces

Rusty Rants:

Yes I know, ha ha Null Pointer, Java, LOL. But that’s an exact line number friends. What did the user do? They tapped the subscribe button. Which page where they on? The Podcast Dialog. Zero ambiguity. Guess how many of our Android crashes we get that for? 100%. In iOS we’d be lucky if even 30% of our crashes had stack traces we can line up to actual things we can then reproduce.

Via Michael Tsai. (I’ve said before and I’ll keep saying that you should subscribe to Michael’s feed. If I had to cut my subscriptions down to two, I’d go with Michael’s blog and Daring Fireball.)

Justin on Auto Layout Debugging

Justin Williams shows how to use layoutDebuggingIdentifier to help when debugging auto layout in iOS 8.

This sounds very useful. It’s private API, so you can use it for debugging only, but that’s totally fine.

I might have taken an alternate approach with the code, though. Instead of NSSelectorFromString, the clang pragmas, and performSelector:​withObject:, I would have done this:

#if DEBUG
  NSString *identifier = @"Email Label";
  [self.emailLabel setValue:identifier forKey:​@"layoutDebuggingIdentifier"];
#endif

Not tested, but ought to work.

Waffle on Social Media

Community Services:

The reason I don’t like social media is that it takes two things that are polar opposites and duct tapes them together. Your own utility – to save links, to write text, to move files or materials, to keep notes, to communicate with yourself in the future, to communicate with some other specific people – and the social media outlet’s desire to fulfill its own objectives first.

I’ve heard blogs classified as a type of social media. Maybe that’s true, and maybe not — I don’t care.

What I do care about is that my blog isn’t part of a system where its usefulness is just a hook to get me to use it. It works the way I want to, and the company running the servers (DreamHost) doesn’t care one fig what I do.

My blog’s older than Twitter and Facebook, and it will outlive them. It has seen Flickr explode and then fade. It’s seen Google Wave and Google Reader come and go, and it’ll still be here as Google Plus fades. When Medium and Tumblr are gone, my blog will be here.

The things that will last on the internet are not owned. Plain old websites, blogs, RSS, irc, email.

Facebook Alternative for Schools

A parent I know complained to their school that using Facebook for organizing and communicating was a bad idea, since Facebook’s mission is to know everything about everybody everywhere, and she doesn’t really want to help them reach that goal.

The school agreed and switched to Edmodo.

Now, I don’t know if Edmodo is good or bad, but I do very much like the idea of services like this that can replace Facebook for specific uses. They can be better for those specific uses.

Faux Pas for Xcode

Faux Pas is a linter/checker thing for Xcode projects. I haven’t downloaded it yet, but I will. Looks cool.

PS I’m back from vacation (beach, island, undisclosed location). It was lovely.

Ian on the Responder Chain

Ian McCullough, Responder Chain Redux!:

Guy indicated that he feels like the decoupling offered by the responder chain is too great — that sending an arbitrary message up the chain, with only the sender for context, is insufficient to convey user intent.…

The delegate-based approach also “cuts off” the chain early, and the argument seems to be that it cuts it off at the “right” level: where context first appears. But what if there’s more than one “right” level, or more than one scope of context?

I’ve always taken it as a clue that action methods take (id)sender as parameter — which I take to mean: “Here’s an action name and a thing. Figure out what to do.”

This is probably more true on Macs, since sender might be a button, menu item, whatever, and the action method may have to do some digging to figure out intent.

But if it’s deterministic and not a guess, is that wrong? I’m not sure.

Update 10:00 pm: Guy responds on his blog:

When the table view cell was asked to perform an action it could simply pass it up the responder chain with itself as the sender. With the simple convention of an -(id)representedObject method (or Protocol if you want to be fancy) we can at least glean from the sender which item to act upon.

More Indie Numbers

Allen Ding posted revenue numbers for his app Saved.

Getting Started with Sinatra for Cocoa Programmers

Sinatra is the little brother to Ruby on Rails.

You’d think that a Cocoa guy like me — someone who’s quite happy working with a large application framework — would prefer Rails, but I find myself attracted to the smaller and lighter-weight Sinatra.

Sinatra and Node are very similar. But as awesome as Node is, it has one giant drawback: you have to write in JavaScript.

That’s also Node’s advantage. JavaScript is easy to learn and lots of people already know it. But while JavaScript, well, exists, Ruby is lovely.

Ruby has a whole lot in common with Objective-C. Both languages count Smalltalk as an ancestor: both are object-oriented and both use dynamic dispatch. I think you’d like it.

The rest of this post will get you up-and-running with Sinatra. Quickly. In like a minute.

(Yes, I know that many readers of this blog know Ruby and web services far better than I do. This is for the ones that don’t.)

Get Sinatra

In Terminal: sudo gem install sinatra

You already have Ruby, since it comes with OS X. gem is Ruby’s package manager. (Think CocoaPods, or think npm if you’re a Node developer.)

You’ll see some messages in Terminal as it downloads and installs Sinatra and its dependencies.

This tutorial will convert some Markdown text to HTML. There’s a gem for that too:

sudo gem install rdiscount

(Markdown? Discount? Got it.)

Create the Server

Create a folder on your desktop called CoolWebSite.

Inside that folder create CoolFile.markdown. Its contents should be simple:

# It Worked!
This is a cool web page served by Sinatra

Then, also inside that folder, create CoolApp.rb.

Its contents are your actual Sinatra-based server.

The top two lines are the equivalent of import statements:

require 'sinatra'
require 'rdiscount'

Then we have Sinatra’s router, where you match http methods and paths to code. This example has just one route which matches a GET request to /.

The code matching that route does the following:

  1. Read the contents of CoolFile.markdown.

  2. Turn the Markdown text into HTML.

  3. Add the HTML bits to the start and end of the HTML.

get "/" do
  file = File.open("CoolFile.markdown", 'r')
  file_text = file.read
  file.close
  markdown_text = RDiscount.new​(file_text).to_html
  page_text = "<html><head><title>​It Worked!</title><body>#{markdown_text}​</body></html>"
end

You can see, I hope, that at this point you’re not that far from a blogging engine that reads Markdown files on disk and returns HTML.

Run the Server

In Terminal, navigate to your CoolWebSite folder. Type the following:

ruby CoolApp.rb

You’ll see that WEBrick starts up, and you’ll see a message like this:

== Sinatra/1.4.5 has taken the stage on 4567 for development with backup from WEBrick

Now, in your browser, go to http://localhost:4567. You should see the It Worked! page, in glorious HTML. (You can view the page source to confirm.)

That’s it. Now you’re a web developer — and, what’s more, you have an easy-to-learn and lightweight framework plus a language that should feel very familiar. (No square brackets, but I’m confident you can get by without them.)

Swift Literal Convertibles

Mattt Thompson, in NSHipster, brings up a potentially controverial issue.

This seems like another giant area for abuse and misuse. It worries me less than custom and overloaded operators, since it feels like something people would do less often.

And I can actually see myself potentially using the feature myself. Maybe.

But my criteria still needs to be: can somebody else read and understand my code?

Michael on Swift and Dynamic Dispatch

Michael Tsai, “It’s a Coup”:

The costs for not using message passing, on the other hand, can be high because they make the code more rigid. You cannot retroactively make compiled code more dynamic. And yet, since dynamic is not the default, the odds are that a lot more methods will be static than need to be. Most of the time, objc_msgSend is not why your code is slow, yet Swift acts like it needs to protect you from this.

I’m no fan of swizzling and don’t care what happens to it. I throw swizzling in the same bucket as SIMBL and APE and haxies, and I’m glad they’re not screwing up my apps these days. (Newer Cocoa developers have no idea what I’m talking about, I realize.)

But I do care about KVO. Very much. (Warts and all.)

This die-hard speed freak has never been concerned with the speed of objc_msgSend. I’ve noticed it in Shark and in Instruments, but the real performance issues were elsewhere in my code.

That said, it hasn’t escaped my notice that most of the time static dispatch would be fine. And most of my classes could be marked as final. So I’m not actually outraged or anything — I keep an open mind that I would actually get noticeably better performance from Swift.

Especially given that now we do have the dynamic modifier.

Objective-C is supple by default. Amazingly so. But I shouldn’t pretend I use that suppleness more than I do. And if Swift isn’t so supple — well, that’s probably closer to what we actually need than what we think we need.

Still, though, there is code in Vesper that can’t be ported to pure Swift. (Model code.) The point that you can use something like Core Data but couldn’t write it in Swift remains important. But, then, Swift is young, not even 1.0, and part of the deal is that it’s up to us developers to communicate our needs.

Server-side Config Files

Brian Schrader, Changing App Behavior Server-Side:

…it seems to me that it should be entirely possible to make iOS apps that are mostly configurable from the server-side. This got me thinking. Why not build the app to download its configuration file (i.e. plist, or Localization.strings file) remotely?

With Vesper we use DB5 for configuring things like fonts, colors, and sizes. What’s stopping us from hosting a DB5.plist on a server, and changing the app on-the-fly?

Nothing. In fact, this is how TapLynx works. (TapLynx was two-projects-ago for me; I worked on it before Glassboard.)

TapLynx is DB5’s dad. The product itself was an Xcode project and static library. You’d configure your app using a plist (they were publication-style apps, made of RSS feeds). You’d set colors, graphics, titles, feed URLs, app structure, etc. — all without writing any code.

The configuration file would ship with the app, but the file could optionally include the URL of a configuration file online which the app would download and then use. That file could be changed at will.

The configuration file could also include URLs to graphics, which the app would also download and use. (Think of graphics for tab bar items. That kind of thing.)

We haven’t done anything like that with Vesper because we don’t anticipate needing it. All it takes a server that can host static files (S3, for instance), but it’s still more moving parts, and I’d rather limit the number of things that can go wrong.

And the kinds of bugs this system can fix are limited. It doesn’t change any actual code.

Update 4:40 pm: Folks on Twitter told me about Ground Control, Mattt Thompson’s thing, which looks cool.

Guy on Responder Chain and Table View Cells

Guy English says not to wire up buttons in table view cells to first responder:

If I find a UIButton tossing some message up the responder chain, documented nowhere else except in the Interface Builder UI? That sucks. Do you think I’m being funny?

I’ve done this myself. Possibly even often. (Though it’s likely I wired it up in code rather than in IB.)

The thing is: I want the enclosing view controller to get the action message. When something needs to happen — changing the model, presenting another view controller, etc. — it’s likely that it’s something the view controller should do. Table view cells shouldn’t have that kind of power.

Guy writes:

Simply sending a message up the responder chain that some sub-item of a view has been tapped doesn’t help make the user’s intention clear except under the most superficial or specifically co-ordinated conditions.

My cases may be superficial. I expect the message only to go as far as the nearest view controller, and the only things it needs in order to know what to do are the message itself and the button that triggered it (the sender).

(If you have the button, you can find out what cell it’s in, get the index path of that cell, then look up what model object it refers to. Which sounds like a pain but actually isn’t.)

I think, though, that it’s likely I’m doing this in cases where I’m not using IB, since there’s no way to wire up the action to the view controller in code without somehow giving the table view cell a reference to the view controller, which I don’t want to do.

Is it possible in IB to wire up a button in a cell to an action method in the enclosing view controller? I would think so. (Memory is hazy on this.) If so, being explicit like that is probably a good thing, especially for the sake of the next person to look at the project.

But there’s a case that worries me: table view cells may be reused in different view controllers. In that case, I think you do want to wire up actions to first responder. (But then document them in the code, so Guy doesn’t have to go crazy.)

There’s a small case like that in Vesper: the Typography settings screen has a text preview feature, and that table view cell is the same table view cell used in the timeline.

(However, there are no buttons and no actions in this case, and so the issue of action targets doesn’t actually come up.)

* * *

Update 4:45 pm: Well, Guy’s right. The way you should do it is to create a protocol that the view controller conforms to. The cell’s delegate is the view controller.

(This way the cell doesn’t need to know about a specific class of view controller: it just needs to know about that protocol. I have real-life cases where a specific UITableViewCell subclass is used by different classes of view controllers.)

The button should be wired to an action method in the cell. That action method should call the right method on the cell’s delegate.

It’s likely, in this scenario, that you also want the cell to have a reference to a model object. I myself would make this opaque — the cell could have an id representedObject property, which is set when the view controller configures the cell.

This way, inside an action method in the cell, to actually do the thing, you’d have code like this:

[self.delegate doAThingWithAModelObject:​self.​representedObject];

Archive