如何从 C 程序中获取 100% 的 CPU 使用率

2024-10-22 08:28:00
admin
原创
68
摘要:问题描述:这个问题很有趣,让我先介绍一下背景。我在美国国家计算机博物馆工作,我们刚刚成功让一台 1992 年的 Cray Y-MP EL 超级计算机运行起来,我们真的很想看看它能跑多快!我们认为最好的方法是编写一个简单的 C 程序来计算素数并显示计算所需的时间,然后在一台快速的现代台式电脑上运行该程序并比较结...

问题描述:

这个问题很有趣,让我先介绍一下背景。我在美国国家计算机博物馆工作,我们刚刚成功让一台 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。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用