DateTime.Now是一个I/O绑定操作吗?

19

当您调用 DateTime.Now 时会发生什么?

我跟随 Reflector 中的属性代码,它似乎将当前区域设置的时区偏移量添加到 UtcNow 中。跟随 UTCNow 一步一步地进行后,最终导致了一个 Win32 API 调用。

我思考了一下,并在 相关问题 上提出了问题,但还没有得到满意的答复。从那个问题的评论中的链接来看,似乎有一个硬件单元保存时间。但我也想知道它保存时间的单位以及它是否使用 CPU 将时间转换为人类可读的单位。这将阐明日期和时间信息的检索是 I/O 绑定还是计算绑定。


IO和计算绑定是相当不明确的术语。您能否澄清您感兴趣的属性或想要实现的目标? - usr
我认为带日期是I/O绑定的,使用DateTime.Now进行转换(它返回可读的值,对吗?)是计算绑定的。那么,两者都是吗? - Ali1928
@usr 由于我对你的问题和I/O和计算绑定概念的理解有限,我无法回答你的问题。 我不确定自己想要什么。 我只是想知道会发生什么,这样我才能计划下一次如何调用DateTime.Now,以便在任何情况下都不会被阻止。 这只是最近让我思考的事情之一。 - Water Cooler v2
1
如果你告诉我们它调用了哪个Win32 API,我们就能给你更好的答案。戏弄一下。 - Bruce Dawson
@BruceDawson 这是通过BCL中类似命名的P/Invoke方法调用GetSystemTimeAsFileTime的。 - Water Cooler v2
显示剩余3条评论
3个回答

35
您在这个问题中涉及到了未经记录的领域。时间由内核提供:底层的本地API调用是NtQuerySystemTime()。随着Windows版本的更迭,它确实得到了修改-特别是Windows 8改变了底层实现方式,并带来了可见的副作用。
它的本质是I/O绑定的:时间是由RTC(Real Time Clock)维护的,它曾经是专用芯片,但现在已集成在芯片组中。但有很强的证据表明,在实践中它并不是I/O绑定的。时间与时钟中断同步更新,所以很可能是中断处理程序读取RTC,然后您会得到该值的副本。当您玩弄timeBeginPeriod()时,这是您可以看到的东西。
当您对其进行分析时,您会发现它在Windows 10上仅需要约7纳秒-完全不可能是I/O绑定的速度。

2
它位于内核中,RTC很可能是中断源。它还严重影响线程调度器,上下文切换发生在中断率上。这就是为什么Sleep()永远不可能比速率更准确的基本原因。更深入的未记录细节不应影响您编码的方式。 - Hans Passant
1
实际上,我认为RTC只在启动时使用,然后内核使用其他计时器(可能是主CPU时钟)自行保持时间。 - André Borie
@AndréBorie HPET使用不同的时序机制,现在在硬件上。当这不可用(旧硬件)时,使用RTC和CPU计时器的组合。据我所知,诸如DateTime.NowSleep等内容使用RTC。您实际上可以在循环中进行测试并查看值以15毫秒的间隔跳动,这是RTC的典型特征。 - atlaste
如果您要将RTC作为I/O进行访问,那么在哪里停止呢?访问内存也属于I/O吗? - Damien_The_Unbeliever
我已经使用benchmarkdotnet对DateTime.Now进行了基准测试,并且在Windows 10(CPU E3-1270 v5 3.60GHz,ProcessorCount = 8)上平均需要627ns,而不是7ns。返回缓存值只需要0.3ns。 - Tim Schmelter

4
您似乎关心阻塞。有两种情况需要避免。
1. 在UI线程上,这关乎延迟。无论您做什么(IO或CPU),都不能花费太长时间。否则,它会冻结UI线程。`UtcNow`非常快,所以这不是问题。
2. 有时,非阻塞IO被用作一种方式来扩展吞吐量,随着负载的增加。在这种情况下,唯一的原因是为了节省线程,因为每个线程消耗大量资源。由于没有异步调用`UtcNow`的方法,所以这个问题无从谈起。您只需按原样调用它即可。
由于Windows上的时间通常以60 Hz推进,我会认为对`UtcNow`的调用会读取一个以60 Hz写入的内存变量。这使得它是CPU绑定的。但无论哪种方式都没关系。

有趣!你知道关于 ~60Hz Windows 时间进度的文章吗?我谷歌搜索了一下,但是所有的结果都是关于显示器刷新率的。 - Isaac
1
它是64 Hz,从不是60。当您使用SO时,往往会达到100 Hz,浏览器也会对其进行调整 :) - Hans Passant
1
现代计算机上它会有很大的变化。能源管理和应用程序可以调整它。SysInternals开发了一个工具来监视它:https://technet.microsoft.com/en-us/sysinternals/bb897568.aspx - Cee McSharpface
内置于Windows中,powercfg.exe /energy非常流畅。 - Hans Passant

3

.NET依赖于API。MSDN对API有如下说明:

https://msdn.microsoft.com/de-de/library/windows/desktop/ms724961(v=vs.85).aspx

系统启动时,它将系统时间设置为基于计算机实时时钟的值,然后定期更新时间[...] GetSystemTime将时间复制到SYSTEMTIME中[...]

我没有找到可靠的来源来支持我的观点,即它存储为SYSTEMTIME结构,在其中进行更新,并在调用GetSystemTime时只是复制到接收缓冲区中。最小逻辑单位是100纳秒,从NtQuerySystemTime系统调用中获得,但在CLR的DateTime对象中,我们得到1毫秒。分辨率并不总是相同。

我们可能能够弄清楚Linux上的Mono,但由于API代码本身不是公开的,因此几乎无法在Windows上弄清楚。所以这里是一个假设:当前时间是内核地址空间中的变量。操作系统将更新它(通常通过系统时钟定时器中断,较少通过网络源 - 文档提到调用者可能不能依赖于单调行为,因为网络同步可以将当前时间向后更正)。操作系统将同步访问以防止并发写入,但除此之外,它不会是一个I/O密集型操作。

在最近的计算机上,定时器间隔不再固定,并且可以由BIOS和操作系统控制。应用程序甚至可以请求更低或更高的时钟速率:https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted


1
最小的逻辑单位实际上是100纳秒。 - Joker_vD
CLR中有一个名为DateTime.Millisecond的成员,文档中描述为“毫秒组件,表示为0到999之间的值”,而_SYSTEMTIME.wMilliseconds字段则被记录在文档中为"毫秒。该成员的有效值为0到999"。如果要查看ns或者甚至是µs,我们需要查找GetSystemTimePreciseAsFileTime此链接,这些已经足够成为一个单独问题的材料了。 - Cee McSharpface

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接