资讯专栏INFORMATION COLUMN

用 JMH 检测 Lambdas 序列化性能

whatsns / 658人阅读

摘要:本文将介绍如何进行序列化性能检测的重要性以及在分布式系统中的应用。另一个鲜有人知的特点就是可被序列化。所以当的使用造成性能问题时,就要使用备选方案。下面就是当在中序列化时,非采集的的情况。

本文将介绍如何进行 Java Lambdas 序列化性能检测、Lambdas 的重要性以及 Lambdas 在分布式系统中的应用。

Lambdas 表达式是 Java 8 中万众期待的新特性,其若干用途包括:

为匿名内部类减少所需样本代码。

缩小值的作用域。Lambdas 表达式中的 this 不会涉及到外部类,减少了内存泄露。

轻松集成现有 API 与新的 Streams API。

Lambdas 另一个鲜有人知的特点就是可被序列化。

为什么对 Lambda 序列化?

序列化有益于对象的状态持久化和网络传输。 Lambdas 应该尽可能无状态,这样就可以保存 Lambdas ,但这并不是典型的用例。

Lambdas 表达式的目的是给程序库传递代码片段,使之与库中正在运行的程序交互。但是如果程序库支持像 Chronicle Engine 这样的分布式系统,那又会怎么样?

什么是 Chronicle Engine?

Chronicle Engine 是一个库,可让用户使用各自的应用程序远程访问数据结构,无论是用 Java、C# 客户端,还是用 NFS 文件系统。该库还支持存储和持久化数据,也支持复制。

分布式系统中的 Lambdas

对于某些局部运行的操作,使用 Lambdas 执行不失为一种简单可行的方法。示例操作如下:

MapView map = acquireMap(“map-name”, 
                                      String.class, Long.class);
map.put(“key”, 1);
long ret = map.applyToKey(“key”, v -> v + 1); // ret == 2

这里没有必要知道数据的具体存储位置,如果是远程服务器,就会在那台服务器上对 Lambda 序列化,然后执行,并将结果返回。

上图显示了 OneAPM 如何监控和让 Java 应用之间的调用关系可视化。

Capturing Lambda

不获取字段的 Lambda 可以被 Java 更高效地处理,因为每个实例都一样,所以并不需要每次都创建新的对象。但是,若编译时 Lambda 获取到未知值,就需要创建新的对象,并将获取的值保存。

Non capturing Lambda

Function appendStar = s -> s + "*"

Capturing Lambda

String star = "*";
    Function appendStar = s -> s + star;
可序列化的 Lambdas

Lambdas 默认是不可序列化的,必须实现一种可序列化的接口。可以使用强制转换把接口类型转换为所需类型的一种方式。

Function appendStar = 
     (Function & Serializable) (s -> s + star);

笔者个人不喜欢这样做,因为这样会破坏减少代码的目标。一个解决的方法就是定义自己所需的可序列化的接口。

