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】
57 0
103.【Java Microbenchmark Harness】(六)
|
Java
103.【Java Microbenchmark Harness】(三)
103.【Java Microbenchmark Harness】
48 0
103.【Java Microbenchmark Harness】(三)
|
Java
103.【Java Microbenchmark Harness】(二)
103.【Java Microbenchmark Harness】
68 0
103.【Java Microbenchmark Harness】(二)
|
Java
103.【Java Microbenchmark Harness】(五)
103.【Java Microbenchmark Harness】
121 0
|
Java 测试技术
103.【Java Microbenchmark Harness】(一)
103.【Java Microbenchmark Harness】
69 0
|
Java 测试技术
在java中使用JMH(Java Microbenchmark Harness)做性能测试
在java中使用JMH(Java Microbenchmark Harness)做性能测试
|
12天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
91 38
|
9天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
4天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
5天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
23 4