Существует вечная борьба между разными языками программирования. «Код в« XYZlang »работает намного быстрее, чем в« ABClang »». Ну, это утверждение несет как минимум три недоразумения. Во-первых, в большинстве случаев это не исходный код, который вы написали, который фактически выполняется, во-вторых, пожалуйста, определите «быстрее», третье, и общее правило экспериментов: не делайте выводов, основываясь на тесте, см. это скорее намек на то, что некоторые вещи, кажется, имеют значение.
В этой статье я не буду обсуждать цифры и причины, по которым код X или язык Y заменяет другой. Есть много людей, которые понимают фоны намного лучше меня — здесь, короткий намек на очень хорошую статью Алексея Шипилева о сравнительном тестировании Java и Scala . Не будет никакого машинного кода, никаких настроек производительности, которые заставят ваш код работать в 100 раз лучше. Я хочу представить вам идеи, как вы можете настроить такие тесты производительности удобным для пользователя способом, который поможет вам легко и просто выполнить ваши тесты. Подробно мы встретимся с этими темами:
- Как настроить одну сборку, которая соответствует всем требованиям?
- Gradle в действии на разных языках
- Сравнительный анализ с JMH (java) и hayai (C / C ++) для подтверждения концепции
- Как сохранить результаты?
1. История
Несколько вступительных слов о предыстории статьи. Несколько лет назад в университете я был членом группы компьютерной графики. Я потратил много времени на программирование на C / C ++, потому что было одно общее мнение: «Если вы хотите действительно быстро, не тратьте время на языки VM и другие« эзотерические »вещи, напишите это на языке, который компилируется в нативный двоичный код , Хорошо, что в этой доктрине много правды, и это должно быть причиной того, что многие критически важные для производительности приложения, работающие с числами, реализованы на таких языках, как C и C ++. Для приложений, связанных с графикой, есть еще одна очевидная причина — доступ к графической карте, если вы хотите использовать ее более продвинутым способом. Наконец, программирование с выравниванием кеша, прямое использование векторизации и доступ к специфическим функциям процессора — это настоящая «вишня на пороге» приложений с собственным кодом.И это то, чему я научился в те дни: нативный код всегда превосходит языки ВМ, и это мнение большинства разработчиков программного обеспечения. Теперь короткая история, особенно для тех, кто уже потерял самообладание из-за этого заявления: Это зависит конечно! И я не хочу открывать еще одну дискуссию о бесконечном списке преимуществ и ловушек для разных языков. Но дело в том, что измерять производительность кода и сравнивать их сложно, потому что нет единого подхода или лучшей практики. Это, будем надеяться, суть этой серии постов в блоге. Интерпретации и выводы, основанные на числах, могут быть сделаны другими;).
2.) Java против C ++ — так что никакой войны сегодня
Я думал, что это будет хорошей отправной точкой, чтобы принять вызов с двумя из самых распространенных объектно-ориентированных языков программирования. Я не хочу вдаваться в подробности о различиях сейчас. Я думаю, что все вы знаете о том факте, что JIT-компиляция происходит в Hotspot, в то время как код C ++ компилируется непосредственно в машинный код на этапе сборки. Это означает, что машинный код, выполняемый JVM, вероятно, изменится во время выполнения, а результат компиляции C ++ не изменится. Я также буду пренебрегать тем фактом, что существуют разные компиляторы и время выполнения, которые будут серьезно влиять на итоговые числа.
3.) К чему это приведет — исторический автоматизированный межязыковой бенчмаркинг
Сравнительный анализ языка в целом не может иметь целью доказать, что одна среда компилятора / среды выполнения опережает другую. Рассматривайте это как задачу выявления различий, которые не очевидны в первую очередь, отправную точку, чтобы углубиться в детали. Или просто поиграть и посмотреть, что произойдет, если вы измените это на одну из реализаций.
Я потратил некоторое время на размышления о том, что было бы полезно, если у вас есть один алгоритм, реализованный на двух разных языках, и теперь вы хотите знать, какой результат, кажется, работает лучше. Это означает, что набор инструментов должен поддерживать кросс-язык . Если я изменю реализацию в одном языковом варианте, я хочу использовать одну общую цепочку инструментов, которая создает отчет о том, как текущие реализации сравниваются друг с другом. Другими словами, создание всех отчетов по сравнительному анализу и возможная агрегация должны выполняться в автоматизированном режиме. путь. Например, измерение прогресса в оптимизации производительности — это зло, когда у вас нет реальной истории отчетов, привязанных к соответствующей версии в вашей системе управления версиями, которая также включает подробные сведения об окружающей среде и подобных вещах. Последняя идея состоит в том, чтобы получить диаграмму, которая показывает изменение результатов эталонных тестов с течением времени, что будет возможно из-за того, что подход будет Историзирован . Учитывая это, я вижу две основные цели для моих идей:
- Fun: Gamified языковые проблемы
- Скучно, но полезно: Сборка, в которой у вас есть эталонная реализация на другом языке, который определяется как границы качества в определенных границах.
Я приложил много усилий для оценки различных возможностей. Следующая установка пытается охватить все эти темы для следующего сценария POC: Давайте предположим, что у нас есть проект Java и C ++, которые реализуют те же алгоритмы. Мы хотим сравнить и сравнить производительность горячих частей кода. Мы также хотим отслеживать изменения во времени, когда исходный код проекта растет и изменяется. Мы будем:
- Сравнительный анализ кода Java с JMH как часть сборки gradle
- Сравнительный код C ++ с Hayai
- Интеграция бинарной компиляции c ++ с Gradle
- Интеграция исполнения бенчмарка Hayai с Gradle
- Объедините Java и C ++ проекты в одну межязыковую цепочку сборки
- Совокупные результаты JMH и Hayai дают третий сложный результат
- Разделить код тестирования из проектов в отдельный проект и выделенный SCM.
- Автоматически отправлять агрегированные результаты тестирования в хитро структурированный репозиторий git, чтобы отслеживать текущие версии исходного кода и результаты тестирования.
4.) Первый шаг: сравнительный анализ кода Java с помощью Gradle и JMH
Несколько месяцев назад Даниэль Миттердорфер опубликовал несколько статей о том, как правильно делать микробенчмарки в Java . Он обрисовывает в общих чертах различные подводные камни и особенности, которые требуют значительного внимания, чтобы получить значимые результаты. Представленный инструмент, который решает многие проблемы, — это JMH (Java Microbenchmarking Harness). Для полноты приведем небольшой пример того, как выглядит такой тест:
package net.chromarenderer.benchmarks; import org.openjdk.jmh.annotations.Benchmark; public class YourBenchmark { @Benchmark public double yourHotSpotMethodBenchmark() { return Math.sqrt(3.0); } }
Существует не большая разница по сравнению с модульным тестом, но типичные аннотации. Вся магия выполнения выполняется дополнительным кодом, который JMH добавляет из-за аннотации @Benchmark. Поскольку мы хотим, чтобы выполнение бенчмарка JMH было частью нашей цепочки инструментов разработки, мы взглянем на плагин jmh-gradle-plugin . К сожалению, в настоящее время есть несколько открытых вопросов в оригинальной версии. Я предоставил некоторые исправления, которые еще не объединены. А пока, пожалуйста, взгляните на нашу разветвленную версию, которая также поддерживает Gradle 2.0. Это будет файл build.gradle, с которого вы хотите начать:
buildscript { repositories { mavenLocal() jcenter() } dependencies { classpath 'me.champeau.gradle:jmh-gradle-plugin:+' } } apply plugin: 'java' apply plugin: 'me.champeau.gradle.jmh' group = 'net.chroma' version = '0.0.1-SNAPSHOT' jmh { jmhVersion = '1.3.4' // Specifies JMH version } compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' repositories { mavenCentral() } dependencies { testCompile 'junit:junit:4.11' jmh 'junit:junit:4.11' }
Этот плагин также предлагает использовать выделенный исходный путь для всех классов JMH. Это будет структура каталогов:
src ├── main ├── java ├── jmh ├── java ├── test ├── java
Плагин предоставляет различные возможности, чтобы при необходимости сделать доступным путь к тестовому источнику из исходного пути JMH. Если все настроено правильно, один
$\> gradle jmh
будет достаточно для запуска задачи jmh gradle. Результаты можно найти в выходном каталоге build / reports / jmh. Будьте терпеливы, JMH по умолчанию выполняет много итераций и прогревов JVM, что может привести к неожиданной продолжительности. Когда закончите, результат может выглядеть так:
Benchmark Mode Samples Score Error Units n.c.MTIB.benchmarkIntersect thrpt 200 72503070.056 ± 239709.512 ops/s
Краткое примечание к рисункам: режим JMH по умолчанию — «пропускная способность», что означает, что JMH будет вызывать ваш метод эталонного теста как можно чаще в течение одной секунды на каждую итерацию. JMH выполняет 20 независимых итераций в 10 разных форках JVM, в результате чего получается 200 выборок, которые используются для вычисления среднего значения и средней ошибки. Наконец, результатом нашего теста является то, что JVM достигает в среднем 72 503 070,056 выполнений пересечения треугольников в секунду. Можем ли мы сделать лучше? ?
5.) Подсказка к исходному коду и дальнейшие шаги
Здесь вы можете найти ветку в моем проекте github, которая демонстрирует работу этой статьи блога: Chroma @ github, Branch: crolabefra_starting_point . Не стесняйтесь оформить заказ и настроить его. В настоящее время вам также необходимо клонировать, собрать и опубликовать проект comSysto jmh-gradle-plugin , поскольку фиксированная версия еще не извлечена из основного проекта.
Если вы хотите больше узнать о темах производительности или микробенчмарках JVM, присоединяйтесь к сессиям Дэниела на JavaLand в Кельне в марте!
В этой статье блога я дал общее представление о том, чего я хотел бы достичь в ближайшие недели, — подход автоматизированного межязыкового сравнительного анализа, основанный на gradle и git. В настоящее время целевыми языками являются Java и C ++, что будет подчеркнуто в JMH и Hayai. Сегодня я дал вам базовое введение в разделы о том, как объединить gradle, jmh и java-код для запуска в одной цепочке инструментов. Следующая статья будет в основном посвящена части C ++ / Hayai и тому, как легко это интегрировать в сборку gradle. Вы будете удивлены :).
Спасибо Дэниелу Миттердорферу, Максу Рабе и Самеру Аль-Хунати за обзор!
Пока! Ура!