서론
최근 XCTest를 이용해 성능 측정을 해보았습니다.
직접 해보기 전에는 그저 결과를 보여주는 용도구나~ 생각했었는데
직접 사용해보니 훨씬 더 자세한 내용을 제공해주었습니다.
이번 포스팅에서는 XCTest를 이용해 성능 측정하는 방법을 간단하게 알아보겠습니다.
Command Line에서 XCTest 사용
먼저 Command Line에서 XCTest를 사용하는 법을 알아보겠습니다.
Xcdoe 14.3 기준입니다.
1. 프로젝트 파일을 열어줍니다.
2. 하단의 + 버튼을 누릅니다.
3. Unit Testing Bundle을 선택합니다.
4. Product Name을 입력하여 생성합니다.
참고로 Product Name은 프로젝트 이름Tests 가 일반적입니다.
5. 생성 완료
(git 표시는 무시해주세요. 포스팅을 위해 삭제하고 다시 생성해서 그렇습니다.)
그럼 XCTest 파일이 생성됩니다.
파일을 열면 기본 코드가 적혀 있습니다.
이번에 저희가 알아볼 성능 테스트는
이부분 입니다.
이름에 대놓고 Performance 라고 적혀 있네요.
성능 측정
XCTest를 이용해 성능 측정을 해보겠습니다.
아래 두 함수를 테스트 해보죠.
func excuteRemoveLast() {
var array = Array(repeating: 0, count: 100_000)
while !array.isEmpty {
array.removeLast()
}
}
func excuteRemoveFirst() {
var array = Array(repeating: 0, count: 100_000)
while !array.isEmpty {
array.removeFirst()
}
}
기본적인 성능 측정
XCTest에서는 XCTMetric을 사용해 세부적인 테스트를 진행할 수 있습니다.
하지만 그 전에 기본적인 성능 측정부터 알아보겠습니다.
measure은 가장 일반적인 성능 측정 방법입니다.
func test_time_1() throws {
measure {
excuteRemoveFirst()
}
}
measure를 사용하면 measure 블록 코드를 10회를 수행하고,
수행하는데 소요된 wallClockTime를 측정합니다.
(수행 횟수는 metrics마다 다를 수 있습니다.)
테스트가 완료되면 경과 시간과 baseline보다 몇 % 차이가 있는지 알려줍니다.
팝업의 Show 버튼을 누르면 Baseline을 포함하여 더 자세한 정보를 볼 수 있습니다.
Edit을 누르면 Baseline을 직접 설정할 수도 있습니다.
Baseline은 성능 기준으로 자동으로 설정되지만, 원하는 값이 있다면 Edit을 눌러서 직접 설정하시면 됩니다.
또한, 모든 테스트가 공통적으로 디버그창에 자세한 정보가 표시됩니다.
경과 시간 측정은 횟수별 경과 시간과 평균 시간 등을 표시해 주네요.
XCTMeasureOptions - iterationCount
기본 수행 횟수인 10회보다 많이 혹은 더 적게 수행하고 싶다면 XCTMeasureOptions - iterationCount를 사용하면 됩니다.
func test_time_2() throws {
let option = XCTMeasureOptions()
option.iterationCount = 20
measure(options: option) {
excuteRemoveLast()
}
}
XCTMeasureOptions 객체를 생성하여 iterationCount 프로퍼티의 값을 수정하세요.
measure에 options 인자로 전달하면 설정한 횟수만큼 수행합니다.
XCTMetric
XCTMetric을 사용하면 더 상세한 테스트가 가능합니다.
measure의 metrics 인자로 XCTMetric 배열을 전달합니다.
XCTMetric은 프로토콜 타입으로 해당 프로토콜을 준수하는 클래스는 총 6개가 있습니다.
- XCTApplicationLaunchMetric
- XCTCPUMetric
- XCTClockMetric
- XCTMemoryMetric
- XCTOSSignpostMetric
- XCTStorageMetric
이번 포스팅에서는 XCTClockMetric, XCTMemoryMetric, XCTCPUMetric 클래스를 알아보겠습니다.
XCTClockMetric
XCTClockMetric은 경과 시간을 Monotonic Time으로 측정할 때 사용합니다.
위에서 알아본 기본 measure는 wall time이고, XCTClockMetric을 사용한 측정은 Monotonic Time을 측정한다는 차이점이 있습니다.
wallClockTime은 저희가 아는 일반적인 시간으로 실제 세계 시간을 나타내고,
Monotonic Time은 OS나 하드웨어가 직접 계산하는 시간을 나타냅니다.
Monotonic Time이 좋은 이유는 불변성을 보장하기 때문입니다.
실제 세계 시간과 다를 수 있지만, OS나 하드웨어가 시작한 시점부터 바뀌지 않고 사용자가 직접 값을 변경할 수도 없습니다.
따라서 불변성을 보장하는 시간 값이 필요한 경우 Monotonic Time을 사용하면 됩니다.
func test_clock() throws {
measure(metrics: [XCTClockMetric()]) {
excuteRemoveFirst()
}
}
XCTClockMetric을 사용해 측정하면 수행에 걸린 Clock Monotonic Time을 알려줍니다.
wall time(위)과 비교해보면 0.01초정도 차이가 나네요!
XCTMemoryMetric
physical 메모리를 측정하고 싶다면 XCTMemoryMetric을 사용하면 됩니다.
Overview를 보니 수행 전후의 메모리 차이를 보여준다고 하네요.
func test_memory() throws {
measure(metrics: [XCTMemoryMetric()]) {
var array = Array(repeating: 0, count: 100_000)
print(array.count)
}
}
정수 10만 개의 Array를 생성하면 675kB의 메모리를 사용하네요!
최고 사용량도 함께 보여주는게 유용한 듯 합니다.
XCTCPUMetric
XCTCPUMetric는 CPU 성능을 측정할 때 사용합니다.
Overview를 보면 CPU time과 CPU Cycles, CPU Instructions을 측정한다고 하네요.
func test_cpu() throws {
measure(metrics: [XCTCPUMetric()]) {
excuteRemoveFirst()
}
}
10만 개의 배열을 removeFirst할 때의 CPU 성능입니다.
XCTCPUMetric은 CPU와 관련된 세 가지를 측정하기 때문에 로그도 세 가지가 출력됩니다.
위에서부터 CPU Time, CPU Instructions, CPU Cycles가 출력되었습니다.
한 번에 측정
measure의 metrics 인자는 XCTMetric "배열" 타입이기 때문에 여러 Metric을 한 번에 전달할 수 있습니다.
그러면 여러 속성을 한 번에 측정할 수 있어요.
func test_all() throws {
measure(metrics: [
XCTClockMetric(),
XCTMemoryMetric(),
XCTCPUMetric()
]) {
excuteRemoveFirst()
}
}
세 가지 측정 결과가 한 번에 표시되었네요.
감사합니다.
참고
https://developer.apple.com/documentation/xcode/writing-and-running-performance-tests
https://developer.apple.com/documentation/xctest/performance_tests
https://developer.apple.com/documentation/xctest/xctestcase/1496284-defaultperformancemetrics
https://developer.apple.com/documentation/xctest/xctmetric
https://velog.io/@yjok/날짜와-시간처리
아직은 초보 개발자입니다.
더 효율적인 코드 훈수 환영합니다!
공감과 댓글 부탁드립니다.