几种取时间的方法(附代码)

news2025/1/19 17:07:30

1.上古版

最原始的取时间的方法大概就是time+localtime了,见代码:


  
  
  1. #include <stdio.h >
  2. #include < time.h >
  3. / / gcc -o time_ 1 time_ 1.c
  4. int main()
  5. {
  6. time_t tm_now;
  7. time( &tm_now); / / 或者写成 tm_now = time( NULL);
  8. / / 1.直接打印: 1970- 1- 1,00: 00: 00到现在的秒数
  9. printf( "now time is %ld second\n", tm_now);
  10. / / 2.转换成本地时间,精确到秒
  11. struct tm *p_local_tm ;
  12. p_local_tm = localtime( &tm_now) ;
  13. printf( "now datetime: %04d-%02d-%02d %02d:%02d:%02d\n",
  14. p_local_tm- >tm_year + 1900,
  15. p_local_tm- >tm_mon + 1,
  16. p_local_tm- >tm_mday,
  17. p_local_tm- >tm_hour,
  18. p_local_tm- >tm_min,
  19. p_local_tm- >tm_sec);
  20. return 0;
  21. }

其中time函数返回的是1970年到现在的秒数,精确到秒。

localtime函数是根据这个秒数和本机的时区,解析出年月日时分秒等信息。

这里特别提醒一点,localtime函数不是多线程安全的,localtime_r才是。

还要特别提醒一点,不要在信号响应函数中使用localtime或localtime_r,程序会卡死!

程序运行结果如下:

2.傻瓜版

另一个比较好用的函数是gettimeofday。

相比其他函数,gettimeofday可以精确到微秒,还可以指定时区,性能也还可以,可以满足绝大多数场景,因此叫傻瓜版。

示例代码如下:


  
  
  1. #include <stdio.h >
  2. #include <sys / time.h >
  3. #include < time.h >
  4. / / gcc -o time_ 2 time_ 2.c
  5. int main()
  6. {
  7. struct timeval tm_now;
  8. / / 1.获取当前时间戳(tv_sec, tv_usec)
  9. gettimeofday( &tm_now, NULL); / / 第二个参数是时区
  10. / / 2.转换成本地时间,精确到秒
  11. struct tm *p_local_tm;
  12. p_local_tm = localtime( &tm_now.tv_sec) ;
  13. printf( "now datetime: %04d-%02d-%02d %02d:%02d:%02d.%06ld\n",
  14. p_local_tm- >tm_year + 1900,
  15. p_local_tm- >tm_mon + 1,
  16. p_local_tm- >tm_mday,
  17. p_local_tm- >tm_hour,
  18. p_local_tm- >tm_min,
  19. p_local_tm- >tm_sec,
  20. tm_now.tv_usec); / / 有微秒时间戳了
  21. return 0;
  22. }

运行结果如下:

3.进阶版

如果微秒级别的精度还不满足要求,可以尝试下clock_gettime,代码如下:


  
  
  1. #include <stdio.h >
  2. #include <unistd.h >
  3. #include < time.h >
  4. / / gcc -o time_ 3 time_ 3.c
  5. void print_timestamp(int use_monotonic)
  6. {
  7. struct timespec tm_now;
  8. / / 1.获取当前时间戳(tv_sec, tv_usec)
  9. if( use_monotonic)
  10. clock_gettime(CLOCK_MONOTONIC, &tm_now); / / 单调时间,屏蔽手动修改时间
  11. else
  12. clock_gettime(CLOCK_REALTIME, &tm_now); / / 机器时间
  13. / / 2.转换成本地时间,精确到秒
  14. struct tm *p_local_tm;
  15. p_local_tm = localtime( &tm_now.tv_sec) ;
  16. printf( "now datetime: %04d-%02d-%02d %02d:%02d:%02d.%09ld\n",
  17. p_local_tm- >tm_year + 1900,
  18. p_local_tm- >tm_mon + 1,
  19. p_local_tm- >tm_mday,
  20. p_local_tm- >tm_hour,
  21. p_local_tm- >tm_min,
  22. p_local_tm- >tm_sec,
  23. tm_now.tv_nsec); / / 有纳秒时间戳了
  24. }
  25. int main(int argc, char **argv)
  26. {
  27. int use_monotonic = 0;
  28. int optval = 0;
  29. while ((optval = getopt(argc, argv, "Mm")) ! = EOF)
  30. {
  31. switch (optval)
  32. {
  33. case 'M':
  34. case 'm':
  35. use_monotonic = 1;
  36. break;
  37. default:
  38. break;
  39. }
  40. }
  41. while( 1)
  42. {
  43. print_timestamp( use_monotonic);
  44. sleep( 1);
  45. }
  46. return 0;
  47. }

