Подтвердить что ты не робот

Вы заметили, что dispatch_after работает на слишком медленном уровне на 10% на устройствах iOS?

В последнее время я использую dispatch_after вместо executeSelector: withObject: afterDelay, когда я хочу запускать некоторый код после задержки. Код чище, он имеет доступ к охватывающей области, я могу поместить код в строку вместо написания метода выброса и т.д. И т.д.

Мой код может выглядеть так:

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, и дополнительная задержка довольно последовательна. Я еще не тестировал более старое устройство, такое как iPhone 4 или iPad 2, хотя я скоро это сделаю.

Я мог бы ожидать 20-50 мс "отскока" в задержке, но непротиворечивость 10% - 11% нечетна.

Я добавил в код код "fudge factor", который настраивается для дополнительной задержки, но я нахожу это удивительным:

#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.
  }
);

Я должен, вероятно, сделать больше анализа и посмотреть, есть ли фиксированное увеличение задержки плюс коэффициент задержки или прямая процентная задержка или, возможно, некоторый нелинейный масштаб ошибки, но на данный момент простой множитель, кажется, делает довольно хорошо.

4b9b3361

Ответ 1

Возможно, вы слышали о Timer Coalescing и App Nap, что помогает снизить энергопотребление.

То, что вы наблюдаете здесь, - это эффект отсрочки системных событий до определенного "значения скорости", чтобы иметь возможность выполнять их все вместе в один момент времени "Таймер коалесцирования". Это увеличит продолжительность работы CPU в режиме пониженной мощности.

Для отправки lib существует флаг, который может быть использован для повышения точности "значения отклонения", что также в конечном итоге влияет на точность таймера (см. ниже). Я не думаю, что это хорошая идея сделать таймеры ненужными точными - например, для мобильных устройств.

Мое подозрение в том, что dispatch_after будет использовать таймер отправки с определенным набором значений, который определяется реализацией.

Вы можете реализовать довольно точные таймеры с диспетчером lib, используя dispatch_source_set_timer(), где вы также можете указать значение "leeway value".

См. также: 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.
 *