Статьи

Тестирование на нескольких языках Made Easy?

Существует вечная борьба между разными языками программирования. «Код в« 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 ++, которые реализуют те же алгоритмы. Мы хотим сравнить и сравнить производительность горячих частей кода. Мы также хотим отслеживать изменения во времени, когда исходный код проекта растет и изменяется. Мы будем:

  1. Сравнительный анализ кода Java с JMH как часть сборки gradle
  2. Сравнительный код C ++ с Hayai
  3. Интеграция бинарной компиляции c ++ с Gradle
  4. Интеграция исполнения бенчмарка Hayai с Gradle
  5. Объедините Java и C ++ проекты в одну межязыковую цепочку сборки
  6. Совокупные результаты JMH и Hayai дают третий сложный результат
  7. Разделить код тестирования из проектов в отдельный проект и выделенный SCM.
  8. Автоматически отправлять агрегированные результаты тестирования в хитро структурированный репозиторий 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. Вы будете удивлены :).

Спасибо Дэниелу Миттердорферу, Максу Рабе и Самеру Аль-Хунати за обзор!

Пока! Ура!