비동기 - dispatch_after가 iOS 기기에서 너무 느리게 ~ 10 % 실행됩니다.
swift 비동기 (1)
최근에 performSelector : withObject : afterDelay 대신 dispatch_after를 사용하여 지연 후 일부 코드를 트리거하려고합니다. 코드가 더 깨끗하고 범위를 접근 할 수 있습니다. throw-away 메서드를 작성하는 대신 코드를 삽입 할 수 있습니다.
내 코드는 다음과 같습니다.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
delay * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
//Delayed-execution code goes here.
}
);
그러나 최근에 나는이 코드의 실행 시간이 요청한 것보다 약 10 % 느리게 계속 실행되는 것으로 나타났습니다. 10 초의 지연을 요청하면 약 11 초 후에 블록이 실행됩니다. 이것은 iOS 기기에 있습니다. 시간은 시뮬레이터에서 아주 가깝게 일치하는 것처럼 보입니다.
이것을 테스트하기 위해 사용하는 코드는 매우 간단합니다.
NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
delay * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
NSTimeInterval actualDelay = [NSDate timeIntervalSinceReferenceDate] - startTime;
NSLog(@"Requested delay = %.3f. Atual delay = %.3f", delay, actualDelay);
//Delayed-execution code goes here.
}
);
필자는 iOS 4S에서 iPad Air로 장치를 테스트했으며 추가 지연이 거의 일정합니다. 나는 아이폰 4 나 아이 패드 2와 같은 구형 장치에 대해서는 아직 테스트하지 않았다.
나는 딜레이에서 20-50ms의 "slop"을 기대할 수 있지만 일관된 10 % - 11 %의 오버 슛은 이상합니다.
여분의 지연을 조정하는 코드에 "퍼지 요소"를 추가했지만 놀라웠습니다.
#define delay_fudge 0.912557 //Value calculated based on averages from testing.
NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
delay * delay_fudge * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
NSTimeInterval actualDelay = [NSDate timeIntervalSinceReferenceDate] - startTime;
NSLog(@"Requested delay = %.3f. Actual delay = %.3f", delay, actualDelay);
//Delayed-execution code goes here.
}
);
나는 아마도 더 많은 분석을해야만하고 지연 요소에 고정 된 증가량, 지연 요소 또는 직선 백분율 지연, 또는 오류에 대한 비선형 척도가 있는지 확인해야하지만, 지금은 단순한 곱셈기가 꽤 잘하는 것처럼 보입니다.
Timer Coalescing과 App Nap 에 대해 들어 보셨을 것입니다. 이는 전력 소비를 줄이는 데 도움이됩니다.
여기서 관찰하고있는 것은 시스템 이벤트를 한 시점에서 모두 함께 실행할 수 있도록 일정한 "여유 값"까지 지연시키는 효과입니다. "타이머 통합". 그러면 CPU가 전력 감소 모드에 머무를 수있는 기간이 늘어납니다.
dispatch lib에는 "여가 값"의 정확도를 높이는 데 사용할 수있는 플래그가 있으며, 이는 결국 타이머의 정확도에도 영향을줍니다 (아래 참조). 나는 타이머를 불필요하게 정확하게 만드는 것이 좋은 생각이라고 생각하지 않는다. 예를 들어 모바일 장치의 경우.
내 생각에, dispatch_after
는 특정 여유 값 세트가있는 디스패치 타이머를 사용하며 구현 정의됩니다.
dispatch_source_set_timer()
사용하여 디스패치 라이브러리로 매우 정확한 타이머를 구현할 수 있습니다. 여기서 "여가 값"도 지정할 수 있습니다.
참고 : dispatch/source.h
/*!
* @typedef dispatch_source_timer_flags_t
* Type of dispatch_source_timer flags
*
* @constant DISPATCH_TIMER_STRICT
* Specifies that the system should make a best effort to strictly observe the
* leeway value specified for the timer via dispatch_source_set_timer(), even
* if that value is smaller than the default leeway value that would be applied
* to the timer otherwise. A minimal amount of leeway will be applied to the
* timer even if this flag is specified.
*
* CAUTION: Use of this flag may override power-saving techniques employed by
* the system and cause higher power consumption, so it must be used with care
* and only when absolutely necessary.
*/
#define DISPATCH_TIMER_STRICT 0x1
...
* Any fire of the timer may be delayed by the system in order to improve power
* consumption and system performance. The upper limit to the allowable delay
* may be configured with the 'leeway' argument, the lower limit is under the
* control of the system.
*