如何从C程序中获得100%的CPU使用率
如何从C程序中获得100%的CPU使用率
这是一个非常有趣的问题,让我先给你介绍一下背景。我在国家计算机博物馆工作,我们刚刚成功启动了一台1992年的Cray Y-MP EL超级计算机,我们非常想知道它能跑多快!\n我们决定通过编写一个简单的C程序来计算质数并显示计算所需的时间,然后在一台快速的现代台式电脑上运行该程序并比较结果。\n我们很快编写了以下代码来计算质数:\n
#include#include 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); }
\n在我们的双核笔记本电脑上运行Ubuntu(Cray运行UNICOS)上,这段代码完美运行,占用100%的CPU使用率,大约需要10分钟左右。当我回到家后,我决定在我六核的现代游戏电脑上尝试一下,这就是我们遇到的第一个问题。\n我首先将代码改写为在Windows上运行,因为游戏电脑正在使用Windows,但我很失望地发现该进程只能获得大约15%的CPU功率。我认为这可能是Windows的问题,所以我启动了一个Ubuntu的Live CD,以为Ubuntu能像之前在我的笔记本电脑上那样充分发挥进程的潜力。\n然而,我只能得到5%的使用率!所以我的问题是,我该如何修改程序,在Windows 7或Live Linux上以100%的CPU利用率在我的游戏机上运行?另外一个很好但不是必需的事情是,最终产品能否是一个容易分发和在Windows机器上运行的.exe文件。\n非常感谢!\n附:当然,这个程序实际上并不能运行在Cray的8个专用处理器上,这是另一个问题...如果你知道如何优化代码以在90年代的Cray超级计算机上运行,请告诉我们!
如何从C程序中获得100%的CPU使用率
我们非常想知道它能有多快!
您生成素数的算法非常低效。将其与在Pentium II-350上仅用8秒生成50847534个小于1000000000的素数的primegen进行比较。
要轻松占用所有CPU,您可以解决一个尴尬地并行问题,例如计算曼德布罗特集或使用遗传编程在多个线程(进程)中绘制《蒙娜丽莎》。
另一种方法是将现有的Cray超级计算机的基准程序移植到现代PC上。
算法的效率并不重要,因为目标不是实际计算素数,而是执行一个普遍困难的任务,并观察它在现代台式机上的表现好坏。高效的算法只会使这种比较更加困难,甚至可能破坏结果,如果它如此出色,以至于故意利用现代CPU的特性/怪癖。
整数除法ALU与内存(或L3缓存)带宽之间的速度比在旧机器和新机器上可能非常不同,因此查看两个基准测试实际上是有趣的。(例如,当在x86汇编中使用位压缩筛法时,筛法Eratosthenes对于越过约512个大于或等于的素数时受到内存带宽的限制时具有一些讨论,因此步幅约为整个缓存行,假设通过仔细优化实现了高效的位访问)
根据上述内容,我们可以得出以下结论:
问题的原因:想要看到C程序在计算任务时能够达到100%的CPU使用率。
解决方法:可以采取以下几种方法来实现:
- 采用尴尬地并行问题,例如计算曼德布罗特集或使用遗传编程在多个线程(进程)中绘制《蒙娜丽莎》。
- 将现有的Cray超级计算机的基准程序移植到现代PC上。
- 不必考虑算法的效率,重点是执行一个普遍困难的任务,并观察它在现代台式机上的表现好坏。
- 考虑不同机器之间整数除法ALU与内存(或L3缓存)带宽的速度比可能不同,因此可以进行不同机器的基准测试。
通过上述方法,我们可以获得C程序的100%CPU使用率。
如何从C程序中获得100%的CPU使用率
在一个多核机器上运行一个进程,所以它只在一个核心上运行。
解决办法很简单,因为你只是想要占满处理器 - 如果你有N个核心,就并行地运行你的程序N次。
下面是一些代码,在多个核心上并行运行你的程序NUM_OF_CORES次。这是POSIX风格的代码 - 它使用fork - 所以你应该在Linux下运行它。如果我对Cray的了解是正确的,那么将这段代码移植过去可能比其他答案中的OpenMP代码更容易。
#include#include #include #include #include #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
好像当你需要运行Prime95时,你需要多个实例...当然有一种方法可以让一个进程使用多个核心吗?就像哈希破解程序一样。
嗯,一个进程可以使用线程来进行多进程处理,但我认为这并不是你的意思,因为在这个上下文中,线程几乎是一个单独的进程。所以,不能让一个单线程的程序在多个核心上运行,你必须重新编写它。有时候这真的很难,有时候甚至是不可能的。
我猜这不会像让程序在Cray上工作那么难。考虑到我对这方面还是新手(是什么暴露了我?:P),从哪里开始学习比较好?
嗯,UNICOS看起来有点类似于Unix(维基百科上是这么说的),所以它可能有fork()。你应该去学习如何使用它。
哦,还有,从shell中运行你的程序8次不应该比下面的命令更难:
for i in `seq 8`; do ./my_program; done
我已经添加了一个例子。
哦!现在你有了例子,我给你点赞了。
如何从C程序获得100%的CPU使用率
想要获得100%的CPU使用率,需要使用超过一个核心,这需要多线程的支持。
以下是使用OpenMP的并行版本:
我不得不将限制增加到1000000,以使其在我的机器上花费超过1秒的时间。
#include#include #include 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; }
输出结果:
This machine calculated all 78498 prime numbers under 1000000 in 29.753 seconds
以下是获得100%CPU的方法:
哇,这不花费你太多时间!我忘了说我们将使用的最终值是1,000,000,而1000只是用于测试(你看到了,这是愚蠢的!)我现在将测试这个,非常感谢!
根据编译器的不同,可能需要启用编译器标志来支持OpenMP。
还可以使用非常有用的函数`omp_get_wtime()`来测量时间。
你用什么编译器编译的?我使用的是MingGW的gcc。
如果必须使用线程,我认为这将很难移植到Cray。最好使用`pthread`,或者更好的是`fork`。
我使用的是带有/openmp的MSVC。对于GCC,我认为标志是`-fopenmp`。
OpenMP是最简单的方法。对于这些分布式机器,你需要MPI。但是这个程序根本不受内存限制,所以它可能在任何(甚至是模拟的)共享内存架构上运行良好。
如果你正在寻找基准数据,那么在Intel i3 M330 .13GHz上,这将生成“This machine calculated all 78498 prime numbers under 1000000 in 173.203 seconds”,这是几年前低端移动处理器的性能。
将这个程序移植到除了OpenMP之外的任何平台都有一些棘手的地方。循环迭代不均匀,随着`num`的增加以及它是否可被小数整除,计算会变得更加昂贵。因此,你将需要使用任何方法来实现一些动态调度。
但是他可能没有在那台机器上安装OpenMP,而且他只是想占用CPU - 他实际上并不关心素数计算方面的事情。你可以使用原始的方法轻松实现这一点。
是的,我主要是为了游戏机回答这个问题。有很多更有趣的方法来占用CPU。我做过的最臭名昭著的基准测试之一是回答这个问题-在我测试的4台机器中,其中2台机器过热了。
啊,非常有趣。然而,这可能仍然不是我们在这里想要的 - 我认为提问者想要的是可以在他的Linux机器和Cray上运行的POSIX代码(似乎运行了一个POSIX OS),并进行一些非常粗略的比较。现在对我来说写代码有点晚了,但明天我会尽量想出一些办法。
离题了:你用的是什么硬件?我的六核AMD @ 3.2Ghz在92秒内完成了这个任务...
他有一颗Core i7 2600K...我真是嫉妒。
太神奇了,我一年多前认为很棒的CPU,比我买它时的价格慢三倍!
Core i7 920 @ 3.5GHz(超频)。我也有一个Core i7 2600K,但我通常不在那台机器上工作。我主要用它来运行长时间运行的代码。
至于为什么它慢3倍。这个基准测试不太能代表实际性能。它只是进行整数除法。我刚刚检查了Agner Fog的表,似乎AMD的整数除法比Intel的慢得多。所以这可能就是原因。
这真是非常有趣,非常感谢你的帮助!在Windows下,该应用程序需要两个dll来运行;我不知道你是如何将它们合并为一个exe的:/
啊!太多...粉色了!
你们一直在说“并行”,这是什么意思?有人能解释一下吗?
基本上,你需要能够并行处理多个任务,才能利用多核计算机。你可以在这里解决这个困惑:[en.wikipedia.org/wiki/Parallel_computing](http://en.wikipedia.org/wiki/Parallel_computing)