호댕의 iOS 개발

[iOS] DateFormatter의 비용?! 본문

Software Engineering/iOS

[iOS] DateFormatter의 비용?!

호르댕댕댕 2022. 2. 12. 17:50

DateFormatter의 비용이 크다는 피드백을 받게 됐다. 

사실 DateFormatter를 사용하면서 크게 고려하지 않던 부분이었는데 이번 계기로 찾아보게 됐다.

 

그래서 다음과 같은 참고 자료들을 찾을 수 있었다. 

https://sarunw.com/posts/how-expensive-is-dateformatter/

 

How expensive is DateFormatter | Sarunw

If you are working on iOS for long enough, there is a chance that you might have known that DateFormatter is expensive, but what is costly about DateFormatter? Let's find out in this article.

sarunw.com

https://deuschl.net/swift/how-expensive-is-dateformatter/

 

How Expensive is DateFormatter?

Inspired by Sarun's post about the price of initializing, configuring and using a DateFormatter I thought I'd do my own tests. Why? Well, two reasons:Who doesn't care about the performance of

deuschl.net

 

이 글들을 보고 컴퓨터의 사항 같은 외부 환경에 따라 실험이 영향을 받을 수 있겠다고 생각했고 직접 실험을 해보자고 생각했다. 

 

아래는 그렇게 생각하고 실행했던 실험의 결과이다.


DateFormatter의 비용 테스트 해보기

해당 실험은 sarunw의 How expensive is DateFormatter article을 보고 해본 실험입니다.

테스트를 위한 반복 횟수: 10000 회

1️⃣ 인스턴스 생성에 소요되는 시간 검증

🧪 검증을 위해 생성한 인스턴스의 종류

  • DateFormatter
  • Date
  • UIView

👨🏻‍🔬 실험 결과

DateFormatter Date UIView
0.005초 0.003초 0.019초

UIView가 가장 많은 시간이 소요됐고 Date와 DateFormatter는 큰 차이는 없었다. 따라서 생성만 할 경우 DateFormatter의 비용이 그렇게 크진 않다는 것을 알 수 있었다.

 

<<사용 테스트 코드>>

// MARK: - 인스턴스 생성에 소요되는 시간 검증
func test_dateFormatterCreation() { // 0.005초
    self.measure {
        for _ in (0..<numberOfIterations) {
            let dateFormatter = DateFormatter()
        }
    }
}

func test_dateCreation() { // 0.003초
    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
        }
    }
}

func test_viewCreation() { // 0.019초
    self.measure {
        for _ in (0..<numberOfIterations) {
            let view = UIView()
        }
    }
}

2️⃣ 사용할 때 소요되는 시간 검증

🧪 검증을 위해 사용한 DateFormatter의 메서드 종류

  • date(from:)
  • string(from:)

추가적으로 반복해서 DateFormatter를 생성할 때와 함수에서 한 번만 생성하고 사용했을 때의 시간 차이도 검증함.

👨🏻‍🔬 실험 결과

string(from:) & 반복해서 생성 string(from:) & 한 번만 생성 date(from:) & 반복해서 생성 date(from:) & 한 번만 생성
0.181초 0.005초 0.313초 0.134초

일단 string(from:)보다 date(from:)이 더 큰 비용이 든다는 것을 알 수 있었다. 또한 한 번만 DateFormatter를 생성해서 사용할 때보다 반복해서 생성할 때 훨씬 많은 비용이 드는 것을 확인할 수 있었다.

즉, 생성 자체에는 그렇게 큰 비용을 차지하지 않았지만 반복해서 생성하고 사용하게 되면 비용이 굉장히 큰 폭으로 증가했다.

 

<<사용 테스트 코드>>

