View

300x250

이번에 최적화 과제를 수행하며 여러 글들을 찾아헤매다가 DateFormatter를 사용할 때 마다 생성해서 사용하는게 시간이 많이 잡아먹는 비싼 행위이기 때문에, 그렇게 하는 대신에 DateFormatter를 하나 생성해두고 재활용을 해야한다는 글을 봤다. 생각보다 다양한 글에서 해당 이슈에 대해 다루고 있었고, 직접 실행 시간을 측정하면서 성능을 비교하는 글들도 많았다.

해당 글 들에서 다룬 방법을 나도 한 번 적용해보면서 함수의 성능을 측정하는 방법도 배우고 내가 개선한 방법이 유의미한 개선을 가져왔는지도 한 번 테스트해보려고 한다!


테스트는 요 사이트에서 사용한 방법을 참고해 시도해봤다.

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

간편하게, 테스트 후 시간을 측정하는 코드는 아래처럼 작성해줬다. 간단하게 10000번 해당 데이터 타입의 인스턴스를 생성하는 코드이다.

let numberOfIterations = 10000
time = progressTime {
    for _ in (0..<numberOfIterations) {
        let a = Type()
    }
}
print("Type : ", time.testTime())

그 결과는 다음과 같다. 여기만 가지고는 DateFormatter를 여러번 생성하는게 그렇게 크리티컬한 이슈인지 파악하기 어렵다. 아니면 컴파일러가 알아서 잘 최적화를 진행한 것이거나?

위 사이트에서도 두 번째 테스트를 한다. 이번에는 “DateFormatter를 사용할 때의 비용”을 추적해본다. 오호, 요기부터는 유의미할 것 같은데?

나는 4가지 경우를 테스트해봤다. 이번에 코드를 짜면서 locale을 매번 생성해줄지 재사용해줄지, format을 매번 생성해줄지 재사용해줄지 고려했었는데, 이번에 바로 그 해답을 찾아보려고 한다. 👀

  1. DateFormatter 생성 + locale 생성 + format 지정
  2. DateFormatter 재사용 + locale 생성 + format 지정
  3. DateFormatter 재사용 + locale 재사용 + format 지정
  4. DateFormatter 재사용 + locale 재사용 + format 재사용
time = progressTime {
    for _ in (0..<numberOfIterations) {
        let a = DateFormatter()
        a.dateFormat = "yyyy-MM-dd"
        a.locale = .init(identifier: "ko_KR")
        a.string(from: Date())
    }
}
print("DateFormatter Init        : ", time.testTime())

let b = DateFormatter()
time = progressTime {
    for _ in (0..<numberOfIterations) {
        b.dateFormat = "yyyy-MM-dd"
        b.locale = .init(identifier: "ko_KR")
        b.string(from: Date())
    }
}
print("DF Init X / locale Init   : ", time.testTime())

let c = DateFormatter()
c.locale = .init(identifier: "ko_KR")
time = progressTime {
    for _ in (0..<numberOfIterations) {
        c.dateFormat = "yyyy-MM-dd"
        c.string(from: Date())
    }
}
print("DF Init X / locale Init X : ", time.testTime())

let d = DateFormatter()
d.dateFormat = "yyyy-MM-dd"
d.locale = .init(identifier: "ko_KR")
time = progressTime {
    for _ in (0..<numberOfIterations) {
        d.string(from: Date())
    }
}
print("Only Formatting           : ", time.testTime())

위 코드를 실행해보면 다음의 결과를 얻을 수 있다.

기대했던 결과이면서 기대와 다른 결과가 나왔다. 위 결과를 통해서 DateFormatter를 사용할 때 마다 생성해주는건 1개 만들고 재활용하는 것보다 확실히 다른 방법보다 약 45배 정도 비싼 방법인 건 캐치를 한 것 같다. 그러나 생각보다 Locale을 Init 하는 비용은 저렴한 것 같다. 나는 어떤 Instance 이든 간에 무언가 메모리에 올리고 생성하는 과정이 비용이 비쌀 것이라 생각했는데, locale을 매번 생성해주나 그냥 사용해주나 비용적인 측면에서는 차이가 없다.

이외에도 위 링크의 블로그에서는 여러가지 테스트를 해보고 있는데, 결론을 요약하면 아래와 같다.

  1. DateFormatter는 생성하는데 비용이 비싸다 → 검증 완료!
  2. DateFormatter는 프로퍼티를 변경하는데 비싸다
  3. Date → String 은 저렴하다 (DateFormatter 생성 / 재활용 시간이 45배 차이) → 검증 완료!
  4. String → Date 는 비싸다 (DateFormatter 생성 / 재활용 시간이 3배 차이)

예전에 DateFormatter 를 사용할 때에는 별 생각없이 아래와 같은 형태로 Date의 Extension으로 만들어줬었다.

extension Date {
  func toFormat() -> String? {
    let dateFormatter: DateFormatter = {
      let df = DateFormatter()
      df.dateFormat = "my format"
      df.locale = .init(identifier: "ko_KR")
      return df
    }()

    return dateFormatter.string(from: self)
  }
}

고런데, 요게 그렇게 적절하지 않은 방법이라는 것을 이번에 배웠다. 이번에 코드를 개선할 때에는 DateFormatter의 재사용을 적극적으로 적용하기 위해 이렇게 변경해줬다. 실제로 작성한 코드보다는 코드를 약간 간소화해서 작성하긴 했지만, 요렇게 DateFormatter extension에 Static으로 만들어둔 DateFormatter를 가져와 사용하도록 구조를 설계하였다.

extension DateFormatter {
    static let krDateFormatter : DateFormatter = {
    let formatter = DateFormatter()
    formatter.locale = .init(identifier: "ko_KR")
    return formatter
    }()

  static func getDateFormatter(locale: DateFormatterLocale) -> DateFormatter {
      return krDateFormatter
  }
}

extension Date {
  func toFormattedString(_ dateFormat: String = "yyyy-MM-dd(EEEEE) a HH:mm") -> String? {
    let dateFormatter = DateFormatter.getDateFormatter()
    dateFormatter.dateFormat = dateFormat
    return dateFormatter.string(from: self)
  }
}

요렇게 개선하고 나서 코드 실행 타임을 체크해보니, 25배 가량 빨라진 것을 확인할 수 있다.

역시 DateFormatter는 생성이 무거웠나보다 👀👀

샤라웃

https://sarunw.com/posts/how-expensive-is-dateformatter/#experiment-%232%3A-how-expensive-is-dateformatter-when-using

320x100
Share Link
reply
반응형
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31