Подтвердить что ты не робот

Производительность Java Realtime

Я работаю с Java-проектом, который требует очень продвинутых манипуляций с изображениями. Фактически, я делаю большую часть манипуляций с помощью OpenCV, и я использую JNI для обертывания функций OpenCV, которые мне нужны. Я очень доволен производительностью OpenCV, люди, которые написали код OpenCV, заслуживают большого вознаграждения за код. В отличие от того, что я испытываю с кодом, написанным разработчиками Java.

Я начал оптимистично относиться к выбору моего языка программирования, моя первая рабочая итерация проекта прекрасно работает, но его производительность нигде не приближается к реальному времени (получается около 1 кадра за 2 секунды). Я сделал некоторые оптимизации МОЙ код и его помогли. Мне удалось увеличить частоту кадров до 10-20 кадров в секунду, что отличает меня, но я обнаружил, что для дальнейших оптимизаций я должен переписать код Java, чтобы сделать то же самое, но 10 -20x более эффективно.

Я потрясен тем, как разработчики Java уделяют очень мало внимания производительности, особенно при написании классов для связанных с медиа классов. Я загрузил OpenJDK, и я изучаю функции, которые я использую. Например, существует функция под классом Raster, называемая getPixels (...), и она получает пиксели изображения. Я ожидал, что эта функция будет высоко оптимизированной функцией в исходном коде, с несколькими вызовами System.arrayCopy для дальнейшей оптимизации производительности. Вместо этого я обнаружил чрезвычайно "классный" код, где они называют 5-6 разных классов и 10-20 разных методов, чтобы выполнить то, что я могу сделать в одной строке:

for (int i =0; i < n; i++) {
  long p = rawFrame[i];
  p = (p << 32) >>> 32;
  byte red = (byte) ((p >> 16) & 0xff);
  byte green = (byte) ((p >> 8) & 0xff);
  byte blue = (byte) ((p) & 0xff);
  byte val = (byte)(0.212671f * red + 0.715160f * green + 0.072169f * blue);
  data[i] = val;
  grayFrameData[i] = (val & 0x80) + (val & (0x7f)); 
}

Приведенный выше код преобразует изображение в оттенки серого и получает данные с поплавковым пикселем примерно в 1-10 мс. Если бы я хотел сделать то же самое с встроенными функциями Java, то преобразование в оттенки серого займет 200-300 мс, а затем захват пикселей с плавающей точкой займет около 50-100 мс. Это неприемлемо для работы в реальном времени. Примечание, чтобы получить ускорение, я активно использую побитовые операторы, от которых Java отступает.

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

Мой вопрос в том, что на этом позднем этапе разработки (у меня уже есть первая итерация, а не я работаю над второй, которая больше работает в режиме реального времени), я должен укусить пулю и переключиться на C/С++, где Я могу настроить мелодию намного больше, или я должен придерживаться Java и надеюсь, что все станет более дружественным в реальном времени, так что мне не придется переписывать уже реализованный Java-код, чтобы получить ускорение.

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

4b9b3361

Ответ 1

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

Оптимизация потенциала:

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

`public static final int getGrayScale(final int pixelRGB){
    return (0.212671f * ((pixelRGB >> 16) & 0xff) + 0.715160f * ((pixelRGB >> 8) & 0xff) + 0.072169f * ((pixelRGB) & 0xff));
}`

и используя это в вашем цикле {pixels}. Используя вызов метода, JVM может значительно оптимизировать эту операцию и, возможно, оптимизировать цикл for.

Если у вас есть RAM для записи, вы можете создать статическую итоговую таблицу поиска выходных черно-белых байтов для всех возможных 24-битных пиксельных цветов. Это будет ~ 16 МБ в ОЗУ, но тогда вам не нужно делать арифметику с плавающей запятой, просто доступ к одному массиву. Это может быть быстрее, в зависимости от того, какой JVM вы используете, и может ли он оптимизировать проверку границ массива.

Места для поиска аналогичного более быстрого кода обработки изображений:

Я бы настоятельно рекомендовал вам взглянуть на код для приложения обработки изображений ImageJ (не может связываться благодаря StackOverflow, который задерживается) и его библиотекам, в частности ij.process.TypeConverter. Как и ваш код, он в значительной степени опирается на операции прямого массива с бит-twiddling и минимум создания дополнительных массивов. Библиотеки Java2D (часть стандартной JRE) и библиотека Java Advanced Imaging (JAI) (не могут связываться с задержкой StackOverflow) предоставляют другие способы обработки изображений непосредственно на данных изображения быстро, без необходимости сворачивать вашу собственную операцию каждый время. Для Java2D вам просто нужно быть осторожным, какие функции вы используете.

Почему библиотеки Java2D настолько косвенны:

Большая часть "класс-iness" связана с поддержкой нескольких цветовых моделей и форматов хранения (изображения I.E. HSB, цветные модели с плавающей точкой, индексированные цветовые модели). Косвенность существует по какой-то причине и иногда фактически повышает производительность - класс BufferedImage (например) перехватывает непосредственно в графическую память в последних виртуальных машинах, чтобы сделать некоторые операции МНОГО быстрее. Функция Indirection позволяет маскировать это от пользователя много времени.

