Putting The Performance Lessons To Use

Putting The Performance Lessons To Use

tl;dr – working with primitives can be _waaay_ faster than objects.

I️ came across this tweet by Nick Lockwood, and the reply from Brian Gesiak regarding a string comparison method to find the “edit distance” between two strings. The repo can be found here

It’s a very cool algorithm, and the Brian explicitly stated that it was not written to be performant. While I am sure that there are number of algorithmic improvements that could be made in order to improve speed, that would also be difficult, and certainly more trouble than it would be to me, since I don’t really have a clue how this algorithm works nor do I have any need to use it right now. I’m not trying to brag about my ignorance, but it’s quite common that a programmer needs to balance effort vs. reward.

However, I did notice that the internal implementation uses NSString comparison methods. And as I learned recently, the first rule regarding performance is that objects are expensive and should be avoided when possible. So, it made curious how much performance improvement could be achieved by merely replacing the internal NSString implementation with an internal implementation using a primitive char array. Again, let me note that Brian explicitly stated he was not optimizing for speed, so this is in no sense a criticism of his code.

Other than dealing with the boilerplate of getting the array of bytes from the NSString, the only change that needed to be made to the code was one line of code for comparing whether a letter at an index.

if (![NSString mdc_sameCharacterAtLeft:left index:y-1 right:right index:x-1]) { substitution++;}vs.
if (lBytes[y-1] - rBytes[x-1]) { substitution++;}This is the implementation of the comparison method in the example (mdc_sameCharacterAtLeft) code:
return [@([left characterAtIndex:leftIndex]) compare:@([right characterAtIndex:rightIndex])] == NSOrderedSame;This line of code is actually carries a lot of object-based overhead, including:
  • 2 characterAtIndex method calls
  • 2 primitive -> object instantiations (@(value))
  • and finally one more compare: method call

I think we can agree that this will be a bit slower than the subtraction of two ints.

Using “sitting” and “kitten”, we get:

Average runtime (koyachi): 14640 s, distance: 3Average runtime (Gesiak ): 11261 s, distance: 3Average runtime (Bytes ): 3562 ns, distance: 3So, the difference I found between Koyachi implementation and Brian's implementation is similar to what was reported, i.e. ~30% faster. However, the Bytes implementation gives a 3x speedup (i.e. 300% faster) over Brian's implementation.

Using the strings defined for comparison in the Github repo on an iPhone 7 with -O3 optimization we get the following results:

Average runtime (koyachi): 0.062313 s, distance: 463Average runtime (Gesiak ): 0.052492 s, distance: 463Average runtime (Bytes ): 0.001969 s, distance: 465You'll notice two things. First of all, to the main point of my exercise, we see a 27x speed up, which is a fairly typical result after multiple runs. That is a big gain. looking at the raw times, if this were UI critical work I️t would be the difference between something that could be done easily within a frame rate refresh and something that would lead to significant frame droppage.

But, we also see that the distance is incorrect. The second string contains this name, “Damerau–Levenshtein”, which included a non-ASCII dash character, something I never would have noticed. When this is replaced with an ASCII dash, the distances all match. However, this clearly demonstrates the tradeoffs between using primitive values vs objects.

If one really wanted to use this approach then the strings could likely be tested for ASCII compliance with something like, [string smallestEncoding] and one could be clever about which method to use, and likely implement primitive methods dedicated to many common so that the fastest methods could be used most of the time (NSString is a class cluster for a reason!). Also, unichar/unicode rather than Byte primitives could also possibly be used, but that would depend on one’s needs and I am not expert enough to comment on that.

modified code:

https://gist.github.com/anonymous/86f736b7874711cd42d747540ba70cf1

Advertisements

Oh, That’s What NSAssert1 Is For!

<smacks forehead>

In the instances, where I have used NSAssert, I have noticed autocompletion suggesting the existence of NSAssert1, NSAssert2, NSAssert3, NSAssert4, NSAssert5, and I proceeded to ignore them every time.

Today, I finally figured it out because I wanted my Assertion message to contain the value of the parameter that is failing, and NSAssert doesn’t work with NSString stringWithFormat:

NSAssert(condition, @"msg to future");
NSAssert1(condition, @"msg to future with parameter: %zd", intParam);
NSAssert2(condition, @"msg to future describing parameters: %zd %zd", intParam, intParam2);

Notice a pattern?

