闲话——我的程序到底运行了多长时间呢

· · 科技·工程

Part 1

在今年提高组初赛中,有这样一道题目:

虽然 NOI 大纲里明确提出了要会这三个命令

不少人可能都是第一次见到,于是就乱选了一个,那么问题来了,假如这是我的程序在运行,到底真正的运行时间是多少呢?

当然是最短的

首先,计算机肯定要有一个标准时间,而这个时间怎么获取,是一个问题。有人可能会说:计算机联网就自动获取了。但是,假设计算机 A 从服务器 B 处获取时间,服务器 B 的时间又是从哪里来的,这个时间一定准确吗?

Part 2

以下内容与本文主题关联性不强,仅做科普

2-1

在大部分计算机内部,有一个叫做晶体振荡器^{[1]}的东西,只要给它施加电压,就会以固定的频率震荡,因此即可测量时间。然而

误差是无处不在的

        ——我说的

这个东西受到温度啊,制造工艺等的影响,随着时间的推移,肯定会变的越来越不准确,我们不能只依赖于这一种方法,所以,计算机可以联网获取时间。

2-2

我们都知道 ,之前天的定义是地球自转一周的时间,但是自转周期也会有微小的误差,这样积累下去误差会变得不可控,秒的定义就变成了如今我们常见的样子:

以铯原子跃迁 9192631770 个周期,所持续的时间长度定义为 1 秒

好,显而易见以地球自转为标准的时间体系与以原子跃迁的时间体系并不相容,我们需要一个协调的系统,这就是

协调世界时(Coordinated Universal Time,简称 UTC)。

而例如“UTC+8”指的就是北京时间(东八区)。

2-3

现在已经有了时间了,下一步要做的就是让各个计算机知道这个时间,并不可能在每台电脑中放一个原子钟(或许在将来有可能),因此,由位于陕西省的国家授时中心,会把精确的时间用各种方式广播出去,聪明的你会想到,假设 A 在 t 时向 B 发送当前的 t,那么 B 在接受到 t 的时候已经是 t+a 时了,会有误差,这就需要 NTP 协议了。

假设有计算机 A(未同步时间),时间服务器 B,A 的时钟为 1:00:00 a.m.,B 的时钟为 2:00:00 a.m.,B 的信息传递到 A 要 1 秒,A 和 B 处理信息的时间均为 1 秒。

传输步骤:

  1. A 发送信息给 B,带有离开时的时间戳(1:00:00 a.m. T1)。

  2. B 接受信息后,加上到达时的时间戳(2:00:01 a.m. T2)。

  3. B 传出此信息给 A,加上离开时的时间戳(2:00:02 a.m. T3)。

  4. A 接收到信息后,加上到达的时间戳(1:00:03 T4)。

然后就可计算出如下信息:

信息来回一个周期的时延:

