【性能测试】使用自定义程序对CPU的程序运行性能进行测试

1 简介

现在市面上可用的CPU的性能测试有很多,比如CPUZ, GeekBench, 鲁大师等。这些工具从不同的角度对CPU的性能进行测试评价,我们可以使用这些工具对CPU的性能有一定的了解。但是,对于一名开发人员来说,CPU的性能对程序的性能影响到底是怎样的,事实上并没有哪款软件以为这个目的服务的。因此,本文就通过一个求质数的Java代码对CPU的性能进行测试。通过这个程序,可以对 CPU执行Java程序的性能影响有初步的认识。

本代码主要包括单核、多核两个部分,计算内容是求大数的质数。程序计算的数据范围是在10亿以上一定范围内求所有的质数的数量。本的算法使用最简单直白的素数判断方法,几乎没有经过特别的优化,因为我们计算的目的并不是为了让CPU尽可能高效地获得结果,相反,我们希望 CPU 尽可能做更多的计算,从而通过处理相同的计算量所使用的时间来判断CPU的计算性能,即通过求素数的总计算数量与CPU使用的时间的比值作为评分,以对CPU的性能表现进行打分,从而通过便于我们通过这些评分对CPU的性能在这个程序当中的表现能够以量化的结果进行对比。

2 测试原理

2.1 质数判断函数

我们编写了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;
}

判断的原理很简单,包括以下几步:

  1. 如果是小于2或是偶数,则不是质数,直接返回 false;
  2. 用区间 [ 3 , ⌊ n ⌋ + 1 ] [3, \lfloor \sqrt{n} \rfloor + 1] [3,n +1]上的所有自然数对待测试的自然数 n 进行求余判断,如果余数为0,表示能整除,则不是质数,返回 false;
  3. 如果第2步循环结束仍然没有退出,则表示是质数,返回 true。

第2步实际上对范围做了简单的优化,因为如果 n 是不是质量,则必可以表示成 p*q 的形式,那么p 和 q 必有1个小于 ⌊ n ⌋ + 1 \lfloor \sqrt{n} \rfloor + 1 n +1,所以可以对判断的范围进行缩小而减少计算量。

2.2 多线程处理

对于给定的区间,使用多线程技术可以并行计算。本算法默认的输入自然的起始为10亿,即long startNumber = 1_000_000_000L;; 处理范围 totalRange = 10_000_000,并使用系数来指定范围,默认值为1即 1千万个数。另一个控制参数是线程数 threadCount,对指定的判断数量,会根据线程数量进行平均拆分,每个线程负责其中的一段。
为了减少数据存储所占用的时间,本算法中只统计质数的数量,而不记录具体值。即在每次调用isPrimeNumber(n) 后,如果返回true,计数变量加1。

3 测试结果与分析

3.1 说明

测试使用了以下参数,含义如下:

  • 1,1 = 使用1线程求1千万个数
  • 1,10 = 使用1线程求1亿个数
  • 32,1 = 使用32线程求1千万个数
  • 32,10 = 使用1线程求1亿个数

3.1 测试结果

测试使用了多种CPU,结果如下表所示:

CPU1,11, 1032, 132, 10
9750H1401411,1051,153
3700X2142091, 6091,589
5800U6646454,4214,152
5950X74372810,70710,110
M1PRO9489567,0087,458

3.2 结果分析

测试发现,不同的CPU的性能差别,比专业软件大得多,比较明显的变化就是随着时间的变化性能有明显的提升。比如最新的5800U、5950X和M1Pro性能都比较好,而且早期的3700X和9750H的表现就差了很多。所以,可以理解为,新的CPU在架构上对程序指令的处理有了明显的优化,性能表现有显著提升。

作为对比,笔者还选择了C#在.Net6.0环境下进行测试,发现性能居然比Java还要强5%~10%。另外,在测试时还发表,通过将long都替换成int,发现所有的性能几乎都有20%左右的提升,所以可以得出这样的结论:虽然CPU、Windows和Java都是64位的,但是在使用32位的int代替64位的long类型时,性能会更好,且使用的类型量比较大时,性能有20%左右的提升。所以,在能够使用int的场合,即int可以满足数据数据范围的要求,仍然推荐使用int而非long。

4 运行代码

4.1 运行方法

核心代码,只有1个单文件 FindPrimeNumbers.java,在本测试中,使用以下命令在控制台运行:

# 编译
javac FindPrimeNumbers.java

# 4种情况的运行代码
java FindPrimeNumbers 1 1
java FindPrimeNumbers 1 10
java FindPrimeNumbers 32 1
java FindPrimeNumbers 32 10

4.2 源代码 (Java)

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);
	}
}

4.3 源代码 (C#)

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;
    }
}

5 测试中的一些数据(仅供参考)

以下是测试中用于测试编写的求和代码

代码3.0

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);
	}	
}

测试代码 2.0

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 ---");
	}	
}

测试代码 1.0

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);
	}
}

不同机器的表现

3700X

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.

MacBook Pro 2021

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.

MacBook Pro 2018

多核跑分:

5.77e+21, 1074.31, 166.83,  6.44

单核跑分

5.78e+19, 10.75, 25.21,  0.47.

5950X 跑分

执行结果:约为 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 占用情况
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值