비동기 - 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.
 *




grand-central-dispatch