본문 바로가기

Software/iOS & Objective-C

Concurrency Programming Guide 요약


해당 글은 https://byunsooblog.wordpress.com/2013/03/26/concurrency-programming-guide-요약/ 에서 작성되었습니다.

Concurrency Programming Guide

concurrency and application design

예전에는 일하는 시간이 CPU의 clock 속도로 결정이 됐었다. 칩의 효율이 점점 발전하니까 다른 성능을 발전시킬 방법이 없나 찾게됬고, 결국 해결방법은 각 칩의 프로세서 코어의 숫자를 늘리는 것. 이제 속도는 빨라지게 됬지만,  신경써야 할 점은 어떻게 추가된 코어의 이점을 이용할 것인가가 되었다. 여러개의 코어의 이점을 얻기 위해서는 소프트웨어가 여러가지 일을 동시에 하도록 만들어져야 한다. 여러개의 코어를 사용하는 방법은 스레드를 여러개 만드는 것인데, 이 방법은 문제가 있다. 그 문제는 스레드를 위한  코드가 임의의 숫자인 코어들을 잘 사용하지 못하는 것 이다. 알아야 할 것은 효율적으로 사용될 수 있는 코어의 숫자이다. 그 숫자를 알아도 처리해야 할 일은 많은데, 그 문제를 해결하기 위해 os가 도와준다.

The Move Away from Thread

스레드를 사용하는 대신, iOS랑 OS X는 concurrency 문제를 해결하기 위해서 asynchronous design approach를 사용한다. 비동기 방식은 나온지 꽤 됬는데, 데이터를 읽거나 하는 오랜 시간이 걸리는 일에 사용 된다. 이게 불리면 씬 뒤에서 해야 하는 일을 하고 실제로 완성될 때 쯤에 리턴을 한다. 보통 백그라운드 스레드를 얻어서, 해야 하는 일을 스레드에서 하고, 일이 완료되면 부른 애한테 결과 Notification을 보낸다. (보통 이건 콜백 함수) 예전에는 asynchronous function을 위해서 직접 코드를 짜야 했지만 요즘은 OS에서 기술을 제공해준다. 

OS에서 제공해주는 기술 중 하나가 GCD.
이 기술이 보통 어플리케이션에 쓸 스레드 관리하는 코드를 시스템 레벨로 옮긴다. 우리가 해야하는 건 실행되기 원하는 task를 정의하고 적절한 dispatch queue에 추가를 하면 된다. GCD가 필요한 스레드를 만들고 task들을 스케쥴링해서 스레드를 실행하는 일들을 해준다. 스레드 관리는 이제 시스템이 하니까, GCD는 task 관리와 실행을 하도록 전체적으로 접근하게 해주고, 예전 스레드 만드는 방식보다 효율이 좋게 해준다. 

다른 방법은 Operation Queue.
이건 dispatch queue같이 작동하는 objective-c 객체이다. 이것도 실행하기 원하는 일을 task로 정의하고 operation queue에 추가한다. GCD처럼 operation queue가 스레드 관리를 하고 task가 가능하면 빨리, 효율적으로 실행될 수 있도록 해준다.

Dispatch Queue

Dispatch queue는 task를 실행하기 위한 C 기반의 매커니즘이다. dispatch queue는 순차적으로나 동시적으로 일을 실행하는데, 언제나 FIFO다. 순차적인 큐는 한번에 하나의 task만 실행한다. 대조적으로 동시적인 큐는 가능하면 많은 task를
시작하고, 이미 시작한 task가 끝나는걸 기다리지 않는다. 

dispatch queue 의 이점 :
직관적이고 간단한 프로그래밍 인터페이스를 제공한다 자동적이고, 전체적인 스레드 관리 풀을 제공한다.
어셈블리를 변경해서 빠르게 했다.
메모리 효율적임 (어플리케이션 메모리 안에서 스레드 스택이 남아있지 않기 때문)
They do not trap to the kernel under load task 를 asynch 하게 dispatch하면 데드락에 걸릴 수 없다.
They scale gracefully under contention.
순차적인 dispatch 큐는 락이나 다른 synchronization 의 기본 요소에 대한 효율적인 대안점을 제공함. dispatch queue에 보내는 task는 함수 안이나 block 객체 안에 요약되어있어야 한다. block함수는 C언어 기능인데, iOS4.0부터 지원이 된다.
개념상으로는 함수 포인터와 비슷하다. 하지만 추가적인 이익이 있다. 블락은 블락의 문법적인 공간에 정의하는 대신에, 다른 함수나 메소드 안에 정의한다. 그래서 그래서 블락이 그 함수나 메소드 안의 다른 변수들에 접근 할 수 있다.
dispatch queue에 블락을 보내면 원래의 범위에서 옮겨져서 힙으로 복사될 수 있다. Dispatch Queue는 GCD 와C runtime 기술 파트 중에 하나이다.

