103.【Java Microbenchmark Harness】(四)

简介: 103.【Java Microbenchmark Harness】

10.ConstantFold (常量折叠)

常数折叠是编译器最佳化技术,被使用在现代的编译器中。进阶的常数传播形式,或称之为稀疏有条件的常量传播,可以更精确地传播常数及无缝的移除无用的程式码

eg: i=20+40+30; 出现了常量折叠。那么编译器会直接省略步骤,直接给我们结果,而不会去在CPU中进行计算的操作,这就是省去无用代码。

源码

package org.openjdk.jmh.samples;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
/**
*  计算常量和变量的耗时情况
*/
import java.util.concurrent.TimeUnit;
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS)  //预热次数和时间
@Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间
public class JMHSample_10_ConstantFold {
    private double x = Math.PI;  //变量
    private final double wrongX = Math.PI;  //常量
    private double compute(double d) {  //计算方法
        for (int c = 0; c < 10; c++) {
            d = d * d / Math.PI;
        }
        return d;
    }
    @Benchmark
    public double baseline() {  // 获取常量
        // simply return the value, this is a baseline
        return Math.PI;
    }
    @Benchmark
    public double measureWrong_1() {  //计算常量
        // This is wrong: the source is predictable, and computation is foldable.
        return compute(Math.PI);
    }
    @Benchmark
    public double measureWrong_2() {  // 计算常量
        // This is wrong: the source is predictable, and computation is foldable.
        return compute(wrongX);
    }
    @Benchmark
    public double measureRight() {  //计算x x因为没有被final修饰,所以是变量
        // This is correct: the source is not predictable.
        return compute(x);
    }
    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JMHSample_10_ConstantFold.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opt).run();
    }
}

11.@OperationsPerInvocation (调用次数)

但报表只有一次

@OperationsPerInvocation(n)  
告诉JMH的Benchmark一次执行相当于执行多少次,就是说JMH的Benchmark被JVM只
执行了一次,但在报表的时候会把这一次当作n次来计算。也就是输出这n次总耗时.

12.Loops (循环)

源码

package org.openjdk.jmh.samples;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
 *   计算两个数相加耗时: 理论上应该一致,但我们发现for循环更快
 */
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS)  //预热次数和时间
@Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间
public class JMHSample_11_Loops {
    int x = 1;
    int y = 2;
    @Benchmark
    public int measureRight() {  //测试变量相加
        return (x + y);
    }
    private int reps(int reps) {  //进行循环相加
        int s = 0;
        for (int i = 0; i < reps; i++) {
            s += (x + y);
        }
        return s;
    }
    @Benchmark
    @OperationsPerInvocation(1)  //告诉JMH的Benchmark一次执行相当于执行多少次,就是说JMH的Benchmark被JVM只执行了一次,但在报表的时候会把这一次当作n次来计算。也就是输出这n次总耗时
    public int measureWrong_1() {  //循环相加 理论上应该与 第一个方式相加一样
        return reps(1);
    }
    @Benchmark
    @OperationsPerInvocation(10) //告诉JMH一次执行相当于执行多少次,就是说JMH执行n次,只不过在报表的时候只会报出n/n个
    public int measureWrong_10() {  //循环相加
        return reps(10);
    }
    @Benchmark
    @OperationsPerInvocation(100)
    public int measureWrong_100() { //循环相加
        return reps(100);
    }
    @Benchmark
    @OperationsPerInvocation(1_000)
    public int measureWrong_1000() { //循环相加
        return reps(1_000);
    }
    @Benchmark
    @OperationsPerInvocation(10_000)
    public int measureWrong_10000() { //循环相加
        return reps(10_000);
    }
    @Benchmark
    @OperationsPerInvocation(100_000)
    public int measureWrong_100000() { //循环相加
        return reps(100_000);
    }
    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JMHSample_11_Loops.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opt).run();
    }
}

13.@fork (线程)

线程 fork(0)