NSAssert is just a macro, and clearly it was not implemented for variadic arguments. The 1, 2, 3… not elegant, but it is quite clear how to use it correctly, which is very important for functions used when asserting. So maybe the lack of a variadic option is not an accident.

Bonus points: NSCAssert(n) is for Asserts inside of C function.

The Term `guard` Is Confusing

Swift introduced the following construct::

guard  else {
    fatalError()/return
}
//perform desired code

As an alternative to:

if condition {
    //perform desired code
} 

It provides a mechanism to encourage a clearly delineated grammar for early returns or fatal errors if a condition is not met. It is an elegant syntax for two reasons:

  1. It makes the intention clearer than just an if statement by letting subsequent readers know that there are particular conditions that should be met before running the code.
  2. It allows for less nesting, particularly when there are multiple, co-dependent conditions, so you can avoid “pyramids of death”. i.e.:
if condition1 {
    if let param = optionalParameter {
        //transformation needed to evaluate cond3
        if condition3 {
            //perform desired code
            //use param 
        }
    }
}

vs.

guard condition1, param = optionalParameter else {
    return
}
//transformation needed to evaluate condition3
guard condition3 else {
    return
}
 
//perform desired code
//use param

So, I really love guard statements, but for a long time, I struggled to remember if I would want a particular condition to be true or false. I basically couldn’t remember if guard meant “guard against this condition” or “guard the code from this condition being false”.

So, I eventually figured out that I could remember what I wanted the guard condition to be if I said in my head “Make sure this condition is true, otherwise…”. i.e.:

makeSure condition else {
    return
}

So, for a while, I was happy to just say to myself “make sure” every time I wrote a guard statement, but I recently thought it would be nice if the language itself made this clearer, and sure enough I just found a Swift evolution pitch proposing exactly that, using the word ensure, but it was rejected for a few (dumb!) reasons, and is unlikely to be revisited. However, I still wish it hadn’t been rejected, or that it were easier to alias the syntax.

Anyway, in honor of Jay Abbott, the author of the above pitch, and in honor of his profound wisdom, insight and common sense, I wrote this little gist for cases where the guard is only merely a boolean:

func ensure(_ arg: Bool, otherwise: (() -> ())) {
    guard arg else {
        otherwise()
        return
    }
}

ensure(str2 == str, otherwise: {
    fatalError()
})

Jay, your pitch will not be forgotten!

Regex Search and Replace in Xcode

WHERE1 HAVE YOU BEEN2 ALL MY LIFE3??

I just discovered after figuring out how to do some regex-based search and replace in Sublime Text 3, that the same thing can be done in Xcode.

Does this sound like some weird advanced programmer thing and perhaps you don’t really care? From time to time, any programmer faces a task that involves rote updating to repeated code. Sometimes that code fits an identical pattern, but isn’t literally identical.

For example, say in your app, there is a lot of this in your app:

#define kSomeStringConstant = @"TheRealStringValue";
#define kAnotherConstantString = @"TheRealStringValue";
#define kSomeConstantCGFloat = 6.283185;

But you’ve decided that #define is not the best practice for doing this, and you want to follow conventions of defining them in a .m file and using extern or FOUNDATION_EXPORT in the .h file.

This is no big deal if you have single digits of entries, but plenty of serious apps can have hundreds of such entries. That’s a lot of boilerplate to change by hand. So let’s just say you have the first portion done in the .m file:

NSString * const SomeConstantString = @"TheRealStringValue";
NSString * const AnotherConstantString = @"TheRealStringValue";
CGFloat const SomeConstantCGFloat = 6.283185;

Instead of taking a couple passes and replacing the = aValue with nothing, and then figuring out how to insert FOUNDATION_EXPORT in front of all the lines, you could use regex search and replace like the following two pictures show:

This is a trivial example, but the 2 valuable bits for me:
1. Knowing where regular expression search option is found in local search.
2. Seeing an example of using the capture group in the replacement.

And obviously, if you are intimidated by regex, don’t be. Start small, once you find a simple reason to use it, you will automatically find other uses for it. I use an app called Oyster, which has worked great for me and it has a nice little guide to regex, and allows you to see the results of your regex on whatever test text you want. I’m sure there are plenty of other such apps as well.

iOS and macOS Performance – Reading Files – Strings vs. Bytes

I can’t quite remember how I stumbled across this book, “iOS and macOS Performance Tuning”, a couple of weeks ago (Twitter, basically). But regardless, it is awesome. Written by Marcel Weiher, it has a couple of simple premises, well explained, that really provides a great roadmap for thinking about performance in iOS.

