上一篇的源码看得十分无趣,官方文档跟黑心棉一样渣。

  这一篇讲讲windows操作系统上的时间戳实现,由于类的声明,方法解释上一篇都贴过了,所以这次直接上对应版本的代码。

  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{}; Time Time::FromFiletime(FILETIME ft) {
// 特殊情况处理
if (ft.dwLowDateTime == && ft.dwHighDateTime == ) {
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) << )) / ;
return Time(us - kTimeToEpochInMicroseconds);
}

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

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

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

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

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

Time Now() {
// 一个误差临界值
const TimeDelta kMaxElapsedTime = TimeDelta::FromMinutes(); // 我目前不想解析所有关于锁的东西
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_;
}

  不计算任何东西,直接返回系统API的时间戳,可以配合注释来理解这两个方法。  

  尴尬了,没想到V8在Time阶段把两个时间戳全用上了。稍微看了一下TimeTicks的实现,发现还有点意思,所以这一篇先这样了,太长了写的累。

深入V8引擎-Time核心方法之win篇(1)的更多相关文章

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

    这一篇讲windows系统下TimeTicks的实现. 对于tick,V8写了相当长的一段discussion来讨论windows系统上计数的三种实现方法以及各自的优劣,注释在time.cc的572行 ...

  2. 深入V8引擎-Time核心方法之mac篇

    由于底层逻辑实现不同操作系统区别很大,所以干脆分篇来说. 主要讲一下Time.TimeTicks两个类里面对于时间戳的实现,其余的运算符重载.边缘工具方法就不看了,先是Time. Time 类本身的说 ...

  3. 浅谈Chrome V8引擎中的垃圾回收机制

    垃圾回收器 JavaScript的垃圾回收器 JavaScript使用垃圾回收机制来自动管理内存.垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担,减少因 长时间运转而带 ...

  4. 浅谈V8引擎中的垃圾回收机制

    最近在看<深入浅出nodejs>关于V8垃圾回收机制的章节,转自:http://blog.segmentfault.com/skyinlayer/1190000000440270 这篇文章 ...

  5. 深入出不来nodejs源码-V8引擎初探

    原本打算是把node源码看得差不多了再去深入V8的,但是这两者基本上没办法分开讲. 与express是基于node的封装不同,node是基于V8的一个应用,源码内容已经渗透到V8层面,因此这章简述一下 ...

  6. Chrome V8引擎系列随笔 (1):Math.Random()函数概览

    先让大家来看一幅图,这幅图是V8引擎4.7版本和4.9版本Math.Random()函数的值的分布图,我可以这么理解 .从下图中,也许你会认为这是个二维码?其实这幅图告诉我们一个道理,第二张图的点的分 ...

  7. (译)V8引擎介绍

    V8是什么? V8是谷歌在德国研发中心开发的一个JavaScript引擎.开源并且用C++实现.可以用于运行于客户端和服务端的Javascript程序. V8设计的初衷是为了提高浏览器上JavaScr ...

  8. V8引擎嵌入指南

    如果已读过V8编程入门那你已经熟悉了如句柄(handle).作用域(scope)和上下文(context)之类的关键概念,以及如何将V8引擎作为一个独立的虚拟机来使用.本文将进一步讨论这些概念,并介绍 ...

  9. EF5+MVC4系列(9) Razor视图引擎的核心原理;@符号的使用;输出html的转义

    一:Razor视图引擎的核心原理 Razor是ASP.NET MVC 3中新加入的技术,以作为ASPX引擎的一个新的替代项 ,他是一个视图引擎 他的核心原理,就是当读取到 @符号的时候,就认为这是开始 ...

随机推荐

  1. Patrick and Shopping

    Patrick and Shopping 今天 Patrick 等待着他的朋友 Spongebob 来他家玩.为了迎接 Spongebob,Patrick 需要去他家附近的两家商店  买一些吃的.他家 ...

  2. Python中bisect的使用方法

    Python中列表(list)的实现其实是一个数组,当要查找某一个元素的时候时间复杂度是O(n),使用list.index()方法,但是随着数据量的上升,list.index()的性能也逐步下降,所以 ...

  3. np.newaxis()用法

    这个是liaspace函数 这个是np.newaxis的用法,增加维度,写一个表示增加一维,两个表示增加2维2位置的:号是对a的取值范围,如果把np.newaxis作为第一个参数是对行增加维度,作为第 ...

  4. 【N-Queens】cpp

    题目: The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two que ...

  5. 使用 Region,RegionManager 在 XNA 中创建特殊区域(十八)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  6. Kafka 配置参数

    Broker  Configs Property Default Description broker.id   每个broker都可以用一个唯一的非负整数id进行标识:这个id可以作为broker的 ...

  7. Mysql存储过程从0开始(上)

    1.首先你要明白,mysql也是一种语言,他也可以编写程序,也是支持逻辑判断,if,elseif,else,switch,while等等的判断 2.mysql赋值一个变量的值操作:set @a = 1 ...

  8. swiper单屏滚动

    .swiper-slide { overflow: auto; } 1. 排除某些屏,不滚动 var startScroll, touchStart, touchCurrent; var aSlide ...

  9. Linux服务器重启后nvidia-smi无法使用的解决方法

    服务器上的nvidia显卡驱动用的好好的,突然有一天,服务器断电了,然后恢复之后发现常用的nvidia-smi命令无法使用了,具体显示什么无法建立和驱动器的通信之类的,上网查了一堆,发现问题的核心:l ...

  10. python获取目录下文件夹名称

    path = '/opt' dirs = os.listdir(path) for dir in dirs: print dir