[英]Rudimentary C Benchmark calculating Primes throws up crazy results and then craps out on Windows?
我編寫了一個基本的 C 程序(Prime C 基准測試的 PCB),它通過計時為 0 和用戶輸入的數字之間的所有自然數查找素數的過程來對系統速度進行基准測試 [用戶輸入一個“加載值”,它乘以10^5]
在我的 Intel i5 5350U 和 LPDDR3(MacBook Air 2017,使用 Apple Clang 11)上 1 的工作負載(即最高 100,000 次)運行 5 次,平均需要 25 秒(插入電源,充電,但在 5% 電池電量時達到 50 秒)。
在我的 Exynos 9611 和 LPDDR4x(Samsung M21,使用“Coding C”應用程序/編譯器)上完全相同的工作負載並運行 5 次,平均需要8秒!
在 Windows(i5 3340M,Win7_SP2,VS2019 最新版本,發布版本,x86)上,當針對任何“加載值”運行 5 次時,該程序絕對崩潰。 我得到的時間是 0?0000.. 什么.. 這里絕對有問題 XD ....\
Linux(Ubuntu 20.04,GCC,與 Win,i3 相同的硬件)需要 21.5 秒。 在我看來,Linux 和 MacOS(所以 Apple Clang 和 GCC)可能做得對......
編碼:
#include <stdio.h>
#include <time.h>
long count = 0;
void bench(double x) {
register unsigned long n, i, q;
for (q = 0; q <= x; q++) {
for (i = 2; i <= q / 2; ++i) {
if (q % i == 0)
count++;
}
}
}
int main() {
double x;
int y;
printf( "\nPCB v0.1\nOpen-source Tool for Benchmarking System Speed.\n\nRecommended Load Value 1 - 3\n");
printf("\nEnter Load Value : ");
scanf("%lf", &x);
printf("\nEnter Frequency for Repetition : ");
scanf("%d", &y);
x = x * 100000;
printf("\nPress Enter to Run ");
getchar();
getchar();
printf("\n(...Running...)\n");
int z;
for (z = 1; z <= y; z++) {
clock_t t;
t = clock();
bench(x);
t = clock() - t;
double time_taken = ((double)t)/CLOCKS_PER_SEC; // in seconds
printf("\nTime Taken #%d = %.4f seconds\n", z, time_taken);
}
printf("\nPress Enter to Exit ");
getchar();
return count;
}
您的素數枚舉代碼有缺陷:
在最初發布的代碼中, benchmark
function 中的for
循環沒有副作用,因此高效的編譯器能夠對其進行優化並且基本上不生成任何代碼。 這解釋了從一個系統到另一個系統的巨大差異。
在上次更新中,您的算法不計算素數的計數,它僅執行大量除法並計算您獲得零余數的次數。 這比實際素數測試的成本要高得多,而實際素數測試本身的效率遠低於執行Eratostenes 篩法。
為了測量和比較系統性能,這種方法誇大了除法操作碼的速度,它顯示出Linux、OS/X和Windows之間的差異很大,可能是因為unsigned long
類型的大小為64位在 Linux 和 OS/X 與 Windows 上的 32 位上,使 Windows 上的模運算更快,即使對於同一組數字也是如此。 此外,這種類型的基准測試使用單核,因此它不能長期測量整體系統性能。
不同系統的相對性能應使用更多樣化的操作集來衡量,重點關注 CPU、memory、存儲和通信系統。
關於素數枚舉,這里是一個帶有素數測試的修改版本:
#include <limits.h>
#include <stdio.h>
#include <time.h>
unsigned long long bench(double x) {
if (x < 0 || x >= ULLONG_MAX) {
printf("invalid benchmark range\n");
return 0;
}
unsigned long long n = (unsigned long long)x;
unsigned long long count = 0;
if (n >= 2)
count++;
for (unsigned long long p = 3; p <= n; p += 2) {
count++;
for (unsigned long long i = 3; i * i <= p; i += 2) {
if (p % i == 0) {
count--;
break;
}
}
}
return count;
}
int main() {
double x;
int y;
clock_t total = 0;
unsigned long long count;
double time_taken;
printf("\nPCB v0.1\nOpen-source Tool for Benchmarking System Speed.\n\nRecommended Load Value 1 - 3\n");
printf("\nEnter load value: ");
if (scanf("%lf", &x) != 1)
return 1;
printf("\nEnter repeat count: ");
if (scanf("%d", &y) != 1)
return 1;
x = x * 100000;
printf("\nPress Enter to Run ");
getchar();
getchar();
printf("\n(...Running...)\n");
for (int z = 0; z < y; z++) {
clock_t t;
t = clock();
count = bench(x);
t = clock() - t;
total += t;
time_taken = ((double)t) / CLOCKS_PER_SEC; // in seconds
printf("\n%llu primes, time taken #%d = %.4f seconds\n", count, z, time_taken);
}
time_taken = ((double)total) / CLOCKS_PER_SEC; // in seconds
printf("\nAverage time taken = %.4f seconds\n", time_taken / y);
printf("\nPress Enter to Exit ");
getchar();
return 0;
}
Output:
PCB v0.1 Open-source Tool for Benchmarking System Speed. Recommended Load Value 1 - 3 Enter load value: 1 Enter repeat count: 5 Press Enter to Run (...Running...) 9592 primes, time taken #0 = 0.0126 seconds 9592 primes, time taken #1 = 0.0117 seconds 9592 primes, time taken #2 = 0.0133 seconds 9592 primes, time taken #3 = 0.0136 seconds 9592 primes, time taken #4 = 0.0137 seconds Average time taken = 0.0130 seconds Press Enter to Exit
這比我筆記本電腦上的初始代碼快了近 2000 倍。
運行 100 的負載給出了這個 output:
PCB v0.1 Open-source Tool for Benchmarking System Speed. Recommended Load Value 1 - 3 Enter load value: 100 Enter repeat count: 5 Press Enter to Run (...Running...) 664579 primes, time taken #0 = 7.4249 seconds 664579 primes, time taken #1 = 7.3742 seconds 664579 primes, time taken #2 = 7.4119 seconds 664579 primes, time taken #3 = 7.3887 seconds 664579 primes, time taken #4 = 7.6725 seconds Average time taken = 7.4544 seconds Press Enter to Exit
這仍然比篩子慢得多:
$ chqrlie > time prime -c 1..10000000 664579 real 0m0.009s user 0m0.006s sys 0m0.001s
這是一個使用 Sieve 方法的簡單實現,它的速度不如我的primes
實用程序中使用的優化方法快,但對於 100 的負載仍然實現了0,0773 seconds
的平均時間,比 Prime 測試循環提高了100 倍:
unsigned long long bench(double x) {
/* simplistic Sieve of Eratostenes version */
if (x < 0 || x >= SIZE_MAX) {
printf("invalid benchmark range\n");
return 0;
}
size_t count = 0;
size_t n = (size_t)x + 1; // array size
if (n > 1) {
unsigned char *a = calloc(n, 1);
if (a == NULL) {
printf("cannot allocate memory\n");
return 0;
}
// 0 and 1 are considered composite
a[0] = a[1] = 1;
// flag all multiples of 2 as composite
for (size_t i = 4; i < n; i += 2) {
a[i] = 1;
}
for (size_t p = 3; p * p < n; p += 2) {
// for all potential prime numbers
if (a[p] == 0) {
// if p is prime, flag all odd multiples of p as composite
for (size_t i = p * p; i < n; i += 2 * p) {
a[i] = 1;
}
}
}
count = n;
// count the number of composite numbers
for (size_t i = 0; i < n; i++) {
count -= a[i];
}
free(a);
}
return count;
}
我建議將bench
function 更改為
unsigned long bench (double x) {
register unsigned long n;
register unsigned long i, q;
unsigned long count = 0;
for(q = 0;q <= x; q++){
int prime = 0;
for (i = 2; i <= q/2 ; ++i) {
if (q % i == 0) {
prime = 1;
break;
}
}
if (prime)
count++;
}
return count;
}
和main
的 function 到:
int main() {
...
unsigned long count = 0;
for(z=1;z <=y; z++)
{
clock_t t;
t = clock();
unsigned long c = bench(x);
t = clock() - t;
count += c;
double time_taken = ((double)t)/CLOCKS_PER_SEC; // in seconds
printf("\nTime Taken #%d = %.4f seconds\n", z, time_taken);
}
printf("\nPress Enter to Exit ");
getchar();
return count > 0 ? 0 : 1; // ensures the use of the return values of bench
}
此外,為了准確測量,您應該確保所有編譯器都以最大優化進行編譯。 這很重要,因為這可能使編譯器有機會使用SIMD指令來加速工作台操作。
但也請注意,僅通過一個操作對系統/CPU 進行標記並不是系統/CPU 之間一般比較的良好基礎。 例如,您的bench
function 僅用於測試 CPU 划分大量連續數字的速度。
@chqrlie 走在正確的道路上......
他是對的:
此外,我可能會補充:
除了 UI 改進,比如取消每個循環的輸出,並且只在此處和那里堅持平均值以及一些大寫字母,我還有:
對於任何更感興趣的人,我將使用此文件中的源代碼構建 PCB 一段時間,請隨時查看或建議更改/批評代碼至 apjo@tuta.io
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.