使用自定义Java程序对CPU的程序运行性能进行测试
郝伟 2022/0/0
现在市面上可用的CPU的性能测试有很多,比如CPUZ, GeekBench, 鲁大师等。这些工具从不同的角度对CPU的性能进行测试评价,我们可以使用这些工具对CPU的性能有一定的了解。但是,对于一名开发人员来说,CPU的性能对程序的性能影响到底是怎样的,事实上并没有哪款软件以为这个目的服务的。因此,本文就通过一个求质数的Java代码对CPU的性能进行测试。通过这个程序,可以对 CPU执行Java程序的性能影响有初步的认识。
本代码主要包括单核、多核两个部分,计算内容是求大数的质数。程序计算的数据范围是在10亿以上一定范围内求所有的质数的数量。本的算法使用最简单直白的素数判断方法,几乎没有经过特别的优化,因为我们计算的目的并不是为了让CPU尽可能高效地获得结果,相反,我们希望 CPU 尽可能做更多的计算,从而通过处理相同的计算量所使用的时间来判断CPU的计算性能,即通过求素数的总计算数量与CPU使用的时间的比值作为评分,以对CPU的性能表现进行打分,从而通过便于我们通过这些评分对CPU的性能在这个程序当中的表现能够以量化的结果进行对比。
我们编写了Java程序,从10亿开始,对指定范围的自然数进行质数判断,使用的判断函数如下:
boolean isPrimeNumber(long n) { if (n < 2 || n % 2 == 0) return false; long m = (long) Math.sqrt(n) + 1; for (long i = 3; i < m; i += 2) { if (n % i == 0) return false; return true; }
判断的原理很简单,包括以下几步:
第2步实际上对范围做了简单的优化,因为如果 n 是不是质量,则必可以表示成 p*q 的形式,那么p 和 q 必有1个小于 ,所以可以对判断的范围进行缩小而减少计算量。
对于给定的区间,使用多线程技术可以并行计算。本算法默认的输入自然的起始为10亿,即long startNumber = 1_000_000_000L;; 处理范围 totalRange = 10_000_000,并使用系数来指定范围,默认值为1即 1千万个数。另一个控制参数是线程数 threadCount,对指定的判断数量,会根据线程数量进行平均拆分,每个线程负责其中的一段。
为了减少数据存储所占用的时间,本算法中只统计质数的数量,而不记录具体值。即在每次调用isPrimeNumber(n) 后,如果返回true,计数变量加1。
测试使用2个参数,第1个表示使用的线程数,第2个表示数量,单位是千万,如:
1,1 表示使用1线程求1千万个数1,10 表示使用1线程求1亿个数32,1 表示使用32线程求1千万个数32,10 表示使用1线程求1亿个数测试使用了多种CPU,结果如下表所示:
| CPU | 1,1 | 1, 10 | 32, 1 | 32, 10 | 测试日期 |
|---|---|---|---|---|---|
| 9750H | 140 | 141 | 1,105 | 1,153 | 2022/02/01 |
| 3700X | 214 | 209 | 1,609 | 1,589 | 2022/02/01 |
| 12700H | 507 | 497 | 5,040 | 5,315 | 2022/03/10 |
| 5800U | 664 | 645 | 4,421 | 4,152 | 2022/02/01 |
| 5950X | 743 | 728 | 10,707 | 10,110 | 2022/03/10 |
| 5950X | 833 | 816 | 12,005 | 11,975 | 2022/03/10 |
| M1PRO | 948 | 956 | 7,008 | 7,458 | 2022/02/01 |
测试发现,不同的CPU的性能差别,比专业软件大得多,比较明显的变化就是随着时间的变化性能有明显的提升。比如最新的5800U、5950X和M1Pro性能都比较好,而且早期的3700X和9750H的表现就差了很多。所以,可以理解为,新的CPU在架构上对程序指令的处理有了明显的优化,性能表现有显著提升。
另外,在测试时还发表,通过将long都替换成int,发现所有的性能几乎都有20%左右的提升,所以可以得出这样的结论:虽然CPU、Windows和Java都是64位的,但是在使用32位的int代替64位的long类型时,性能会更好,且使用的类型量比较大时,性能有20%左右的提升。所以,在能够使用int的场合,即int可以满足数据数据范围的要求,仍然推荐使用int而非long。
核心代码,只有1个单文件 FindPrimeNumbers.java,在本测试中,使用以下命令在控制台运行:
# 编译
javac FindPrimeNumbers.java
# 4种情况的运行代码
java FindPrimeNumbers 1 1
java FindPrimeNumbers 1 10
java FindPrimeNumbers 32 1
java FindPrimeNumbers 32 10
import java.util.Random; public class FindPrimeNumbers { public static Random rand = new Random(0); /** * Arguments [thread_count] [coefficient] thread_count: number of thread * coefficient: be ratio to the amount of task. */ public static void main(String[] args) throws Exception { // show help if (args.length > 0 && args[0].toLowerCase().equals("help")) { System.out.println("SUM, COUNT (B), TIME(s), SPEED(B/s)"); return; } // get arguments int threadCount = args.length > 0 ? Integer.parseInt(args[0]) : 20; int coefficient = args.length > 1 ? Integer.parseInt(args[1]) : 1; // check numbers between 1b and 2b. long startNumber = 1_000_000_000L; long totalRange = 10_000_000 * coefficient; long interval = totalRange / threadCount; // define variables double[] results = new double[threadCount]; Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { threads[i] = new Thread("Subthread-" + i) { @Override public void run() { int tid = Integer.parseInt(getName().split("-")[1]); long start = startNumber + interval * tid; long end = tid == threadCount - 1 ? startNumber + totalRange : startNumber + interval * tid + interval; int primeNo = 0; // System.out.printf("id: %d, start=%d, end=%d.\n", tid, start, end); for (long i = start; i < end; i++) if (isPrimeNumber(i)) primeNo++; results[tid] = primeNo; } boolean isPrimeNumber(long n) { if (n < 2 || n % 2 == 0) return false; long m = (long) Math.sqrt(n) + 1; for (long i = 3; i < m; i += 2) if (n % i == 0) return false; return true; } }; } // start all threads long t1 = System.currentTimeMillis(); for (int i = 0; i < threads.length; i++) threads[i].start(); // wait threads to finish for (int i = 0; i < threads.length; i++) threads[i].join(); long t2 = System.currentTimeMillis(); // sum all results long total_sum = 0; for (int i = 0; i < threads.length; i++) { total_sum += results[i]; } // format and output results double time = (t2 - t1) / 1000.0; double speed = totalRange / time /1000; System.out.printf("# of Primes=%d, total=%d, time=%6.2fs, score=%5.0f.\n", total_sum, totalRange, time, speed); } }
public class Program { public static void Main(string[] args) { // show help if (args.Length > 0 && args[0].ToLower().Equals("help")) { Console.WriteLine("SUM, COUNT (B), TIME(s), SPEED(B/s)"); return; } // get arguments int threadCount = args.Length > 0 ? int.Parse(args[0]) : 1; int coefficient = args.Length > 1 ? int.Parse(args[1]) : 1; // check numbers between 1b and 2b. long startNumber = 1_000_000_000L; long totalRange = 10_000_000 * coefficient; long interval = totalRange / threadCount; // define variables long[] results = new long[threadCount]; Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { threads[i] = new Thread( () => { int tid = int.Parse(Thread.CurrentThread.Name.Split("-")[1]); long start = startNumber + interval * tid; long end = tid == threadCount - 1 ? startNumber + totalRange : startNumber + interval * tid + interval; int primeNo = 0; // System.out.printf("id: %d, start=%d, end=%d.\n", tid, start, end); for (long i = start; i < end; i++) if (isPrimeNumber(i)) primeNo++; results[tid] = primeNo; // Console.WriteLine(); } ); threads[i].Name = "Thread-" + i; } // start all threads var t1 = DateTime.Now; for (int i = 0; i < threadCount; i++) threads[i].Start(); // wait threads to finish for (int i = 0; i < threadCount; i++) threads[i].Join(); var t2 = DateTime.Now; // sum all results long total_sum = 0; for (int i = 0; i < threadCount; i++) { total_sum += results[i]; } // format and output results double time = (t2 - t1).TotalSeconds; var speed = (int)(totalRange / time / 1000); Console.WriteLine($"PrimeCount={total_sum}, Range={totalRange}, Time={time:0.00}s, Score={speed}.\n"); } public static bool isPrimeNumber(long n) { if (n < 2 || n % 2 == 0) return false; long m = (long)Math.Sqrt(n); for (long i = 3; i < m; i += 2) if (n % i == 0) return false; return true; } }
以下是测试中用于测试编写的求和代码
import java.util.Random; public class SpeedTest { public static Random rand = new Random(0); /** * Arguments [thread_count] [coefficient] * thread_count: number of thread * coefficient: be ratio to the amount of task. */ public static void main(String[] args) throws Exception { // show help if(args.length > 0 && args[0].toLowerCase().equals("help")) { System.out.println("SUM, COUNT (B), TIME(s), SPEED(B/s)"); return; } // get arguments int threadCount = args.length > 0 ? Integer.parseInt(args[0]) : 1; int coefficient = args.length > 1 ? Integer.parseInt(args[1]) : 1; // define variables double[] results = new double[threadCount]; long[] counts = new long[threadCount]; Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { threads[i] = new Thread("Subthread-" + i) { @Override public void run() { int tid = Integer.parseInt(getName().split("-")[1]); counts[tid] = 1048576L * (1024 + rand.nextInt(2)) * coefficient; results[tid] = get_sum(0, counts[tid]); } double get_sum(long start, long end) { double result = 0; for(long i = start; i < end; i ++) result += i; return result; } }; } // start all threads var t1 = System.currentTimeMillis(); for (int i = 0; i < threads.length; i++) threads[i].start(); // wait threads to finish for (int i = 0; i < threads.length; i++) threads[i].join(); var t2 = System.currentTimeMillis(); // sum all results double total_sum = 0; double total_count = 0; for (int i = 0; i < threads.length; i++){ total_sum += results[i]; total_count += counts[i]; } // format and output results total_count /= 1E9; // in Billions double time = (t2 - t1)/1000.0; var speed = total_count / time; System.out.printf("%8.3E, %8.3f, %8.3f, %8.3f.\n", total_sum, total_count, time, speed); } }
import java.util.ArrayList; import java.util.Random; public class SpeedTest { public static double res = 0; public static long count = 0; public static Random rand; public static double[] results; public static long[] counts; public static int[] elasptime; public static int threadCount = 100; public static void main(String[] args) throws Exception { threadCount = 100; Random rand = new Random(0); results = new double[threadCount]; counts = new long[threadCount]; elasptime = new int[threadCount]; Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { threads[i] = new Thread() { @Override public void run() { var name = Thread.currentThread().getName(); if(!name.contains("-")) return; int tid = Integer.parseInt(name.split("-")[1]); long len = 1024 * 1024 * (1024 + rand.nextInt(2)); double sum = 0; for (long i = 0; i < len; i++) sum += i; counts[tid] = len; results[tid] = sum; } }; } // System.out.println("---- Start ----"); var t1 = System.currentTimeMillis(); for (int i = 0; i < threads.length; i++) threads[i].start(); // System.out.println("---- Threads Join ---"); for (int i = 0; i < threads.length; i++) threads[i].join(); var t2 = System.currentTimeMillis(); double total_sum = 0; double total_count = 0; for (int i = 0; i < threads.length; i++){ total_sum += results[i]; total_count += counts[i]; } total_count /= 1E9; // in Billions double time = (t2 - t1)/1000.0; var speed = total_count / time; System.out.printf("%5.2e, %5.2f, %5.2f, %5.2f.\n", total_sum, total_count, time, speed); // System.out.println("--- FINISH ---"); } }
import java.util.ArrayList; import java.util.Random; public class SpeedTest { public static double res = 0; public static long count = 0; public static Random rand = new Random(0); public static void main(String[] args) throws Exception { // heavy_task(); double[] results = new double[100]; Thread[] threads = new Thread[30]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread() { @Override public void run() { HwTimer timer = new HwTimer(); timer.start(); timer.result = 0; timer.count = 1024 * 1024 * (1024 + rand.nextInt(2)); for (long i = 0; i < timer.count; i++) timer.result += i; timer.stop(); timer.show(); RandomDemo.count += timer.count; // var res1 = heavy_task(); var name = Thread.currentThread().getName(); if(name.contains("-")) { results[Integer.parseInt(name.split("-")[1])] = timer.result; // System.out.println("timer.result: " + timer.result); } } }; } System.out.println("---- Start ----"); HwTimer timer = new HwTimer(); timer.start(); for (int i = 0; i < threads.length; i++) threads[i].start(); // System.out.println("---- Threads Join ---"); for (int i = 0; i < threads.length; i++) threads[i].join(); timer.stop(); timer.count = RandomDemo.count; timer.result = 0; for (int i = 0; i < threads.length; i++) timer.result += results[i]; timer.show(); } public static double heavy_task() { HwTimer timer = new HwTimer(); timer.start(); timer.result = 0; timer.count = 1024 * 1024 * (1021 + rand.nextInt(2)); for (long i = 0; i < timer.count; i++) timer.result += i; timer.stop(); timer.show(); return timer.result; } public static void randDemo() { Random rand = new Random(11); ArrayList<Integer> origIDs = new ArrayList<Integer>(); ArrayList<Integer> randIDs = new ArrayList<Integer>(); int count = 10; for (int i = 0; i < count; i++) origIDs.add(i); while (origIDs.size() > 0) { int pos = rand.nextInt(origIDs.size()); randIDs.add(origIDs.get(pos)); origIDs.remove(pos); } for (Integer i : randIDs) System.out.print(i + ", "); // output: 6, 8, 1, 3, 0, 5, 9, 4, 7, 2, } public static void speedDemo() { var t1 = System.currentTimeMillis(); long result = 0; long len = 1024 * 1024 * 1024; len *= 3; for (long i = 0; i < len; i++) result += i; var t2 = System.currentTimeMillis(); System.out.println("\nresult: " + result + ", time: " + (t2 - t1) + "ms"); } }
public class HwTimer { public static void main(String[] args) { // TODO Auto-generated method stub } public long startTime = 0; public long endTime = 0; public String name = "no name"; public double result = 1; public long count = 1; public void start() { startTime = System.currentTimeMillis(); } public void stop() { endTime = System.currentTimeMillis(); } public void show() { var name = Thread.currentThread().getName(); var time = (endTime - startTime) / 1000.0; var speed = count / time / 1E9; var count1 = count * 1.0 / 1E9; System.out.printf("%s: res=%5.2e, count=%5.2f B, time=%5.2fs, speed = %5.2f B/s\n", name, result, count1, time, speed); } }
c:\java_code>javac SpeedTest.java && java SpeedTest
5.776E+17, 1.075, 2.747, 0.391.
c:\java_code>javac SpeedTest.java && java SpeedTest
5.776E+17, 1.075, 2.742, 0.392.
c:\java_code>java SpeedTest 16
9.235E+18, 17.190, 2.930, 5.867.
c:\java_code>java SpeedTest 100
5.771E+19, 107.431, 9.990, 10.754.
c:\java_code>java SpeedTest 1 10
5.776E+19, 10.748, 27.741, 0.387.
c:\java_code>java SpeedTest 10
5.772E+18, 10.745, 2.846, 3.775.
c:\java_code>java SpeedTest 20
1.154E+19, 21.487, 3.711, 5.790.
c:\java_code>java SpeedTest 30
1.731E+19, 32.227, 4.559, 7.069.
c:\java_code>java SpeedTest 40
2.308E+19, 42.970, 5.500, 7.813.
c:\java_code>java SpeedTest 5
2.887E+18, 5.373, 2.828, 1.900.
c:\java_code>java SpeedTest 100
5.771E+19, 107.431, 8.992, 11.947.
c:\java_code>java SpeedTest 200
1.154E+20, 214.858, 16.149, 13.305.
c:\java_code>java SpeedTest 200
1.154E+20, 214.858, 15.068, 14.259.
c:\java_code>java SpeedTest 200 10
1.154E+22, 2148.585, 156.073, 13.767.
haowei@Haos-MacBook-Pro-2021-M1-Pro java_code % java SpeedTest
5.776E+17, 1.075, 4.482, 0.240.
haowei@Haos-MacBook-Pro-2021-M1-Pro java_code % java SpeedTest
5.776E+17, 1.075, 4.509, 0.238.
haowei@Haos-MacBook-Pro-2021-M1-Pro java_code % java SpeedTest 2
1.155E+18, 2.150, 4.583, 0.469.
haowei@Haos-MacBook-Pro-2021-M1-Pro java_code % java SpeedTest 14
8.079E+18, 15.041, 7.040, 2.136.
haowei@Haos-MacBook-Pro-2021-M1-Pro java_code % java SpeedTest 200
1.154E+20, 214.858, 83.269, 2.580.
haowei@Haos-MacBook-Pro-2021-M1-Pro java_code % java SpeedTest 1 10
5.776E+19, 10.748, 44.697, 0.240.
多核跑分:
5.77e+21, 1074.31, 166.83, 6.44
单核跑分
5.78e+19, 10.75, 25.21, 0.47.
执行结果:约为 15.50 左右
除了个别几次由于某些原因导致的降速度为10左右,大部分结果在15-16,故取15.50为性能结果。
for /L %i in (1,1,10) do @echo off && java SpeedTest
# SUM, COUNT (B), TIME(s), SPEED(B/s)
5.77e+19, 107.43, 10.68, 10.06.
5.77e+19, 107.43, 10.33, 10.40.
5.77e+19, 107.43, 6.87, 15.64.
5.77e+19, 107.43, 11.94, 9.00.
5.77e+19, 107.43, 6.97, 15.41.
5.77e+19, 107.43, 6.46, 16.62.
5.77e+19, 107.43, 6.47, 16.61.
5.77e+19, 107.43, 7.05, 15.23.
5.77e+19, 107.43, 6.92, 15.52.
5.77e+19, 107.43, 6.77, 15.87.
CPU 占用情况
