Apache Spark admitirá el control y la programación de recursos a nivel de etapa

Apache Spark admitirá el control y la programación de recursos a nivel de etapa

Big Data de memoria pasada Big Data de memoria pasada

antecedentes

Aquellos que están familiarizados con Spark saben que cuando se inicia un trabajo de Spark, necesitamos especificar la cantidad de Ejecutores e información como la memoria y la CPU. Sin embargo, cuando un trabajo de Spark se está ejecutando, puede contener muchas etapas. Estas diferentes etapas pueden requerir diferentes recursos. Debido al diseño actual de Spark, no podemos establecer recursos detallados para cada etapa. Además, es difícil incluso para un ingeniero superior estimar con precisión una configuración más apropiada, de modo que los parámetros establecidos cuando se inicia el trabajo sean adecuados para cada etapa de Spark.

Consideremos este escenario: tenemos un trabajo de Spark, que tiene un total de dos etapas. La primera etapa realiza principalmente operaciones ETL básicas en los datos que ingresamos. En esta etapa, generalmente se inicia una gran cantidad de tareas, pero cada tarea solo requiere una pequeña cantidad de memoria y una pequeña cantidad de núcleos (como 1 núcleo). Después de que se procesa la primera etapa, usamos el resultado del procesamiento ETL como la entrada del algoritmo ML. Esta etapa requiere solo unas pocas tareas, pero cada tarea requiere mucha memoria, GPU y CPU.

A menudo, debe encontrar escenarios comerciales como el anterior. Necesitamos establecer diferentes recursos para diferentes etapas. Sin embargo, el Spark actual no admite esta configuración de recursos detallada, por lo que tenemos que configurar una gran cantidad de recursos cuando se inicia el trabajo, lo que puede generar un desperdicio de recursos, especialmente en el contexto del aprendizaje automático.

Sin embargo, es una buena noticia que Thomas Graves, ingeniero jefe de software de sistemas de NVIDIA, le dio a la comunidad un PROBLEMA, que es SPIP: configuración y programación de recursos a nivel de etapa de soporte, que tiene como objetivo permitir que Spark admita la configuración y programación de recursos a nivel de etapa. También puede saber por el nombre que se trata de un SPIP (abreviatura de Spark Project Improvement Proposals), SPIP es principalmente para marcar cambios importantes orientados al usuario o entre dominios, en lugar de pequeñas mejoras incrementales. Por lo tanto, se puede ver que esta función ha modificado en gran medida a Spark, lo que tendrá un impacto relativamente grande en los usuarios.

El autor envió un correo electrónico a la comunidad después de mencionar este SPIP, explicando el propósito de este SPIP, los problemas que resuelve, etc., y luego dejar que todos voten para decidir si se debe desarrollar este SPIP. La buena noticia es que ha habido una ronda de votaciones por parte de la comunidad, con 6 votos a favor y 1 voto en contra. Significa que el SPIP ha pasado y entrará en el estado de desarrollo.

Apache Spark admitirá el control y la programación de recursos a nivel de etapa

Si desea obtener información sobre Spark, Hadoop o artículos relacionados con Hbase a tiempo, siga la cuenta pública de WeChat: iteblog_hadoop

diseño

Mucho en el frente, echemos un vistazo a cómo está diseñado este esquema. Para lograr esta función, es necesario agregar algunas API nuevas a la clase RDD existente para especificar los recursos necesarios para este cálculo RDD, como agregar los dos métodos siguientes:


def withResources(resources: ResourceProfile): this.type
def getResourceProfile(): Option[ResourceProfile]