A 相对 B 的时间差: $Offset=\frac{(T3-T4)+(T2-T1)}{2}$。 可知: Delay=2 (s) Offset=1 (h) 就可以同步啦。 ## Part 3 回到这张图片: ![](https://cdn.luogu.com.cn/upload/image_hosting/rupmqxcm.png) 可以发现 `real` 命令用时最长,比 `user` 返回的时间加 `sys` 返回的时间都要长,这是因为三个命令分别是这样的: ``` real:指的是从开始到结束所花费的时间。比如进程在等待I/O完成,这个阻塞时间也会被计算在内。 user:指的是进程在用户态(User Mode)所花费的时间,只统计本进程所使用的时间,注意是指多核。 sys:指的是进程在核心态(Kernel Mode)花费的CPU时间量,指的是内核中的系统调用所花费的时间,只统计本进程所使用的时间。 ``` (以下用 real,sys,user 代指命令分别返回的结果) 通俗的来讲,real 命令包括了程序的运行时间和各种延迟时间,`user` 表示程序在 CPU 中自身执行的时间,`sys` 表示程序在调用系统资源使用的时间(比如创建进程啦等等)。 real 分为 CPU 执行时间和延迟时间,而 user 和 sys 合起来就是 CPU 执行时间,可知,在大多数情况下: `real>user+sys` 当然,创建线程也需要时间,而 real 仅统计单线程时间,在多线程情况下,可能有 `real<user+sys` 因为此时线程可以并行执行。 由上可知,你真实感受到的时间是 real,所以这道题应该选 A,(我蒙对了)。通过观察也可见,此题中 `real>sys+user`,题目中也强调了是单核 CPU。 ## Part 4 我的程序是选用哪种计时的呢? 当然是 `user+sys` 命令(不要妄想选最少的),这点还是很贴心的,至少已经把延迟去掉了。而我们的程序也都是单线程的(CCF 禁止调用线程相关函数),这可以看为是最贴近程序运行时间的。 在 c++ 中有 `clock` 函数,它返回的也是相当于 `user+sys`,有的同学会问了,那我在程序中 Sleep(1000),会多出这些时间吗? 答案是不会的,Sleep 函数是在内核层面上阻塞,影响的是 real 的时间,对于 `clock` 函数的计时并没有影响。 还有这样一个函数:`time`,它返回的是 1970 年 1 月 1 日至今所经历的时间(以秒为单位),如果用程序结束时的 `time` 减去程序开始时的 `time` 得到的相当于 `real` 的时间。 随机数也是利用了 `time` 作为时间种子,这在往期日报里有过讲述。 ## Part 5 ### 5-1 我如何给自己运行的程序计时 首先最简单的,在运行完一个程序后会有这样一行字:![](https://cdn.luogu.com.cn/upload/image_hosting/j1n9j818.png) 圈起来的数字就是程序运行的时间(以秒为单位),不过这包含了键盘输入的时间,所以推荐使用文件输入输出。 > 指正:在运行程序后显示的运行时间是由ConsolePauser.exe提供的功能,在Dev-C++以外的IDE中可能没有,需要手动下载ConsolePauser.exe并且在程序运行时调用(ConsolePauser.exe 程序路径) 感谢 @Allenyou1126 提出 也可以在程序中使用 `clock` 函数或者是 `time` 函数计时。比如说,我想使用 `clock` 计算我的单循环程序运行时间,可以这样: ```cpp #include<windows.h> #include<bits/stdc++.h> using namespace std; int a[100000007]; int main(){ for(int i=1;i<=100000000;i++){ a[i]=i;//防止被编译器优化 } cout<<"clock 计时: "<<clock()<<endl; return 0; } ``` ![](https://cdn.luogu.com.cn/upload/image_hosting/to52mni9.png) 在该程序中,有一个 $10^7$ 的循环,共用时 316ms,诶,这个程序没有输入啊,为什么下面显示的是 713.3ms 呢,clock 统计的仅仅是 CPU 时间,也就相当于 user+sys,所以嘛,会有这样的结果。 接下来是使用 time 计时的程序: ```cpp #include<bits/stdc++.h> using namespace std; int a; int main(){ int st=time(NULL); for(int i=1;i<=1000000000;i++){ a++;//防止被编译器优化 } int et=time(NULL); cout<<"time 计时: "<<et-st<<endl; return 0; } ``` ![](https://cdn.luogu.com.cn/upload/image_hosting/hcc8abtw.png) 由于 time 以秒为单位,所以程序运行的时间需要长一点才能体现出来,否则在 1s 之内的话 time 计时都是 0。 不对啊,不是说程序结束时的时间减去程序开始时的时间就是 real 的时间吗?注意看,这里所获取到的 time 值是在程序里执行的,也就是说,这仅是两个 time 中间的 real 时间而已。 在 CCF 的规则中,有这样一句: > 自测用时超过题目时限的5%,并由此提出的申诉,不予受理 那为什么有这 5% 呢,就是考虑到系统性能波动啊,延时误差啊等等。 ## Part 6 题外话 千年虫事件 在 20 世纪 60 年代的时候,计算机还是非常的昂贵,存储成本也很高,开发人员们就习惯性的用六位数字来表示年份,如 1967.12.13 表示成 67/12/13 等,然而,他们没想到自己写的程序能用那么久,在 2000 即将到来时,人们发现程序识别不出是 2000 年 1 月 1 日还是 1900 年 1 月 1 日,因此,在两个世纪交接的时候,弄出了非常大的麻烦。 因此,养成良好的代码习惯,从我们做起吧(滑稽 ## 注释及参考资料 [1]:[晶体振荡器](https://baike.baidu.com/item/%E6%99%B6%E4%BD%93%E6%8C%AF%E8%8D%A1%E5%99%A8/8742969) 参考资料: [【网络干货】NTP时间同步技术详解](https://zhuanlan.zhihu.com/p/138339057) [Linux系统-real/user/sys time](https://blog.csdn.net/baidu_35692628/article/details/77387827) 有不足之处欢迎指出