运行结果如下:

  

clock_gettime的第一个参数可以指定一个clock_id参数:

常见的有两个:

1) CLOCK_REALTIME

即普通的时间,跟其他时间函数取出来的时间并无区别,运行效果如上。

2) CLOCK_MONOTONIC

即单调时间,跟系统的启动时间有关,不受手动修改系统时间的影响。

  

如上图,表示系统已经启动了6 05:47:53(东8区零点是1970-01-01 08:00:00)。

表面上看,这个函数精度不错,功能完备,但却存在一个突出缺点–。对于性能敏感的函数,频繁调用会影响性能,这一点我们后面仔细说。

4.专家版

专家版本的计时函数有两个突出优点:

  • 性能高:绕过内核直接读寄存器,开销很小
  • 精度高:时间测量的最小单位是1/CPU频率秒,可达0.3纳秒(假设CPU频率为3GHz)

下面是示例程序:


  
  
  1. #include <stdio.h >
  2. #include <unistd.h >
  3. #include <stdlib.h > / / for atof
  4. #include <stdint.h > / / for uint 64_t
  5. / / gcc -o time_ 4 time_ 4.c
  6. / /获取CPU频率
  7. uint 64_t get_cpu_freq()
  8. {
  9. FILE *fp =popen( "lscpu | grep CPU | grep MHz | awk {'print $3'}", "r");
  10. if(fp = = nullptr)
  11. return 0;
  12. char cpu_mhz_str[ 200] = { 0 };
  13. fgets(cpu_mhz_str, 80,fp);
  14. fclose(fp);
  15. return atof(cpu_mhz_str) * 1000 * 1000;
  16. }
  17. / /读取时间戳寄存器
  18. uint 64_t get_tsc() / / TSC = = Time Stamp Counter寄存器
  19. {
  20. #ifdef __i 386__
  21. uint 64_t x;
  22. __asm__ volatile( "rdtsc" : "=A"(x));
  23. return x;
  24. #elif defined(__amd 64__) || defined(__x 86_ 64__)
  25. uint 64_t a, d;
  26. __asm__ volatile( "rdtsc" : "=a"(a), "=d"(d));
  27. return (d < < 32) | a;
  28. # else / / ARM架构CPU
  29. uint 32_t cc = 0;
  30. __asm__ volatile ( "mrc p15, 0, %0, c9, c13, 0": "=r" (cc));
  31. return (uint 64_t)cc;
  32. #endif
  33. }
  34. int main(int argc, char **argv)
  35. {
  36. uint 64_t cpu_freq = get_cpu_freq();
  37. printf( "cpu_freq is %lu\n", cpu_freq);
  38. uint 64_t last_tsc = get_tsc();
  39. while( 1)
  40. {
  41. sleep( 1);
  42. uint 64_t cur_tsc = get_tsc();
  43. printf( "TICK(s) : %lu\n", cur_tsc - last_tsc);
  44. printf( "Second(s) : %.02lf\n", 1.0 * (cur_tsc - last_tsc) / cpu_freq);
  45. last_tsc = cur_tsc;
  46. }
  47. return 0;
  48. }

TSC的全称是Time Stamp Counter,它是一个保存着CPU运转时钟周期数的寄存器,在X86等平台下均有提供(ARM平台下是CCR-Cycle Counter Register)。

通过专门的rdtsc汇编指令,可绕过操作系统内核直接从寄存器中读取数值,因此速度极快。

通过上述的get_tsc函数可以从这个寄存器中读出一个64位的数值,连续两次读取的值的差值,即是连续两次调用之间CPU运行的周期数。用这个周期数除以CPU运行的频率(通过上面的get_cpu_freq函数获得),即可得到具体的秒数。

