Роль Tungsten в Apache Spark

Автор Категория ,
Роль Tungsten в Apache Spark

Что такое Tungsten, зачем он нужен в Apache Spark и как этот проект устраняет узкие места вычислительного движка, чтобы повысить его производительность и эффективность утилизации ресурсов за счет приближения JVM к bare metal. Рассматриваем самые важные для разработчика распределенных приложений особенности и разбираемся, при чем здесь вольфрам и почему с ним искра сияет ярче.

Что такое Tungsten и зачем он Apache Spark

Рабочие нагрузки Spark часто замедляются из-за узких мест в памяти или ЦП, а не из-за операций ввода-вывода или сетевого взаимодействия. Поэтому в 2015 году стартовал проект Tungsten, направленный на повышение производительности Spark-приложений за счет повышения эффективности процессора и снижения нагрузки на память. Tungsten включает целый набор инициатив по внесению изменений в механизм выполнения Apache Spark, чтобы приблизить его производительность к пределам современного оборудования. В Tungsten выделяют следующие инициативы:

  • управление памятью и двоичная обработка – использование семантики приложения для явного управления памятью и устранения накладных расходов на объектную модель JVM и сборку мусора;
  • вычисления с учетом кэша – построение алгоритмов и структур данных с использованием иерархии памяти, т.е. локальность кэша физических машин на разных уровнях (L1, L2, L3);
  • кодогенерация – поэтапное создание кода средствами современных компиляторов и процессоров;
  • отсутствие диспетчеризации виртуальных функций – уменьшает количество вызовов ЦП, которые могут повлиять на производительность;
  • промежуточные данные в памяти и регистрах ЦП – на порядок ускоряет вычисления за счет снижения количества тактов для получения данных из регистров процессора, а не из памяти;
  • развертывание циклов и SIMD с помощью современных компиляторов и возможностей ЦП для эффективной компиляции и выполнения простых циклов for вместо сложных графов вызовов функций.

Благодаря использованию Tungsten такая структура данных Apache Spark как DataFrame работает быстрее RDD, т.к. не требуется использовать сериализацию Java для кодирования данных – механизм сам явно управляет памятью и динамически генерирует байт-код для оценки выражений. А DataSet API обрабатывает преобразование между объектами JVM в табличное представление c использованием безопасного двоичного формата Tungsten, чтобы выполнить операцию с сериализованными данными, эффективно используя память, т.к. предоставляется доступ к индивидуальному атрибуту по требованию без десериализации всего объекта. Подробнее про структуры данных Apache Spark мы писали здесь.

Tungsten учитывает мощные характеристики современного оборудования: высокую пропускную способность ввода-вывода порядка 10 Гбит/с на SSD или RAID-массивах, оптимизируя IO-операции Spark за счет ограничения перемещения данных путем сокращения ненужного ввода и эффективных форматов данных для больших файлов типа Parquet.

Проект делает фокус на семантике и схеме отдельного Spark-приложения, чтобы выгодно использовать все его специфические особенности вместо погони за общими решениями. Общность влечет за собой огромные затраты по сравнению со специфичностью. Tungsten стремится улучшить выполнение Spark-приложений за счет оптимизации заданий, эффективно утилизируя ЦП и память вместо сетевого и дискового ввода-вывода, которые сегодня и так являются достаточно быстрыми. Tungsten фокусируется на аппаратной архитектуре платформы, на которой работает Spark, включая JVM, LLVM, GPU, NVRAM и пр., учитывая негативное влияние сериализации и хэширования на процессорную нагрузку. Как это реализовано, рассмотрим далее.

Блеск и нищета JVM в распределенных приложениях и при чем здесь вольфрам

Сериализация   и десериализация данных являются основой выполнения любых вычислений. Сериализация   — это процесс преобразования объекта в последовательность байтов, которая может быть сохранена на диске или в базе данных или может быть отправлена в виде потока. Обратный процесс создания объекта из последовательности байтов называется десериализацией, которая часто случается в Spark при выполнении shuffle-операций, например, при соединении таблиц с использованием JOIN в SQL-запросах. Хеширование используется для индексации и извлечения информации из базы данных, ускоряя процесс поиска за счет применения короткого хэш-ключа вместо исходного значения элемента данных. Все эти операции существенно увеличивают нагрузку на ЦП.

Tungsten реализует собственный формат сериализации, называемого небезопасной строкой (UnsafeRow). Он быстрее и компактнее Kryo и сериализация Java. Этот формат называется небезопасным, поскольку он представляет собой изменяемую внутреннюю необработанную память, позволяя явно управлять памятью без необходимости в объектной модели JVM. Он вводит новое представление Sun.misc.Unsafe для объектов. Объекты JVM известны своими накладными расходами на пространство и затратами на сборку мусора. Небезопасные объекты занимают меньше места и снижают нагрузку на ЦП, избегая сборки мусора вне кучи.

