티스토리 뷰

반응형

안녕하세요 :) Zedd입니다.

제가 <GCD - Dispatch Queue사용법>글에서 



이렇게 말을 했죠! 그 이유는...제가 Energy Efficiency Guide for iOS Apps를 공부를 어차피 해야해서ㅎㅎ

여기에서 QoS를 자세히 다뤄서...ㅎㅎ

한번 알아봅시다.이 Guide자체가 워낙..길어서...




Prioritize Work with Quality of Service Classes



제가 <GCD - Dispatch Queue사용법>글에서도 말했죠



QoS를 적절히 활용하면 앱의 에너지 효율성도 좋아지고 ~.~ 반응적이게되고 ~.~

암튼 뭐 QoS를 안쓰는 것보다....좋겠죠?...ㅇㅋ


앱과 operation은 CPU, 메모리, 네트워크 인터페이스 등의 유한한(finite)리소스를 사용하기 위해 경쟁(compete)합니다. responsive(반응성?)과 효율성(efficient)을 유지하기 위해 시스템은 task의 priority(우선순위)를 정하고, 언제 실행할지 지능적으로(intelligent) 결정해야 합니다.

UI업데이트와 같이 사용자에게 직접 영향을 주는 작업은 매우 중요하며, 백그라운드에서 발생 할 수 있는 다른 작업보다 우선시합니다. 이 priority가 높은 작업은 시스템 리소스에 대한 즉각적이고 실질적인(immediate and substantia ) 액세스가 필요할 수 있으므로 더 많은 에너지를 사용합니다. 


개발자는 중요도에 따라 앱의 작업을 분류하여 시스템이 보다 효과적으로 priority을 지정하도록 도울 수 있습니다. 최적 시간까지 작업을 지연하는 등의 다른 효율성 개선을 구현한 경우에도, 시스템은 여전히 일정 수준의 priority 지정을 수행해야합니다. 그러므로 앱이 수행하는 작업을 “분류”하는 것은 여전히 중요합니다.






About Quality of Service Classes

QoS (Quality of Service) 클래스를 사용하면 NSOperation, NSOperationQueue, NSThread 객체, dispatch queues 및 pthread (POSIX 스레드)로 수행할 작업을 분류할 수 있습니다. 작업을 위해 QoS를 할당하면, 해당 중요도(importance)를 표시하고, 시스템이 해당 QoS의 priority를 지정하여 적절하게 스케쥴링합니다.

예를들어, 시스템은 백그라운드 작업보다 사용자가 시작한 작업을 더 빠르게 수행하며, 이는 최적의 시간까지 연기할 수 있습니다.(계속 최적의 시간까지 작업을 지연한다, 연기한다 그러는데 이게 priority를 할당하면 어쩔 수 없이 먼저 실행되는 작업이 있는데 그 먼저 실행되는 작업에 의해 밀리는? 나중에 실행될 작업을 미뤄야 할텐데 그걸 계속 미루는게 아니라, 딱 적당한? 작업 수행에 영향을 끼치지 않을 만큼의 시간?? 이걸 최적의 시간이라고 한 것 같아요. 적당한 시간만큼 미룰 수 있는 것...을 최적의 시간까지 연기 할 수 있다 이렇게 표현한 것 같아요. 물론 제 예상임 ㅎ)

어떤 경우에는 priority가 낮은 작업에서 시스템 자원을 재할당하고, priority가 높은 작업에 할당 할 수 있습니다.


priority가 높은 작업은 우선순위가 낮은 작업보다 더 빨리 수행되고 리소스가 많으므로 일반적으로 priority가 낮은 작업보다 더 많은 에너지가 필요합니다. 앱이 수행하는 작업에 대해 적절한 QoS클래스를 정확하게 지정하면, 앱이 에너지 효율적일뿐만 아니라, 반응적임을 보장 할 수 있습니다.(저번 문서에 나온말이네요zzz)


그러하다.



Choosing a Quality of Service Class

시스템은 QoS정보를 사용하여 스케쥴링, CPU 및 I/O처리량 및 타이머 대기 시간과 같은 priority를 조정합니다. 결과적으로 수행된 작업은 성능과 에너지 효율성 간의 균형을 유지합니다. 

task에 QoS를 할당 할 때, 사용자에게 미치는 영향과 다른 작업에 미치는 영향을 고려하세요. 다음에서 볼 수 있듯이, 4개의 primary QoS class가 있으며, 각 level은  작업 중요도에 해당됩니다.


