Как мне написать (протестировать) код, который не будет оптимизирован компилятором / JIT? - PullRequest
10 голосов
/ 14 февраля 2009

Я не особо разбираюсь во внутренностях оптимизации компилятора и 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 в этот процесс.

Кроме того, есть ли какие-либо типичные случаи оптимизации, о которых мне следует знать при создании модульных тестов?

Большое спасибо!

Ответы [ 7 ]

6 голосов
/ 14 февраля 2009

Не беспокойся об этом. Нельзя оптимизировать что-либо, что может повлиять на вашу систему (кроме скорости). Если вы создаете новый объект, вызывается код, выделяется память, она ДОЛЖНА работать.

Если у вас есть защита с помощью if (false), где false - окончательный вариант, его можно полностью оптимизировать вне системы, тогда он может обнаружить, что метод ничего не делает, и оптимизировать его (теоретически). ).

Изменить: кстати, он также может быть достаточно умным, чтобы определить, что этот метод:

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

всегда ничего не сделает, если b ложно, и в конечном итоге выяснит, что в одном месте вашего кода B всегда ложно, и полностью скомпилирует эту подпрограмму из этого кода.

Здесь JIT может делать вещи, которые практически невозможны на любом неуправляемом языке.

5 голосов
/ 14 февраля 2009

Я думаю, что если вы беспокоитесь о том, что он будет оптимизирован, возможно, вы проводите небольшое тестирование излишне.

На статическом языке я склонен считать компилятор тестом. Если он проходит компиляцию, это означает, что определенные вещи есть (например, методы). Если у вас нет другого теста, который использует ваш конструктор по умолчанию (который докажет, что он не будет генерировать исключения), вы можете подумать, почему вы пишете этот конструктор по умолчанию (YAGNI и все такое).

Я знаю, что есть люди, которые не согласны со мной, но я чувствую, что такого рода вещи просто раздувают ваше количество тестов без всякой причины, даже если они смотрят на это через очки TDD.

2 голосов
/ 21 мая 2013

JIT разрешено выполнять только те операции, которые не влияют на гарантированную семантику языка. Теоретически, он может удалить выделение и вызвать конструктор MyObject , если , это может гарантировать, что вызов не имеет побочных эффектов и никогда не вызовет исключение (не считая OutOfMemoryError).

Другими словами, , если JIT оптимизирует вызов из вашего теста, тогда ваш тест в любом случае прошел бы .

PS: обратите внимание, что это применимо, потому что вы проводите тестирование функциональности , а не тестирование производительности . При тестировании производительности важно убедиться, что JIT не оптимизирует работу, которую вы измеряете, иначе ваши результаты станут бесполезными.

2 голосов
/ 14 февраля 2009

Думайте об этом так:

Предположим, что компилятор может определить, что граф вызовов не имеет побочных эффектов (я не думаю, что это возможно, я смутно помню кое-что о P = NP из моих курсов CS). Это оптимизирует любой метод, который не имеет побочных эффектов. Поскольку большинство тестов не имеют и не должны иметь побочных эффектов, компилятор может оптимизировать их все.

1 голос
/ 14 февраля 2009

Кажется, что в C # я мог бы сделать это:

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

AFAIU, метод GC.KeepAlive не будет встроен JIT, поэтому код гарантированно будет работать как положено. Однако я не знаю подобную конструкцию в Java.

0 голосов
/ 14 февраля 2009

Каждый ввод / вывод является побочным эффектом, поэтому вы можете просто поставить

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

и ты в порядке.

0 голосов
/ 14 февраля 2009

Почему это должно иметь значение? Если компилятор / JIT может статически определить, что никакие утверждения не будут поражены (что может вызвать побочные эффекты), то все в порядке.

...