模拟具有固定工作量的 CPU 密集型任务

Simulating CPU-intensive Task having fixed amount of work

我正在模拟一个 CPU-绑定任务。每个 CPU 绑定的任务计算 800 的阶乘。每个 CPU 绑定的任务都是一个由 Thread 执行的 Runnable 对象。当我增加线程数(每个线程运行一个 Runnable tak)时,我发现一些线程运行得如此之快以至于服务时间趋于 Zero.I 无法理解这个 behaviour.The 代码如下。

import java.math.BigInteger;    
public class CpuBoundJob  implements Runnable {     
    public void run() {    
         BigInteger factValue = BigInteger.ONE;
            long t1=System.nanoTime();      
            for ( int i = 2; i <= 800; i++){
              factValue = factValue.multiply(BigInteger.valueOf(i));
            }
        long t2=System.nanoTime();              
        System.out.println("Service Time(ms)="+((double)(t2-t1)/1000000));
    }    
}

public class TaskRunner extends Thread {
    CpuBoundJob job=new CpuBoundJob();
    public void run(){

    job.run();  
    }
}

public class Test {
int numberOfThreads=5;
public Test(){
    for(int i=1;i<=numberOfThreads;i++){
        TaskRunner t=new TaskRunner();
        t.start();
        }
}
public static void main(String[] args) {
    new Test(); 
    }
}

在 5 个线程的情况下,输出如下。

Service Time(ns)=28.765821
Service Time(ns)=33.489663
Service Time(ns)=29.19727
Service Time(ns)=34.259404
Service Time(ns)=37.347448

在 10 个线程的情况下,输出如下。

Service Time(ns)=45.647232
Service Time(ns)=3.972654
Service Time(ns)=23.494475
Service Time(ns)=12.210069
Service Time(ns)=19.382478
Service Time(ns)=15.34706
Service Time(ns)=54.769652
Service Time(ns)=20.646827
Service Time(ns)=3.28936
Service Time(ns)=29.809905
Service Time(ns)=60.798897
Service Time(ns)=50.718839
Service Time(ns)=2.727253
Service Time(ns)=2.882779
Service Time(ns)=63.864835
Service Time(ns)=42.601425
Service Time(ns)=4.029496
Service Time(ns)=4.339761
Service Time(ns)=79.396239
Service Time(ns)=2.923832
Service Time(ns)=5.773848
Service Time(ns)=3.064359
Service Time(ns)=2.446592
Service Time(ns)=2.205802
Service Time(ns)=2.212513
Service Time(ns)=2.265408
Service Time(ns)=82.51073
Service Time(ns)=2.200276
Service Time(ns)=2.289487
Service Time(ns)=2.322645
Service Time(ns)=2.201459
Service Time(ns)=2.217644
Service Time(ns)=2.197908
Service Time(ns)=2.252381
Service Time(ns)=13.564814
Service Time(ns)=2.238171
Service Time(ns)=2.199486
Service Time(ns)=2.179355
Service Time(ns)=2.237381
Service Time(ns)=2.593041
Service Time(ns)=2.444225
Service Time(ns)=2.42054
Service Time(ns)=38.745219
Service Time(ns)=81.232565
Service Time(ns)=19.612216
Service Time(ns)=22.31381
Service Time(ns)=59.521916
Service Time(ns)=59.511258
Service Time(ns)=54.439255
Service Time(ns)=11.582434

我无法理解 2.4 等的服务时间,有时服务时间会下降到 0.8。为什么线程 运行 固定数量的工作执行得如此之快?

很可能是计时功能太不准确,无法测量如此快速的操作。试试 100000 的阶乘或类似的东西。

nanotime 方法的文档说它不能保证提供纳秒精度:

This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis().

你是如何开始这些测试的?如果您 运行 每次都冷启动它们,我怀疑 JVM 是“warming up" and Just In Time compiling the code. If this is the case, the tests with a few threads are running as interpreted code, and later runs are compiled, and even later runs are optimized based upon previous runs. The JVM is magic that way.

降低优化成功率的想法:

  • 使用随机数作为工作参数(而不是循环索引)
  • 使用时间作为工作参数(同上)
  • 加入一些字符串连接
  • 根据工作参数改变循环长度

JIT 可能会在某个时候启动并优化代码。尝试 运行先 "warming up" 系统进行实验,即进行 N 次迭代(您将需要试验什么是 N 的良好值)运行 系统而不测量 - 然后测量,例如:

public class Test {

private int numberOfThreads;
private int warmUpIterations;

public Test(int numberOfThreads, int warmUpIterations) {
    this.numberOfThreads = numberOfThreads;
    this.warmUpIterations = warmUpIterations;
}

public void runTests() {
    for (int i = 0; i < warmUpIterations; i++) {
        test(); // don't collect timing here
    }

    test(); // collect timing here
}

private void test() {
    for (int i = 0; i < numberOfThreads; i++) {
        TaskRunner t = new TaskRunner();
        t.start();
    }
}

public static void main(String[] args) {
    new Test(10, 10000).runTests();
}

}

run() 中打印统计数据也是有问题的,考虑将它们累积在某个集合中,并在测试完成后打印它们。