User-interactive : main thread에서 작업, 사용자 인터페이스(UI) 새로고침 또는 애니메이션 수행과 같이 사용자와 상호작용 하는 작업입니다. 작업이 신속하게 수행되지 않으면, UI가 중단된 상태로 표시될 수 있습니다. 반응성(responsiveness)과 성능(performance)에 중점을 둡니다.

Duration of work to be performed(수행해야될 작업의 기간?) - 순식간에 끝난다.(Work is virtually instantaneous.)



User-initiated : 사용자가 시작한 작업이며, 저장된 문서를 열거나, 사용자 인터페이스에서 무언가를 클릭할 때 작업을 수행하는 것과 같은 즉각적인 결과가 필요합니다. 사용자 상호작용을 계속하려면 작업이 필요합니다. (The work is required in order to continue user interaction) 반응성과 성능에 중점을 둡니다. 

Duration of work to be performed : 거의 순식간이며, 몇 초 또는 그 이하입니다.


Utility : 작업을 완료하는 데 약간의 시간이 걸릴 수 있으며, 데이터 다운로드 또는 import와 같은 즉각적인 결과가 필요하지 않습니다. 유틸리티 작업에는 일반적으로 사용자가 볼 수 있는 progress bar가 있습니다. 반응성, 성능 및 에너지 효율성 간에 균형을 유지하는 데 중점을 둡니다. 

Duration of work to be performed : 작업은 몇초에서 몇분정도가 걸립니다. 


Background : 백그라운드에서 작동하며, indexing, 동기화 및 백업과 같이 사용자가 볼 수 없는 작업. 에너지 효율성에 중점을 둡니다.

Duration of work to be performed : 작업은 분(minutes) 또는 시간(hour)과 같은 상당한 시간(significant time)이 걸립니다.


중요 : 최적으로는, 사용자 작업이 발생하지 않는 시간의 90%이상을 Utility QoS level에서 실행하는 것이 좋습니다.



Special Quality of Service Classes

기본 QoS클래스 외에도 두가지 특수한 타입의 QoS가 있습니다. 대부분의 경우, 당신은 이러한 클래스에 노출되지 않지만(you won’t be exposed to these classes,) 그들이 존재한다는 것을 알면 여전히 가치가 있습니다. (헉 이 말 너무 멋있는...................but there is still value in knowing they exist.)


Default : 이 QoS의 priority level은 user-initiated와 utility사이에 있습니다. 이 QoS는 개발자가 작업을 분류하는데 사용하기 위한 것이 아닙니다. QoS정보가 할당되지 않은 작업은 Default로 처리되며 GCD global queue는 이 level(default)에서 실행됩니다.


Unspecified : 이는 QoS정보가 없음을 나타내며, 환경 QoS(environmental QoS)를 추론해야 한다는 단서를 시스템에 제공합니다. 쓰레드가 기존(legacy) API를 사용하는 경우, Unspecified QoS를 사용할 수 있으며, 이경우 쓰레드가 QoS를 벗어날 수 있습니다. (기존 API가 뭔데......)



Specify a QoS for Operations and Queues

(문서에는 objc기준인데 그냥 Swift3로 바꿔서 적을게용 Swift코드도 있는데 Swift 2버전ㅎ;;;;;;;;업뎃좀 해주세요 OTL)

앱이 operation과 queue를 사용하여 작업을 수행하는 경우, 해당 작업에 대한 QoS를 지정 할 수 있습니다. Operation과 OperationQueue는 둘 다 다음 값 중 하나로 설정할 수 있는 QualityOfService타입의 qualityOfService프로퍼티를 가지고 있습니다.





let myOperation: Operation = Operation()

myOperation.qualityOfService = .utility



순간 이걸 보고..엥?!?!?!?!?!?! 했습니다. normal아니었나?1?!?!?! 그렇게 읽은거 같은데 Operation queue에서..



let myOperation: Operation = Operation()

myOperation.qualityOfService = .utility

myOperation.queuePriority = .normal


그거는 queuePriority고..ㅎㅎ지금 QoS하고있다는 걸 잊지맙시다......나는 바보...아니 왜 헷갈렸냐면

아니 default QoS Default여야 같지 않나요????????아니 background????아니 순간 뜬금없이 background 나오니까...위에서도 할당안하면 Default라며...

그래서!!!!!!!!!!못믿겠어서 print해봄ㅎ_;;


let myOperation: Operation = Operation()

print(myOperation.qualityOfService.rawValue)//-1



맞자나 default...아니 제가 지금 뭘 놓치고 있는건가용?

문서 중간에 너무 당당히..있달까..괜히....헷갈리게...Objc는 다른가...?.......암튼 Feedback보냄 ㅎ


