iOS NSString stringWithFormat: Performance (Objective-C)

Torrey Betts / Monday, October 21, 2013

Introduction

The string functions of most programming languages can cause performance slowdowns when they are frequently used. With iOS development you will see NSString references and its method calls all over the place. It is well known that Objective-C does have a little overhead when compared to C language that it extends because of the Smalltalk style messaging that goes on behind the scenes. In this post we'll measure the performance of the NSString stringWithFormat: method against C's sprintf function. Additionally, to even the score, following the sprintf function, the C string will be converted to an NSString. Each method will be executed a total of 100,000 times and the time span caluated and charted.

NSString stringWithFormat: VS sprintf

To test the performance of NSString stringWithFormat: against the sprintf function that is then converted into an NSString, a single method was written that creates these strings 100,000 times. Extra code is added to the method below for displaying the results as well as creating the custom class that stores the results for charting. These additions do not play a role in the execution time that is calculated. It should be noted that this method also uses a static buffer size. Using a static buffer for a production app isn't recommended, to avoid buffer overflows you would need to make use of the malloc function for allocating a dynamic amount of memory and then release after its use.

-(void)runTest
{
    ComparisonItem *comparisonItem = [[ComparisonItem alloc] init];

    //Test NSString stringWithFormat:
    NSDate *start = [NSDate date];
    for (int i = 0; i < 100000; i++) {
        NSString *s = [NSString stringWithFormat:@"Hi %@; Hi to you %@.", @"Torrey", @"too"];
    }
    NSDate *end = [NSDate date];
    NSString *span = [NSString stringWithFormat:@"%.0f", ([end timeIntervalSinceDate:start] * 1000)];
    comparisonItem.formatNSString = (int)([end timeIntervalSinceDate:start] * 1000);
    _label.text = [NSString stringWithFormat:@"Format = %.0f milliseconds", ([end timeIntervalSinceDate:start] * 1000)];

    //Test sprintf that is converted to NSString
    start = [NSDate date];
    for (int i=0; i < 100000; i++) {
        char buffer[64];
        sprintf(buffer, "Hi %s; Hi to you %s.", "Torrey", "too");
        NSString *s = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
    }
    end = [NSDate date];
    span = [NSString stringWithFormat:@"%.0f", ([end timeIntervalSinceDate:start] * 1000)];
    comparisonItem.formatSprintf = (int)([end timeIntervalSinceDate:start] * 1000);
    _label.Text = [NSString stringWithFormat:@"%@\nsprintf = %.0f millseconds", _label.text, ([end timeIntervalSinceDate:start] * 1000)];

    [_data addObject:comparisonItem];
}

The Results

The chart below illustrates the results of 10 runs on an iPad 3 device. In most cases using sprintf that gets converted to an NSString is 2.5x faster than the NSString stringWithFormat: method! Other iOS devices have comparable results in that they retain their 2.5x performance increase, but the total execute time can be slower or faster. In apps or custom controls that make frequent use of NSString stringWithFormat: changing over to the sprintf function would be beneficial for performance.

Note: Testing on the iOS simulator will result in varied performance, this is due to hardware architecture differences.

The Results

The Results When Using malloc

To demonstrate the use of malloc with this performance test, I tweaked the loop containing the sprintf function.

for (int i=0; i < 100000; i++) {
    size_t size = strlen("Hi Torrey; Hi to you too.");
    char *buffer = (char *) malloc(size);
    sprintf(buffer, "Hi %s; Hi to you %s.", "Torrey", "too");
    NSString *s = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
    free(buffer);
}

The use of malloc adds a little extra processing time for the allocation of memory, but the results when ran on an iOS device will still allow for a 2.1x performance increase. Other iOS devices have comparable results in that they retain their 2.1x performance increase, but the total execute time can be slower or faster.

The Results When Using malloc

Download the Example Project

The Xcode project source code for this post can be downloaded by clicking this link. This project requires the IGChart.framework which is used for charting the performance results, the NucliOS trial can be downloaded here.

By Torrey Betts