JavaScript之深入V8引擎-Time核心方法之win篇(1)
从安 2019-06-21 来源 : 阅读 1050 评论 0

摘要:本篇文章主要讲述JavaScript之深入V8引擎-Time核心方法之win篇(1),希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入。

本篇文章主要讲述JavaScript之深入V8引擎-Time核心方法之win篇(1),希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入。

JavaScript之深入V8引擎-Time核心方法之win篇(1)

  windows与mac很不一样,实现了一个新的Clock类来管理时间,如下。

// We implement time using the high-resolution timers so that we can get
// timeouts which are smaller than 10-15ms. To avoid any drift, we
// periodically resync the internal clock to the system clock.
class Clock final {
 public:
  Clock() : initial_ticks_(GetSystemTicks()), initial_time_(GetSystemTime()) {}
 
  Time Now() { /* */ }
 
  Time NowFromSystemTime() { /* */ }
 
 private:
  static TimeTicks GetSystemTicks() { /* */ }
 
  static Time GetSystemTime() { /* */ }
 
  TimeTicks initial_ticks_;
  Time initial_time_;
  Mutex mutex_;
};

 

  从注释和方法名可以看出,windows完全用这个新类代替了老的Time、TimeTicks,因为这个方法拥有更好的性能,这个类同时会周期性的与系统时间同步数据。

  下面正式开始。

 

  先从Now方法看起,看windows系统是如何获取本地的时间戳。

DEFINE_LAZY_LEAKY_OBJECT_GETTER(Clock, GetClock)
#define DEFINE_LAZY_LEAKY_OBJECT_GETTER(T, FunctionName, ...) \
  T* FunctionName() {                                         \
    static ::v8::base::LeakyObject<T> object{__VA_ARGS__};    \
    return object.get();                                      \
  }
 
Time Time::Now() { return GetClock()->Now(); }

 

  这个方法的定义也不一般,直接用了一个特殊宏,宏就不展开了,简单说就是懒加载,调用的时候会分配空间生成一个Clock类,初始化完后第二次调用就直接返回了,当成一个单例来理解。

  直接看宏的返回类型,刚好是上面的Clock,该类只有一个无参构造函数,初始化两个时间戳属性。

  先看后那个,也就是系统时间的时间戳。

static Time GetSystemTime() {
    FILETIME ft;
    ::GetSystemTimeAsFileTime(&ft);
    return Time::FromFiletime(ft);
  }

  这里的FILETIME和GetSystemTimeAsFileTime都是windowsAPI,可以获取当前系统的日期和时间,但是返回值很奇怪。

typedef struct _FILETIME {
  DWORD dwLowDateTime;
  DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;
 
Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).

 

  这是结构体的声明与解释,High、Low分别代表时间的高位与地位,而那个方法就是配合这个使用的。

  可以从上面看到,这个API返回的时间竟然是从1601年1月1日开始算的,不知道那一年发生了什么。

  下面写一个测试代码。

int main()
{
    FILETIME ft;
    LARGE_INTEGER t;
    ::GetSystemTimeAsFileTime(&ft);
    t.LowPart = ft.dwLowDateTime;
    t.HighPart = ft.dwHighDateTime;
    cout << t.QuadPart << endl;
}

 

  得到的输出为132034487665022709,由于单位是100纳秒,所以这个数字乘以100的,然后换算一下。

 

  由于基准是1601年,而Date是从1970年开始算,所以年份上差了369年,刚好是2019,很合理。

  来看看V8的处理。

// Time between windows epoch and standard epoch.static const int64_t kTimeToEpochInMicroseconds = int64_t{11644473600000000};
 
Time Time::FromFiletime(FILETIME ft) {
  // 特殊情况处理
  if (ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0) {
    return Time();
  }
  if (ft.dwLowDateTime == std::numeric_limits<DWORD>::max() &&
      ft.dwHighDateTime == std::numeric_limits<DWORD>::max()) {
    return Max();
  }
  // 换算
  int64_t us = (static_cast<uint64_t>(ft.dwLowDateTime) +
                (static_cast<uint64_t>(ft.dwHighDateTime) << 32)) / 10;
  return Time(us - kTimeToEpochInMicroseconds);
}

 

  前面的特殊情况看看就行了,主要是换算这一步,就是简单的将高低位的数值拼到了一起,除以10之后,单位从100纳秒变成了微秒。

  最后的计算,也是为了平衡标准的时间戳和windows时间戳两者的差异,如下。

 

  为什么不是1970 - 1601 = 369年整呢?因为中间有闰年,很合理。

  最后得到微秒单位的标准时间戳,将该数值赋到类的属性上。

 

  回到最初的Now方法,初始化完后,会调用Clock自身的Now方法获取最终的时间戳,如下。

Time Now() {
  // 一个误差临界值
  const TimeDelta kMaxElapsedTime = TimeDelta::FromMinutes(1);
  
  // 我目前不想解析所有关于锁的东西
  MutexGuard lock_guard(&mutex_);
 
  // 再次获取当前的硬件时间戳与本地时间戳
  TimeTicks ticks = GetSystemTicks();
  Time time = GetSystemTime();
 
  // 这里进行误差修正
  TimeDelta elapsed = ticks - initial_ticks_;
  // 1.当前时间小于初始化时间 可参考上一篇中类方法的注释(the system might adjust its clock...)
  // 2.硬件时间戳的时间差超过临界值 这种情况基本可以认定初始化的时间完全不可信了
  if (time < initial_time_ || elapsed > kMaxElapsedTime) {
    initial_ticks_ = ticks;
    initial_time_ = time;
    return time;
  }
 
  return initial_time_ + elapsed;
}

 

  虽然在构造函数中获取了时间戳,但是V8考虑到由于函数调用、系统修正等原因导致的误差(比如第一次初始化),再次进行了修正,具体操作和原因可以直接看注释,最后返回的时间戳是计算获得的理论本地时间戳加上硬件时间戳差值。

  至于NewFromSystemTime就比较简单了,在mac中这两个方法是一个,在windows里如下。

Time NowFromSystemTime() {
  MutexGuard lock_guard(&mutex_);
  // 更新两个时间戳
  initial_ticks_ = GetSystemTicks();
  initial_time_ = GetSystemTime();
  // 直接返回最新获得的时间戳
  return initial_time_;
}

 

本文由职坐标整理发布,学习更多的相关知识,请关注职坐标IT知识库!

本文由 @从安 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程