Dispatch Sources

Dispatch Source는 특정한 시스템 이벤트를 asynchronous하게 처리하기 위한 C 기반의 메커니즘이다.
Dispatch Source는 이벤트가 발생하면, 특정한 시스템 이벤트 타입에 대한 정보를 요약하고 정해진 block 객체나 함수를 dispatch queue로 보낸다. 

Dispatch source는 아래 시스템 이벤트 타입으로 모니터 할 수 있다. 

Timers
Signal handlers
Descriptor-related events
Process-related events
Mach port events
Custom events that you trigger

Dispatch source도 GCD 기술 중 하나

Operation Queues

Operation Queue는 concurrent dispatch queue와 동등한 Cocoa이다. 그리고 NSOperationQueue 클래스에 의해 구현되어있다. Dispatch Queue는 언제나 FIFO로 실행되지만, Operation Queue는 task의 실행 순서를 결정할 때 다른 factor도 계산에
넣는다. 이런 factor중 중요한 점은 주어진 task가 다른 task의 완성에 영향을 받는지 안받는지 이다.
task를 정의할 때 이런 의존도를 설정할 수 있다. Operation Queue 에 보내는 task는 NSOperation의 instance여야한다. Operation Object는 Objective-C 객체이고, 수행했으면 하는 일과 그 일에 필요한 데이터가 요약되어있다. NSOperation은 기본적으로 base 클래스를 상속받기 때문에, 보통 task를 수행하게 하기 위해서 커스텀한 서브클래스를 정의한다. Foundation 프레임워크가 이런 서브클래스들을 정해서 포함하고 있다. Operation 객체는 Key-value observing (KVO)
노티피케이션을 만드는데, 이건 task의 처리가 어떻게 되어가는지 모니터링하기에 좋다. Operation 큐가 언제나 operation을 concurrently하게 실행을 하더라도, dependency를 이용해서 필요하면 순차적으로 실행되게 할 수 있다.

Asynchronous Design techniques

코드가 Concurrency 하도록 디자인을 다시 고려할 때, 이게 정말 필요한건지 생각해볼 필요가 있다.
Concurrency 메인 스레드가 사용자 이벤트에 편하게 응답하도록 코드의 반응이 빨라지게 개선할 수 있다. 코어 를 더 써서 일을하게 만들어서 효율적으로 코드가 돌게 할 수도 있다. 그렇지만 코드의 복잡성과 오버헤드는 증가한다. 코드 쓰기도 힘들게 하고 디버깅 하기도 힘들게 된다. 디자인을 잘 하지않으면 더 느려질 수 있기 때문에 조심해야 한다.
어플리케이션마다 다른 요구사항과 task일이 다르기 때문에 가이드를 정할 수는 없다. 

Define Your Application’s Expected Behavior
어플리케이션이 무슨 일을 할지 이해하면, 디자인이 결정된다. 처음 해야 할 일은 어플리케이션이 수행하는 일과 그 task 에 연관된 객체나 데이터 구조를 열거해야 한다. 처음에는 사용자가 메뉴 버튼을 눌러서 시작하는 task가 있을수도 있고, 사용자와는 상관없는 타이머 기반의 task가 있을 수 있다.
high-level의 task를 나열한 후, 각 task를 작은일로 쪼갠다. 그리고 각 객체와 데이터 구조의 의존성도 확인해야한다. 예를들어 배열을 여러 객체에서 고칠 때, 다른 객체도 그 사실을 알아야 한다. 아니면 그 배열이 각 객체마다 독립적으로 수정된다면, concurrent하게 수행되도 된다. 

