Почему код Java, сгенерированный для выполнения операции, работает медленнее, чем «цикл интерпретатора»? - PullRequest
2 голосов
/ 18 мая 2009

У меня есть некоторый код Java, который выполняет побитовые операции над BitSet. У меня есть список операций, и я могу «интерпретировать» их, зацикливая их, но для меня важно, чтобы я мог выполнять эти операции как можно быстрее, поэтому я пытался динамически генерировать код для их применения. Я генерирую исходный код Java для выполнения операций и компилирую класс, реализующий эти операции, используя Javassist.

К сожалению, мой динамически сгенерированный код работает медленнее, чем интерпретируемый код. Похоже, что это происходит потому, что HotSpot оптимизирует интерпретируемый код, но не оптимизирует скомпилированный код: после того, как я запустил его несколько тысяч раз, мой интерпретированный код работает в два раза быстрее, чем вначале, но мой скомпилированный код не показывает ускорения. В соответствии с этой гипотезой мой интерпретируемый код изначально медленнее скомпилированного кода, но в конечном итоге быстрее.

Я не уверен, почему это происходит. Я предполагаю, что, возможно, Javassist использует загрузчик классов, классы которых HotSpot не трогает. Но я не эксперт по загрузке классов в Java, поэтому я не уверен, что это разумное предположение или как его протестировать. Вот как я создаю и загружаю класс с помощью Javassist:

ClassPool pool = ClassPool.getDefault();
CtClass tClass = pool.makeClass("foo");

// foo implements MyInterface, with one method
tClass.addInterface(pool.get(MyInterface.class.getName()));

// Get the source for the method and add it
CtMethod tMethod = CtNewMethod.make(getSource(), tClass);
tClass.addMethod(tMethod);

// finally, compile and load the class
return (MyInterface)tClass.toClass().newInstance();

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

Я использую серверную виртуальную машину Sun 1.6 в 32-разрядной версии Windows XP.

Ответы [ 3 ]

3 голосов
/ 18 мая 2009

HotSpot не волнует, откуда взялся код. Например, он будет рад встроенному коду, вызываемому посредством вызова виртуального метода, с реализацией, загруженной другим загрузчиком классов.

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

Существует несколько причин, по которым HotSpot может не оптимизировать код так сложно, как мог бы. Например, очень длинные методы будут иметь тенденцию не быть встроенными или иметь встроенный метод в них.

1 голос
/ 18 мая 2009

Есть настройки JVM, которые управляют скоростью компиляции кода -XX: CompileThreshold = 10000

Количество вызовов методов / веток перед компиляцией [-client: 1500]

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

1 голос
/ 18 мая 2009

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

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

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

...