Apache Spark prendra en charge le contrôle et la planification des ressources au niveau de l'étape

Apache Spark prendra en charge le contrôle et la planification des ressources au niveau de l'étape

Mémoire du passé Big Data Mémoire du passé Big Data

Contexte

Ceux qui connaissent Spark savent que lorsqu'un travail Spark est démarré, nous devons spécifier le nombre d'exectueurs et des informations telles que la mémoire et le processeur. Toutefois, lorsqu'un travail Spark est en cours d'exécution, il peut contenir plusieurs étapes. Ces différentes étapes peuvent nécessiter des ressources différentes. En raison de la conception actuelle de Spark, nous ne pouvons pas définir de ressources affinées pour chaque étape. De plus, il est difficile, même pour un ingénieur senior, d'estimer avec précision une configuration plus appropriée, de sorte que les paramètres définis au début du travail soient adaptés à chaque étape de Spark.

Considérons ce scénario: Nous avons un travail Spark, qui a un total de deux étapes. La première étape effectue principalement des opérations ETL de base sur les données que nous entrons. Dans cette étape, un grand nombre de tâches sont généralement démarrées, mais chaque tâche ne nécessite qu'une petite quantité de mémoire et un petit nombre de cœurs (par exemple 1 cœur). Une fois la première étape traitée, nous utilisons le résultat du traitement ETL comme entrée de l'algorithme ML. Cette étape ne nécessite que quelques tâches, mais chaque tâche nécessite beaucoup de mémoire, de GPU et de CPU.

Vous devriez souvent rencontrer des scénarios commerciaux comme celui ci-dessus. Nous devons définir différentes ressources pour différentes étapes. Cependant, le Spark actuel ne prend pas en charge cette configuration de ressources à granularité fine, nous devons donc définir un grand nombre de ressources au démarrage du travail, ce qui peut entraîner un gaspillage de ressources, en particulier dans le contexte de l'apprentissage automatique.

Cependant, c'est une bonne nouvelle que Thomas Graves, ingénieur logiciel système en chef de NVIDIA, a donné à la communauté un PROBLÈME, qui est SPIP: Configuration et planification des ressources au niveau du support Stage, qui vise à permettre à Spark de prendre en charge la configuration et la planification des ressources au niveau Stage. Vous pouvez également dire à partir du nom qu'il s'agit d'un SPIP (abréviation de Spark Project Improvement Proposals), SPIP est principalement destiné à marquer des changements majeurs orientés utilisateur ou inter-domaines, plutôt que de petites améliorations incrémentielles. Par conséquent, on peut voir que cette fonction a considérablement modifié Spark, ce qui aura un impact relativement important sur les utilisateurs.

L'auteur a envoyé un e-mail à la communauté après avoir mentionné ce SPIP, expliquant le but de ce SPIP, les problèmes qu'il résout, etc., puis a laissé tout le monde voter pour décider si ce SPIP doit être développé. La bonne nouvelle est qu'il y a eu un tour de scrutin par la communauté, avec 6 voix pour et 1 voix contre. Cela signifie que le SPIP est passé et entrera dans l'état de développement.

Apache Spark prendra en charge le contrôle et la planification des ressources au niveau de l'étape

Si vous souhaitez en savoir plus sur les articles Spark, Hadoop ou Hbase à temps, veuillez suivre le compte public WeChat: iteblog_hadoop

conception

Beaucoup à l'avant, voyons comment ce schéma est conçu. Afin de réaliser cette fonction, de nouvelles API doivent être ajoutées à la classe RDD existante pour spécifier les ressources nécessaires pour ce calcul RDD, comme l'ajout des deux méthodes suivantes:


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

La méthode withResources ci-dessus est principalement utilisée pour définir le resourceProfile du RDD actuel et renvoyer l'instance RDD actuelle. Les ressources spécifiées dans ResourceProfile incluent le processeur, la mémoire et les ressources supplémentaires (GPU / FPGA / etc). Nous pouvons également l'utiliser pour réaliser d'autres fonctions, telles que la limitation du nombre de tâches pour chaque étape et la spécification de certains paramètres pour le shuffle. Cependant, afin de simplifier la conception et la mise en œuvre, envisagez actuellement de ne prendre en charge que les ressources actuellement prises en charge par Spark. Pour Task, vous pouvez définir le processeur et des ressources supplémentaires (GPU / FPGA / etc.); pour Executor, vous pouvez définir le processeur, la mémoire et des ressources supplémentaires (GPU / FPGA / Wait). Les ressources de l'exécuteur incluent le processeur, la mémoire et des ressources supplémentaires (GPU, FPGA, etc.). En ajoutant la méthode ci-dessus à la classe RDD existante, tout le RDD évolué hérite des ressources de paramétrage du support RDD, bien sûr, y compris le RDD généré par le fichier d'entrée.

Lors de la programmation, l'utilisateur peut définir le ResourceProfile via la méthode withResources, bien sûr, il n'est certainement pas possible de définir des ressources illimitées. Les ressources requises par l'exécuteur et la tâche peuvent être définies en même temps via ResourceProfile.require. L'interface spécifique est la suivante:


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 raison pour laquelle ResourceProfile est utilisé pour encapsuler ExecutorResourceRequest ou TaskResourceRequest est qu'il peut être facilement implémenté si nous devons ajouter de nouvelles fonctionnalités ultérieurement. Par exemple, nous pouvons ajouter la méthode ResourceProfile.prefer à ResourceProfile pour réaliser que le programme exécutera le travail si suffisamment de ressources sont demandées, et le travail échouera si des ressources suffisantes ne sont pas demandées.

Bien entendu, la réalisation de cette fonction doit s'appuyer sur le mécanisme d'allocation dynamique de Spark. Si l'utilisateur n'active pas l'allocation dynamique (spark.dynamicAllocation.enabled = false) ou si l'utilisateur ne définit pas le ResourceProfile pour le RDD, il s'exécutera selon le mécanisme d'application de ressource existant, sinon le nouveau mécanisme sera utilisé.

Étant donné que chaque RDD peut spécifier ResourceProfile et que DAGScheduler peut calculer la conversion de plusieurs RDD en une seule étape, Spark doit résoudre les conflits d'application de ressources de plusieurs RDD au même stade. Bien sûr, certains RDD auront également des situations croisées, telles que reductionByKey, donc pour cette situation, Spark doit appliquer les paramètres ResourceProfile à ces deux étapes.

comment utiliser

Ensuite, si le RDD ajoute la méthode ci-dessus, nous pouvons définir l'utilisation des ressources de chaque tâche comme suit:


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()

Le ResourceProfile ci-dessus spécifie que Executor nécessite 2 Go de mémoire, 2 cœurs et un GPU; la tâche nécessite un GPU.

Pour résumer

Cet article ne présente que l'implémentation simple de cette fonction. De nombreux problèmes doivent être pris en compte dans la conception et le développement proprement dits. Pour plus de détails, reportez-vous à SPARK-27495. Pour les documents de conception correspondants, reportez-vous aux annexes SPIP de planification au niveau des étapes. API / Conception. Comme il s'agit d'une fonctionnalité relativement importante, la mise en œuvre peut prendre plusieurs mois. Je pense qu'avec cette fonction, nous utiliserons les ressources du cluster de manière plus raisonnable.

Je suppose que tu aimes

Origine blog.51cto.com/15127589/2677059
conseillé
Classement