上述代码运行效果如下:

可以看到,我测试用的机器的CPU频率是2.9Ghz的,我每sleep一秒输出一下两次CPU计数器的差值,发现跟频率也能对的上。

事实上,上面的所有取时间的函数,都是基于底层的类似rdtsc指令封装的,我们直接使用最底层的命令,固然快且精确,但是也不可避免的要直面一些坑。

比如我们可能碰见多CPU问题、多线程问题、进程上下文切换问题,计算机主动调节CPU频率问题等。为了顺利地使用这个指令,我们就要对程序和操作系统做一系列的限制,比如rdtsc的结果不在CPU间共享、进程运行时绑定CPU以避免被切换到另外的CPU上去、禁止计算机主动调频功能等。

5.关于性能

我们写了一个测试程序,跑10亿次,取平均时间,分别测试几个函数的性能:


  
  
  1. #include <stdio.h >
  2. #include <unistd.h >
  3. #include <stdlib.h >
  4. #include <stdint.h >
  5. #include < time.h >
  6. #include <sys / time.h >
  7. / / gcc -o time_ 5 time_ 5.c
  8. uint 64_t get_ by_ time()
  9. {
  10. time_t tm_now;
  11. time( &tm_now);
  12. return tm_now;
  13. }
  14. uint 64_t get_ by_gettimeofday()
  15. {
  16. struct timeval tm_now;
  17. gettimeofday( &tm_now, NULL);
  18. return tm_now.tv_sec;
  19. }
  20. uint 64_t get_ by_clock_gettime()
  21. {
  22. struct timespec tm_now;
  23. clock_gettime(CLOCK_REALTIME, &tm_now);
  24. return tm_now.tv_sec;
  25. }
  26. uint 64_t get_cpu_freq()
  27. {
  28. FILE *fp =popen( "lscpu | grep CPU | grep MHz | awk {'print $3'}", "r");
  29. if(fp = = NULL)
  30. return 0;
  31. char cpu_mhz_str[ 200] = { 0 };
  32. fgets(cpu_mhz_str, 80,fp);
  33. fclose(fp);
  34. return atof(cpu_mhz_str) * 1000 * 1000;
  35. }
  36. uint 64_t get_ by_tsc()
  37. {
  38. uint 64_t a, d;
  39. __asm__ volatile( "rdtsc" : "=a"(a), "=d"(d));
  40. return (d < < 32) | a;
  41. }
  42. void print_diff(uint 64_t loop_ times, uint 64_t beg_tsc, uint 64_t end_tsc)
  43. {
  44. double tt_ns = ( end_tsc - beg_tsc) * 1.0 * 1000 * 1000 * 1000 / get_cpu_freq();
  45. printf( "Number Loop : %lu\n", loop_ times);
  46. printf( "Total Time : %.02lf ns\n", tt_ns);
  47. printf( "Avg Time : %.02lf ns\n", tt_ns / loop_ times);
  48. }
  49. #define LOOP_ TIMES 1000000000
  50. int main(int argc, char **argv)
  51. {
  52. uint 64_t beg_tsc, end_tsc;
  53. long loop;
  54. printf( "-------------time()-------------\n");
  55. loop = LOOP_ TIMES;
  56. beg_tsc = get_ by_tsc();
  57. while(loop--)
  58. get_ by_ time();
  59. end_tsc = get_ by_tsc();
  60. print_diff(LOOP_ TIMES, beg_tsc, end_tsc);
  61. printf( "-------------gettimeofday()-------------\n");
  62. loop = LOOP_ TIMES;
  63. beg_tsc = get_ by_tsc();
  64. while(loop--)
  65. get_ by_gettimeofday();
  66. end_tsc = get_ by_tsc();
  67. print_diff(LOOP_ TIMES, beg_tsc, end_tsc);
  68. printf( "-------------clock_gettime()-------------\n");
  69. loop = LOOP_ TIMES;
  70. beg_tsc = get_ by_tsc();
  71. while(loop--)
  72. get_ by_clock_gettime();
  73. end_tsc = get_ by_tsc();
  74. print_diff(LOOP_ TIMES, beg_tsc, end_tsc);
  75. printf( "-------------rdtsc-------------\n");
  76. loop = LOOP_ TIMES;
  77. beg_tsc = get_ by_tsc();
  78. while(loop--)
  79. get_ by_tsc();
  80. end_tsc = get_ by_tsc();
  81. print_diff(LOOP_ TIMES, beg_tsc, end_tsc);
  82. return 0;
  83. }

