10

我不太了解编译器和 JIT 优化的内部原理,但我通常会尝试使用“常识”来猜测哪些可以优化,哪些不能。所以我今天写了一个简单的单元测试方法:

@Test  // [Test] in C#
public void testDefaultConstructor() {
    new MyObject();
}

这种方法实际上是我所需要的。它检查默认构造函数是否存在并无异常运行。

但后来我开始考虑编译器/JIT 优化的效果。编译器/JIT 能否通过完全消除该new MyObject();语句来优化此方法?当然,它需要确定调用图对其他对象没有副作用,这是简单初始化对象内部状态的普通构造函数的典型情况。

我认为只有 JIT 可以执行这样的优化。这可能意味着这不是我应该担心的事情,因为测试方法只执行一次。我的假设正确吗?

不过,我正在尝试考虑一般主题。当我想到如何防止这个方法被优化时,我想我可以assertTrue(new MyObject().toString() != null),但这非常依赖于toString()方法的实际实现,即使这样,JIT 也可以确定该toString()方法总是返回一个非空字符串(例如如果实际Object.toString()被调用),从而优化整个分支。所以这种方式行不通。

我知道在 C# 中我可以使用[MethodImpl(MethodImplOptions.NoOptimization)],但这不是我真正想要的。我希望找到一种(与语言无关的)方法来确保我的代码的某些特定部分实际上会按我的预期运行,而 JIT 不会干扰这个过程。

此外,在创建单元测试时,我应该注意哪些典型的优化案例?

非常感谢!

4

7 回答 7

6

别担心。不允许优化任何可能对您的系统产生影响的东西(速度除外)。如果你新建一个对象,调用代码,分配内存,它必须工作。

如果你用 if(false) 保护它,其中 false 是最终结果,它可以在系统之外完全优化,然后它可以检测到该方法没有做任何事情并优化它(理论上)。

编辑:顺便说一句,它也可以足够聪明地确定这种方法:

newIfTrue(boolean b) {
    if(b)
        new ThisClass();
}

如果 b 为假,将永远不做任何事情,并最终发现代码中的某个时刻 B 始终为假,并完全从该代码中编译出该例程。

这就是 JIT 可以做任何非托管语言几乎不可能完成的事情的地方。

于 2009-02-13T22:37:28.927 回答
5

我认为如果你担心它会被优化掉,你可能会做一些测试过度。

在静态语言中,我倾向于将编译器视为一种测试。如果它通过编译,这意味着某些东西在那里(比如方法)。如果您没有另一个测试来练习您的默认构造函数(这将证明它不会抛出异常),您可能想首先考虑为什么要编写默认构造函数(YAGNI 和所有这些)。

我知道有些人不同意我的观点,但我觉得这种事情只会无缘无故地增加你的测试数量,即使通过 TDD 护目镜来看也是如此。

于 2009-02-13T22:34:05.313 回答
2

这样想:

让我们假设编译器可以确定调用图没有任何副作用(我认为不可能,我隐约记得我的 CS 课程中关于 P=NP 的一些内容)。它将优化任何没有副作用的方法。由于大多数测试没有也不应该有任何副作用,因此编译器可以将它们全部优化。

于 2009-02-13T22:40:09.217 回答
2

JIT 只允许执行不影响语言保证语义的操作。从理论上讲,如果它可以保证调用没有副作用并且永远不会抛出异常(不算数) ,它可以删除分配并调用MyObject构造函数。OutOfMemoryError

换句话说,如果 JIT 优化了您的测试调用,那么您的测试无论如何都会通过

PS:请注意,这适用,因为您正在进行功能测试而不是性能测试。在性能测试中,确保 JIT 不会优化您正在测量的操作非常重要,否则您的结果将变得毫无用处。

于 2013-05-20T20:12:30.570 回答
1

似乎在 C# 中我可以这样做:

[Test]
public void testDefaultConstructor() {
    GC.KeepAlive(new MyObject());
}

AFAIU,该GC.KeepAlive方法不会被 JIT 内联,因此代码将保证按预期工作。但是,我不知道 Java 中有类似的构造。

于 2009-02-13T22:40:57.280 回答
0

为什么要重要?如果编译器/JIT 可以静态确定不会命中任何断言(这可能会导致副作用),那么您就可以了。

于 2009-02-13T22:37:27.600 回答
0

每个 I/O 都是一个副作用,所以你可以把

Object obj = new MyObject();
System.out.println(obj.toString());

你很好。

于 2009-02-14T08:01:42.750 回答