Например, строка abcd занимает всего 4 байта с использованием собственной кодировки UTF-8, а Java-объект с этими данными будет весить уже 48 байтов, включая заголовок объекта, хэш-код, служебные данные и символы. Формат UnsafeRow занимает гораздо меньше места и не зависит от JVM, позволяя Spark-приложению размещать объекты в памяти вне кучи благодаря собственному управлению двоичной памятью. API Sun.misc.Unsafe используется для создания структур данных как в куче, так и вне кучи, предоставляя прямой доступ к памяти, чтобы обойти JVM и избежать сборки мусора. С помощью метода ручного управления памятью структуры данных определяются так, что доступ к памяти становится последовательным, снижая накладные расходы на пространство и упрощая процесс сканирования за счет уменьшения количества неправильных моделей доступа к памяти.

Поскольку наборы данных имеют известную схему, Tungsten правильно и более компактно и эффективно размещает объекты самостоятельно. Это дает преимущества, аналогичные использованию расширений, написанных на низкоуровневых и поддерживающих аппаратное обеспечение языках типа C или ассемблер. Применение этих плюсов видно сразу, когда данные уже сериализованы, что дополнительно уменьшает или полностью исключает сериализацию между представлением объекта JVM и внутренним представлением Spark.

Tungsten ускоряет SQL-запросы, применяя более быстрое выполнение сортировки и хеширования для операций агрегирования, объединения и перемешивания. В частности, Spark может выполнять некоторые SQL-операции как агрегирование сериализованной формы данных на необработанных байтах без дополнительной интерпретации. Также это снижает время ожидания выборки данных из памяти благодаря механизму кэширования. Shuffle-операции, которые считаются самыми медленными, оптимизируются благодаря лучшей пропускной способности UnsafeRow.

Наконец, полная поэтапная генерация кода Java повышает производительность выполнения запроса за счет свертывания дерева запросов в единую оптимизированную функцию, которая устраняет вызовы виртуальных функций и использует регистры ЦП для промежуточных данных. Полноэтапная кодогенерация напрямую генерирует байт-код, который будет оцениваться для получения результата. Напомним, JavaBytecode — это скомпилированный формат программ Java. После преобразования Java-программы в Javabytecode ее можно передать по сети и выполнить в JVM. JavaByteCode не зависит от платформы, поскольку JVM преобразует байт-код для понимания базовым оборудованием. Благодаря использованию очень быстрого и легковесного компилятора Janino, Tungsten очень быстро генерирует JavaBytecode.

В Apache Spark чтобы получить результат обработки данных, например, SQL-запроса, оценивается логика выражения. Общая оценка логики на JVM очень затратна из-за вызовов виртуальных функций, создания и упаковки объектов, потребления памяти и пр. Tungsten использует внутреннюю семантику приложения для создания пользовательского байт-кода и автоматически оптимизирует его, позволяя пропустить интерпретируемую оценку операторов. Это помогает устранить вызовы виртуальных функций и пройти по дереву узлов выражений для каждой отдельной строки данных. Поскольку Tungsten генерирует код во время компиляции и генерирует байт-код JVM для доступа к управляемым структурам памяти, Spark-приложение может работать очень быстро. Таким образом, проект Tungsten значительно повышает эффективность Spark-приложений, позволяя разработчику создавать распределенные программы еще быстрее и надежнее.

В заключение отметим, что на русский язык слово Tungsten переводится как Вольфрам – самый тугоплавкий редкоземельный металл, который активно используется в лампах накаливания и других осветительных приборах, что близко к переводному значению Spark (искра). Вольфрамовые сплавы отличаются жаропрочностью, кислотостойкостью, твердостью и устойчивостью к истиранию, поэтому применяются для изготовления хирургических инструментов, деталей самолетов и ракет, изделий военной промышленности и работы с радиоактивными веществами. Вольфрам торгуется на бирже ценных металлов и скупается пунктами приема вторсырья.

Больше практических деталей по применению Apache Spark для задач дата-инженерии, разработки распределенных приложений и аналитики больших данных вы узнаете на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.

Источники

  1. https://databricks.com/glossary/tungsten
  2. https://jaceklaskowski.gitbooks.io/mastering-spark-sql/content/spark-sql-tungsten.html
  3. https://teepika-r-m.medium.com/spark-shines-brighter-with-project-tungsten-7317c630e708