测试结果如下:

  

可以看到:

  • time函数最快,但是精度太低
  • gettimeofday和clock_gettime虽然精度高,但是都比较慢
  • rdtsc精度和速度都十分优秀

另外需要注意一点的是,上述测试结果跟机器配置有很大关系,我测试所用的机器是一台ubuntu虚拟机,CPU只有2.9GHz。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1344870.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探讨kernel32.dll文件是什么,有效解决kernel32.dll丢失

在使用电脑时&#xff0c;你是否遇到过kernel32.dll丢失的困扰&#xff1f;面对这个问题&#xff0c;我们需要及时去解决kernel32.dll丢失的问题。接下来&#xff0c;我们将深入探讨kernel32.dll的功能以及其在操作系统和应用程序中的具体应用领域&#xff0c;相信这将对你解决…

鸿蒙HarmonyOS-带笔锋手写板(三)

笔者用ArkTS 写了一个简单的带笔锋的手写板应用&#xff0c;并且可以将手写内容保存为图片。 一、效果图 手写效果如下&#xff08;在鸿蒙手机模拟器上运行&#xff0c;手写时反应可能会有点慢&#xff09; 二、实现方法 参考文章&#xff1a; 支持笔锋效果的手写签字控件_a…

2023年03月21日_chatgpt宕机事件的简单回顾

你能想象吗 ChatGPT挂了 昨天半夜呢 来自全球各地的用户纷纷发现 ChatGPT的网站弹出了报错警告的信息 然后立即就无法使用了 即使是有特权的plus账户也未能幸免 一时之间呢 chatgptdown的话题在Twitter刷屏 不少重度的用户表示很着急 有的用户说呢没了ChatGPT 这工作…

【数据结构】链式家族的成员——循环链表与静态链表

循环链表与静态链表 导言一、循环链表1.1 循环单链表1.2 循环双链表 二、静态链表2.1 静态链表的创建2.2 静态链表的初始化2.3 小结 结语 导言 大家好&#xff01;很高兴又和大家见面啦&#xff01;&#xff01;&#xff01; 经过前面的介绍&#xff0c;相信大家对链式家族的…

【Linux驱动】设备树简介 | 内核对设备树的处理

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《Linux驱动》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f9f2;设备树简介&#x1f3f9;设备树语法&#x1f3f9;常见节点和属性&#x1f3f9…

状态模式-概述

在软件系统中&#xff0c;有些对象也像水一样具有多种状态&#xff0c;这些状态在某些情况下能够相互转换&#xff0c; 而且对象在不同的状态下也将具有不同的行为。相同的方法在不同的状态中可能会有不同的实现。 为了实现不同状态下对象的各种行为以及对象状态之间的相互转换…

读书笔记1——用户画像平台构建与业务实践

目录 1.画像的基本概念 2、OLAP的3种建模类型 3.OLAP相关技术发展历程 4.业界画像平台介绍 神策数据 2.火山引擎增长分析 3. GrowingLo 4.阿里云智能用户增长 5.涉及岗位 这是一本从功能模块、技术实现、平台构建、业务应用4个层次由浅入深地讲解用户画像的著作。作者在…

2023 搞懂git 工作目录---暂存区---本地仓库---版本库

最近了解了下git的底层原理&#xff08;大神录制的视频放在最下方&#xff09;&#xff0c;记录下&#xff1a; 工作区 就是存放待提交文件的目录&#xff08;下图图解标注&#xff09;比如pyhon_test目录暂存区 .git目录下的index文件 对应的指令 git add本地仓库 .gi…

使用vmware,在ubuntu18.04中使用笔记本的摄像头

步骤1&#xff1a;在windows中检查相机状态 win10系统中&#xff0c;在左下的搜索栏&#xff0c;搜索“相机”&#xff0c;点击进入即可打开相机&#xff0c;并正常显示图像。 注意&#xff1a;如果相机连接到了虚拟机&#xff0c;则不能显示正常。 步骤2&#xff1a;在ubuntu…