Quality of Service Inference and Promotion

QoS는 작업 및 queue에 대한 정적(static) 설정이 아니며, 다양한 기준, 시간이 지남에 따라 변동 될 수 있습니다. 예를들어, operation의 QoS와 queue의 QoS가 일치하지 않고, operation과 종속 operation이 일치하지 않거나, operation에 QoS가 할당되지 않은 경우가 발생 할 수 있습니다. 이러한 시나리오에서는 QoS를 추론 할 수 있습니다.

queue및 operation과 관련하여 QoS추론 및 승격(promotion)이 발생하는 방식은 수많은 규칙에 의해 결정됩니다.


<상황 : 결과> : OperationQueue에 대한 rule

queue에 할당된 QoS가 없으며, QoS가 있는 operation이 queue에 추가된 경우 : queue 및 기타 operation(있는 경우)은 영향을 받지 않습니다.


queue에는 QoS가 할당되어 있으며, QoS가 있는 operation이 queue에 추가되는 경우 : 새 operation의 QoS가 높으면, queue의 QoS가 승격됩니다. QoS가 낮은 queue의 operation도 승격됩니다. 나중에 queue에 추가되는 QoS가 낮은 모든 operation은 더 높은 QoS로 추론합니다.


queue의 QoS를 qualityOfService프로퍼티를 변경하여 상승한 경우 : QoS가 낮은 queue의 operation은 높은 QoS로 승격됩니다. 나중에 queue에 추가되는 QoS가 낮은 모든 operation은 높은 QoS로 추론합니다.


queue의 qualityOfService프로퍼티를 변경하여 queue의 QoS를 낮춘 경우 : queue의 operation은 영향을 받지 않습니다. 나중에 queue에 추가되는 operation은 더 높은 QoS가 할당되어 있지 않으면, 낮은 QoS로 추론합니다. 이 경우 할당된 QoS수준은 유지됩니다.


<상황 : 결과> : Operation에 대한 rule

operation에 할당된 QoS가 없는 경우 : operation은 상위 operation, queue, [NSProcessInfo performActivityWithOptions : reason : usingBlock :] 블록(이게 모지..) 또는 쓰레드(있을경우)의 QoS를 유추합니다. main thread에서 operation이 생성되는 경우, User-initiated의 QoS가 유추됩니다. 


QoS가 있는 operation이 QoS가 높은 queue에 추가되는 경우 : operation의 QoS는 queue의 QoS와 일치하도록 승격됩니다. 


operation이 포함된 queue의 QoS가 향상되는 경우 : operation은 operation의 현재 QoS보다 높은 경우, queue의 새 QoS로 추론합니다.


operation(자식)이 다른 operation(부모)에 종속되는 경우 : 부모 operation은 QoS가 더 높으면 자식 operation의 QoS를 추론합니다.


operation의 QoS가 operation의 qualityOfService 프로퍼티를 변경하여 오른 경우 : operation은 새로운 QoS를 추론합니다. 모든 하위 operation이 더 높은 경우, 모든 하위 operation은 새 QoS로 승격됩니다.  


operation의 QoS는 operation의 qualityOfService를 변경하여 낮춘 경우 : operation은 새로운 QoS를 추론합니다. 모든 하위 operation은 영향을 받지 않습니다. operation queue도 영향을 받지 않습니다.


Adjust the QoS of a Running Operation

operation이 실행되면, 다음 중 한 방법으로 QoS를 변경 할 수 있습니다.

● operation의 qualityOfService프로퍼티를 변경하세요. 이렇게 하면 operation을 실행중인 쓰레드의 QoS도 변경됩니다.

● 실행중인 operation queue에 더 높은 QoS의 새 operation을 추가하세요. 이렇게 하면 실행중인 operation의 QoS가 operation의 QoS와 일치하도록 승격됩니다.

● addDependency를 사용하여 실행중인 operation에 더 높은 QoS의 operation을 종속항목으로 추가합니다.

● waitUntilFinished 또는 waitUntilAllOperationsAreFinished를 사용하세요. (waitUntilFinished는 operation꺼고, waitUntilAllOperationsAreFinished는 operation queue꺼임) 이렇게 하면 호출자의 QoS와 일치하도록 실행중인 operation의 QoS가 향상됩니다.



Specify a QoS for Dispatch Queues and Blocks

앱에서 GCD를 사용하는 경우, QoS클래스를 dispatch queues와 block에 적용 할 수 있습니다.

Dispatch Queues

dispatch queues의 경우, queue를 만들 때, dispatch_queue_attr_make_with_qos_class를 호출하여 QoS를 지정할 수 있습니다. 

