FunTester Groovy as 关键字性能测试

FunTester · 2023年04月19日 · 3214 次阅读

之前写了文章介绍 Groovy 关键字as,我个人是十分推荐使用,特别在基础类型数据转换场景下,as关键字能够随心所欲将数据转换成我们需要的类型。但在实际使用过程中,由于没看到具体实现,也难以对as关键字的性能有个了解。所以今天准备测试几种我经常使用的场景进行as关键字的性能测试。

这次我准备使用Java 微基准测试神器 JMH 初探来进行基准测试,自从用上了JMH,就再也没有自己写过基准测试用例了。十分推荐。

String 转 double

这个是非常常用的,还有一个类似的 String 转 int,由于极其相似就不再测试了。其实结论大差不差。

下面是我的测试用例,分成了两部分,一部分 Groovy 类,用于实现各种不同方法,一部分是 JMH 的内容。原因是我还没有找到 JMH 完美兼容 Groovy 的方法。

Groovy 类:


class JmhG {


    static double groovy(String str) {
        str as double
    }

    static double java(String str) {
        Double.valueOf(str);
    }

    static double java2(String str) {
        str.toBigDecimal()
    }

}

JMH 测试 Case:


import com.funtester.frame.SourceCode;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.format.ResultFormatType;
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;

@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS)
@Threads(2)
@Fork(1)
@State(value = Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class JmhT extends SourceCode {


    @Param(value = {"13323222320", "2323230.2323233232", "532873873828382738201"})
    private String str;

    @Benchmark
    public void groovy() {
        JmhG.groovy(str);
    }

    @Benchmark
    public void java() {
        JmhG.java(str);
    }

    @Benchmark
    public void javaForce() {
        JmhG.java2(str);
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(JmhT.class.getSimpleName())
                .result("result.json")
                .resultFormat(ResultFormatType.JSON)
                .forks(1)
                .threads(2)
                .warmupIterations(1)
                .warmupBatchSize(1)
                .measurementIterations(2)
                .measurementBatchSize(2)
                .build();
        new Runner(options).run();
    }

}

测试结果:


Benchmark                       (str)   Mode  Cnt      Score   Error   Units
JmhT.groovy               13323222320  thrpt    2   9314.268          ops/ms
JmhT.groovy        2323230.2323233232  thrpt    2   4219.366          ops/ms
JmhT.groovy     532873873828382738201  thrpt    2   4442.363          ops/ms
JmhT.java                 13323222320  thrpt    2  19418.404          ops/ms
JmhT.java          2323230.2323233232  thrpt    2   5679.708          ops/ms
JmhT.java       532873873828382738201  thrpt    2   6331.530          ops/ms
JmhT.javaForce            13323222320  thrpt    2  27996.119          ops/ms
JmhT.javaForce     2323230.2323233232  thrpt    2   3400.895          ops/ms
JmhT.javaForce  532873873828382738201  thrpt    2   1614.033          ops/ms

从结果看出来,Java 自带的方法Double.valueOf(str)性能最好,Groovy 关键字as次之,而org.codehaus.groovy.runtime.StringGroovyMethods#toBigDecimal这个方法的性能有点飘忽不定,在 int 类型字符串情况下,它性能最高。在较长小数点情况下,又稍逊一筹,到了 long 类型数据直接崩了。各位可以根据使用场景自行选择。

数字转 String

这个用得少,通常直接使用 Java 自带的加一个空字符串实现。不过趁着这次测试看一下各方面不同的性能表现。

Groovy 方法类:


class Jmh {


    static String groovy(double number) {
        number as String
    }

    static String java(double number) {
        number + ""
    }

    static String java2(double number) {
        number.toString()
    }

}

JMH 的测试 Case 就不发了,复用上面的,下面是测试结果:

Benchmark                       (num)   Mode  Cnt      Score   Error   Units
JmhT.groovy               13323222320  thrpt    2   5533.081          ops/ms
JmhT.groovy        2323230.2323233232  thrpt    2   2713.348          ops/ms
JmhT.groovy     532873873828382738201  thrpt    2   2845.852          ops/ms
JmhT.java                 13323222320  thrpt    2   7226.492          ops/ms
JmhT.java          2323230.2323233232  thrpt    2   2572.057          ops/ms
JmhT.java       532873873828382738201  thrpt    2   2652.787          ops/ms
JmhT.javaForce            13323222320  thrpt    2  19337.224          ops/ms
JmhT.javaForce     2323230.2323233232  thrpt    2   3979.556          ops/ms
JmhT.javaForce  532873873828382738201  thrpt    2   3940.559          ops/ms

可以看出 Groovy 方法性能明显弱于 Java 的视线,而两种 Java 实现性能相差无几。这里建议追求极限性能的使用 Java 的java.lang.Double#toString()或者java.lang.Double#toString(double)这俩其实是一个方法。

double 转 int

下面分享一下数字类型之间相互转换,以 double 转 int 为例。

Groovy 方法类:

class JmhG {


    static int groovy(double d) {
        d as int
    }

    static int java(double d) {
        (int) d
    }

}

JMH 测试方法部分内容:

参数化做了点修改,其他内容不变。

@Param(value = {"13.20", "2323230.2323233232"})
private double num;

下面是测试结果:

Benchmark                 (num)   Mode  Cnt       Score   Error   Units
JmhT.groovy               13.20  thrpt    2   16441.947          ops/ms
JmhT.groovy  2323230.2323233232  thrpt    2   13305.805          ops/ms
JmhT.java                 13.20  thrpt    2  846022.670          ops/ms
JmhT.java    2323230.2323233232  thrpt    2  861913.384          ops/ms

结论差不多,Groovy 关键字as的性能更差了。几十倍的差距已经无法直视,后面处理脚本层面会用到 groovy 关键字as,框架部分和业务部分都会避免使用as关键字了。

结论

结尾放一下 Groovy 关键字as的总结陈词。

Groovy 是一种基于 JVM 的动态语言,它可以与 Java 平滑地集成,支持 Java 的语法和库,并且具有更高的灵活性和易用性。在 Groovy 中,as 是一个关键字,它用于类型转换和类型推断。

在 Groovy 中,as 关键字用于将一个对象转换为指定类型的对象。例如,可以使用 as 关键字将一个字符串转换为整数,或将一个 Map 转换为 JSON 字符串。as 关键字还可以用于类型推断,即根据上下文推断出对象的类型。例如,在 Groovy 中,可以使用 def 关键字定义一个变量,然后根据赋值语句自动推断出变量的类型。

as 关键字的用途有以下几点:

  • 类型转换:as 关键字可以将一个对象转换为指定类型的对象,方便程序员进行类型转换操作。

  • 类型推断:as 关键字可以根据上下文推断出对象的类型,简化了变量定义和类型转换的代码。

  • 继承和多态:as 关键字可以用于继承和多态的场景,比如将一个子类对象转换为父类对象或将一个接口对象转换为实现类对象。

总之,as 关键字是 Groovy 语言的一个重要特性,它可以帮助程序员更方便地进行类型转换和类型推断操作,提高代码的可读性和可维护性,同时也支持继承和多态的特性,方便程序员进行面向对象编程。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册