@FunctionalInterface
public interface SerializableFunction 
       extends Function, Serializable {

这就需要如下所写:

SerializableFunction appendStar = s -> s + star;

或者按照这种方法写入:

 R applyToKey(K key, @NotNull SerializableFunction function) {

该库的调用者就可以如下所写,而不需要任何样本代码。

String s = map.applyToKey(“key”, s-> s + “*”);
Lambdas 的实时查询

利用序列化的 Lambdas,可进行如下所示的实时查询:

// print the last name of all the people in NYC
    acquireMap(“people”, String.class, Person.class).query()
    .filter(p -> p.getCity().equals(“NYC”)) // executed on the server
    .map(p → p.getLastName())  // executed on the server
    .subscribe(System.out::println); // executed on the client.

可查询接口是必需的,因此过滤器 Predicate 和 map 函数也必须隐式可序列化。如果需要使用 Streams API,那就要使用早期较为复杂的数据类型转换函数 cast。

序列化 Lambdas 的性能

笔者曾经在一个字符串中写入符号“*”,并使用 JMH 对简单的序列化的和反序列化的 Lambda 进行时延采样,然后比较采集和非采集两种情况下的时延,发送枚举时两种情况下的时延也一并比较。代码和结果如下表所示:

99.99%的时延意味着该试验的99.99%都是在时延之中。时间都用微秒计算。

Test Typical latency 99.99% latency
Java Serialization, non-capturing 33.9 µs 215 µs
Java Serialization, capturing 36.3 µs 704 µs
Java Serialization, with an enum 7.9 µs 134 µs
Chronicle Wire (Text), non-capturing 20.4 µs 147 µs
Chronicle Wire (Text), capturing 22.5 µs 148 µs
Chronicle Wire (Text), with an enum 1.2 µs 5.9 µs
Chronicle Wire (Binary), non-capturing 11.7 µs 103 µs
Chronicle Wire (Binary), capturing 12.7 µs 135 µs
Chronicle Wire (Binary), with an enum 1.0 µs 1.2 µs
为什么要使用枚举?

使用 Lambda 是很简单,但它效率不高时,就需要找一个备选方案。所以当 Lambda 的使用造成性能问题时,就要使用备选方案。

enum Functions implements SerializableFunction {
    APPEND_STAR {
        @Override
        public String apply(String s) {
            return s + "*";
        }
    }
}

为考察使用枚举所起到的作用,可以比较发送到服务器的数据量的多少,在那里可以看到所有序列化的数据。
下面就是当在 TextWire 中序列化时,非采集的 Lambda 的情况。(基于 YAML)

!SerializedLambda {
  cc: !type lambda.LambdaSerialization,
  fic: net/openhft/chronicle/core/util/SerializableFunction,
  fimn: apply,
  fims: (Ljava/lang/Object;)Ljava/lang/Object;,
  imk: 6,
  ic: lambda/LambdaSerialization,
  imn: lambda$textWireSerialization$ea1ad110$1,
  ims: (Ljava/lang/String;)Ljava/lang/String;,
  imt: (Ljava/lang/String;)Ljava/lang/String;,
  ca: [
  ]
}

枚举序列化如下所示:

!Functions APPEND_STAR

注意:当需要采集某些值时,不可以使用枚举。我们要做的就是让你通过传递有附加参数的枚举,以获得最有效的组合。

使用枚举就像存储过程

用枚举代替 Lambdas 的一个好处就是,可以跟踪所有功能客户执行和整合的方式。某些函数使用广泛,运用枚举使得修复任一多带带函数中产生的bug更为容易,因此会被经常使用。举一个简单的例子,MapFunction 起初有很多不同的 Lambdas,而现在已经被归为一类。

结论

如果所使用的 API 支持,可将 Lambdas 用于分布式应用程序。如果需要附加的性能,也可以使用枚举。

原文地址:https://dzone.com/articles/measuring-the-serialization-performance-of-lambdas

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65313.html

相关文章

  • Java 8 的 JVM 有多快?Fork-Join 性能基准测试

    摘要:这减轻了手动重复执行相同基准测试的痛苦,并简化了获取结果的流程。处理项目的代码并从标有注释的方法处生成基准测试程序。用和运行该基准测试得到以下结果。同时,和的基线测试结果也有略微的不同。 Java 8 已经发布一段时间了,许多开发者已经开始使用 Java 8。本文也将讨论最新发布在 JDK 中的并发功能更新。事实上,JDK 中已经有多处java.util.concurrent 改动,但...

    Euphoria 评论0 收藏0
  • 使JMH对比BeanUtils和BeanCopier

    摘要:背景许多时候需要对比不同的框架或工具或算法,选择使用性能更优的那一个。通常的做法是但这样的做法非常不严谨,因为当独立频繁运行这一小块代码时,可能会针对性的做一些优化工作,而在实际的生产环境中是不会有此优化的。 背景 许多时候需要对比不同的框架或工具或算法, 选择使用性能更优的那一个。通常的做法是 long start = System.currentTimeMillis(); for(...

    yuxue 评论0 收藏0
  • 漫谈Exception与Result

    摘要:分析性能的影响但是需要注意时间单位,只是微秒而已,毫秒的千分之一秒的百万分之一。在这种情况下,优化毫秒的性能隐患无异于捡了芝麻丢了西瓜。 同步自:https://sulin.me/2019/T2ZXZB.... 在分布式系统开发中,我们经常需要将各种各样的状态码、错误信息传递给最外层的调用方,这个调用方通常是http/api接口,错误信息比如登录失效、参数错误等等。 最外层接口暴露的...

    张利勇 评论0 收藏0
  • Java8 新特性:Lambda表达式和虚拟扩展方法标注

    摘要:摘要添加了表达式闭包和特性支持,包括方法的引用,增强类型推断,和虚拟扩展方法。围绕的语言功能支持包括虚拟扩展方法,这将使接口的源代码和二进制兼容的方式演变升级。 Author:Joseph D. Darcy Organization:Oracle Owner:Brian Goetz Created:2011/11/1 Updated:2013/2/21 Type:Feature Sta...

    UsherChen 评论0 收藏0

发表评论

0条评论

whatsns

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<