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();  //当他启动的时候,才会开始运行基准
    }
}


相关文章
|
JSON 数据可视化 Java
103.【Java Microbenchmark Harness】(六)
103.【Java Microbenchmark Harness】
61 0
103.【Java Microbenchmark Harness】(六)
|
Java
103.【Java Microbenchmark Harness】(三)
103.【Java Microbenchmark Harness】
50 0
103.【Java Microbenchmark Harness】(三)
|
Java
103.【Java Microbenchmark Harness】(二)
103.【Java Microbenchmark Harness】
71 0
103.【Java Microbenchmark Harness】(二)
|
Java
103.【Java Microbenchmark Harness】(五)
103.【Java Microbenchmark Harness】
127 0
|
Java 测试技术
103.【Java Microbenchmark Harness】(一)
103.【Java Microbenchmark Harness】
71 0
|
Java 测试技术
在java中使用JMH(Java Microbenchmark Harness)做性能测试
在java中使用JMH(Java Microbenchmark Harness)做性能测试
|
20天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
11天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
6天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
11天前
|
安全 Java 开发者
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
41 1