设为首页 加入收藏

TOP

深入V8引擎-Time核心方法之win篇(2)(一)
2019-09-17 19:00:38 】 浏览:77
Tags:深入 引擎 -Time 核心 方法 win篇

  这一篇讲windows系统下TimeTicks的实现。

  对于tick,V8写了相当长的一段discussion来讨论windows系统上计数的三种实现方法以及各自的优劣,注释在time.cc的572行,这里直接简单翻译一下,不贴出来了。

CPU cycle counter.(Retrieved via RDTSC)

  CPU计数器拥有最高的分辨率,消耗也是最小的。然而,在一些老的CPU上会有问题;1、每个处理器独立唯一各自的tick,并且处理器之间不会同步数据。2、计数器会因为温度、功率等原因频繁变化,有些情况甚至会停止。

QueryPerformanceCounter (QPC)

  QPC计数法就是之前libuv用的API,分辨率也相当的高。比起CPU计数器,优点就是不存在多处理器有多个tick,保证数据的唯一。但是在老的CPU上,也会因为BIOS、HAL而出现一些问题。

System Time

  通过别的windowsAPI返回的系统时间来计数。

 

  上一篇Clock类的构造函数中,对TimeTicks属性的初始化也只是调用了老TimeTicks的Now方法,所以直接上Now的代码。

TimeTicks InitialTimeTicksNowFunction();

using TimeTicksNowFunction = decltype(&TimeTicks::Now);
TimeTicksNowFunction g_time_ticks_now_function = &InitialTimeTicksNowFunction;

TimeTicks TimeTicks::Now() {
  TimeTicks ticks(g_time_ticks_now_function());
  DCHECK(!ticks.IsNull());
  return ticks;
}

  windows系统下,会预先一个初始化方法,这里的语法不用去理解,只需要知道调用InitialTimeTicksNowFunction方法后,将其返回作为参数构造一个TimeTicks对象,返回的就是硬件时间戳。

  这个方法比较简单,如下。

TimeTicks InitialTimeTicksNowFunction() {
  InitializeTimeTicksNowFunctionPointer();
  return g_time_ticks_now_function();
}

  可以看到,那个g_time_ticks_now_function又被调用了一次,但是作为一个函数指针,第二次调用的时候指向的就不是同一个方法。至于为什么特意弄一个函数指针,后面会具体解释。

  看这里的第一个方法。

void InitializeTimeTicksNowFunctionPointer() {
  LARGE_INTEGER ticks_per_sec = {};
  if (!QueryPerformanceFrequency(&ticks_per_sec)) ticks_per_sec.QuadPart = 0;

  // 如果windows不支持QPC或者该方法不可靠 会降级去使用低分辨率的lowB方法
  TimeTicksNowFunction now_function;
  CPU cpu;
  // QPC不好使的情况
  if (ticks_per_sec.QuadPart <= 0 || !cpu.has_non_stop_time_stamp_counter() ||
      IsBuggyAthlon(cpu)) {
    now_function = &RolloverProtectedNow;
  }
  // 好使的情况 
  else {
    now_function = &QPCNow;
  }

  // 这里不需要担心多线程问题 因为更改的都是同一个全局变量
  g_qpc_ticks_per_second = ticks_per_sec.QuadPart;
  // 先不管这个 不然讲不完
  ATOMIC_THREAD_FENCE(memory_order_release);
  g_time_ticks_now_function = now_function;
}

  从几个赋值可以看到,整个函数都是围绕着函数指针now_function的指向,其实也就是g_time_ticks_now_function,根据系统对QPC的支持,来选择不同的方法实现TimeTicks。

  所以,特意用一个函数指针来控制Now方法的目的也明显了,理论上只有第一次调用会进到这个特殊函数,检测当前操作系统的QPC是否适用,然后选择对应的方法。后面再次调用的时候,就直接进入选好的方法(具体思想可以参考《java script高级程序设计》高级技巧章节的惰性载入函数)。这个情况有一点像我在解析node事件轮询时提到的线程池初始化情形,不同的是,这里V8没有特意去加一个锁来防止多线程竞态。原因也很简单,因为此处只是对一个全局的函数指针做赋值,就算多赋值几次对后续的线程并没有任何影响,没有必要特意做锁。

  关于QueryPerformanceFrequency方法(这些函数名都好TM长)的具体用法,可以参考我别的博客,啥都解释写不完啦。

  存在两种情况的实现,先看支持QPC的,删掉了合法性检测宏,这些宏无处不在,太碍眼了。

TimeTicks QPCNow() { return TimeTicks() + QPCValueToTimeDelta(QPCNowRaw()); }

V8_INLINE uint64_t QPCNowRaw() {
  LARGE_INTEGER perf_counter_now = {};
  // According to the MSDN documentation for QueryPerformanceCounter(), this
  // will never fail on systems that run XP or later.
  // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
  // 这里说理论上XP以后的系统都支持QPC
  BOOL result = ::QueryPerformanceCounter(&perf_counter_now);
  return perf_counter_now.QuadPart;
}

// To avoid overflow in QPC to Microseconds calculations, since we multiply
// by kMicrosecondsPerSecond, then the QPC value should not exceed
// (2^63 - 1) / 1E6. If it exceeds that threshold, we divide then multiply.
static constexpr int64_t kQPCOverflowThreshold = INT64_C(0x8637BD05AF7);

TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) {
  /
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇聊聊webpack 4 下一篇前端笔记之React(一)初识React&..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目