java 编写代码

当我第一次写此博客时,我的目的是向您介绍ThreadLocalRandom类,它是Java 7中新增的用于生成随机数的类。 我在一系列微基准测试中分析了ThreadLocalRandom的性能,以了解其在单线程环境中的性能。

结果相对令人惊讶:尽管代码非常相似,但ThreadLocalRandom速度是Math.random()两倍! 结果引起了我的兴趣,我决定对此进行进一步的研究。 我已经记录了我的分析过程。 它是对分析步骤,技术和一些JVM诊断工具的示例性介绍,以了解小型代码段的性能差异。 使用上述工具集和技术的一些经验将使您能够为特定的Hotspot目标环境编写更快的Java代码。

好的,这已经足够了,让我们开始吧! 我的机器是运行Windows XP的普通Intel 386 32位双核。

Math.random()处理Random的静态单例实例,而ThreadLocalRandom -> current() -> nextDouble()处理ThreadLocalRandom的线程本地实例,该实例是Random的子类。 ThreadLocal在对current()方法的每次调用中引入了变量查找的开销。 考虑到我刚才说的话,那么在单个线程中它的速度是Math.random()的两倍,这真的有点令人惊讶吗? 我没想到会有如此大的差异。

同样,我使用的是Heinz博客之一中介绍的微型基准测试框架。 Heinz开发的框架解决了在现代JVM上对Java程序进行基准测试时遇到的一些挑战。 这些挑战包括:热身,垃圾回收,Java时间API的准确性,测试准确性的验证等等。

这是我可运行的基准测试类:

public class ThreadLocalRandomGenerator implements BenchmarkRunnable {private double r;@Overridepublic void run() {r = r + ThreadLocalRandom.current().nextDouble();}public double getR() {return r;}@Overridepublic Object getResult() {return r;}}public class MathRandomGenerator implements BenchmarkRunnable {private double r;@Overridepublic void run() {r = r + Math.random();}public double getR() {return r;}@Overridepublic Object getResult() {return r;}
}

让我们使用Heinz的框架运行基准测试:

public class FirstBenchmark {private static List<BenchmarkRunnable> benchmarkTargets = Arrays.asList(new MathRandomGenerator(),new ThreadLocalRandomGenerator());public static void main(String[] args) {DecimalFormat df = new DecimalFormat("#.##");for (BenchmarkRunnable runnable : benchmarkTargets) {Average average = new PerformanceHarness().calculatePerf(new PerformanceChecker(1000, runnable), 5);System.out.println("Benchmark target: " + runnable.getClass().getSimpleName());System.out.println("Mean execution count: " + df.format(average.mean()));System.out.println("Standard deviation: " + df.format(average.stddev()));System.out.println("To avoid dead code coptimization: " + runnable.getResult());}}
}

注意:为了确保JVM不会将代码标识为“死代码”,我返回了一个字段变量,并立即打印出基准测试的结果。 这就是为什么我的可运行类实现名为RunnableBenchmark的接口。 我已经运行了三次基准测试。 第一次运行是在默认模式下,启用了内联和JIT优化:

Benchmark target: MathRandomGenerator
Mean execution count: 14773594,4
Standard deviation: 180484,9
To avoid dead code coptimization: 6.4005410634212025E7
Benchmark target: ThreadLocalRandomGenerator
Mean execution count: 29861911,6
Standard deviation: 723934,46
To avoid dead code coptimization: 1.0155096190946539E8

然后再次不进行JIT优化(VM选项-Xint ):

Benchmark target: MathRandomGenerator
Mean execution count: 963226,2
Standard deviation: 5009,28
To avoid dead code coptimization: 3296912.509302683
Benchmark target: ThreadLocalRandomGenerator
Mean execution count: 1093147,4
Standard deviation: 491,15
To avoid dead code coptimization: 3811259.7334526842

最后一个测试是使用JIT优化,但是使用-XX:MaxInlineSize=0 ,它(几乎)禁用了内联:

