如何从 C 程序中获取 100% 的 CPU 使用率
- 2024-10-22 08:28:00
- admin 原创
- 68
问题描述:
这个问题很有趣,让我先介绍一下背景。我在美国国家计算机博物馆工作,我们刚刚成功让一台 1992 年的 Cray Y-MP EL 超级计算机运行起来,我们真的很想看看它能跑多快!
我们认为最好的方法是编写一个简单的 C 程序来计算素数并显示计算所需的时间,然后在一台快速的现代台式电脑上运行该程序并比较结果。
我们很快想出了这个代码来计算素数:
#include <stdio.h>
#include <time.h>
void main() {
clock_t start, end;
double runTime;
start = clock();
int i, num = 1, primes = 0;
while (num <= 1000) {
i = 2;
while (i <= num) {
if(num % i == 0)
break;
i++;
}
if (i == num)
primes++;
system("clear");
printf("%d prime numbers calculated
",primes);
num++;
}
end = clock();
runTime = (end - start) / (double) CLOCKS_PER_SEC;
printf("This machine calculated all %d prime numbers under 1000 in %g seconds
", primes, runTime);
}
在我们运行 Ubuntu 的双核笔记本电脑上(Cray 运行 UNICOS),它运行完美,CPU 使用率达到 100%,耗时约 10 分钟。回到家后,我决定在我的六核现代游戏电脑上试用它,这就是我们遇到的第一个问题。
我首先修改了代码以使其在 Windows 上运行,因为游戏 PC 使用的是 Windows,但令人沮丧的是,我发现该进程仅占用了大约 15% 的 CPU 功率。我想这一定是 Windows 的问题,所以我启动了 Ubuntu 的 Live CD,认为 Ubuntu 可以让该进程充分发挥其潜力,就像之前在我的笔记本电脑上所做的那样。
但是我的使用率只有 5%!所以我的问题是,我如何才能让该程序在我的游戏机上以 100% 的 CPU 利用率在 Windows 7 或实时 Linux 上运行?另一件很棒但不是必要的事情是,如果最终产品可以是一个可以轻松分发并在 Windows 机器上运行的 .exe。
多谢!
PS 当然,这个程序实际上无法与 Crays 8 专用处理器兼容,这是另一个问题...如果您知道如何优化代码以在 90 年代的 Cray 超级计算机上运行,也请告诉我们!
解决方案 1:
如果要 100% 的 CPU 利用率,则需要使用超过 1 个核心。为此,您需要多个线程。
以下是使用 OpenMP 的并行版本:
我不得不增加限制以使其1000000
在我的计算机上花费的时间超过 1 秒。
#include <stdio.h>
#include <time.h>
#include <omp.h>
int main() {
double start, end;
double runTime;
start = omp_get_wtime();
int num = 1,primes = 0;
int limit = 1000000;
#pragma omp parallel for schedule(dynamic) reduction(+ : primes)
for (num = 1; num <= limit; num++) {
int i = 2;
while(i <= num) {
if(num % i == 0)
break;
i++;
}
if(i == num)
primes++;
// printf("%d prime numbers calculated
",primes);
}
end = omp_get_wtime();
runTime = end - start;
printf("This machine calculated all %d prime numbers under %d in %g seconds
",primes,limit,runTime);
return 0;
}
输出:
这台机器在 29.753 秒内计算出了 1000000 以下的所有 78498 个质数
这是你的 100% CPU:
解决方案 2:
您在多核机器上运行一个进程 - 因此它仅在一个核心上运行。
解决方案很简单,因为您只是试图固定处理器 - 如果您有 N 个核心,则运行您的程序 N 次(当然是并行的)。
例子
这是一些并行运行程序的代码NUM_OF_CORES
。它是 POSIXy 代码 - 它使用fork
- 所以你应该在 Linux 下运行它。如果我读到的关于 Cray 的内容是正确的,那么移植此代码可能比移植其他答案中的 OpenMP 代码更容易。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define NUM_OF_CORES 8
#define MAX_PRIME 100000
void do_primes()
{
unsigned long i, num, primes = 0;
for (num = 1; num <= MAX_PRIME; ++num) {
for (i = 2; (i <= num) && (num % i != 0); ++i);
if (i == num)
++primes;
}
printf("Calculated %d primes.
", primes);
}
int main(int argc, char ** argv)
{
time_t start, end;
time_t run_time;
unsigned long i;
pid_t pids[NUM_OF_CORES];
/* start of test */
start = time(NULL);
for (i = 0; i < NUM_OF_CORES; ++i) {
if (!(pids[i] = fork())) {
do_primes();
exit(0);
}
if (pids[i] < 0) {
perror("Fork");
exit(1);
}
}
for (i = 0; i < NUM_OF_CORES; ++i) {
waitpid(pids[i], NULL, 0);
}
end = time(NULL);
run_time = (end - start);
printf("This machine calculated all prime numbers under %d %d times "
"in %d seconds
", MAX_PRIME, NUM_OF_CORES, run_time);
return 0;
}
输出
$ ./primes
Calculated 9592 primes.
Calculated 9592 primes.
Calculated 9592 primes.
Calculated 9592 primes.
Calculated 9592 primes.
Calculated 9592 primes.
Calculated 9592 primes.
Calculated 9592 primes.
This machine calculated all prime numbers under 100000 8 times in 8 seconds
解决方案 3:
我们真的想看看它能跑多快!
您的生成素数算法效率很低。与primegen相比,primegen 在 Pentium II-350 上仅用 8 秒就生成了 50847534 个素数(最多 1000000000)。
为了轻松耗尽所有 CPU,您可以解决令人尴尬的并行问题,例如,计算曼德布洛特集或使用遗传编程在多个线程(进程)中绘制蒙娜丽莎。
另一种方法是采用 Cray 超级计算机的现有基准测试程序并将其移植到现代 PC 上。
解决方案 4:
在六核处理器上获得 15% 的原因是您的代码以 100% 使用了 1 个核心。100/6 = 16.67%,使用带有进程调度的移动平均数(您的进程将在正常优先级下运行)可以轻松报告为 15%。
因此,为了使用 100% 的 CPU,您需要使用 CPU 的所有核心 - 为六核 CPU 启动 6 条并行执行代码路径,并将这个规模扩展到您的 Cray 机器所拥有的处理器数量:)
解决方案 5:
还要注意CPU 的负载情况。一个 CPU 可以执行许多不同的任务,虽然其中许多任务会被报告为“100% 负载”,但它们可能各自使用 CPU 不同部分的 100%。换句话说,很难比较两个不同的 CPU 的性能,尤其是两个不同的 CPU 架构。执行任务 A 可能有利于一个 CPU 而不是另一个,而执行任务 B 则很容易出现相反的情况(因为两个 CPU 可能具有不同的内部资源,并且执行代码的方式可能非常不同)。
这就是为什么软件对于计算机的最佳性能与硬件同样重要的原因。对于“超级计算机”来说也是如此。
CPU 性能的一个衡量标准可能是每秒指令数,但不同的 CPU 架构上指令数并不相同。另一个衡量标准可能是缓存 IO 性能,但缓存基础设施也不相同。然后一个衡量标准可能是每瓦指令数,因为在设计集群计算机时,功率输送和耗散通常是一个限制因素。
因此,您的第一个问题应该是:哪个性能参数对您来说很重要?您想测量什么?如果您想看看哪台机器能从 Quake 4 中获得最高的 FPS,答案很简单;您的游戏装备会,因为 Cray 根本无法运行该程序 ;-)
干杯,斯蒂恩
解决方案 6:
TLDR;接受的答案既低效又不兼容。以下算法运行速度快100 倍。
MAC 上可用的 gcc 编译器无法运行omp
。我不得不安装 llvm (brew install llvm )
。但在运行 OMP 版本时,我没有看到 CPU 空闲时间下降。
这是OMP版本运行时的屏幕截图。
或者,我使用了基本的 POSIX 线程,可以使用任何 c 编译器运行,当= = 4(MacBook Pro,2.3 GHz Intel Core i5)时,几乎整个 CPU 都被用完了。以下是程序 -nos of thread
`no of cores`
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define NUM_THREADS 10
#define THREAD_LOAD 100000
using namespace std;
struct prime_range {
int min;
int max;
int total;
};
void* findPrime(void *threadarg)
{
int i, primes = 0;
struct prime_range *this_range;
this_range = (struct prime_range *) threadarg;
int minLimit = this_range -> min ;
int maxLimit = this_range -> max ;
int flag = false;
while (minLimit <= maxLimit) {
i = 2;
int lim = ceil(sqrt(minLimit));
while (i <= lim) {
if (minLimit % i == 0){
flag = true;
break;
}
i++;
}
if (!flag){
primes++;
}
flag = false;
minLimit++;
}
this_range ->total = primes;
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
struct timespec start, finish;
double elapsed;
clock_gettime(CLOCK_MONOTONIC, &start);
pthread_t threads[NUM_THREADS];
struct prime_range pr[NUM_THREADS];
int rc;
pthread_attr_t attr;
void *status;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for(int t=1; t<= NUM_THREADS; t++){
pr[t].min = (t-1) * THREAD_LOAD + 1;
pr[t].max = t*THREAD_LOAD;
rc = pthread_create(&threads[t], NULL, findPrime,(void *)&pr[t]);
if (rc){
printf("ERROR; return code from pthread_create() is %d
", rc);
exit(-1);
}
}
int totalPrimesFound = 0;
// free attribute and wait for the other threads
pthread_attr_destroy(&attr);
for(int t=1; t<= NUM_THREADS; t++){
rc = pthread_join(threads[t], &status);
if (rc) {
printf("Error:unable to join, %d" ,rc);
exit(-1);
}
totalPrimesFound += pr[t].total;
}
clock_gettime(CLOCK_MONOTONIC, &finish);
elapsed = (finish.tv_sec - start.tv_sec);
elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
printf("This machine calculated all %d prime numbers under %d in %lf seconds
",totalPrimesFound, NUM_THREADS*THREAD_LOAD, elapsed);
pthread_exit(NULL);
}
注意整个 CPU 是如何被耗尽的 -
PS-如果增加线程数,则实际 CPU 使用率会下降(尝试使线程数 = 20。),因为系统在上下文切换上花费的时间比实际计算的时间更多。
顺便说一句,我的机器不如@mystical(接受的答案)那么强大。但我的版本使用基本的 POSIX 线程,运行速度比 OMP 版本快得多。结果如下 -
PS 将线程负载增加到 250 万来查看 CPU 使用率,因为它在不到一秒的时间内完成。
解决方案 7:
Linux(和其他 Unix 系统)报告 CPU 使用率时,单线程进程占用单个 CPU 核心的情况显示为 100%。 这是您在 Ubuntu 系统上看到的情况:程序始终处于运行状态,并且实际上在一个核心或另一个核心上运行。
在具有 N 个核心的机器上,当所有核心都处于繁忙状态时,Linux/Unix 将其显示为 N * 100%,或平均负载为 N。(平均负载还包括等待 I/O 或等待 CPU 运行的任务,因此它可能高于 CPU 核心的数量)。
例如,具有 8 个逻辑核心的机器上的负载为 800%,无论是跨 4 个具有超线程的物理核心还是跨 8 个独立的物理核心。
在 Windows 机器上,所有核心繁忙都被报告为 100% 负载。 而单线程程序占用一个核心的最大利用率是 100% / N,在多核机器上查看单线程程序是否花费时间休眠或等待 I/O 非常不方便。
无论您的操作系统如何报告,为了同时最大化所有核心,您需要运行 N 个进程,或者有一个启动 N 个线程的进程,或者两者的某种组合。
解决方案 8:
尝试使用 OpenMP 等来并行化您的程序。这是一个非常简单且有效的编写并行程序的框架。
解决方案 9:
为了快速改进一个核心,请删除系统调用以减少上下文切换。删除以下几行:
system("clear");
printf("%d prime numbers calculated
",primes);
第一种情况特别糟糕,因为每次迭代都会产生一个新进程。
解决方案 10:
只需尝试压缩和解压缩一个大文件,无需担心,因为繁重的 I/O 操作会占用 CPU。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件