Factor Out Executable Units of Work
dispatch queue와 operation queue를 쓰는 장점은 기존의 스레드를 쓰는 것보다 많은 경우에 cost가 적게 든다. 언제나 성능을 측정하고 task의 사이즈를 알맞게 적용해야 하지만, 너무 task를 작게 고려하지 않아도 된다.

Identify the Queue You Need
task를 블락 객체나 Operation 객체를 이용해서 작게 만들었으면, 이제 큐를 정해야 한다. 만약 블락을 써서 task를 구현하면, 순차적이나 동시적으로 큐에 추가할 수 있다.
정해진 순서대로 실행을 해야되면, 순차적인 dispatch 큐에다가 블락을 추가해야한다. 순서가 필요하지않으면 concurrent dispatch 큐에 블락을 추가하거나 몇개의 다른 큐들에다가 추가를 할 수 있다. Operation 객체를 써서 task를구현하면, 큐에대한 선택보다는 객체에 더 신경을 쓴다. operation 객체를 순차적으로 실행하기위해서, 객체와 관계된 의존성을 신경써야한다.

Tips for Improving Efficiency

메모리 사용을하면 값을 task 안에서 직접적으로 계산하는걸 고려해야한다.  
직접 값을 계산하는게 메인 메모리에 캐쉬 된 값을 로딩하는 것보다 빠르다. 레지스터나 프로세서 코어의 캐쉬를 사용해서 직접 값을 계산하는게, 메인메모리보다 빠르다.
순차적인 task를 먼저 확인하고, 더 concurrent하게 처리할 수 있도록 한다.
공유된 자원때문에 순차적으로 실행되어야 한다면, 그 공유된 자원을 지우도록 설계를 바꾸는걸 고려해보는게 좋다. 리소스의 카피를 만들 수도 있다.
lock 거는걸 피하는게 좋다.
이걸 위해서 dispatch queue나 operation queue는 lock을 대부분의 상황에서 필요없게만들고 있다. 공유된 자원에 lock을 거는 것 보다는 순차적인 큐를 써서 task를 실행하는게 더 낫다.
가능하다면 시스템 프레임워크에 의존해라. 
가장 좋은 concurrency의 이점을 얻는 방법은 시스템 프레임워크에서 제공해주는 것을 이용하는 것이다. 많은 프레임워크들이 concurrent한 것을 구현하기 위해서 내부적으로 스레드랑 다른 기술을 사용하고 있다.

Performance Implications

Operation Queue, dispatch queue, dispatch source가 코드를 concurrent하게 하는데 도움을 많이 주지만 무조건 어플리케이션이 개선되고 효율적으로 된다는 보장은 없다. 이건 여전히 큐를 사용하는 개발자의 책임이다. 예를들어 10000개의 operation 객체를 만들어서 큐에 넣을수는 있더라도 어플리케이션이 적지않은 양의 메모리를 할당하게 만들고, 페이징을 유발하거나 성능이 좋지않게 만들게 된다. 툴을 이용해서 이런 성능을 체크 해 보는게 좋다.

Concurrency and Other Technologies

OpenCL Threads 오퍼레이션 큐랑 디스페치 큐가 task를 동시적으로 수행하는데 선호되는 방식이라 하더라도, 만병통치약은 아니다. 어플리케이션에 따라서 커스텀 스레드를 만들 필요도 있다. 만약 커스텀 스레드를 만든다면 가능하면 스레드를 조금 만들려고 노력하고, 딱 필요한 task만 사용하도록 해야한다. 스레드는 여전히 실시간으로 코드가 실행되도록 구현하는데 좋은 방법이다. 디스페치 큐가 가능하면 task가 빨리 실행되도록 하지만 실시간은 아니다. 만약 백그라운드에서 예측할수있는 일을 하는 코드를 실행하려면, 스레드가 여전히 좋은 방법이다. 

링크 : 애플
iOS 개발자 사이트 

반응형

'Software > iOS & Objective-C' 카테고리의 다른 글

Audio Unit Hosting에 대하여...  (0) 2016.12.05
iOS Audio Play  (0) 2016.11.23
[iOS] App 내에서 언어 변경법  (0) 2016.03.02
클래스 기본 문법  (0) 2015.11.09
iOS Crash Log 추출  (0) 2015.11.06