Benchmark target: MathRandomGenerator
Mean execution count: 13789245
Standard deviation: 200390,59
To avoid dead code coptimization: 4.802723374491231E7
Benchmark target: ThreadLocalRandomGenerator
Mean execution count: 24009159,8
Standard deviation: 149222,7
To avoid dead code coptimization: 8.378231170741305E7

让我们仔细地解释结果:借助完整的JVM JIT优化, ThreadLocalRanom速度是Math.random()两倍。 关闭JIT优化表明,两者的性能相同(差)。 方法内联似乎使性能相差30%。 其他差异可能归因于其他优化技术 。

JIT编译器可以更有效地调整ThreadLocalRandom原因之一是ThreadLocalRandom.next()的改进实现。

public class Random implements java.io.Serializable {
...protected int next(int bits) {long oldseed, nextseed;AtomicLong seed = this.seed;do {oldseed = seed.get();nextseed = (oldseed * multiplier + addend) & mask;} while (!seed.compareAndSet(oldseed, nextseed));return (int)(nextseed >>> (48 - bits));}
...
}public class ThreadLocalRandom extends Random {
...protected int next(int bits) {rnd = (rnd * multiplier + addend) & mask;return (int) (rnd >>> (48-bits));}
...
}

第一个片段显示Random.next() ,它在Math.random()的基准测试中大量使用。 与ThreadLocalRandom.next()相比,该方法需要更多的指令,尽管这两种方法都做同样的事情。 在Random类中, seed变量将全局共享状态存储到所有线程,并且每次调用next()方法时都会更改。 因此,需要AtomicLong安全地访问和更改对nextDouble()调用中的seed值。 另一方面, ThreadLocalRandom是–很好的–线程局部:-) next()方法不必是线程安全的,可以使用普通的long变量作为种子值。

关于方法内联和ThreadLocalRandom

方法内联是一种非常有效的JIT优化。 在频繁执行的热路径中,热点编译器决定将被调用方法(子方法)的代码内联到调用方方法(父方法)中。 内联具有重要的好处。 它大大降低了方法调用的动态频率,从而节省了执行这些方法调用所需的时间。 但更重要的是,内联会产生更大的代码块,以供优化程序使用。 这就造成了一种情况,大大提高了传统编译器优化的效率,克服了提高Java编程语言性能的主要障碍。”

从Java 7开始,您可以使用诊断JVM选项监视方法内联。 使用' -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining '运行代码将显示JIT编译器的内联工作。 以下是Math.random()基准测试输出的相关部分:

@ 13   java.util.Random::nextDouble (24 bytes)@ 3   java.util.Random::next (47 bytes)   callee is too large@ 13   java.util.Random::next (47 bytes)   callee is too large

JIT编译器无法内联Random.next()中调用的Random.nextDouble() 。 这是ThreaLocalRandom.next()的内联输出:

@ 8   java.util.Random::nextDouble (24 bytes)@ 3   java.util.concurrent.ThreadLocalRandom::next (31 bytes)@ 13   java.util.concurrent.ThreadLocalRandom::next (31 bytes)

由于next()方法较短(31个字节),因此可以内联。 因为在两个基准测试中都强烈调用next()方法,所以该日志表明方法内联可能是ThreadLocalRandom显着提高执行速度的原因之一。

为了验证并发现更多信息,需要深入研究汇编代码。 使用Java 7 JDK,可以将汇编代码打印到控制台中。 有关如何启用-XX:+PrintAssembly VM选项的信息,请参见此处 。 该选项将打印出JIT优化的代码,这意味着您可以看到JVM实际执行的代码。 我已经将相关的汇编代码复制到下面的链接中。

此处的ThreadLocalRandomGenerator.run()的汇编代码。
MathRandomGenerator.run()的汇编代码在此处 。
Math.random() 在此处调用的Random.next()的汇编代码。