I will actually write up a couple of blogposts out of this book, because I like it so much and want the lessons to take root. This post will focus on something that I had never really explored carefully, which is working with NSData rather than just initializing Foundation objects with NSData and working with the resulting objects. The two places where this is a natural thing to do is with strings and with images.

I am going to do a very basic demonstration of his principles at work in reading and parsing a large text file, complete with benchmarks!

One of the primary lessons from this book is that regardless of the continuing speedup of devices and storage, the cost of using objects and their associated methods to process data does add up.1 If you are doing relatively complex transformations on a small number of objects, then using the most abstracted models and methods completely makes sense. But, for operations on large numbers of objects, there are opportunities to achieve profound speedup.

A second lesson is that for most operations and code, Objective-C still is faster than the equivalent Swift code. This is an important point that can get lost. Swift is incredibly powerful, and absolutely lends itself to modern programming practices that can lead to a better, safer, more stable codebase. But when it comes to raw performance, it is still difficult to optimize Swift as thoroughly as Objective-C

Read More »

Apple Watch Tips and Thoughts After 2 Years and 2 Months

I have been using my Apple Watch Series 1 for over two years, ever since I was lucky enough to get an early release watch through the Apple developers program. When a friend recently purchased a used one, I sent him a few tips. I figured it has most of the things I would want to pass on to any new user, so without further ado.

For me the series 1 watch has been entirely waterproof. Inspired by Craig Hockenberry. I have gone swimming vigorously in the pool, hot tub, ocean, shower, etc. This is not a guarantee or even recommendation and not what apple warrantees, but >2 years in, it’s been fine for me.

Since the WatchOS 3 updates, texting on the phone watch app is actually pretty decent. Quick replies and emoji actually cover a lot of situations. Siri works quite well. The ability to scribble text for short custom messages when I can’t speak is pretty great and reliable.

Possibly my favorite feature is being able to tap “find phone” in the watch’s control center from watch, which makes my phone beep loudly.

The watch’s integration with Apple maps is fairly slick when driving. looking at my wrist shows the upcoming step quite reliably, and as I am approaching a turn, it taps either left or right patterns to both remind me that the turn is coming and indicate which way to go.

Best workout app for me so far has been workouts . Very customizable, plus it uses the crown to end or pause a workout.

Best sleep app: Autosleep. I have a couple quibbles, but overall, it’s great. Totally automagical. I don’t have to turn on an app when I start sleeping, it just looks at my watches internal motion and heart rate information.

It’s a niche use case, but using the watch with wireless headphones alone without phone does actually work fairly well. Getting content onto the Watch is a little bit of a PITA. You can have one synced playlist with iTunes, and now Overcast app allows you to sync a couple of podcasts to be on the watch itself. The interface for this is simple and great. It is ok from a reliability standpoint, but when I have wanted to go for runs without my phone it has worked ~ 75% of the time. Hopefully, Apple makes this increasingly easier for developers.

Using the complications as a place to launch apps from is really helpful. I have a couple of different watch faces set up with my most used apps for just this purpose.

Timer and Alarm complications are probably my most used functions.

Having the timer as a complication is something I use all the time because of my failing memory to remind me to do something in a fixed period of time. Simple, but effective.

The alarm app on the watch is 10 times better than on the phone. First of all, setting alarms on my watch is more pleasant than on the phone due to the crown. But most of all, for my sleeping alarm, being woken up with some taps on my wrist is less jarring and I don’t have to worry about waking up my bride if we are getting up at different times.

Lastly, since I clearly use watch functions while I sleep, I need it to have enough charge for the night. I find that ~20% is the minimum for a full night of sleep, but that is cutting it close. So I try to make sure it’s at least 35%, especially if I will be relying on the alarm. Likewise, an hour of working out requires ~20-25% as a minimum.

I typically charge it while I shower, and when I flop on the bed before sleep, if I remember, but because it’s important to have a charge while I sleep for my alarms particularly, I did break down and get a second charger that I keep in my office. I added the office charging to my routines, since I use it for workouts, which makes the margin for error slimmer at night, especially if I forget to end a workout and it is running the heart rate monitor all that time.

Overall, I still love it and after over two years, it is running strong. I’ll be curious to see how long the battery will be able to keep up. I’m pretty sure that I will be tempted to get a (currently non-existent) series 3/4 before it has become unable to hold a charge.