Ответ 2

Мой вопрос в том, что на этом позднем этапе разработки (у меня уже есть первая итерация, а не я работаю над второй, которая больше работает в режиме реального времени), я должен укусить пулю и переключиться на C/С++, где Я могу настроить мелодию намного больше, или я должен придерживаться Java и надеюсь, что все станет более дружественным в реальном времени, так что мне не придется переписывать уже реализованный Java-код, чтобы получить ускорение.

Вы спрашиваете, должен ли я

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

Могут быть и другие варианты.... но вариант 2 не кажется реалистичным, вы не можете просто "надеяться", что код будет быстрее: p

Несколько замечаний:

  • OpenJDK не обладает такой же производительностью, как Sun JDK, вы пробовали Sun JDK?
  • Если оптимизация производительности, которую вам нужно выполнить, состоит из нескольких методов, тогда, возможно, стоит перезаписать их и придерживаться Java...

Ответ 3

Мое предложение состояло бы в зависимости от того, насколько важно манипулировать изображениями по сравнению с проектом в целом и относительно любых преимуществ, которые приносит Java. Очевидно, что вы можете написать быстрый код в java (как вы продемонстрировали), если вам нужно. Однако, если 80% вашего проекта будет состоять из такой оптимизации, я бы, конечно, переосмыслил Java как выбор языка здесь.

С другой стороны, если это составляет 20% приложения, а остальные 80% - это пользовательская функциональность, обеспечивающая это преобразование, то, возможно, придется выполнить эту работу, чтобы выполнить манипуляцию, является целесообразным компромиссом, чтобы не должны иметь дело с вашим собственным управлением памятью и иметь любые другие API-интерфейсы java, предоставляющие вам взаимодействие с пользователем (Web, Swing, SWT, независимо от того, что вы используете).

Java не известен своими способностями в реальном времени благодаря сборщику мусора. Возможно, это тоже укусит вас, поэтому будьте осторожны.

Ответ 4

Я не знаю, сколько повышения производительности вы получите, но если у вас есть длительный процесс, делающий повторяющиеся действия, вы должны попытаться запустить VMware Hotspot с помощью java -server. Он выполняет гораздо лучше, чем клиентская VM, которая по умолчанию используется в Windows, которая оптимизирована для быстрого запуска.

Ответ 5

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

В обычной реализации Java, такой как OpenJDK, сборщик мусора является самой большой проблемой для достижения поведения в реальном времени. Это связано с тем, что сборщик мусора может прерывать программу в любой момент, чтобы выполнить свою работу. У моей компании aicas есть реализация Java, которая не требует отдельного потока для сбора мусора. Вместо этого во время распределения выполняется небольшая работа с GC. Эффективно, распределение оплачивается путем маркировки или подметания нескольких блоков для каждого освобожденного блока. Это потребовало полной повторной виртуальной машины.

Компиляция - еще одна точка, где Java в реальном времени отличается от обычных реализаций Java. Технология Java в реальном времени имеет тенденцию использовать статическую или Ahead-время (AoT) компиляцию вместо компиляции JIT. JiT может быть в порядке для вашего приложения, так как вы можете терпеть "разогрев" времени, требуемый обычной виртуальной машиной для компиляции наиболее используемых классов. Если это так, то вы, вероятно, не имеете требований в реальном времени, просто пропускные.

Если вы заинтересованы в том, чтобы декодирование кадра не прерывалось сбором мусора, тогда было бы разумно использовать реализацию Java в реальном времени и, возможно, компиляцию AoT. Спецификация в реальном времени для Java (RTSJ) также обеспечивает другую поддержку для реального времени и встроенных программ, таких как RelatimeThread, AsyncEventHandler и RawMemoryAccess.

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

Ответ 6

Преждевременная оптимизация - это корень всего зла.

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

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

Ответ 7

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

Таким образом, вам не нужно надеяться, что JDK сможет стать лучше. Вы можете помочь сделать это.

Ответ 8

Насколько я понимаю, самые последние версии Java (или, возможно, это JavaFX) имеют методы, позволяющие вам получить доступ к расширенным функциям в оборудовании Video вашей системы. Мне жаль, что я настолько генерал, что, по-моему, я слышал об этом на Java Posse, и поскольку я застрял на земле Java 1.3, у меня никогда не было возможности проверить это, но я помню, что слышал что-то подобное.

Вот что-то об этом: Но похоже, что это будет только в Java 7:(

Также выглядит так, что он будет поддерживать воспроизведение потока и рудиментарных манипуляций с потоками вначале, но, возможно, подход "Подождите и улучшит Java" может действительно работать.

Ответ 9

Что мешает вам писать оптимизированную версию методов, которые вы хотите использовать, вместо использования встроенных методов? Если это невозможно, почему бы не записать ваши объекты на более родном языке и импортировать их в существующее приложение?