업뎃좀 해주세요 Apple님들 ㅎ흐ㅏㄹ라ㅏㅁㄴ아ㅏㅁㄴ라ㅏㄹㅁ 최근에 문서를 읽는게 제대로 공부하는게 맞는지 의심이 들기 시작했음


let zeddQos = DispatchQoS.userInteractive

let zeddQueue = DispatchQueue(label: "zedd", qos: zeddQos)



GCD QoS classes (defined in sys/qos.h)

Corresponding Foundation QoS classes

QOS_CLASS_USER_INTERACTIVE

NSQualityOfServiceUserInteractive

QOS_CLASS_USER_INITIATED

NSQualityOfServiceUserInitiated

QOS_CLASS_UTILITY

NSQualityOfServiceUtility

QOS_CLASS_BACKGROUND

NSQualityOfServiceBackground


Operation의 QoS와 GCD의 QoS가 저렇게 똑같다고 보면 됩니당. 아 물론 Objc버전..앞에 보지마시고 뒤에만 보세용.


Global Concurrent Queues

과거에는 GCD가 high, default, low, 그리고 background global concurrent queue를 제공했습니다. 이제는 해당 queue대신 해당 QoS클래스를 사용해야합니다.

Global queue

Corresponding QoS class

Main thread

User-interactive

DISPATCH_QUEUE_PRIORITY_HIGH

User-initiated

DISPATCH_QUEUE_PRIORITY_DEFAULT

Default

DISPATCH_QUEUE_PRIORITY_LOW

Utility

DISPATCH_QUEUE_PRIORITY_BACKGROUND

Background

Priority Inversions(우선순위 역전)

priority가 높은 작업이 priority가 낮은 작업에 종속되거나 priority가 낮은 작업의 결과가 되면, priority inversions이 발생합니다. 따라서 blocking, spinning 그리고 polling이 발생 할 수 있습니다. 동기식(synchronous)작업의 경우, 시스템은 역전 기간동안 priority가 낮은 작업의 QoS를 높이면 priority inversion을 자동으로 해결하려고 합니다.  비동기 작업의 경우, 시스템은 Serial queue에서 발생하는 priority inversion을 해결하려고 시도합니다.

중요 : 개발자는 priority inversion이 처음부터 발생하지 않도록;;;;;;;;;;해야하므로;;; 시스템이 해결하려고 시도하지 않도록 해야합니다.


About CloudKit and Quality of Service

앱에서 CloudKit 프레임워크를 사용하는 경우, 특정 CloudKit클래스가 기본적으로 맞춤 QoS동작을 구현한다는 점에 유의해야합니다.

● CKOperation은 Operation클래스의 하위 클래스입니다. Operation클래스의 기본 QoS는 NSQualityOfServiceBackground이지만(아니 여기서도 이렇게 나오네..이러니까ㄸ 진짜 NSQualityOfServiceBackground같잖아.....................피드백까지 보냈는데............) CKOperation객체의 기본 QoS level은 Utility입니다. 이 level에서는 앱을 사용하지 않을 때 네트워크 요청이 임의로 처리됩니다. iPhone의 경우, 저전력 모드가 활성화 되면, 임의의 작업이 일시중지됩니다.(오..)

● CKContainer는 NSObject클래스의 하위클래스입니다. CKContainer객체와의 상호작용은 기본적으로 User-Initiated QoS level에서 발생합니다.

● CKDatabase는 NSObject클래스의 하위클래스입니다. CKContainer?객체와의 상호작용은 기본적으로 User-Initiated QoS level에서 발생합니다. 읭........... CKDatabase이야기 중인데 왜 갑자기 또 CKContainer가 나오지...............Apple도 복붙을 하는건가....일단 또 피드백...보냄..근데 이거 답장?그런건 오는건가요?....버그리포트도 아니고 그냥 문서 자체에 있는 피드백이라..로그인도 안했는데... 

저거 오른쪽 아래에 있는거..암튼 제 피드백이 적용되면 넘나 기쁠 것 같은데..아 물론 제가 틀렸을 수도 있습니다... CKDatabase이야기 중에.. CKContainer 다시 나올 수 있지..그래...

아무튼 CloudKit은 제가 벼르고 있는..주제 중 하나.....꼭 글 쓰고싶네요.


Debugging Quality of Service Classes

테스트중에, Xcode에 breakpoint를 설정하거나, 앱을 일시중지 함으로서 디버그 navigator의 CPU사용량 측정기로 앱을 검사하여 요청된 QoS클래스가 적용되고 있는지 확인 할 수 있습니다.


    반응형