2010-04-09 2 views
8

У меня есть следующие две программы:скорость Итерация междунар против длинного

long startTime = System.currentTimeMillis(); 
for (int i = 0; i < N; i++); 
long endTime = System.currentTimeMillis(); 
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs"); 

и

long startTime = System.currentTimeMillis(); 
for (long i = 0; i < N; i++); 
long endTime = System.currentTimeMillis(); 
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs"); 

Примечание: единственным отличием является тип переменной цикла (int и long).

Когда я запускаю это, первая программа последовательно печатает от 0 до 16 мс независимо от значения N. Второй занимает намного больше времени. Для N == Integer.MAX_VALUE он работает примерно на 1800 мсек на моей машине. Время работы кажется более или менее линейным в N.

Так почему же это?

Я полагаю, JIT-компилятор оптимизирует цикл int до смерти. И не зря, потому что явно ничего не делает. Но почему это не делается для цикла long?

Коллега подумал, что мы можем измерить компилятор JIT, выполняющий его работу в цикле long, но поскольку время работы кажется линейным в N, это, вероятно, не так.

Я использую JDK 1.6.0 обновление 17:

C:\>java -version 
java version "1.6.0_17" 
Java(TM) SE Runtime Environment (build 1.6.0_17-b04) 
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01, mixed mode) 

Я на Windows XP Professional x64 Edition с пакетом обновления 2 с процессором Intel Core2 Quad на 2,40 ГГц.


ОТКАЗ

Я знаю, что microbenchmarks не являются полезными в производстве. Я также знаю, что System.currentTimeMillis() не так точна, как предполагает его название. Это то, что я заметил, обманывая, и мне было просто любопытно, почему это происходит; больше ничего.

+0

Я абсолютно согласен с @Andrzej здесь, но если это на самом деле просто ради любопытства, вы можете использовать плагин PrintAssembly, чтобы действительно посмотреть на сгенерированный код: http://wikis.sun.com/display/HotSpotInternals/PrintAssembly –

+0

Что произойдет, если вы используете флаг -Xint? Это предотвращает компиляцию Hotspot, поэтому вы получите лучшее сравнение. –

+0

@Steven: но с '-Xint' результат * еще меньше * значим для всего, что похоже на реальное использование. –

ответ

5

Это интересный вопрос, но, честно говоря, я не уверен, что рассмотрение поведения Hotspot здесь принесет полезную информацию. Любые полученные вами ответы не будут передаваться в общем случае (потому что мы смотрим на оптимизацию, которую Hotspot выполняет в одной конкретной ситуации), поэтому они помогут вам понять, почему один не-оператор быстрее другого, , но они не помогут вам быстрее писать «реальные» программы.

Это также невероятно легко написать очень вводящие в заблуждение микро-тесты вокруг такого рода вещей - см. this IBM DW article для некоторых распространенных ошибок, как их избежать и некоторых общих комментариев к тому, что вы делаете.

Так вот, это ответ «без комментариев», но я думаю, что это единственный действительный ответ.Циклический цикл no-op во время компиляции не нуждается в, чтобы быть быстрым, поэтому компилятор не оптимизирован, чтобы быть быстрым в некоторых из этих условий.

+0

Вы правы, конечно. Причина, о которой я прошу, - это чистое любопытство по поводу того, что я заметил, обманывая; Я не собираюсь делать это в реальной программе. :) – jqno

4

Возможно, вы используете 32-битную JVM. Результаты, вероятно, будут отличаться от 64-битной JVM. В 32-разрядной JVM int может быть сопоставлен с собственным 32-битным целым числом и может быть увеличен с помощью одной операции. То же самое не выполняется долго, что потребует дополнительных операций для увеличения.

См. Это question для обсуждения вопроса об инт и длинных размерах.

+0

Хорошая точка. Тем не менее, я использую 64-битную JVM. Я уточню вопрос. – jqno

+0

Также будет полезно опубликовать операционную систему и некоторые сведения о конфигурации вашего оборудования (главным образом, в ЦП). – kgiannakakis

3

Мое предположение - и это только догадка - это:

Виртуальная машина Java делает вывод о том, что первый цикл эффективно ничего не делает, поэтому она удаляет его полностью. Никакая переменная «ускользает» из цикла for.

Во втором случае петля также ничего не делает. Но может быть, что JVM-код, который определяет, что цикл ничего не делает, имеет предложение if (type of i) == int ". В этом случае оптимизация для удаления do-nothing for-loop работает только с int.

Оптимизация, которая удаляет код, должна быть уверенной в отсутствии побочных эффектов. Кодеры JVM, похоже, ошибались на стороне осторожности.

1

Микро-бенчмаркинг, подобный этому, не имеет большого смысла, потому что результаты во многом зависят от внутренней работы Hotspot JIT.

Также обратите внимание, что значения системных часов, которые вы получаете с System.currentTimeMillis(), не имеют разрешения 1 мс во всех операционных системах. Вы не можете использовать это для очень точного времени очень коротких событий.

Посмотрите на эту статью, которая объясняет, почему делает микро-тесты в Java не так просто, как большинство людей думают: Anatomy of a flawed microbenchmark