JRuby 10 ускоряет время запуска
JRuby 10 ускоряет время запускаВ последней версии реализации Ruby для JVM также обновлена поддержка версий Java и совместимость с языком Ruby.

Если что-то и можно назвать навязчивой идеей разработчиков и девелоперов, так это способы повышения производительности приложений. В конечном счете, повышение производительности ведет к снижению затрат (за счет уменьшения использования ресурсов) или увеличению прибыли (за счет улучшения качества обслуживания, что привлекает больше клиентов).
Конечно, существует много, много способов повысить производительность, но один из самых очевидных - это «разделяй и властвуй». Допустим, вы оптимизировали свои алгоритмы и обновили аппаратное обеспечение, но все равно не добились нужной производительности. Решение может лежать глубже в стеке - на уровне процессора, где векторные операции могут обрабатывать несколько точек данных одновременно. Возможность выполнять несколько действий одновременно часто (но не всегда) сокращает время, необходимое для выполнения задачи.
Термины concurrent и parallel часто используются как взаимозаменяемые при обсуждении повышения производительности, поэтому стоит пояснить разницу между ними.
Считается, что две задачи выполняются параллельно, если вторая задача начинает выполняться после начала первой и до ее завершения. При этом нет требования, чтобы в любой момент времени обе задачи выполнялись одновременно. Эта техника используется уже давно, особенно в операционных системах. До появления многоядерных и многопроцессорных машин единый блок выполнения должен был быть общим для всех процессов. Чтобы создать иллюзию одновременного выполнения процессов, несколько из них запускались одновременно и делили между собой блок исполнения, быстро меняясь местами (это называется операционной системой с разделением времени).
Чтобы задачи выполнялись параллельно, они должны выполняться одновременно, а не просто перекрывать друг друга.
Закон Мура позволил втиснуть больше транзисторов в то же пространство. Для повышения производительности теперь существуют многоядерные процессоры, которые позволяют параллельным процессам выполняться параллельно. На более низком уровне процессор также содержит аппаратное обеспечение для параллельного выполнения определенных типов задач, которые называются векторными операциями.
Допустим, у вас есть набор чисел, которые вы хотите обработать, применив ко всем из них одну и ту же операцию. Например, все значения нужно увеличить на единицу. В Java типичным способом решения этой задачи было бы хранение всех значений в массиве, создание цикла для итерации по массиву и прибавление единицы к каждому значению в теле цикла. Когда вы запускаете Java-приложение, часто используемый код компилируется из байткодов набора инструкций виртуальной машины в нативные инструкции. JVM делает это с помощью компилятора just-in-time (JIT).
JIT достаточно умен, чтобы понять базовую архитектуру процессора, и оптимизирует цикл для использования векторных операций (это называется автовекторизацией).
Векторная обработка использует очень широкие регистры для хранения более чем одного значения. Например, инструкции AVX-2 Intel используют регистры шириной 256 бит. Целые числа Java хранятся в 32 битах, поэтому каждый векторный регистр может содержать восемь целых чисел Java (ints). JIT сгенерирует код для загрузки значений из массива группами по восемь. Затем код может использовать одну из инструкций AVX-2, чтобы указать процессору прибавить единицу к каждому из этих восьми значений независимо (и справиться с переполнением, чтобы не повредить соседние значения). Это настоящая параллельная обработка, поскольку все значения обрабатываются за один цикл машинных инструкций. В итоге обработка массива занимает лишь восьмую часть времени, чем без автовекторизации.
Все это звучит замечательно и означает, что разработчики Java могут писать код так, как им хочется, и позволить JIT-компилятору оптимизировать его во время выполнения.
К сожалению, это не вся история...
Автовекторизация хорошо работает в простых ситуациях, подобных только что описанной. Однако если усложнить цикл еще немного, то это быстро перечеркнет возможности JIT-компилятора по повышению производительности. Если мы добавим в тело цикла простое условие, проверяющее, следует ли увеличивать значение, JIT вернется к использованию последовательного подхода и не будет использовать векторные операции.
Одно из решений этой проблемы заключается в том, чтобы позволить разработчикам Java писать код, в котором явно указано, как должны использоваться векторные операции. JIT-компилятор может транслировать его напрямую, без необходимости автовекторизации. Именно для этого предназначен Java Vector API, представленный в качестве инкубаторного модуля в JDK 16. Интересно, что этот API является рекордсменом по продолжительности инкубации в OpenJDK, поскольку с выходом JDK 24 он будет уже в девятой итерации. И это не потому, что он находится в состоянии вечного движения, а потому, что он является частью более крупного проекта, Valhalla. Когда Valhalla, которая добавит типы значений в Java, будет представлена в OpenJDK, Vector API станет окончательным.
Vector API предоставляет обширный набор функциональных возможностей. Во-первых, есть классы для представления каждого примитивного числового типа Java в виде вектора. Векторный вид объединяет эти примитивные векторные формы с регистрами, специфичными для процессора, так что понять, как заполнить данные из массива, очень просто. Векторами можно манипулировать с помощью богатого набора операторов. Их 103, и они покрывают все, что вам может понадобиться в реальности.
Vector API предоставляет разработчикам все необходимое для того, чтобы JIT-компилятор мог генерировать высокооптимизированный код для операций с интенсивным использованием чисел. Так как большинство операций связано с манипулированием числами (строки - это, в конце концов, просто последовательности символов, закодированные в числа), это может привести к значительному увеличению производительности.
В идеале Vector API не нужен; автовекторизация справится с этим прозрачно. Хорошей новостью является то, что высокопроизводительные JVM включают в себя другой JIT-компилятор. JIT-компилятор Falcon (который пришел на смену OpenJDK C2 JIT) основан на другом проекте с открытым исходным кодом, LLVM. Это позволяет распознавать значительно больше случаев использования векторов, что приводит к улучшению производительности приложений без необходимости изменения кода.
JIT-компилятор Falcon доступен в Azul Platform Prime JDK, который является бесплатным для разработки и оценки. Как JDK, прошедший проверку TCK (Technology Compatibility Kit), Azul Platform Prime является универсальной заменой. Почему бы не опробовать его в своих приложениях?
JRuby 10 ускоряет время запускаВ последней версии реализации Ruby для JVM также обновлена поддержка версий Java и совместимость с языком Ruby.
6 языков, которые можно развернуть на WebAssembly прямо сейчас Rust, Go, JavaScript, C/C++ и Python могут работать на Wasm. Вот что вам нужно знать.
Недавно компания Microsoft решила переписать цепочку инструментов TypeScript, используя язык Go.