当前位置: 首页 > news >正文

linux下cpu多核运行程序以及运行时间统计

一、多核心运行程序

在linux下我们可以指定线程或者进程运行在指定的cpu核心上,操作方法如下:

1)运行进程指定cpu核心

taskset -c 2 ./app //-c指定运行的cpu核心号,从0计数,查看效果如下:

2)运行线程指定cpu核心

主要是设置线程的属性,具体代码如下,详看main函数。

#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <pthread.h>
#include <time.h>void* thread_func0(void* arg) {int iterations = 1000000000; // 迭代次数double a = 3.14159; // 乘法操作的乘数double b = 2.71828; // 乘法操作的被乘数double sum = 0.0; // 加法操作的累加器struct timespec start, end;clock_gettime(CLOCK_MONOTONIC, &start);// 执行乘法运算for (int i = 0; i < iterations; ++i) {double c = a * b;sum += c; // 将乘法结果加到累加器上}clock_gettime(CLOCK_MONOTONIC, &end);double time_spent = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;printf("cpu0 Performed %d in %f seconds\n", iterations, time_spent);printf("cpu0 Resulting sum: %f\n", sum);return NULL;
}void* thread_func1(void* arg) {int iterations = 1000000000; // 迭代次数double a = 3.14159; // 乘法操作的乘数double b = 2.71828; // 乘法操作的被乘数double sum = 0.0; // 加法操作的累加器struct timespec start, end;clock_gettime(CLOCK_MONOTONIC, &start);// 执行乘法运算for (int i = 0; i < iterations; ++i) {double c = a * b;sum += c; // 将乘法结果加到累加器上}clock_gettime(CLOCK_MONOTONIC, &end);double time_spent = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;printf("cpu1 Performed %d in %f seconds\n", iterations, time_spent);printf("cpu1 Resulting sum: %f\n", sum);return NULL;
}int main() {pthread_t thread0, thread1; // 为每个线程使用不同的变量pthread_attr_t attr0, attr1; // 为每个线程属性使用不同的变量unsigned long mask0 = 1UL << 2; // 将线程绑定到CPU核心2unsigned long mask1 = 1UL << 3; // 将线程绑定到CPU核心3// 初始化线程属性pthread_attr_init(&attr0);pthread_attr_init(&attr1);pthread_attr_setaffinity_np(&attr0, sizeof(mask0), &mask0);pthread_create(&thread0, &attr0, thread_func0, NULL);pthread_attr_setaffinity_np(&attr1, sizeof(mask1), &mask1);pthread_create(&thread1, &attr1, thread_func1, NULL);// 等待线程0和线程1完成pthread_join(thread0, NULL);pthread_join(thread1, NULL);return 0;
}

编译后运行效果如下(代码指定了2和3,从0计数):

3)两者的优先级

代码中的线程属性设置cpu核心为2和3,进程以cpu核心0来运行,实际运行在核心2和3上。

测试代码用上述不变,运行命令为:taskset -c 0 ./app

因此,可得结论:在进程和线程都指定运行的cpu核心时,以线程为准。

二、多核心运行的时间测量需要注意的事项

1)多核计时异常

在测试运行时间时,遇到一个问题,最开始我使用的时间测试代码如下:

clock_t start = clock();//测量开始时间
//功能代码
//...
clock_t end = clock();// 测量结束时间
double time_spent = (double)(end - start) / CLOCKS_PER_SEC;

这个代码在测试单核运行的代码时,时间是对的,但是使用多核运行时,发现这个代码统计的时间是我实际手机计时的2倍,怀疑是该函数统计了本程序对所有cpu的占用时间,即双核的时间。

后修改时间测量代码如下:

struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
//功能代码
//...
clock_gettime(CLOCK_MONOTONIC, &end);
double time_spent = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;

测试时间和手机测试的一致。

2)clock()clock_gettime() 比较

