High Precision Timers in iOS / OS X

The note will cover the do's and dont's of using high precision timers on iOS and OS X.

High Precision Timers in iOS / OS X

The note will cover the do's and dont's of using high precision timers on iOS and OS X.

Do I need a high precision timer?

Don't use a high precision timer unless you really need it. They consume compute cycles and battery. There can only be a limited number of high precsion timers active at once. A high precision timer is "first in line", and not every timer can be first. When too many try, all the timers lose accuracy.

Example applications that are candidates for high precision timers are games that need to provide precise frame rates, or daemons that are streaming data to hardware with limited buffering, such as audio or video data.

A suggestion for synchronizing with display updates

If you're writing code that needs to synchronize with frame buffer or display updates, Apple has already done much of the hard work for you. If you are developing for iOS, please see the CADisplayLink class, found in the QuartzCore.framework. If you are targeting OS X, see CVDisplayLink, also found in QuartzCore.framework.

iOS: CADisplayLink Class Reference

OS X: CVDisplayLink Reference

How do timers work?

There are many API's in iOS and OS X that allow waiting for a specified period of time. They may be Objective C or C, and they take different kinds of arguments, but they all end up using the same code inside the kernel. Each timer api tells the kernel it needs to wait until a certain time, for example 10 seconds from now. The kernel keeps track of every thread, and when a timer request comes in, that thread is marked as "I'd like to run in 10 seconds".

The kernel tries to be as frugal as possible with cpu cycles, so if there is no other work to do, it will put the cpu's to sleep for 10 seconds, then wake up and run your thread.

Of course, that is an optimum situation, and in the real world, things never seem to work that easily! In a real situation, there are many threads that want to run, and many threads making timer requests, and the kernel has to manage them all. With thousands of threads and only a few cpu's, its easy to see how timers might be inaccurate.

How do high precision timers work?

The only difference between a regular timer and a high precision timer is the scheduling class of the thread making the timer request. Threads that are in the real time scheduling class get first class treatment. They go to the front of the line whenever they need to run. If there is a conflict with multiple threads wanting to run in 10 seconds, a real time thread always goes first.

How do I get put into the real time scheduling class?

Listing 1  The following code will move a pthread to the real time scheduling class

#include <mach/mach.h>
#include <mach/mach_time.h>
#include <pthread.h>
 
void move_pthread_to_realtime_scheduling_class(pthread_t pthread)
{
    mach_timebase_info_data_t timebase_info;
    mach_timebase_info(&timebase_info);
 
    const uint64_t NANOS_PER_MSEC = 1000000ULL;
    double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
 
    thread_time_constraint_policy_data_t policy;
    policy.period      = 0;
    policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work
    policy.constraint  = (uint32_t)(10 * clock2abs);
    policy.preemptible = FALSE;
 
    int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
                   THREAD_TIME_CONSTRAINT_POLICY,
                   (thread_policy_t)&policy,
                   THREAD_TIME_CONSTRAINT_POLICY_COUNT);
    if (kr != KERN_SUCCESS) {
        mach_error("thread_policy_set:", kr);
        exit(1);
    }
}

The period, computation, constraint, and preemptible fields do have an effect, and more can be learned about them at:

Using the Mach Thread API to Influence Scheduling

Which timing API(s) should I use?

As mentioned above, all the timer methods end up in the same place inside the kernel. However, some of them are more efficient than the others. At the time this note was written, mach_wait_until() is the api we would recommend using. It has the lowest overhead of all the timing API that we measured. However, if you have specific needs that aren't met by mach_wait_until(), for example you need to wait on a condvar, then feel free to use the appropriate timer API.

Listing 2  This example code demonstrates using mach_wait_until() to wait exactly 10 seconds.

#include <mach/mach.h>
#include <mach/mach_time.h>
 
static const uint64_t NANOS_PER_USEC = 1000ULL;
static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;
 
static mach_timebase_info_data_t timebase_info;
 
static uint64_t abs_to_nanos(uint64_t abs) {
    return abs * timebase_info.numer  / timebase_info.denom;
}
 
static uint64_t nanos_to_abs(uint64_t nanos) {
    return nanos * timebase_info.denom / timebase_info.numer;
}
 
void example_mach_wait_until(int argc, const char * argv[])
{
    mach_timebase_info(&timebase_info);
    uint64_t time_to_wait = nanos_to_abs(10ULL * NANOS_PER_SEC);
    uint64_t now = mach_absolute_time();
    mach_wait_until(now + time_to_wait);
}

How accurate should high precision timers be?

Timer accuracy depends on many factors, including the type of hardware being run on, the load on that hardware, and the power available (battery vs plugged in). However, if an otherwise unloaded machine or device is consistently missing your scheduled time by more than 500 microseconds, that should be considered an error, please file a bug with Apple. Often times we can do much better than that, but its best to measure for yourself.

Timers are not accurate across a sleep and wake cycle of the hardware.

What are the do's and dont's of code running with high precision timers?