package org.openjdk.jmh.samples;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.lang.management.ManagementFactory;
import java.util.concurrent.TimeUnit;
/**
 *  我们发现进程pid是一样的,初步推测fork(0),代表没有进程新建
 */
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)  //预热次数和时间
@Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) //测试次数和时间
public class JMHSample_12_Forking {
    @Benchmark
    @Fork(0)  //通过源码我们可以直到,0代表 no fork
    public int measure_1_c1() {
        return 1;
    }
    @Setup(Level.Trial)  // 在基准执行之前先打印pid。
    public void setup() {
        printProcessID("setup");
    }
    public static void printProcessID(String name) {  //打印pid
        System.out.println();
        System.out.println("--------------");
        System.out.println(name + " pid is : " + ManagementFactory.getRuntimeMXBean().getName());
        System.out.println("--------------");
        System.out.println();
    }
    public static void main(String[] args) throws RunnerException {
        printProcessID("main");  // 执行main方法的时候打印pid
        Options opt = new OptionsBuilder()
                .include(JMHSample_12_Forking.class.getSimpleName())
                .build();
        new Runner(opt).run();  //当他启动的时候,才会开始运行基准
    }
}

线程fork(1)

package org.openjdk.jmh.samples;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.lang.management.ManagementFactory;
import java.util.concurrent.TimeUnit;
/**
 *  我们发现进程pid是一样的,初步推测fork(1),代表新开一个进程
 */
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)  //预热次数和时间
@Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) //测试次数和时间
public class JMHSample_12_Forking {
    @Benchmark
    @Fork(1)  //通过源码我们可以直到,1代表 1 fork
    public int measure_1_c1() {
        return 1;
    }
    @Setup(Level.Trial)  // 在基准执行之前先打印pid。
    public void setup() {
        printProcessID("setup");
    }
    public static void printProcessID(String name) {  //打印pid
        System.out.println();
        System.out.println("--------------");
        System.out.println(name + " pid is : " + ManagementFactory.getRuntimeMXBean().getName());
        System.out.println("--------------");
        System.out.println();
    }
    public static void main(String[] args) throws RunnerException {
        printProcessID("main");  // 执行main方法的时候打印pid
        Options opt = new OptionsBuilder()
                .include(JMHSample_12_Forking.class.getSimpleName())
                .build();
        new Runner(opt).run();  //当他启动的时候,才会开始运行基准
    }
}


相关文章
|
7月前
|
JSON 数据可视化 Java
103.【Java Microbenchmark Harness】(六)
103.【Java Microbenchmark Harness】
38 0
103.【Java Microbenchmark Harness】(六)
|
7月前
|
Java
103.【Java Microbenchmark Harness】(五)
103.【Java Microbenchmark Harness】
32 0
|
7月前
|
Java
103.【Java Microbenchmark Harness】(三)
103.【Java Microbenchmark Harness】
33 0
103.【Java Microbenchmark Harness】(三)
|
7月前
|
Java
103.【Java Microbenchmark Harness】(二)
103.【Java Microbenchmark Harness】
42 0
103.【Java Microbenchmark Harness】(二)
|
7月前
|
Java 测试技术
103.【Java Microbenchmark Harness】(一)
103.【Java Microbenchmark Harness】
49 0
|
Java 测试技术
在java中使用JMH(Java Microbenchmark Harness)做性能测试
在java中使用JMH(Java Microbenchmark Harness)做性能测试
|
4天前
|
安全 Java 调度
Java线程:深入理解与实战应用
Java线程:深入理解与实战应用
23 0
|
1天前
|
缓存 Java
Java并发编程:深入理解线程池
【4月更文挑战第26天】在Java中,线程池是一种重要的并发工具,它可以有效地管理和控制线程的执行。本文将深入探讨线程池的工作原理,以及如何使用Java的Executor框架来创建和管理线程池。我们将看到线程池如何提高性能,减少资源消耗,并提供更好的线程管理。
|
1天前
|
消息中间件 缓存 NoSQL
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
|
1天前
|
数据采集 存储 Java
高德地图爬虫实践:Java多线程并发处理策略
高德地图爬虫实践:Java多线程并发处理策略