clock()clock_gettime() 都是 C 语言标准库中用于获取时间的函数,但它们在用途和行为上有一些重要的区别:

  1. 定义和来源

    • clock() 函数定义在 <time.h> 头文件中,它测量的是程序占用 CPU 的时间(也称为 CPU 时间或处理器时间)。它返回程序启动以来的时钟周期数,这个值是通过处理器的时钟周期来计算的。
    • clock_gettime() 函数是 POSIX.1-2001 标准的一部分,也定义在 <time.h> 中,它提供了更精确的时间测量。它可以测量多种类型的时间,包括实时时间(系统时间)、进程时间、线程时间等。
  2. 时间类型

    • clock() 只能测量程序占用 CPU 的时间。
    • clock_gettime() 可以测量多种时间,通过指定不同的时钟 ID 来获取不同类型的时间。例如:
      • CLOCK_REALTIME:返回以系统实时时间为基础的时间,受系统时间的更改影响。
      • CLOCK_MONOTONIC:返回一个单调时钟,它以系统启动为基础,不受系统时间更改的影响,适合测量时间间隔。
      • CLOCK_PROCESS_CPUTIME_ID:返回调用进程占用的 CPU 时间总和。
      • CLOCK_THREAD_CPUTIME_ID:返回调用线程占用的 CPU 时间。
  3. 精度

    • clock() 的精度受限于系统和硬件,通常以秒为单位,精度较低。
    • clock_gettime() 通常提供更高的精度,可以测量到纳秒级别。
  4. 返回值

    • clock() 返回一个 clock_t 类型的值,表示程序占用 CPU 的时钟周期数。如果失败,返回 ((clock_t) -1)
    • clock_gettime() 成功时返回 0,失败时返回 -1,并设置 errno 以指示错误。
  5. 使用场景

    • clock() 适用于需要测量程序执行时间的场景,但它不适用于测量墙上时钟时间(实际时间)。
    • clock_gettime() 适用于需要高精度时间测量的场景,包括但不限于测量墙上时钟时间、系统时间、进程或线程的 CPU 时间。
  6. 多线程环境

    • 在多线程环境中,clock() 可能无法准确反映单个线程的 CPU 时间,因为它测量的是整个进程的 CPU 时间。
    • clock_gettime() 通过 CLOCK_THREAD_CPUTIME_ID 可以准确测量单个线程的 CPU 时间。
  7. 系统依赖性

    • clock() 是 C 语言标准的一部分,几乎所有的 C 语言环境都支持。
    • clock_gettime() 是 POSIX 标准的一部分,可能在非 POSIX 兼容的系统上不可用或需要特定的编译器标志。

总结来说,clock_gettime() 提供了更多的功能和更高的精度,是现代 C 语言编程中推荐的时间测量函数。而 clock() 由于其较低的精度和对多线程支持的限制,在现代编程中使用较少。


http://www.mrgr.cn/news/15706.html

相关文章:

  • 【复杂系统系列(初级)】自动调节动态平衡模型——生物体的稳态机制
  • 【HTML】使用过程中的随记
  • 关于springboot的Rest请求映射处理的源码分析(二)
  • 构建智慧人才档案,驱动未来发展新引擎 —— 解锁人才管理新篇章
  • 【C++类和对象】拷贝构造、运算符重载以及日期类的实现
  • pdf转dwg怎么转换?5个软件教你轻松转换文件
  • 投屏软件哪个好用又免费?4款投屏软件,远程且兼容多平台!
  • 矩阵性质简介
  • C#如何查看/写入日志到Windows事件查看器
  • 国产系统-共享文件夹
  • 关于Scrapy的那些事儿(四)Scrapy Shell
  • 集成电路学习:什么是ARM先进精简指令集计算机
  • 石油设备和相关机械都包涵那些?
  • 你知道吗?这些plm项目管理系统大厂项目经理都在用!
  • Crypto City盛夏狂欢:Hotcoin推出15,000 USDT迎新礼
  • 【GeoScenePortal】安装和部署
  • C# opencv识别二维码
  • 从挫败到精通:三步克服编程学习的难关
  • 衡石产品手册--数据集如何配置对接和开发
  • 爆品是测出来的,不是选出来的