Identifying Performance Bottlenecks in Number Formatter Creation and Usage

Recently I was working on optimizing a part of the code, where we create a formatter, cache it and then use this formatter hundreds of time.

This is a pretty harmless piece of code where you basically create a formatter, and then cache it. We retrieve the formatter from the cache using a key and save it in the cache. I extracted the stack trace from instruments. This is as shown below.

Seems like there is something fishy going on. If you look closely at the arrow, you can see there is a regenerate method being called. Let’s dig in more closely. You can go into the source code by clicking on the row.

We are seeing that a simple locale assignment is taking around 29.00ms. This is fast, but not really fast, when you look at the whole method. Now I am curious. I try to dig in to the method called _regenerateFormatter

In order to know the implementation, I look into the source code here. Its pretty old, but the calls remain pretty much the same.


The setLocale is calling a method called [self _reset]

The reset method, resets the formatter and calls regenerate. The culprit seems to be the recreation of the formatter. The regenerate method basically checks for each and every attribute and then recreates the formatter.

There you go, now we know why the formatter is taking time. In order to fix this, we should set the locale when the formatter is being created, and not outside the if loop. This will save us time.

formatter = NumberFormatter()
if accuracyType == .byFraction {
   formatter?.maximumFractionDigits = maxDigit
   formatter?.minimumFractionDigits = minDigit
} else if accuracyType == .bySignificant {
   formatter?.minimumSignificantDigits = minDigit
   formatter?.maximumSignificantDigits = maxDigit
   formatter?.roundingMode = roundMode
   formatter?.usesGroupingSeparator = usesGroup
   formatter?.numberStyle = numberStyle
   formatter?.locale = locale

Hopefully this tutorial helps.


Leave a Reply

Your email address will not be published. Required fields are marked *