当前位置: 首页 > 面试题库 >

静态空数组实例的性能优势

巫懿轩
2023-03-14
问题内容

将常量的空数组返回值提取为静态常量似乎是常见的做法。像这儿:

public class NoopParser implements Parser {
    private static final String[] EMPTY_ARRAY = new String[0];

    @Override public String[] supportedSchemas() {
        return EMPTY_ARRAY;
    }

    // ...
}

大概这样做是出于性能方面的考虑,因为new String[0]直接返回会在每次调用该方法时创建一个新的数组对象-真的吗?

我一直在想,这样做是否真的有可衡量的性能优势,或者只是过时的民间智慧。空数组是不可变的。VM是否无法将所有空String数组都合并为一个?VM不能new String[0]基本免费赚钱吗?

将这种做法与返回空字符串进行对比:通常,我们非常乐于编写return "";,而不是return EMPTY_STRING;


问题答案:

我对这两个习惯用法在实际,现实世界中的实际性能差异最感兴趣。我没有进行微基准测试的经验(它可能不是解决此类问题的正确工具),但我还是尝试了一下。

该基准模拟了一个较为典型的“现实”设置。仅查看返回的数组,然后将其丢弃。没有参考信息,不需要参考相等性。

一个接口,两个实现:

public interface Parser {
    String[] supportedSchemas();
    void parse(String s);
}



public class NoopParserStaticArray implements Parser {
    private static final String[] EMPTY_STRING_ARRAY = new String[0];

    @Override public String[] supportedSchemas() {
        return EMPTY_STRING_ARRAY;
    }

    @Override public void parse(String s) {
        s.codePoints().count();
    }
}



public class NoopParserNewArray implements Parser {
    @Override public String[] supportedSchemas() {
        return new String[0];
    }

    @Override public void parse(String s) {
        s.codePoints().count();
    }
}

以及JMH基准测试:

import org.openjdk.jmh.annotations.Benchmark;

public class EmptyArrayBenchmark {
    private static final Parser NOOP_PARSER_STATIC_ARRAY = new NoopParserStaticArray();
    private static final Parser NOOP_PARSER_NEW_ARRAY = new NoopParserNewArray();

    @Benchmark
    public void staticEmptyArray() {
        Parser parser = NOOP_PARSER_STATIC_ARRAY;
        for (String schema : parser.supportedSchemas()) {
            parser.parse(schema);
        }
    }

    @Benchmark
    public void newEmptyArray() {
        Parser parser = NOOP_PARSER_NEW_ARRAY;
        for (String schema : parser.supportedSchemas()) {
            parser.parse(schema);
        }
    }
}

我的机器上的结果Java 1.8.0_51(HotSpot VM):

Benchmark                              Mode  Cnt           Score          Error  Units
EmptyArrayBenchmark.staticEmptyArray  thrpt   60  3024653836.077 ± 37006870.221  ops/s
EmptyArrayBenchmark.newEmptyArray     thrpt   60  3018798922.045 ± 33953991.627  ops/s
EmptyArrayBenchmark.noop              thrpt   60  3046726348.436 ±  5802337.322  ops/s

在这种情况下,两种方法之间没有显着差异。实际上,它们与无操作情况是没有区别的:显然,JIT编译器认识到返回的数组始终为空,并完全优化了循环!

用管道parser.supportedSchemas()插入黑洞而不是在其上循环,使静态数组实例的方法具有约30%的优势。但是它们的大小绝对相同:

Benchmark                              Mode  Cnt           Score         Error  Units
EmptyArrayBenchmark.staticEmptyArray  thrpt   60   338971639.355 ±  738069.217  ops/s
EmptyArrayBenchmark.newEmptyArray     thrpt   60   266936194.767 ±  411298.714  ops/s
EmptyArrayBenchmark.noop              thrpt   60  3055609298.602 ± 5694730.452  ops/s

最终,答案可能是通常的“取决于情况”。我有一种预感,在许多实际情况下, 将数组创建分解出去的 性能 优势并不明显。

我认为可以这样说

  • 如果方法合同使您每次可以自由返回一个新的空数组实例,并且
  • 除非您需要避免出现问题性或病理性使用模式和/或追求理论上的最佳性能,

然后new String[0]直接返回就可以了。

就个人而言,我喜欢的表现力和简洁性return new String[0];,而不必引入额外的静态场。

出乎我的意料,我写这篇文章一个月后,一位真正的性能工程师调查了这个问题:请参阅AlexeyShipilёv的博客文章“远古智慧的数组”中的这一部分:

如预期的那样, 在很小的集合大小上可以观察到唯一的影响,而这仅是对的边际改进new Foo[0]这种改进似乎并不能证明在事物的整体方案中缓存阵列是合理的。作为微小的微优化,在一些严格的代码中可能有意义,但我不在乎。

这样就解决了。我将勾选标记并将其专用于Alexey。



 类似资料:
  • 本文向大家介绍SQLite 性能优化实例分享,包括了SQLite 性能优化实例分享的使用技巧和注意事项,需要的朋友参考一下 最早接触 iOS 开发了解到的第一个缓存数据库就是 SQLite,后面一直也以 SQLite 作为中坚力量使用,以前没有接触到比较大量数据的读写,所以在性能优化方面关注不多,这次对一个特定场景的较多数据批量读写做了一个性能优化,使性能提高了十倍。 大致应用场景是这样: 每次程

  • 本文向大家介绍实例分析ORACLE数据库性能优化,包括了实例分析ORACLE数据库性能优化的使用技巧和注意事项,需要的朋友参考一下 ORACLE数据库的优化方式和MYSQL等很大的区别,今天通过一个ORACLE数据库实例从表格、数据等各个方便分析了如何进行ORACLE数据库的优化。 tsfree.sql视图 这个sql语句迅速的对每一个表空间中的空间总量与每一个表空间中可用的空间的总量进行比较 表

  • 对于静态网站,主要可以优化的地方就是网站的加载速度。 了解你的网站性能 查看世界各地访问该网站的速度。 使用ping.pe,ping jimmysong.io的效果如图: 另外还可以使用Grafana的插件,并安装worldping插件,效果如下图所示:

  • 所以我的编码器朋友讨厌使用编码。然而我的Java程序中却充满了类之间的链接,而且我有很多! 是否值得重写整个代码来移除静态方法? 用一个比用另一个有什么好处吗?

  • js的类包含 静态属性和实例属性 静态方法和实例方法 请问什么场景应该把这个属性定义为静态的,什么场景应该为实例的? 请问什么场景应该把这个方法定义为静态的,什么场景应该为实例的?我能想到的是类似于Integer.parse()之类的静态方法,我恍惚能理解静态方法的使用场景,但是,还是没有那种概括性的思路。 感谢大佬~~

  • 本文向大家介绍es6中class类静态方法,静态属性,实例属性,实例方法的理解与应用分析,包括了es6中class类静态方法,静态属性,实例属性,实例方法的理解与应用分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了es6中class类静态方法,静态属性,实例属性,实例方法。分享给大家供大家参考,具体如下: es6新增了一种定义对象实例的方法,使用class关键字定义类,与class相关