Do use as little cpu as possible during your timer loop. Remember that your thread now has special privileges, and when you're running, no one else can. Do try to stretch out your timer requests as much as you safely can. This allows the kernel to use less battery by sleeping more often and longer.

Don't spin loop! This burns cpu and battery at very high rates, and when newer faster hardware is released you'll burn cpu and battery even faster!

Don't create large numbers of real time threads. Real time scheduling isn't magic, it works by making your thread higher priority than other threads on the system. If everyone tries to crowd to the front of the line, all the real time threads will fail.

High Precision Timers in iOS / OS X的更多相关文章

  1. iOS/OS X线程安全的基础知识

    处理多并发和可重入性问题,是每个库发展过程中面临的比较困难的挑战之一.在Parse平台上,我们尽最大的努力保证你在使用我的SDKs时所做的操作都是线程安全的,保证不会出现性能问题. 在这篇文章中我们将 ...

  2. Pop - Facebook 开源 iOS & OS X 动画库

    Pop 是一个可扩展的 iOS & OS X 动画引擎.除了基本的静态动画,它支持弹簧和动态衰减的动画,因此可以用于构建现实的,基于物理的交互效果. 它的 API 可以与现有的 Objecti ...

  3. IOS OS X 中集中消息的传递机制

    1 KVO (key-value Observing) 是提供对象属性被改变是的通知机制.KVO的实现实在Foundation中,很多基于 Foundation 的框架都依赖与它.如果只对某一个对象的 ...

  4. timer实现

    实现一个 timer 前段时间写过一篇 blog 谈到 用 timer 驱动游戏 的一个想法.当 timer 被大量使用之后,似乎自己实现一个 timer 比用系统提供的要放心一些.最近在重构以前的代 ...

  5. OS开发小记:iOS富文本框架DTCoreText在UITableView上的使用

    要在页面中显示自己的布局,比如文字的字体和颜色.图文并排的样式,我们要用iOS SDK的原生UI在app本地搭建,如果一个页面需要在服务器端获取数据的话,我们也要在本地搭建好固定的布局,解析服务器传回 ...

  6. iOS 精确定时器

    Do I need a high precision timer? Don't use a high precision timer unless you really need it. They c ...

  7. Github上关于iOS的各种开源项目集合(强烈建议大家收藏,查看,总有一款你需要)

    下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITableVie ...

  8. 浅谈iOS视频开发

     浅谈iOS视频开发 这段时间对视频开发进行了一些了解,在这里和大家分享一下我自己觉得学习步骤和资料,希望对那些对视频感兴趣的朋友有些帮助. 一.iOS系统自带播放器 要了解iOS视频开发,首先我们从 ...

  9. iOS开发中可能有用的那些分类们Categories

    Categories是给你得不到源码的classes增加功能的一种方法. UIImageView+FaceAwareFill 这个类别使用了Aspect Fill内容模式,可以自动根据图像内容进行调整 ...

随机推荐

  1. iOS及Mac开源项目和学习资料【超级全面】

    UI 下拉刷新 EGOTableViewPullRefresh – 最早的下拉刷新控件. SVPullToRefresh – 下拉刷新控件. MJRefresh – 仅需一行代码就可以为UITable ...

  2. 复旦高等代数 I(15级)思考题

    1.证明: 第三类分块初等变换是若干个第三类初等变换的复合. 特别地, 第三类分块初等变换不改变行列式的值. 2.设 $n\,(n\geq 2)$ 阶方阵 $A=(a_{ij}(x))$, 其中每个元 ...

  3. uploadify springMVC

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  4. Delphi IDE 设置

    显示编译进度 Tools/Environment Options

  5. 中国Linux源镜像站大全

    原文链接:http://www.centoscn.com/yunwei/news/2012/1227/131.html 一.国内的linux源镜像站点: 1.企业源:阿里云开源镜像站: http:// ...

  6. Java命令提示符编译

    Java利用命令提示符编译 1:最简单的方式:直接编译 /** 文件路径:G:\测试项目\java\src 文件名称:JacaText.java 编写时间:2016/6/2 作 者:郑晨辉 编写说明: ...

  7. JAVA 使用POI导出数据格式为Execl

    需要下载一个poi的jar包. 控制器 @Override public void getContractListExecl(Contract contract, BindingResult resu ...

  8. neon指令,注意事项

    1. vbic_s8 (int8x8_t a, int8x8_t b) 是  ~(ai & bi),一开始理解成  (~ai )& bi 导致出错 2.uint8x8_t vqshrn ...

  9. Git基础操作

    配置秘钥 1.检查本机有没有秘钥 检查~/.ssh看看是否有名为d_rsa.pub和id_dsa.pub的2个文件. $ ~/.sshbash: /c/Users/lenovo/.ssh: Is a ...

  10. Sqlserver2012 中文乱码解决

    1.在Windows Azure的数据库中,如果选择默认字符编码,那么在创建表字段是,字符串类型应该为nvarchar,如果是varchar将会出现乱码,同样的的在sql语句中生命变量,也是需要将字符 ...