El método withResources anterior se utiliza principalmente para establecer el resourceProfile del RDD actual y devolver la instancia RDD actual. Los recursos especificados en ResourceProfile incluyen CPU, memoria y recursos adicionales (GPU / FPGA / etc.). También podemos usarlo para lograr otras funciones, como limitar el número de tareas para cada etapa y especificar algunos parámetros para la reproducción aleatoria. Sin embargo, para simplificar el diseño y la implementación, actualmente solo considere admitir los recursos que actualmente admite Spark. Para Task, puede configurar la CPU y recursos adicionales (GPU / FPGA / etc.); para Executor, puede configurar la CPU, la memoria y recursos adicionales (GPU / FPGA / Wait). Los recursos del ejecutor incluyen CPU, memoria y recursos adicionales (GPU, FPGA, etc.). Al agregar el método anterior a la clase RDD existente, esto hace que todos los RDD evolucionados se hereden de los recursos de configuración de soporte RDD, por supuesto, incluido el RDD generado por el archivo de entrada.

Al programar, el usuario puede configurar ResourceProfile a través del método withResources, por supuesto, ciertamente no es posible configurar recursos ilimitados. Los recursos requeridos por el Ejecutor y la tarea se pueden configurar al mismo tiempo a través de ResourceProfile.require. La interfaz específica es la siguiente:


def require(request: TaskResourceRequest): this.type
def require(request: ExecutorResourceRequest): this.type

class ExecutorResourceRequest(
 val resourceName: String,
 val amount: Int, // potentially make this handle fractional resources
 val units: String, // units required for memory resources
 val discoveryScript: Option[String] = None,
 val vendor: Option[String] = None)

class TaskResourceRequest(
 val resourceName: String,
 val amount: Int) // potentially make this handle fractional resources

La razón por la que ResourceProfile se usa para envolver ExecutorResourceRequest o TaskResourceRequest es que se puede implementar fácilmente si necesitamos agregar nuevas funciones más adelante. Por ejemplo, podemos agregar el método ResourceProfile.prefer a ResourceProfile para darnos cuenta de que el programa ejecutará el trabajo si se solicitan suficientes recursos, y el trabajo fallará si no se solicitan suficientes recursos.

Por supuesto, la realización de esta función debe depender del mecanismo de asignación dinámica de Spark. Si el usuario no habilita la asignación dinámica (spark.dynamicAllocation.enabled = false) o el usuario no establece el ResourceProfile para el RDD, entonces se ejecutará de acuerdo con el mecanismo de aplicación de recursos existente; de ​​lo contrario, se utilizará el nuevo mecanismo.

Debido a que cada RDD puede especificar ResourceProfile y DAGScheduler puede calcular la conversión de varios RDD en una etapa, Spark necesita resolver los conflictos de aplicación de recursos de varios RDD en la misma etapa. Por supuesto, algunos RDD también tendrán situaciones de etapas cruzadas, como reduceByKey, por lo que, para esta situación, Spark necesita aplicar la configuración de ResourceProfile a estas dos etapas.

cómo utilizar

Luego, si el RDD agrega el método anterior, podemos establecer el uso de recursos de cada tarea de la siguiente manera:


val rp = new ResourceProfile()
rp.require(new ExecutorResourceRequest("memory", 2048))
rp.require(new ExecutorResourceRequest("cores", 2))
rp.require(new ExecutorResourceRequest("gpu", 1, Some("/opt/gpuScripts/getGpus")))
rp.require(new TaskResourceRequest("gpu", 1))

val rdd = sc.makeRDD(1 to 10, 5).mapPartitions { it =>
  val context = TaskContext.get()
  context.resources().get("gpu").get.addresses.iterator
}.withResources(rp)

val gpus = rdd.collect()

El ResourceProfile anterior especifica que Executor requiere 2 GB de memoria, 2 núcleos y una GPU; la tarea requiere una GPU.

para resumir

Este artículo solo presenta la implementación simple de esta función. Hay muchas cuestiones que deben tenerse en cuenta en el diseño y desarrollo reales. Para obtener más detalles, consulte SPARK-27495. Para obtener los documentos de diseño correspondientes, consulte los Apéndices de SPIP de programación de nivel de etapa API / Diseño. Debido a que esta es una característica relativamente grande, puede llevar varios meses implementarla. Creo que con esta función utilizaremos los recursos del clúster de manera más razonable.

Supongo que te gusta

Origin blog.51cto.com/15127589/2677059
Recomendado
Clasificación