围绕断言语句的迭代器会影响生成构建的性能吗?

ada*_*m.r 5 java compiler-construction performance

我最近在Java中发现了" assert "语句,并且在我调试它的时候一直乱丢我的软件.我最初的本能是避免将控制流语句用于处理断言语句,但后来我意识到这些控制语句可能会在生产构建期间被删除,因为它们的块将是空的.我的印象是它们将被JIT编译器淘汰.

不幸的是,我正在研究JIT编译器如何工作的模糊回忆,并且找不到合适的文档.我能找到的最好的是IBM对优化过程的简要概述.

在我养成实施基于断言的测试的习惯之前,我想知道是否有最佳实践来最小化它们对性能的影响.我讨厌实施一系列测试只是为了发现它们的集体效果是大幅降低性能,即使它们的影响可以忽略不计.

您是否可以告诉我以下几行是否会拖累生产版本的性能(默认情况下禁用"断言")?

for (T obj : Collection){
    assert obj.someProperty();
}
Run Code Online (Sandbox Code Playgroud)

如果我有更复杂的东西,包括仅用于断言的短期对象,该怎么办呢?

TreeMap<Integer,T> map = new TreeMap<>();
int i = 0;
for (T obj : Collection){
    map.put(i,obj);
    assert obj.someProperty();
    i++;
}
// assert something about map, then never use it again
Run Code Online (Sandbox Code Playgroud)

或者一种方法,其唯一的作用是调用"断言"?

提前致谢!


Oracle关于" 断言 "声明的文档的相关摘录:

这些讨论如何从类文件中删除断言,这不是我所关心的.

从类文件中删除所有断言跟踪程序员为资源受限设备开发应用程序可能希望完全从类文件中删除断言.虽然这使得无法在现场启用断言,但它也减少了类文件的大小,可能会提高类加载性能.在没有高质量的JIT的情况下,它可以减少占地面积并提高运行时性能.

断言工具不提供直接支持从类文件中剥离断言.但是,断言语句可以与Java语言规范中描述的"条件编译"惯用法一起使用,使编译器能够从它生成的类文件中消除这些断言的所有痕迹:

static final boolean asserts = ...; // false以消除断言

if(断言)断言;

并从FAQ部分:

为什么不提供编译器标志来完全消除目标文件中的断言?坚定的要求是可以在现场启用断言,以提高可维护性.本来可以允许开发人员在编译时消除目标文件中的断言.断言可以包含副作用,但它们不应该,并且这样的标志因此可以以显着的方式改变程序的行为.每个有效的Java程序只有一个语义,这被认为是件好事.此外,我们希望鼓励用户将断言保留在目标文件中,以便可以在现场启用它们.最后,规范要求断言在类初始化之前运行时就像启用一样.如果断言从类文件中删除,则不可能提供这些语义.但请注意,Java语言规范中描述的标准"条件编译习惯用法"可用于为真正想要它的开发人员实现此效果.

Mar*_*iot 3

您可以从 内部进行方法调用assert,因此我更喜欢的语法如下:

private static boolean testSomePropertyTrue(Collection<T> collection) {
    boolean test = true;
    for (T obj : collection){
        test = test && obj.someProperty();
    }
    return test;
}
Run Code Online (Sandbox Code Playgroud)

...

assert testSomePropertyTrue(collection);
Run Code Online (Sandbox Code Playgroud)

这使得 JIT 优化的代码没有任何歧义,并且当启用断言时将执行相同的不变检查。

正如所写,您的第二个示例将始终创建TreeMap,无论是否启用断言。将整个事情包装在一个函数中并将其作为单个断言进行评估将在运行时完全消除该代码路径。

就像这个问题的另一个答案所暗示的那样,断言将留下字节码,无论它们是否启用,但使用上面显示的样式将确定性地阻止这些代码路径执行,除非启用断言。