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

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 uint64_t
  5. // gcc -o time_4 time_4.c
  6. //获取CPU频率
  7. uint64_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. uint64_t get_tsc() // TSC == Time Stamp Counter寄存器
  19. {
  20. #ifdef __i386__
  21. uint64_t x;
  22. __asm__ volatile("rdtsc" : "=A"(x));
  23. return x;
  24. #elif defined(__amd64__) || defined(__x86_64__)
  25. uint64_t a, d;
  26. __asm__ volatile("rdtsc" : "=a"(a), "=d"(d));
  27. return (d << 32) | a;
  28. #else // ARM架构CPU
  29. uint32_t cc = 0;
  30. __asm__ volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (cc));
  31. return (uint64_t)cc;
  32. #endif
  33. }
  34. int main(int argc, char **argv)
  35. {
  36. uint64_t cpu_freq = get_cpu_freq();
  37. printf("cpu_freq is %lu\n", cpu_freq);
  38. uint64_t last_tsc = get_tsc();
  39. while(1)
  40. {
  41. sleep(1);
  42. uint64_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. uint64_t get_by_time()
  9. {
  10. time_t tm_now;
  11. time(&tm_now);
  12. return tm_now;
  13. }
  14. uint64_t get_by_gettimeofday()
  15. {
  16. struct timeval tm_now;
  17. gettimeofday(&tm_now,NULL);
  18. return tm_now.tv_sec;
  19. }
  20. uint64_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. uint64_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. uint64_t get_by_tsc()
  37. {
  38. uint64_t a, d;
  39. __asm__ volatile("rdtsc" : "=a"(a), "=d"(d));
  40. return (d << 32) | a;
  41. }
  42. void print_diff(uint64_t loop_times, uint64_t beg_tsc, uint64_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. uint64_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.mzph.cn/news/586146.shtml

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

相关文章

Python入门学习篇(十二)——内置函数匿名函数

1 内置函数——数学函数 1.1 绝对值函数 1.1.1 语法 abs(参数) # 里面的参数既可以是整数,也可以是小数1.1.2 示例代码 m -1.99 n -9 print(f"{m}的绝对值为: {abs(m)}") print(f"{n}的绝对值为: {abs(n)}")1.1.3 运行截图 1.2 求商和余数 1.2.1 语…

Linux基础知识学习

开发工具&#xff1a;Xshell7VMware 首先要明确的是在Linux系统中“目录”文件夹 而所谓"家目录"指的是每个用户所拥有的一个目录&#xff0c;通常默认在 /home 目录下&#xff0c;其名称与用户的用户名相同。 ~代表的是就就是家目录 cd ~就可以直接进入当前用户下…

PostgreSQL 作为向量数据库:入门和扩展

PostgreSQL 拥有丰富的扩展和解决方案生态系统&#xff0c;使我们能够将该数据库用于通用人工智能应用程序。本指南将引导您完成使用 PostgreSQL 作为向量数据库构建生成式 AI 应用程序所需的步骤。 我们将从pgvector 扩展开始&#xff0c;它使 Postgres 具有特定于向量数据库…

ZigBee案例笔记 - 无线点灯

文章目录 无线点灯实验概述工程关键字工程文件夹介绍Basic RF软件设计框图简单说明工程操作Basic RF启动流程Basic RF发送流程Basic RF接收流程 无线点灯案例无线点灯现象 无线点灯实验概述 ZigBee无线点灯实验&#xff08;即Basic RF工程&#xff09;&#xff0c;由TI公司提供…

C语言实验3:函数的定义

目录 一、实验要求 二、实验原理 1.函数头 2.函数体 3.函数的定义及使用 三、实验内容 1. sum函数 代码 截图 分析 2. sum函数 代码 截图 分析 3. rank_grade函数 代码 截图 分析 4. rank_grade函数 代码 截图 分析 5. 函数的嵌套使用 代码 截图 分析…

uniapp中uview组件库的丰富Upload 上传上午用法

目录 基础用法 #上传视频 #文件预览 #隐藏上传按钮 #限制上传数量 #自定义上传样式 API #Props #Methods #Slot #Events 基础用法 可以通过设置fileList参数(数组&#xff0c;元素为对象)&#xff0c;显示预置的图片。其中元素的url属性为图片路径 <template>…

python文件打包实战技巧

众所周知&#xff0c;python是一种脚本语言&#xff0c;python程序必须在python环境下运行&#xff0c;所以如果想把自己写的程序给别人看的话&#xff0c;就比较麻烦&#xff0c;他需要先配置python环境&#xff0c;对于电脑小白来说这是“要命”的事情。而且如果是客户的话&a…

PulseGAN

研究背景 远程光电容积描记术 (rPPG) 是一种非接触式技术&#xff0c;用于测量面部视频中的心脏信号。健康监测和情绪识别等许多领域都迫切需要高质量的 rPPG 脉冲信号。然而&#xff0c;由于脉搏信号不准确的限制&#xff0c;现有的大多数rPPG方法只能用于获取平均心率&#…

AD域组策略

题目&#xff1a; 除manager 组和IT组&#xff0c;所有用户隐藏C盘&#xff1b;除manager 组和IT组&#xff0c;所有普通给用户禁止使用cmdIT01用户登陆域后&#xff0c;会自动增加驱动器X&#xff0c;该驱动器自动关联DC1的C:\tools文件夹&#xff1b;sales用户组的InternetE…

ESP32S3+HX8347+3线SPI运行LVGL例程

一、clone lv_port_esp32到本地 git clone https://github.com/lvgl/lv_port_esp32.git 二、增加hx8347.c、hx8347.h components\lvgl_esp32_drivers\lvgl_tft下新增2个文件&#xff1a;hx8347.c、hx8347.h。因为lv_port_esp32中没有hx8347的驱动&#xff0c;需要自己写。这两个…

1.2.0 IGP高级特性之FRR

理论部分参考文档&#xff1a;Segment Routing TI-LFA FRR保护技术 - 华为 一、快速重路由技术 FRR(Fast Reroute)快速重路由 实现备份链路的快速切换&#xff0c;也可以与BFD联动实现对故障的快速感知。 随着网络的不断发展&#xff0c;VoIP和在线视频等业务对实时性的要求越…

web:[BJDCTF2020]The mystery of ip(ssti模板注入、Smarty 模板引擎)

题目 进入页面显示如下 点击flag页面得到ip 点击hint页面 在hint.php的源代码页面中发现 由题目可以知道要从ip入手 这里尝试抓包加上X-Forwarded-For请求头修改为127.0.0.1 因为直接将127.0.0.1输出到页面&#xff0c;可以猜测是ssti模板注入 可以继续验证 这里发现输入什么…

【数据结构】八、查找

一、基本概念 静态查找&#xff1a;只查找&#xff0c;不改变集合内数据元素 动态查找&#xff1a;有则输出元素&#xff0c;无则添加元素 二、静态查找表 2.1顺序查找 在线性表、链表、树中依次查找 2.2折半查找&#xff08;二分查找&#xff09; 在有序的线性表中&…

macos下转换.dmg文件为 .iso .cdr文件的简单方法

为了让镜像文件在mac 和windows平台通用, 所以需要将.dmg格式的镜像文件转换为.iso文件, 转换方法也非常简单, 一行命令即可 hdiutil convert /path/to/example.dmg -format UDTO -o /path/to/example.iso 转换完成后的文件名称默认是 example.iso.cdr 这里直接将.cdr后缀删…

C#高级 08Json操作

1.概念 Json是存储和交换文本信息的语法。类似于XML。Json比XML更小、更快、更易解析。Json与XML一样是一种数据格式。Json是一种轻量级的数据交换格式。它基于ECMAScript的一个子集。Json采取完全独立于语言的文本格式&#xff0c; 但是也使用了类似于C语言的习惯。这些特性使…

2047过滤空格(C语言)

目录 一&#xff1a;题目 二&#xff1a;思路分析 三&#xff1a;代码 一&#xff1a;题目 二&#xff1a;思路分析 1.首先&#xff0c;这道题是一个字符串的问题&#xff0c;我们要先知道字符串存放在char类型的数组中的&#xff0c;并不是一个变量就可直接存放的下一个完整…

1.Linux快速入门

Linux快速入门 Linux操作系统简介Linux操作系统优点Linux操作系统发行版1. Red Hat Linux2. CentOS3. Ubuntu4. SUSE Linux5. Fedora Linux 32位与64位操作系统的区别Linux内核命名规则 Linux操作系统简介 Linux操作系统是基于UNIX以网络为核心的设计思想&#xff0c;是一个性…

云计算:OpenStack 配置云主机实例的存储挂载并实现外网互通

目录 一、实验 1. 环境 2.配置存储挂载 3.云主机实例连接外部网络&#xff08;SNAT&#xff09; 4.外部网络连接云主机实例&#xff08;DNAT&#xff09; 二、问题 1.云主机 ping 不通外部网络 2.nova list 查看云主机列表报错 3.nova list 与 virsh list --all有何区…

【代码随想录】刷题笔记Day42

前言 这两天机器狗终于搞定了&#xff0c;一个控制ROS大佬&#xff0c;一个计院编程大佬&#xff0c;竟然真把创新点这个弄出来了&#xff0c;牛牛牛牛&#xff08;菜鸡我只能负责在旁边喊加油&#xff09;。下午翘了自辩课来刷题&#xff0c;这次应该是元旦前最后一刷了&…

行车记录仪变清晰,变高清的办法一定要收藏

有时候我们会发现行车记录仪拍摄的视频不够清晰&#xff0c;特别是出现事故需要视频为证的时候&#xff0c;如果视频太模糊&#xff0c;很难获得交警的支持&#xff0c;那么如何让行车记录仪拍摄的视频变得更加清晰呢&#xff1f; 小编给大家分享几个办法&#xff0c;建议收藏…