汇编代码是特定于机器的低级代码, 比起bytecode读起来要复杂得多。 让我们尝试在我的基准测试中验证方法内联对性能的影响,以及:JIT编译器如何对待ThreadLocalRandomMath.random ()还有其他明显的区别吗? 在ThreadLocalRandomGenerator.run() ,没有对任何子例程(如Random.nextDouble()ThreatLocalRandom.next()过程调用。 对ThreadLocal.get()仅有一个虚拟的(因此很昂贵)方法调用(请参阅ThreadLocalRandomGenerator.run()程序集的第35行)。 其他所有代码都内联到ThreadLocalRandomGenerator.run() 。 在的情况下MathRandomGenerator.run()两个虚拟方法调用到Random.next()见块B4线204页及以后中的汇编代码MathRandomGenerator.run() 这一事实证实了我们的怀疑,即方法内联是导致性能差异的一个重要根本原因。 此外,由于同步的麻烦, Random.next()需要的汇编指令要多得多(并且有些昂贵!),这在执行速度方面也适得其反。

了解invokevirtual指令的开销

那么,为什么(虚拟)方法调用昂贵且方法内联如此有效? invokevirtual指令的指针不是类实例中具体方法的偏移量。 编译器不知道类实例的内部布局。 相反,它生成对实例方法的符号引用,这些符号引用存储在运行时常量池中。 这些运行时常量池项在运行时解析以确定实际的方法位置。 这种动态(运行时)绑定需要验证,准备和解决,​​这可能会大大影响性能。 (有关详细信息,请参见JVM规范中的调用方法和链接 )。

目前为止就这样了。 免责声明:当然,解决性能难题需要了解的主题列表无穷无尽。 除了微基准测试,JIT优化,方法内联,java字节代码,assemby语言等等之外,还有很多东西要理解。 同样,除了虚拟方法调用或昂贵的线程同步指令之外,还有更多导致性能差异的根本原因。 但是,我认为我介绍的主题是此类深入研究的一个很好的开始。 期待批评和愉快的评论!

参考资料:来自JCG合作伙伴 Niklas的“ Java 7:如何编写非常快速的Java代码”。

翻译自: https://www.javacodegeeks.com/2012/01/java-7-how-to-write-really-fast-java.html

java 编写代码

java 编写代码_Java 7:如何编写非常快速的Java代码相关推荐

  1. java输入输出语句_Java中的常用输入输出语句的操作代码

    一.概述 输入输出可以说是计算机的基本功能.作为一种语言体系,java中主要按照流(stream)的模式来实现.其中数据的流向是按照计算机的方向确定的,流入计算机的数据流叫做输入流(inputStre ...

  2. java web购物车代码_java web开发之购物车功能实现示例代码

    之前没有接触过购物车的东东,也不知道购物车应该怎么做,所以在查询了很多资料,总结一下购物车的功能实现. 查询的资料,找到三种方法: 1.用cookie实现购物车: 2.用session实现购物车: 3 ...

  3. java 构造块_java中构造方法、普通块、静态代码块、构造块的执行顺序

    静态块.构造块.构造方法.普通块 静态块:在类中用"{}"括号括起来,并在括号前用static修饰的块为静态块;静态块在类加载时候被调用,并且在整个生命中只调用一次 同步块 使用s ...

  4. java命令大全_Java命令行工具:javac、java、javap 的使用详解

    1.javac javac用来编译.java文件的.常用格式如下所示:javac -d destdir srcfile -d destdir:指定存放编译生成的 .class 文件的路径.如果想省事一 ...

  5. 【超硬核】Java SE 基础语法知识点 (C语言快速转Java)

    目录 1.Java程序基础 1.1 JDK和JRD 1.2 命令行操作 1.3 pubilic+class 1.4 字节码文件 2.标识符 2.1 标识符的组成 2.2 遵守驼峰命名方式 2.3 类名 ...

  6. java多线程死锁代码_java多线程死锁 编写高质量代码:改善Java程序的151个建议...

    java多线程死锁 编写高质量代码:改善Java程序的151个建议 第1章 Java开发中的通用方法和准则 建议1:不要在常量和变量中出现易混淆的字母 建议2:莫让常量蜕变成变量 建议3:三元操作符的 ...

  7. java实现数组排序代码_Java使用选择排序法对数组排序实现代码

    编写程序,实现将输入的字符串转换为一维数组,并使用选择排序法对数组进行排序. 思路如下: 点击"生成随机数"按钮,创建Random随机数对象: 使用JTextArea的setTex ...

  8. java连接摄像头_Java实现 海康摄像头抓拍图像(示例代码)

    先抱怨一下,打死都想不到,海康的摄像头SDK居然是一个Java类,还有必须的两个jar包(jna.jar,examples.jar).鬼能想得到会这么命名. 下面开始吧. 把从官网下载的SDK(和当前 ...

  9. java设计功能怎么实现代码_Java中的门面设计模式及如何用代码实现

    门面设计模式又叫外观设计模式,其核心思想正如其字面意思,向用户提供一个门户,用户只需要访问这个门户来获取他们想要的数据,无需管理这个门户内部的构成,也无需知道里面的运行流程等等,对于开发者来说,使用门 ...

  10. java登录注册抽奖完整代码_JAVA实现用户抽奖功能(附完整代码)

    需求分析 1)实现三个基本功能:登录.注册.抽奖. 2)登录:用户输入账号密码进行登录,输入账号后会匹配已注册的用户,若输入用户不存在则退出,密码有三次输入机会,登录成功后主界面会显示已登录用户的账号 ...

最新文章

  1. 2022-2028年中国环烷基润滑油行业市场研究及前瞻分析报告
  2. Java设计模式---外观模式
  3. 作业 3 利用分支和循环结构解决问题
  4. PIC单片机入门_汇编/混编/C编比较
  5. 微信小程序1. Forgot to add page route in app.json. 2. Invoking Page() in async task.
  6. python中cd是什么意思_python的cd的
  7. 自然语言处理以及对话系统知识点总结
  8. Junit运行报initializationError错误
  9. mysql 存储过程执行ddl_mysql存储过程执行ddl语句
  10. 数字万用表校准软件|数字万用表自动计量系统NSAT-3030
  11. Springboot JPA日志输出打印SQL语句和传入的参数 高阶篇,java微服务架构视频下载
  12. 公开处刑,专家博主开发游戏的老王专栏抄袭事件始末
  13. 特拉华大学计算机科学,特拉华大学计算机科学理学硕士研究生申请要求及申请材料要求清单...
  14. html5 手绘效果,浅谈基于Canvas的手绘风格图形库Rough.js
  15. 【系统函数】2. 系统的因果性、稳定性
  16. 精心为学弟学妹整理的 C语言/C++ 项目合集
  17. 【数据结构期末例题】
  18. 用cookie登录KinhDown教程
  19. 光一个html文件能加图片吗,网站页面设计中光的特效设计
  20. iRobot 推出 Create 3,内置 ROS 2 机器人开发平台(转载)

热门文章

  1. 班级日常分享,一天一瞬间
  2. python注释的用法(单and多行)
  3. logback-spring.xml配置文件
  4. java的jdbc驱动server_win7下java用jdbc驱动来连接sql server的方法 (转载)
  5. 键盘录入一个正整数,把它的各个位上的数字倒着排列形成一个新的整数并输出。 例如:12345 数出54321 78760 输出6787(0省去)
  6. js 时间戳转换成时间_JavaScript 时间戳转成日期格式
  7. kafka 学习 非常详细的经典教程
  8. zxing qr区域判断_如何在Java中使用Zxing和JFreeSVG创建QR Code SVG?
  9. 在Selenium中按TagName定位元素
  10. getopt java_Java命令行界面(第28部分):getopt4j