func test_dateFormatterCreationAndMakeString() { // 0.181
    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
            let dateFormatter = DateFormatter() // 매번 DateFormatter를 새롭게 생성
            let dateToString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationOnceAndMakeString() { // 0.005
    let dateFormatter = DateFormatter()

    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
            let dateToString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationAndMakeDate() { // 0.313
    self.measure {
        for _ in (0..<numberOfIterations) {
            let dateFormatter = DateFormatter() // 매번 DateFormatter를 새롭게 생성
            let dateToString = dateFormatter.date(from: "30/01/2020")
        }
    }
}

func test_dateFormatterCreationOnceAndMakeDate() { // 0.134
    let dateFormatter = DateFormatter()

    self.measure {
        for _ in (0..<numberOfIterations) {
            let dateToString = dateFormatter.date(from: "30/01/2020")
        }
    }
}

3️⃣ 커스터마이즈해서 사용할 때 소요되는 시간 검증

🧪 검증을 위해 사용한 DateFormatter의 프로퍼티 종류

  • calendar
  • timeZone
  • locale
  • dateFormat
  • dateStyle & timeStyle

추가적으로 반복해서 DateFormatter를 생성할 때와 함수에서 한 번만 생성하고 사용했을 때의 시간 차이도 검증함.

👨🏻‍🔬 실험 결과

calendar & 반복해서 생성 calendar & 한 번만 생성 timeZone & 반복해서 생성 timeZone & 한 번만 생성
0.531초 0.045초 0.508초 0.020초
locale & 반복해서 생성 locale & 한 번만 생성 dateFormat & 반복해서 생성 dateFormat & 한 번만 생성
0.178초 0.009초 0.390초 0.015초
dateStyle & timeStyle & 반복해서 생성 dateStyle & timeStyle & 한 번만 생성 - -
0.497초 0.020초 - -

<<사용 테스트 코드>>

func test_dateFormatterCreationAndSetCalendar() { // 0.531
    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
            let dateFormatter = DateFormatter()
            dateFormatter.calendar = Calendar(identifier: .buddhist)
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationOnceAndSetCalendar() {
    let dateFormatter = DateFormatter()

    self.measure { // 0.045
        for _ in (0..<numberOfIterations) {
            let date = Date()
            dateFormatter.calendar = Calendar(identifier: .buddhist)
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationAndSetTimezone() { // 0.508
    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
            let dateFormatter = DateFormatter()
            dateFormatter.timeZone = TimeZone(secondsFromGMT: 60 * 60 * 8)
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationOnceAndSetTimezone() { // 0.020
    let dateFormatter = DateFormatter()

    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
            dateFormatter.timeZone = TimeZone(secondsFromGMT: 60 * 60 * 8)
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationAndSetLocale() { // 0.178
    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
            let dateFormatter = DateFormatter()
            dateFormatter.locale = Locale(identifier: "th")
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationOnceAndSetLocale() { // 0.009
    let dateFormatter = DateFormatter()

    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
            dateFormatter.locale = Locale(identifier: "th")
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationAndSetDateFormat() { // 0.390
    self.measure {
        for _ in (0..<numberOfIterations) {
            let date = Date()
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy.MM.dd"
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationOnceAndSetDateFormat() {
    let dateFormatter = DateFormatter()

    self.measure { // 0.015
        for _ in (0..<numberOfIterations) {
            let date = Date()
            dateFormatter.dateFormat = "yyyy.MM.dd"
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationAndSetDateStyle() {
    self.measure { // 0.497
        for _ in (0..<numberOfIterations) {
            let date = Date()
            let dateFormatter = DateFormatter()
            dateFormatter.dateStyle = .medium
            dateFormatter.timeStyle = .medium
            let dateString = dateFormatter.string(from: date)
        }
    }
}

func test_dateFormatterCreationOnceAndSetDateStyle() {
    let dateFormatter = DateFormatter()

    self.measure { // 0.020
        for _ in (0..<numberOfIterations) {
            let date = Date()
            dateFormatter.dateStyle = .medium
            dateFormatter.timeStyle = .medium
            let dateString = dateFormatter.string(from: date)
        }
    }
}

💭 결과를 통해 알게 된 점

DateFormatter를 생성만 할 때에는 그렇게 큰 비용이 들지 않았지만 사용할 때마다 매번 생성하게 된다면 비용이 굉장히 커지는 것을 볼 수 있었다. 앞으로도 DateFormatter를 반복적으로 사용할 때에는 최대한 한 번만 생성하고 사용할 수 있도록 해야겠다~~

Comments