这是由 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 中的
一个主要的优化点就是循环,过去的循环(标量循环),一次在一个元素上执行,那很慢。现在,您可以使用 Vector API 将标量算法转换为速度更快的数据并行算法。一个使用 Vector 的例子:
//测试指标为吞吐量
@BenchmarkMode(Mode.Throughput)
//需要预热,排除 jit 即时编译以及 JVM 采集各种指标带来的影响,由于我们单次循环很多次,所以预热一次就行
@Warmup(iterations = 1)
//单线程即可
@Fork(1)
//测试次数,我们测试10次
@Measurement(iterations = 10)
//定义了一个类实例的生命周期,所有测试线程共享一个实例
@State(value = https://www.it610.com/article/Scope.Benchmark)
public class VectorTest {
private static final VectorSpecies 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 即:文章图片
文章图片
测试结果:
BenchmarkModeCntScoreErrorUnits
VectorTest.testScalarthrpt107380697.998 ± 1018277.914ops/s
VectorTest.testVectorthrpt1037151609.182 ± 1011336.900ops/s
其他使用,请参考:fizzbuzz-simd-style,这是一篇比较有意思的文章(虽然这个性能优化感觉不只由于 SIMD,还有算法优化的功劳,哈哈)
关于一些更加详细的使用,以及设计思路,可以参考这个音频:https://www.youtube.com/watch...
Records Serialization 关于 Java Record 的序列化,我也写过一篇文章进行分析,参考:[Java Record 的一些思考 - 序列化相关]()
其中,最重要的是一些主流的序列化框架的兼容
由于 Record 限制了序列化与反序列化的唯一方式,所以其实兼容起来很简单,比起 Java Class 改个结构,加个特性导致的序列化框架更改来说还要简单。
- Jackson:
- Issue: Support for record types in JDK 14
- Pull Request: Support for record types in JDK 14
- 对应版本:jackson-databind-2.12.0
- Kryo
- Issue: Java 14 records : how to deal with them?
- Pull Request: Add support for Records in JDK 14
- 对应版本:kryo-5.1.0
- XStream
- Issue: Support for record types in JDK 14
- Pull Request: Add support for Record types in JDK 14
- 对应版本:1.5.x,还未发布
- 实现一个针对 Record 的专用的 Serializer 以及Deserializer。
- 通过反射(Java Reflection)或者句柄(Java MethodHandle)验证当前版本的 Java 是否支持 Record,以及获取 Record 的规范构造函数(canonical constructor)以及各种 field 的 getter 进行反序列化和序列化。
- 硬核 - Java 随机数相关 API 的演进与思考(上)
- 硬核 - Java 随机数相关 API 的演进与思考(下)
根据之前的分析,应该还是 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 = https://www.it610.com/article/Scope.Benchmark)
public class TestRandomGenerator {
@Param({"Random", "SecureRandom", "SplittableRandom", "Xoroshiro128PlusPlus", "Xoshiro256PlusPlus", "L64X256MixRandom",
"L64X128StarStarRandom", "L64X128MixRandom", "L64X1024MixRandom", "L32X64MixRandom", "L128X256MixRandom",
"L128X128MixRandom", "L128X1024MixRandom"
})
private String name;
ThreadLocal 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)ModeCntScoreErrorUnits
TestRandomGenerator.testRandomIntRandomthrpt50276250026.985 ± 240164319.588ops/s
TestRandomGenerator.testRandomIntSecureRandomthrpt502362066.269 ±1277699.965ops/s
TestRandomGenerator.testRandomIntSplittableRandomthrpt50365417656.247 ± 377568150.497ops/s
TestRandomGenerator.testRandomIntXoroshiro128PlusPlusthrpt50341640250.941 ± 287261684.079ops/s
TestRandomGenerator.testRandomIntXoshiro256PlusPlusthrpt50343279172.542 ± 247888916.092ops/s
TestRandomGenerator.testRandomIntL64X256MixRandomthrpt50317749688.838 ± 245196331.079ops/s
TestRandomGenerator.testRandomIntL64X128StarStarRandomthrpt50294727346.284 ± 283056025.396ops/s
TestRandomGenerator.testRandomIntL64X128MixRandomthrpt50314790625.909 ± 257860657.824ops/s
TestRandomGenerator.testRandomIntL64X1024MixRandomthrpt50315040504.948 ± 101354716.147ops/s
TestRandomGenerator.testRandomIntL32X64MixRandomthrpt50311507435.009 ± 315893651.601ops/s
TestRandomGenerator.testRandomIntL128X256MixRandomthrpt50187922591.311 ± 137220695.866ops/s
TestRandomGenerator.testRandomIntL128X128MixRandomthrpt50218433110.870 ± 164229361.010ops/s
TestRandomGenerator.testRandomIntL128X1024MixRandomthrpt50220855813.894 ±47531327.692ops/s
TestRandomGenerator.testRandomIntWithBoundRandomthrpt50248088572.243 ± 206899706.862ops/s
TestRandomGenerator.testRandomIntWithBoundSecureRandomthrpt501926592.946 ±2060477.065ops/s
TestRandomGenerator.testRandomIntWithBoundSplittableRandomthrpt50334863388.450 ±92778213.010ops/s
TestRandomGenerator.testRandomIntWithBoundXoroshiro128PlusPlusthrpt50252787781.866 ± 200544008.824ops/s
TestRandomGenerator.testRandomIntWithBoundXoshiro256PlusPlusthrpt50247673155.126 ± 164068511.968ops/s
TestRandomGenerator.testRandomIntWithBoundL64X256MixRandomthrpt50273735605.410 ±87195037.181ops/s
TestRandomGenerator.testRandomIntWithBoundL64X128StarStarRandomthrpt50291151383.164 ± 192343348.429ops/s
TestRandomGenerator.testRandomIntWithBoundL64X128MixRandomthrpt50217051928.549 ± 177462405.951ops/s
TestRandomGenerator.testRandomIntWithBoundL64X1024MixRandomthrpt50222495366.798 ± 180718625.063ops/s
TestRandomGenerator.testRandomIntWithBoundL32X64MixRandomthrpt50305716905.710 ±51030948.739ops/s
TestRandomGenerator.testRandomIntWithBoundL128X256MixRandomthrpt50174719656.589 ± 148285151.049ops/s
TestRandomGenerator.testRandomIntWithBoundL128X128MixRandomthrpt50176431895.622 ± 143002504.266ops/s
TestRandomGenerator.testRandomIntWithBoundL128X1024MixRandomthrpt50198282642.786 ±24204852.619ops/s
在之前的结果验证中,我们已经知道了 SplittableRandom 的在单线程中的性能最好,多线程环境下表现最好的是算法与它类似但是做了多线程优化的 ThreadLocalRandom.
如何选择随机算法
【Java 内幕新闻第二期深度解读】原则是,看你的业务场景,所有的随机组合到底有多少个,在什么范围内。然后找大于这个范围的 Period 中,性能最好的算法。例如,业务场景是一副扑克除了大小王 52 张牌,通过随机数决定发牌顺序:
- 第一张牌:
randomGenerator.nextInt(0, 52)
,从剩余的 52 张牌选 - 第二张牌:
randomGenerator.nextInt(0, 51)
,从剩余的 51 张牌选 - 以此类推
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:
文章图片
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)