malcommac / SwiftDate

🐔 Toolkit to parse, validate, manipulate, compare and display dates, time & timezones in Swift.
MIT License
7.62k stars 768 forks source link

Performance issues when formatting dates #723

Open phaphan opened 4 years ago

phaphan commented 4 years ago

When using the .toFormat() formatter, I'm experiencing a significant impact on performance.

As far as I can see in the documentation, a sharedFormatter should be the default formatter, if a custom formatter has not been set. However, I created a simple time profiling of a singleton DateFormatter in comparison to the .toFormat(), and it seems that the performance difference is quite significant.

swiftdate_performance

Am I doing something wrong, or Is it possible that there is an issue with the sharedFormatter not performing as expected?

malcommac commented 4 years ago

I tried to debug a little this issue and I'm not sure of the result.
Here you can see the stack trace. Most of the time is spent on Calendar._bridgeToObjectiveC() which is set on assignment of the calendar

static func sharedFormatter(forRegion region: Region?, format: String? = nil) -> DateFormatter  {
 ...
 if let region = region {
    formatter.timeZone = region.timeZone
    formatter.calendar = region.calendar
    formatter.locale = region.locale
  }
Screenshot 2020-09-16 at 15 01 48

If I try to assign only on change:

static func sharedFormatter(forRegion region: Region?, format: String? = nil) -> DateFormatter {
        let name = "SwiftDate_\(NSStringFromClass(DateFormatter.self))"
        let formatter: DateFormatter = threadSharedObject(key: name, create: {
            DateFormatter()
        })

        if region?.timeZone != formatter.timeZone {
            formatter.timeZone = region?.timeZone
        }

        if region?.calendar != formatter.calendar {
            formatter.calendar = region?.calendar
        }

        if region?.locale != formatter.locale {
            formatter.locale = region?.locale
        }
        formatter.dateFormat = (format ?? DateFormats.iso8601)
        return formatter
    }

it perform ~4x faster (1s with Unit Test Measurement). I have not other ideas.

malcommac commented 4 years ago

This is really interesting https://medium.com/@Tho_Stark/speed-up-your-dateformatter-63efec7b6723:

But the surprise is that changing the calender property costs much more time than instantiating a new DateFormatter. And it is nearly 5 times slower than setting it once. Also changing the format seems to be a very expensive task. Whereas changing the timezone causes no significant difference compared to not changing it.