Java 内幕新闻第二期深度解读
这是由 Java 官方发布,Oracle JDK 研发 Nipafx 制作的节目,包含 JDK 近期的研发进展和新特性展望和使用,这里加上个人译制的字幕搬运而来。我把 Nipafx 的扩展资料详细研读并提取精华做了个人详细解读:视频地址(熟肉)
⎯⎯⎯⎯⎯⎯ Chapters ⎯⎯⎯⎯⎯⎯
- 0:00 - Intro
- 0:33 - Vector API
- 0:56 - Vector API - SIMD and Vector Instructions
- 2:22 - Vector API - Current State
- 3:10 - Vector API - More
Inside Java podcast Ep. 7 - 3:59 - Records Serialization
- 5:22 - JDK 17 - Enhanced Pseudo-Random Number Generators
- 6:06 - Outro
这一节的内容不是很多,但是都比较有意思。
Vector API
相关 JEP:
- JEP 338: Vector API (Incubator)
- JEP 414: Vector API (Second Incubator):Java 17 中的
- JEP 417: Vector API (Third Incubator):Java 18 中的
其中最主要的应用就是使用了 CPU 的 SIMD(单指令多数据)处理,它提供了通过程序的多通道数据流,可能有 4 条通道或 8 条通道或任意数量的单个数据元素流经的通道。并且 CPU 一次在所有通道上并行组织操作,这可以极大增加 CPU 吞吐量。通过 Vector API,Java 团队正在努力让 Java 程序员使用 Java 代码直接访问它;过去,他们必须在汇编代码级别对向量数学进行编程,或者使用 C/C++ 与 Intrinsic 一起使用,然后通过 JNI 提供给 Java。
一个主要的优化点就是循环,过去的循环(标量循环),一次在一个元素上执行,那很慢。现在,您可以使用 Vector API 将标量算法转换为速度更快的数据并行算法。一个使用 Vector 的例子:
//测试指标为吞吐量
@BenchmarkMode(Mode.Throughput)
//需要预热,排除 jit 即时编译以及 JVM 采集各种指标带来的影响,由于我们单次循环很多次,所以预热一次就行
@Warmup(iterations = 1)
//单线程即可
@Fork(1)
//测试次数,我们测试10次
@Measurement(iterations = 10)
//定义了一个类实例的生命周期,所有测试线程共享一个实例
@State(value = Scope.Benchmark)
public class VectorTest {
private static final VectorSpecies<Float> SPECIES =
FloatVector.SPECIES_256;
final int size = 1000;
final float[] a = new float[size];
final float[] b = new float[size];
final float[] c = new float[size];
public VectorTest() {
for (int i = 0; i < size; i++) {
a[i] = ThreadLocalRandom.current().nextFloat(0.0001f, 100.0f);
b[i] = ThreadLocalRandom.current().nextFloat(0.0001f, 100.0f);
}
}
@Benchmark
public void testScalar(Blackhole blackhole) throws Exception {
for (int i = 0; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
@Benchmark
public void testVector(Blackhole blackhole) {
int i = 0;
//高于数组长度的 SPECIES 一次处理数据长度的倍数
int upperBound = SPECIES.loopBound(a.length);
//每次循环处理 SPECIES.length() 这么多的数据
for (; i < upperBound; i += SPECIES.length()) {
// FloatVector va, vb, vc;
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vc = va.mul(va)
.add(vb.mul(vb))
.neg();
vc.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(VectorTest.class.getSimpleName()).build();
new Runner(opt).run();
}
}
注意使用处于孵化的 Java 特性需要加上额外的启动参数将模块暴露,这里是--add-modules jdk.incubator.vector
,需要在 javac 编译和 java 运行都加上这些参数,使用 IDEA 即:
测试结果:
Benchmark Mode Cnt Score Error Units
VectorTest.testScalar thrpt 10 7380697.998 ± 1018277.914 ops/s
VectorTest.testVector thrpt 10 37151609.182 ± 1011336.900 ops/s
其他使用,请参考:fizzbuzz-simd-style,这是一篇比较有意思的文章(虽然这个性能优化感觉不只由于 SIMD,还有算法优化的功劳,哈哈)
关于一些更加详细的使用,以及设计思路,可以参考这个音频:https://www.youtube.com/watch?v=VYo3p4R66N8&t=427s
Records Serialization
关于 Java Record 的序列化,我也写过一篇文章进行分析,参考:Java Record 的一些思考 - 序列化相关
其中,最重要的是一些主流的序列化框架的兼容
由于 Record 限制了序列化与反序列化的唯一方式,所以其实兼容起来很简单,比起 Java Class 改个结构,加个特性导致的序列化框架更改来说还要简单。
这三个框架中实现对于 Record 的兼容思路都很类似,也比较简单,即:
- 实现一个针对 Record 的专用的 Serializer 以及Deserializer。
- 通过反射(Java Reflection)或者句柄(Java MethodHandle)验证当前版本的 Java 是否支持 Record,以及获取 Record 的规范构造函数(canonical constructor)以及各种 field 的 getter 进行反序列化和序列化。
JDK 17 - Enhanced Pseudo-Random Number Generators
Java 17 针对随机数生成器做了统一接口封装,并且内置了 Xoshiro 算法以及自己研发的 LXM 算法,可以参考我的这个系列文章:
这里截取一部分分析:
根据之前的分析,应该还是 SplittableRandom 在单线程环境下最快,多线程环境下使用 ThreadLocalRandom 最快。新增的随机算法实现类,Period 约大需要的计算越多, LXM 的实现需要更多计算,加入这些算法是为了适应更多的随机应用,而不是为了更快。不过为了满足大家的好奇心,还是写了如下的代码进行测试,从下面的代码也可以看出,新的 RandomGenerator API 使用更加简便:
package prng;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
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;
//测试指标为吞吐量
@BenchmarkMode(Mode.Throughput)
//需要预热,排除 jit 即时编译以及 JVM 采集各种指标带来的影响,由于我们单次循环很多次,所以预热一次就行
@Warmup(iterations = 1)
//线程个数
@Threads(10)
@Fork(1)
//测试次数,我们测试50次
@Measurement(iterations = 50)
//定义了一个类实例的生命周期,所有测试线程共享一个实例
@State(value = Scope.Benchmark)
public class TestRandomGenerator {
@Param({
"Random", "SecureRandom", "SplittableRandom", "Xoroshiro128PlusPlus", "Xoshiro256PlusPlus", "L64X256MixRandom",
"L64X128StarStarRandom", "L64X128MixRandom", "L64X1024MixRandom", "L32X64MixRandom", "L128X256MixRandom",
"L128X128MixRandom", "L128X1024MixRandom"
})
private String name;
ThreadLocal<RandomGenerator> randomGenerator;
@Setup
public void setup() {
final String finalName = this.name;
randomGenerator = ThreadLocal.withInitial(() -> RandomGeneratorFactory.of(finalName).create());
}
@Benchmark
public void testRandomInt(Blackhole blackhole) throws Exception {
blackhole.consume(randomGenerator.get().nextInt());
}
@Benchmark
public void testRandomIntWithBound(Blackhole blackhole) throws Exception {
//注意不取 2^n 这种数字,因为这种数字一般不会作为实际应用的范围,但是底层针对这种数字有优化
blackhole.consume(randomGenerator.get().nextInt(1, 100));
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(TestRandomGenerator.class.getSimpleName()).build();
new Runner(opt).run();
}
}
测试结果:
Benchmark (name) Mode Cnt Score Error Units
TestRandomGenerator.testRandomInt Random thrpt 50 276250026.985 ± 240164319.588 ops/s
TestRandomGenerator.testRandomInt SecureRandom thrpt 50 2362066.269 ± 1277699.965 ops/s
TestRandomGenerator.testRandomInt SplittableRandom thrpt 50 365417656.247 ± 377568150.497 ops/s
TestRandomGenerator.testRandomInt Xoroshiro128PlusPlus thrpt 50 341640250.941 ± 287261684.079 ops/s
TestRandomGenerator.testRandomInt Xoshiro256PlusPlus thrpt 50 343279172.542 ± 247888916.092 ops/s
TestRandomGenerator.testRandomInt L64X256MixRandom thrpt 50 317749688.838 ± 245196331.079 ops/s
TestRandomGenerator.testRandomInt L64X128StarStarRandom thrpt 50 294727346.284 ± 283056025.396 ops/s
TestRandomGenerator.testRandomInt L64X128MixRandom thrpt 50 314790625.909 ± 257860657.824 ops/s
TestRandomGenerator.testRandomInt L64X1024MixRandom thrpt 50 315040504.948 ± 101354716.147 ops/s
TestRandomGenerator.testRandomInt L32X64MixRandom thrpt 50 311507435.009 ± 315893651.601 ops/s
TestRandomGenerator.testRandomInt L128X256MixRandom thrpt 50 187922591.311 ± 137220695.866 ops/s
TestRandomGenerator.testRandomInt L128X128MixRandom thrpt 50 218433110.870 ± 164229361.010 ops/s
TestRandomGenerator.testRandomInt L128X1024MixRandom thrpt 50 220855813.894 ± 47531327.692 ops/s
TestRandomGenerator.testRandomIntWithBound Random thrpt 50 248088572.243 ± 206899706.862 ops/s
TestRandomGenerator.testRandomIntWithBound SecureRandom thrpt 50 1926592.946 ± 2060477.065 ops/s
TestRandomGenerator.testRandomIntWithBound SplittableRandom thrpt 50 334863388.450 ± 92778213.010 ops/s
TestRandomGenerator.testRandomIntWithBound Xoroshiro128PlusPlus thrpt 50 252787781.866 ± 200544008.824 ops/s
TestRandomGenerator.testRandomIntWithBound Xoshiro256PlusPlus thrpt 50 247673155.126 ± 164068511.968 ops/s
TestRandomGenerator.testRandomIntWithBound L64X256MixRandom thrpt 50 273735605.410 ± 87195037.181 ops/s
TestRandomGenerator.testRandomIntWithBound L64X128StarStarRandom thrpt 50 291151383.164 ± 192343348.429 ops/s
TestRandomGenerator.testRandomIntWithBound L64X128MixRandom thrpt 50 217051928.549 ± 177462405.951 ops/s
TestRandomGenerator.testRandomIntWithBound L64X1024MixRandom thrpt 50 222495366.798 ± 180718625.063 ops/s
TestRandomGenerator.testRandomIntWithBound L32X64MixRandom thrpt 50 305716905.710 ± 51030948.739 ops/s
TestRandomGenerator.testRandomIntWithBound L128X256MixRandom thrpt 50 174719656.589 ± 148285151.049 ops/s
TestRandomGenerator.testRandomIntWithBound L128X128MixRandom thrpt 50 176431895.622 ± 143002504.266 ops/s
TestRandomGenerator.testRandomIntWithBound L128X1024MixRandom thrpt 50 198282642.786 ± 24204852.619 ops/s
在之前的结果验证中,我们已经知道了 SplittableRandom 的在单线程中的性能最好,多线程环境下表现最好的是算法与它类似但是做了多线程优化的 ThreadLocalRandom.
如何选择随机算法
原则是,看你的业务场景,所有的随机组合到底有多少个,在什么范围内。然后找大于这个范围的 Period 中,性能最好的算法。例如,业务场景是一副扑克除了大小王 52 张牌,通过随机数决定发牌顺序:
- 第一张牌:
randomGenerator.nextInt(0, 52)
,从剩余的 52 张牌选 - 第二张牌:
randomGenerator.nextInt(0, 51)
,从剩余的 51 张牌选 - 以此类推
那么一共有 52! 这么多结果,范围在 2^225 ~ 2^226 之间。如果我们使用的随机数生成器的 Period 小于这个结果集,那么某些牌的顺序,我们可能永远生成不了。所以,我们需要选择一个 Period > 54! 的随机数生成器。
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:
Java 内幕新闻第二期深度解读的更多相关文章
- Java并发指南12:深度解读 java 线程池设计思想及源码实现
深度解读 java 线程池设计思想及源码实现 转自 https://javadoop.com/2017/09/05/java-thread-pool/hmsr=toutiao.io&utm_ ...
- 【转载】深度解读 java 线程池设计思想及源码实现
总览 开篇来一些废话.下图是 java 线程池几个相关类的继承结构: 先简单说说这个继承结构,Executor 位于最顶层,也是最简单的,就一个 execute(Runnable runnable) ...
- 小象学院Python数据分析第二期【升级版】
点击了解更多Python课程>>> 小象学院Python数据分析第二期[升级版] 主讲老师: 梁斌 资深算法工程师 查尔斯特大学(Charles Sturt University)计 ...
- Java并发之synchronized关键字深度解析(二)
前言 本文继续[Java并发之synchronized关键字深度解析(一)]一文而来,着重介绍synchronized几种锁的特性. 一.对象头结构及锁状态标识 synchronized关键字是如何实 ...
- 寻找大学目标及行动步骤——记ITAEM团队第二期宣讲会(2014.05.14)
·昨晚8:00-9:40.在 钟海楼03029 ,进行了ITAEM团队第二期宣讲会(第一期见第一期宣讲会总结).来參加的主要是大一学生.以信院为主.也有法学院.文学院的同学. 在宣讲会中,大家都比較积 ...
- [翻译]现代java开发指南 第二部分
现代java开发指南 第二部分 第二部分:部署.监控 & 管理,性能分析和基准测试 第一部分,第二部分 =================== 欢迎来到现代 Java 开发指南第二部分.在第一 ...
- AI 新技术革命将如何重塑就业和全球化格局?深度解读 UN 报告(上篇)
欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 张钦坤 腾讯研究院秘书长蔡雄山 腾讯研究院法律研究中心副主任祝林华 腾讯研究院法律研究中心助理研究员曹建峰 腾讯研究院法律研究中心高级研究员 ...
- 如何用区块链技术解决信任问题?Fabric 架构深度解读
阿里妹导读:区块链技术,随着比特币的兴起而为大家所知.但是具体到技术本身,大家相对熟悉的几个词可能是“数据不可篡改”.“公开链”.“分布式数据”.“共识机制”等. 这篇文章将抛砖引玉,通过深度解读Hy ...
- 重写equal()时为什么也得重写hashCode()之深度解读equal方法与hashCode方法渊源
今天这篇文章我们打算来深度解读一下equal方法以及其关联方法hashCode(),我们准备从以下几点入手分析: 1.equals()的所属以及内部原理(即Object中equals方法的实现原理) ...
随机推荐
- 日历的种类(Project)
<Project2016 企业项目管理实践>张会斌 董方好 编著 日历有三种:标准日历.24小时日历和夜班日历. 设置的位置在[项目]>[属性]>[更改工作时间]>[ ...
- java 多线程 集合的包装方法Collections.synchronizedXXXXX;线程安全的集合类:Java.util.concurrent.ConcurrentXXX;java.util.concurrent.CopyOnWriteXXXX
问题:ArrayList 等线程不安全 当多线程并发修改一个集合数据时,可能同一个下标位置被覆盖. 示例代码: 一个List,我们创建10个线程,每个线程往这个List中添加1000条数据,结果往往 ...
- 前端实现list排序
需求 针对list中某个字段,实现list的升序和降序 效果图 代码 我是用在angular1.X中项目的,根据list中的sort字段进行排序. # sort.html <style> ...
- SpringBoot整合MQTT (使用官方demo)
依赖 <dependency> <groupId>org.eclipse.paho</groupId> <artifactId>org.eclipse. ...
- C笔试题:将int型数组强制转换为char*,再求strlen,涉及大小端
1 #include<stdio.h> 2 #include<string.h> 3 int main() 4 { 5 int a[2000]; 6 char *p = (ch ...
- java源码——统计字符串中字符出现的次数
对于任意输入的一段字符串,读取并且计算其中所有字符出现的次数. 使用HashMap存储字符和其对应的出现的次数,输出时,对HashMap进行遍历. 难点在于对HashMap的遍历,第一次使用,也是学习 ...
- 【LeetCode】942. DI String Match 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- 【LeetCode】543. Diameter of Binary Tree 解题报告 (C++&Java&Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 日期 题目地址:https://leetcode ...
- 【LeetCode】926. Flip String to Monotone Increasing 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Prefix计算 动态规划 参考资料 日期 题目地址 ...
- World is Exploding(hdu5792)
World is Exploding Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Other ...