Signpost를 이용해서 앱 Performance 개선하기
Xcode Instruments에서 제공하고 있는 Signpost 사용법을 알아보자.
개발 경험
개발 프로젝트 일정의 막바지에 이르게되면 어디든 버그 수정에 온 정신을 집중하게 된다. 하지만 요즘 시대의 프로그램들 완성도뿐이 아니라 사용자의 기대 Performance 수준이 높기 때문에 당연히 프로그램 동작 속도까지 체크해야한다.
사실 나는 오랜 iOS 개발을 하면서 Performance 이슈는 항상 발생을 했다. 개발을할 때 내가 집중한 부분이 다르기도 하지만, 개발자가 고려하고 생각해야하는 것들이 너무 많다.
외부적으로는 일정과 요건에 대한 기술 협의 내부적으로는 개발 아키텍처링이나 코드 자체가 옳바른 책임을 가지고 있는지 생각하고, 기획 요건에 실제 구현하면서 문제를 해결하고 또 다른 코드나 버그상황에 대해서 미리 생각해야 한다.
그래서 Performance 부분이 개발자의 머리속에서 소외될 수 있기 때문에 이것까지 잘 챙겨야 멋진 개발자될 수 있다.
아무튼. Performance를 개선하기 위해선 크개 두가지 작업이 선행되야 한다.
- Performance를 개선한 할 곳 (주로, 메인화면이나, 대용량 데이터를 처리한다던가 매번 그 부분은 정해져 있다.)
- 실제 Performance가 느린 곳 찾기
- 코드 수정 후 Performance 개선 확인
이번 포스팅에서는 2번에 대해서 이야기를 해보려고 한다.
기존의 Performance 개선 방식
첫번째는 메모리 Leak의 해결이다. 일단 메모리릭이 있으면 퍼포먼스 개선 단계가 아니다. 우선 메모리릭부터 모두 해결한다. 그런 다음 실제 속도 체크를 하는데, 나는 TimeChecker 클래스를 만들었다.
@objc class TimeChecker: NSObject {
static var startTime:Date?
@objc static func start(){
if startTime == nil{
startTime = Date()
}
}
@objc static func end(){
if let startTime = startTime{
let interval = Date().timeIntervalSince(startTime)
print("TimeChecker \(interval)")
self.startTime = nil
}
}
}
간단한게 시작 체크 시작 TimeChecker.start()를 호출하고, Task가 종료되는 부분에서 TimeChecker.end()를 호출하면 시작과 끝사이의 interval 시간이 표시된다.
이 방법의 문제는 여러개의 task를 동시에 Thread에서 처리할 경우 체크가 불가능하다는 단점이 있어 항상 언젠간 개선을 해야 겠다는 생각을 하고 있었다.
그런데 마침 이번 WWDC2018에서 Measuring Performance Using Logging 섹션이 소개되었다.
Signpost
Apple은 2년전에 OSLog를 소개했는데, 이것은 효율성과 Privacy를 목표로 만들었다고 한다. 그런데 이번 WWCD2018에서 소개하고 있는 Logging은 OSLog를 이용하여 Instruments에서 시각적으로 보는 방법을 소개했다. 그리고, Signpost는 각 Task 별 시간 체크도 수 있다고 한다.


Signpost 코드 삽입
일단 나는 기존에 class TimeChecker의 확장을 시도했다.
그 코드는 아래와 같다
@objc class TimeChecker: NSObject {
//MARK: - Signpost
static let osLog = OSLog(subsystem: "com.measure.signpost", category: "Create Object")
@objc static func startOSLog(){
if #available(iOS 12.0, *) {
os_signpost(.begin, log: TimeChecker.osLog, name: "load")
} else {
// Fallback on earlier versions
}
}
@objc static func endOSLog(){
if #available(iOS 12.0, *) {
os_signpost(.end, log: TimeChecker.osLog, name: "load")
} else {
// Fallback on earlier versions
}
}
}
간략하게 설명하면, OSLog 의 init(subsystem: String, category: String)메소드를 통해 Log Hendle 객체를 생성하고, os_signpost 메소드의 파라메터에 .begin과 .end를 넣어주면 끝난다.
생각보다 진입장벽이 높지는 않다.
다만 주의해야 할것은 파라메터중 name이 StaticString 이기 때문에 변수를 넣을 수가 없어 function으로써 기능을 만들기는 어렵다.
public func os_signpost(_ type: OSSignpostType, dso: UnsafeRawPointer = #dsohandle, log: OSLog, name: StaticString, signpostID: OSSignpostID = default)
이제 Performance 체크를 하고 싶은 곳에 위 코드를 삽입하여 실제 어떻게 체킹되고 있는지 확인해 보자.
Instruments 실행
이제 Profile을 실행해보자.

Instuments가 켜지면 좌측상단에 있는 Blank를 선택하고,

플러스 버튼을 누른 후 ```signpost``를 검색하고 드레그하여 추가한다.

다음, Instruments의 🔴 버튼을 눌러 실행한 후, 앱을 동작하면 타임체크가 되는 것을 볼수 있다.

이미지에 보이는 것과 같이 체크 시간도 볼 수 있고, 시각적으로 Task가 언제 동작되었는지도 알 수 있다.
결론
이 포스트는 속도 이슈에 대한 해결책 자체를 가지고 있지는 않다. 하지만 복잡하게 돌아가는 우리의 앱안에서 어느부분에 시간이 많이 소요되고 있는지 파악하기에는 더할 나위 없이 좋은 방법이다. 앞으로 여러곳에서 signspost를 이용하여 Performance 체크를 해보야겠다.
댓글남기기