模式识别与机器学习-集成学习

集成学习 集成学习思想过拟合与欠拟合判断方法 K折交叉验证BootstrapBagging随机森林的特点和工作原理&#xff1a; BoostingAdaBoost工作原理&#xff1a;AdaBoost的特点和优点&#xff1a;AdaBoost的缺点&#xff1a; Gradient Boosting工作原理&#xff1a;Gradient Boostin…

『番外篇七』SwiftUI 获取视图全局位置在 NavigationStack 中失效的解决方法

概览 在 番外篇六』SwiftUI 取得任意视图全局位置的三种方法 这篇博文里,我们详细讨论了在 SwiftUI 中获取任意视图全局坐标的几种方法。 不过,我们也从中提到了某些方法无法适用于 NavigationStack 视图,本篇博文由此应运而生。 在本篇博文种,您将学到如下内容: 概览1.…

9.传统的轨道画线算法(完成)

轨道画线分为以下步骤&#xff1a; 1.读取摄像头图片 2.图片灰度处理&#xff0c;截取轨道区域的图片 3.中值滤波处理&#xff0c;并区域取均值后做期望差的绝对值。本人通过一些轨道图片实验&#xff0c;用这种方法二值化得到的效果比caany算子等方法的效果好 4.二值化后再…

共享单车之数据可视化

文章目录 第1关&#xff1a;绘制地图第2关&#xff1a;绘制流量最高的五条线路的路程图 第1关&#xff1a;绘制地图 任务描述 本关任务&#xff1a;使用JSP在百度地图上绘制一条共享单车起始路程。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 如何创建地…

JavaScript使用教程(二):类型、值和变量

计算机程序通过操作值&#xff08;如数值3.14&#xff09;或文本&#xff08;如“Hello World”&#xff09;来工作。编程语言中这些可以表示和操作的值被称为类型&#xff0c;而一门语言支持的类型集也是这门语言最基本的特征。程序在需要把某个值保存下来以便将来使用时&…

python可视化界面自动生成,python如何做可视化界面

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python gui可视化操作界面制作&#xff0c;python做出的炫酷的可视化&#xff0c;现在让我们一起来看看吧&#xff01; 目录 前言 一.环境配置 插件&#xff1a; 1.python 2.Chinese 3.Open In Default Browser 安装pyt…

链表的一些典型问题

求链表的中间节点/倒数第K个节点 等类似的随机访问&#xff0c;可以考虑用快慢指针 例 求链表的中间节点 可以定义两个指针&#xff0c;一个一次走两步一个一次走一步&#xff0c;当走的快的走到NULL时&#xff0c;走的慢的就是链表的中间节点。&#xff08;此法求出的偶数个…

ES的使用(Elasticsearch)

ES的使用&#xff08;Elasticsearch&#xff09; es是什么&#xff1f; es是非关系型数据库&#xff0c;是分布式文档数据库&#xff0c;本质上是一个JSON 文本 为什么要用es? 搜索速度快&#xff0c;近乎是实时的存储、检索数据 怎么使用es? 1.下载es的包&#xff08;环境要…

软件测试/测试开发丨学习笔记之 Python 函数

python 函数 函数的作用 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现单一或相关联功能的代码段函数能提高应用的模块性和代码的重复利用率python 内置函数&#xff1a;docs.python.org/zh-cn/3.8/l… 函数定义 def&#xff1a;函数定义关键词function_nam…

轻松调整视频时长,创意与技术的新篇章

传统的视频剪辑工具往往难以精确控制时间&#xff0c;而【媒体梦工厂】凭借其先进的算法和界面设计&#xff0c;让视频时长的调整变得简单而精确&#xff0c;助你释放无限的创意&#xff0c;用技术为你的创意插上翅膀&#xff0c;让每一秒都有意义。 所需工具&#xff1a; 一…

MongoDB 概念介绍

1、MongoDB 应用场景 传统的关系型数据库&#xff0c;在数据操作的"三高"需求以及应对Web2.0的网站需求面前&#xff0c;显得力不从心。 High performance -对数据库高并发读写的需求。Huge Storage -对海量数据的高效率存储和访问的需求。High Scalability &&…