Machine Learning

Параметрическая оптимизация задач Spark

Время прочтения: 3 мин.

Настройка параметров выполнения задачи Spark является достаточно сложной и трудоёмкой, ведь помимо того, что необходимо вычислить оптимальный набор параметров в зависимости от возможностей кластера и объема данных, необходимо учитывать и то, что на кластере работает несколько человек. Одним из возможных решений данной проблемы является оптимизация работы задачи Spark при помощи задания параметров, характеризующих процесс обработки и хранения данных.

Перед тем, как рассмотрим процесс задания параметров, опишем то, как Spark производит обработку. Spark использует архитектуру ведущий/ведомый (master/slave) с одним центральным координатором и множеством распределенных рабочих узлов. Центральный координатор называется драйвером (driver). Драйвер взаимодействует с (возможно) большим числом рабочих узлов, которые называют исполнителями (executors). Драйвер и исполнители выполняются в отдельных и независимых друг от друга процессах Jаvа и составляют приложение Spark. В связи с чем основными параметрами, позволяющими настроить задачу Spark являются параметры настройки driver’а и executor’а.

Для настройки данных параметров Apache Spark предоставляет следующие варианты:

  • Динамическое использование ресурсов кластера
  • Использование параметров, установленных по умолчанию
  • Задача параметров работы задачи Spark вручную

Вышеперечисленные варианты мы рассмотрим на примере фреймворка pyspark, позволяющего запускать задачи, используя понятный и удобный интерфейс языка Python.

В фреймворке pyspark параметры задаются при помощи класса SparkConf. В общем случае для установки параметра задачи используется метод set объекта SparkConf.

Рассмотрим каждый вариант отдельно.

Настройки Spark по умолчанию предполагают исключительно гарантированное исполнение задания при очень маленьких значениях кластера, что в свою очередь позволяет уменьшить расход ресурсов кластера, однако неприменимо для обработки больших объемов данных в силу большого времени обработки и большой вероятности завершения задачи с ошибкой нехватки памяти. Для использования данного способа достаточно просто создать задачу без задания каких-либо параметров.

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

Пример:

conf = SparkConf().setAppName('SparkContextExample')\ 
.setMaster("yarn-client")\
.set('spark.dynamicAllocation.enabled', 'true')\#включен режим        
                                           динамических параметров
.set('spark.dynamicAllocation.executorIdleTimeout', '90')\ #время    
                        простоя, по умолчанию infinity = 60 секунд
.set(spark.dynamicAllocation.initialExecutors', '9')\ # начальное                             
                                          количество исполнителей

Наиболее оптимальным решением для работы на кластерах общего доступа является установка параметров вручную. Под задачей параметров в данном случае понимается определение размеров выделяемой памяти и количества executor’ов и driver’ов.

Пример:

conf = SparkConf().setAppName('SparkContextExample')\
    .setMaster("yarn-client")\
    .set('spark.dynamicAllocation.enabled', 'false')\ # отключен режим 
                                             динамических параметров
.set('spark.local.dir', 'sparktmp')\ # директория хранения времен-
                                       ных файлов
.set('spark.executor.memory','5g')\ # объем памяти для  каждого 
                                       исполнителя
.set('spark.driver.maxResultSize','5g')\ # объем памяти для ре-
   зультата по умолчанию 1g, при «0» проигнорирует ограничение
.set('spark.executor.instances', '1')\
.set('spark.executor.cores', '8')\ # число ядер приложения 
.set('spark.port.maxRetries', '150')\ 

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

По итогу мы рассмотрели различные варианты задания параметров задачи Spark, описали их плюсы и минусы и привели простые примеры использования в Python. Сама я использую вариант статического задания параметров ввиду наибольшей производительности при работе на кластере общего доступа.

Надеюсь статья для Вас будет полезна. Буду рада Вашим комментариям и лайкам.

Советуем почитать