81

这是一个非常有趣的问题,所以让我来设置场景。我在国家计算机博物馆工作,我们刚刚设法让一台 1992 年的 Cray Y-MP EL 超级计算机运行起来,我们真的很想看看它的运行速度有多快!

我们决定最好的方法是编写一个简单的 C 程序来计算素数并显示计算所需的时间,然后在快速的现代台式 PC 上运行该程序并比较结果。

我们很快想出了这个代码来计算素数:

#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\n",primes);
        num++;
    }

    end = clock();
    runTime = (end - start) / (double) CLOCKS_PER_SEC;
    printf("This machine calculated all %d prime numbers under 1000 in %g seconds\n", primes, runTime);
}

在我们运行 Ubuntu(The Cray 运行 UNICOS)的双核笔记本电脑上,它运行良好,获得 100% 的 CPU 使用率,大约需要 10 分钟左右。当我回到家时,我决定在我的六核现代游戏 PC 上尝试一下,这就是我们遇到的第一个问题。

我首先将代码修改为在 Windows 上运行,因为那是游戏 PC 使用的,但很遗憾地发现该进程只获得了大约 15% 的 CPU 功率。我想这一定是 Windows 就是 Windows,所以我启动到 Ubuntu 的 Live CD,认为 Ubuntu 将允许该进程充分发挥其潜力,就像它之前在我的笔记本电脑上所做的那样。

但是我只有 5% 的使用率!所以我的问题是,我怎样才能使程序在我的游戏机上以 100% 的 CPU 利用率在 Windows 7 或 Live Linux 上运行?另一件很棒但不是必需的事情是,最终产品是否可以是一个可以在 Windows 机器上轻松分发和运行的 .exe。

非常感谢!

PS 当然,这个程序并不能真正与 Crays 8 专业处理器一起使用,那完全是另外一回事了……如果您对优化代码以在 90 年代 Cray 超级计算机上工作有任何了解,也请给我们留言!

4

9 回答 9

82

如果你想要 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\n",primes);
    }

    end = omp_get_wtime();
    runTime = end - start;
    printf("This machine calculated all %d prime numbers under %d in %g seconds\n",primes,limit,runTime);

    return 0;
}

输出:

这台机器在 29.753 秒内计算了 1000000 以下的所有 78498 个素数

这是你的 100% CPU:

在此处输入图像描述

于 2012-02-11T22:27:09.327 回答
24

您在多核机器上运行一个进程 - 所以它只在一个核心上运行。

解决方案很简单,因为您只是想固定处理器 - 如果您有 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.\n", 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\n", 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
于 2012-02-11T22:16:41.823 回答
7

我们真的很想看看它能跑多快!

您生成素数的算法非常低效。将其与在 Pentium II-350 上在 8 秒内生成高达 1000000000 的 50847534 个素数进行比较。

要轻松消耗所有 CPU,您可以解决一个令人尴尬的并行问题,例如,计算Mandelbrot 集或使用遗传编程在多个线程(进程)中绘制蒙娜丽莎。

另一种方法是采用现有的 Cray 超级计算机基准程序并将其移植到现代 PC。

于 2012-02-11T23:06:06.803 回答
5

您在六核处理器上获得 15% 的原因是因为您的代码以 100% 使用 1 个内核。100/6 = 16.67%,使用带有进程调度的移动平均值(您的进程将在正常优先级下运行)可以很容易地报告为 15%。

因此,为了使用 100% 的 cpu,您需要使用 CPU 的所有内核 - 为六核 CPU 启动 6 个并行执行代码路径,并使其扩展到您的 Cray 机器拥有的处理器数量:)

于 2012-02-11T22:25:02.830 回答
2

还要非常注意你是如何加载 CPU 的。一个 CPU 可以执行许多不同的任务,虽然其中许多任务会被报告为“100% 加载 CPU”,但它们可能每个都使用了 100% 的 CPU 不同部分。换句话说,很难比较两种不同 CPU 的性能,尤其是两种不同的 CPU 架构。执行任务 A 可能有利于一个 CPU 而不是另一个,而执行任务 B 则很容易反过来(因为两个 CPU 内部可能有不同的资源,并且执行代码的方式可能非常不同)。

这就是软件对于使计算机性能与硬件一样重要的原因。这对于“超级计算机”来说也确实如此。

CPU 性能的一种衡量标准可能是每秒指令数,但同样,指令在不同的 CPU 架构上创建的并不相同。另一个衡量标准可能是缓存 IO 性能,但缓存基础设施也不相同。然后衡量指标可能是每瓦使用的指令数,因为在设计集群计算机时,功率传输和耗散通常是一个限制因素。

所以你的第一个问题应该是:哪个性能参数对你很重要?你想测量什么?如果您想查看哪台机器从 Quake 4 中获得最高 FPS,答案很简单;您的游戏设备会,因为 Cray 根本无法运行该程序;-)

干杯,斯蒂恩

于 2013-04-29T07:56:56.647 回答
2

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 threadno 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\n", 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\n",totalPrimesFound, NUM_THREADS*THREAD_LOAD, elapsed);
    pthread_exit(NULL);
}

注意整个 CPU 是如何用完的 - 在此处输入图像描述

PS - 如果您增加线程数,那么实际 CPU 使用率会下降(尝试使线程数 = 20 。)因为系统在上下文切换中使用的时间比实际计算要多。

顺便说一句,我的机器不如@mystical 强大(接受的答案)。但是我的带有基本 POSIX 线程的版本比 OMP 更快。这是结果 -

在此处输入图像描述

PS 将线程负载增加到 250 万以查看 CPU 使用情况,因为它在不到一秒的时间内完成。

于 2018-02-12T06:06:33.810 回答
0

尝试使用例如 OpenMP 来并行化您的程序。它是一个非常简单有效的构建并行程序的框架。

于 2012-02-11T22:20:41.607 回答
0

为了快速改进一个内核,删除系统调用以减少上下文切换。删除这些行:

system("clear");
printf("%d prime numbers calculated\n",primes);

第一个特别糟糕,因为它每次迭代都会产生一个新进程。

于 2012-02-15T23:09:03.527 回答
0

只需尝试 Zip 和 Unzip 一个大文件,繁重的 I/O 操作无法使用 cpu。

于 2018-02-12T06:11:59.170 回答