Partager via


Exécuter des tâches simultanément pour optimiser l’utilisation des nœuds de calcul Batch

Vous pouvez optimiser l’utilisation des ressources sur un plus petit nombre de nœuds de calcul dans votre pool en exécutant plusieurs tâches simultanément sur chaque nœud.

Bien que certains scénarios fonctionnent mieux avec toutes les ressources d’un nœud dédiées à une seule tâche, certaines charges de travail peuvent voir des temps de travail plus courts et des coûts inférieurs lorsque plusieurs tâches partagent ces ressources. Tenez compte des scénarios suivants :

  • Réduisez le transfert de données pour les tâches capables de partager des données. Vous pouvez réduire considérablement les frais de transfert de données en copiant des données partagées vers un plus petit nombre de nœuds, puis en exécutant des tâches en parallèle sur chaque nœud. Cette stratégie s’applique particulièrement si les données à copier sur chaque nœud doivent être transférées entre les régions géographiques.
  • Optimisez l’utilisation de la mémoire pour les tâches qui nécessitent une grande quantité de mémoire, mais uniquement pendant de courtes périodes de temps et à des moments variables pendant l’exécution. Vous pouvez utiliser moins de nœuds de calcul, mais plus volumineux, avec plus de mémoire pour gérer efficacement ces pics. Ces nœuds ont plusieurs tâches s’exécutant en parallèle sur chaque nœud, mais chaque tâche peut tirer parti de la mémoire abondante des nœuds à différents moments.
  • Limiter les limites de nombre de nœuds lorsque la communication entre nœuds est requise dans un pool. Actuellement, les pools configurés pour la communication entre nœuds sont limités à 50 nœuds de calcul. Si chaque nœud d’un tel pool est en mesure d’exécuter des tâches en parallèle, un plus grand nombre de tâches peut être exécuté simultanément.
  • Répliquez un cluster de calcul local, par exemple lorsque vous déplacez un environnement de calcul vers Azure. Si votre solution locale actuelle exécute plusieurs tâches par nœud de calcul, vous pouvez augmenter le nombre maximal de tâches de nœud pour refléter plus étroitement cette configuration.

Exemple de scénario

Par exemple, imaginez une application de tâche avec des exigences de processeur et de mémoire, de sorte que les nœuds Standard_D1 sont suffisants. Toutefois, pour terminer le travail dans le temps nécessaire, 1 000 de ces nœuds sont nécessaires.

Au lieu d’utiliser Standard_D1 nœuds qui ont un cœur de processeur, vous pouvez utiliser Standard_D14 nœuds qui ont chacun 16 cœurs et activer l’exécution de tâches parallèles. Vous pouvez potentiellement utiliser 16 fois moins de nœuds au lieu de 1 000 nœuds, seulement 63 seraient requis. Si des fichiers d’application volumineux ou des données de référence sont requis pour chaque nœud, la durée et l’efficacité du travail sont améliorées, car les données sont copiées sur seulement 63 nœuds.

Activer l’exécution de tâches parallèles

Vous configurez des nœuds de calcul pour l’exécution de tâches parallèles au niveau du pool. Avec la bibliothèque Batch .NET, définissez la propriété CloudPool.TaskSlotsPerNode lorsque vous créez un pool. Si vous utilisez l’API REST Batch, définissez l’élément taskSlotsPerNode dans le corps de la requête lors de la création du pool.

Note

Vous pouvez définir l’élément et la taskSlotsPerNode propriété TaskSlotsPerNode uniquement au moment de la création du pool. Ils ne peuvent pas être modifiés une fois qu’un pool a déjà été créé.

Azure Batch vous permet de définir les emplacements de tâches par nœud jusqu’à (4x) le nombre de cœurs de nœud. Par exemple, si le pool est configuré avec des nœuds de taille « Grand » (quatre cœurs), alors taskSlotsPerNode peut être fixé à 16. Toutefois, quel que soit le nombre de cœurs dont dispose le nœud, vous ne pouvez pas avoir plus de 256 emplacements de tâche par nœud. Pour plus d’informations sur le nombre de cœurs pour chacune des tailles de nœud, consultez Tailles des services cloud (classique). Pour plus d’informations sur les limites de service, consultez Quotas et limites de service Batch.

Conseil / Astuce

Veillez à prendre en compte la taskSlotsPerNode valeur lorsque vous construisez une formule de mise à l’échelle automatique pour votre pool. Par exemple, une formule qui évalue $RunningTasks peut être considérablement affectée par une augmentation des tâches par nœud. Pour plus d’informations, consultez Créer une formule automatique pour mettre à l’échelle des nœuds de calcul dans un pool Batch.

Spécifier la distribution des tâches

Lors de l’activation des tâches simultanées, il est important de spécifier la façon dont vous souhaitez que les tâches soient distribuées entre les nœuds du pool.

En utilisant la propriété CloudPool.TaskSchedulingPolicy , vous pouvez spécifier que les tâches doivent être affectées uniformément sur tous les nœuds du pool (« propagation »). Vous pouvez également spécifier que le nombre de tâches que possible doit être affecté à chaque nœud avant que les tâches ne soient affectées à un autre nœud du pool (« emballage »).

Par exemple, considérez le pool de nœuds Standard_D14 (dans l’exemple précédent) configuré avec une valeur CloudPool.TaskSlotsPerNode de 16. Si CloudPool.TaskSchedulingPolicy est configuré avec un ComputeNodeFillType of Pack, il optimise l’utilisation de tous les 16 cœurs de chaque nœud et permet à un pool de mise à l’échelle automatique de supprimer les nœuds inutilisés (nœuds sans tâches affectées) du pool. La mise à l’échelle automatique réduit l’utilisation des ressources et peut économiser de l’argent.

Définir des emplacements variables par tâche

Une tâche peut être définie avec la propriété CloudTask.RequiredSlots , en spécifiant le nombre d’emplacements requis pour s’exécuter sur un nœud de calcul. La valeur par défaut est 1. Vous pouvez définir des emplacements de tâches variables si vos tâches ont des pondérations différentes associées à leur utilisation des ressources sur le nœud de calcul. Les emplacements de tâches variables permettent à chaque nœud de calcul d’avoir un nombre raisonnable de tâches en cours d’exécution simultanées sans surcharger les ressources système telles que l’UC ou la mémoire.

Par exemple, pour un pool avec la propriété taskSlotsPerNode = 8, vous pouvez soumettre des tâches intensives en UC nécessitant plusieurs cœurs avec requiredSlots = 8, tandis que d’autres tâches peuvent être définies via requiredSlots = 1. Lorsque cette charge de travail mixte est planifiée, les tâches nécessitant beaucoup d’UC s’exécutent exclusivement sur leurs nœuds de calcul, tandis que d’autres tâches peuvent s’exécuter simultanément (jusqu’à huit tâches en même temps) sur d’autres nœuds. La charge de travail mixte vous aide à équilibrer votre charge de travail entre les nœuds de calcul et à améliorer l’efficacité de l’utilisation des ressources.

Assurez-vous de ne pas spécifier qu'une tâche requiredSlots dépasse la capacité du pool taskSlotsPerNode, sinon la tâche ne s'exécutera jamais. Le service Batch ne valide actuellement pas ce conflit lorsque vous envoyez des tâches. Il n’effectue pas la validation de conflit, car le travail peut ne pas avoir de pool lié au moment de l’envoi, ou être remplacé par un pool différent par désactivation/réactivation.

Conseil / Astuce

Lorsque vous utilisez des emplacements de tâches variables, il est possible que les tâches volumineuses avec plus d’emplacements requis ne puissent pas être planifiées temporairement, car les emplacements insuffisants sont disponibles sur n’importe quel nœud de calcul, même s’il y a toujours des emplacements inactifs sur certains nœuds. Vous pouvez augmenter la priorité de travail pour ces tâches afin d’augmenter leurs chances de rivaliser pour les emplacements disponibles sur les nœuds.

Le service Batch émet taskScheduleFailEvent lorsqu’il ne parvient pas à planifier l’exécution d’une tâche et continue à réessayer jusqu’à ce que les emplacements requis soient disponibles. Vous pouvez écouter cet événement pour détecter les problèmes potentiels de planification des tâches et atténuer en conséquence.

Exemple de traitement par lot .NET

Les extraits de code d’API Batch .NET suivants montrent comment créer un pool avec plusieurs emplacements de tâche par nœud et comment envoyer une tâche avec des emplacements requis.

Créer un pool avec plusieurs emplacements de tâches par nœud

Cet extrait de code montre une demande de création d’un pool qui contient quatre nœuds, avec quatre emplacements de tâche autorisés par nœud. Il spécifie une stratégie de planification des tâches qui remplit chaque nœud avec des tâches avant d’affecter des tâches à un autre nœud du pool.

Pour plus d’informations sur l’ajout de pools à l’aide de l’API Batch .NET, consultez BatchClient.PoolOperations.CreatePool.

CloudPool pool =
    batchClient.PoolOperations.CreatePool(
        poolId: "mypool",
        targetDedicatedComputeNodes: 4
        virtualMachineSize: "standard_d1_v2",
        VirtualMachineConfiguration: new VirtualMachineConfiguration(
            imageReference: new ImageReference(
                                publisher: "MicrosoftWindowsServer",
                                offer: "WindowsServer",
                                sku: "2019-datacenter-core",
                                version: "latest"),
            nodeAgentSkuId: "batch.node.windows amd64");

pool.TaskSlotsPerNode = 4;
pool.TaskSchedulingPolicy = new TaskSchedulingPolicy(ComputeNodeFillType.Pack);
pool.Commit();

Créer une tâche avec les créneaux requis

Cet extrait de code crée une tâche avec une valeur non définie requiredSlots. Cette tâche s’exécute lorsqu’il existe suffisamment d’emplacements gratuits disponibles sur un nœud de calcul.

CloudTask task = new CloudTask(taskId, taskCommandLine)
{
    RequiredSlots = 2
};

Répertorier les nœuds de calcul avec le nombre de tâches en cours d'exécution et de créneaux disponibles

Cet extrait de code répertorie tous les nœuds de calcul du pool et imprime le nombre de tâches et d’emplacements de tâches en cours d’exécution par nœud.

ODATADetailLevel nodeDetail = new ODATADetailLevel(selectClause: "id,runningTasksCount,runningTaskSlotsCount");
IPagedEnumerable<ComputeNode> nodes = batchClient.PoolOperations.ListComputeNodes(poolId, nodeDetail);

await nodes.ForEachAsync(node =>
{
    Console.WriteLine(node.Id + " :");
    Console.WriteLine($"RunningTasks = {node.RunningTasksCount}, RunningTaskSlots = {node.RunningTaskSlotsCount}");

}).ConfigureAwait(continueOnCapturedContext: false);

Lister les nombres de tâches pour le travail

Cet extrait de code obtient le nombre de tâches pour le travail, qui comprend le nombre de tâches et le nombre d’emplacements de tâches par état de tâche.

TaskCountsResult result = await batchClient.JobOperations.GetJobTaskCountsAsync(jobId);

Console.WriteLine("\t\tActive\tRunning\tCompleted");
Console.WriteLine($"TaskCounts:\t{result.TaskCounts.Active}\t{result.TaskCounts.Running}\t{result.TaskCounts.Completed}");
Console.WriteLine($"TaskSlotCounts:\t{result.TaskSlotCounts.Active}\t{result.TaskSlotCounts.Running}\t{result.TaskSlotCounts.Completed}");

Exemple de Batch REST

Les extraits de code d’API REST Batch suivants montrent comment créer un pool avec plusieurs emplacements de tâche par nœud et comment envoyer une tâche avec des emplacements requis.

Créer un pool avec plusieurs emplacements de tâches par nœud

Cet extrait de code montre une demande de création d’un pool qui contient deux nœuds volumineux avec un maximum de quatre tâches par nœud.

Pour plus d’informations sur l’ajout de pools à l’aide de l’API REST, consultez Ajouter un pool à un compte.

{
  "odata.metadata":"https://myaccount.myregion.batch.azure.com/$metadata#pools/@Element",
  "id":"mypool",
  "vmSize":"large",
  "virtualMachineConfiguration": {
    "imageReference": {
      "publisher": "canonical",
      "offer": "ubuntuserver",
      "sku": "20.04-lts"
    },
    "nodeAgentSKUId": "batch.node.ubuntu 20.04"
  },
  "targetDedicatedComputeNodes":2,
  "taskSlotsPerNode":4,
  "enableInterNodeCommunication":true,
}

Créer une tâche avec les créneaux requis

Cet extrait de code affiche une demande d’ajout d’une tâche avec une valeur non définie requiredSlots. Cette tâche s’exécute uniquement lorsqu’il existe suffisamment d’emplacements gratuits disponibles sur le nœud de calcul.

{
  "id": "taskId",
  "commandLine": "bash -c 'echo hello'",
  "userIdentity": {
    "autoUser": {
      "scope": "task",
      "elevationLevel": "nonadmin"
    }
  },
  "requiredSLots": 2
}

Exemple de code sur GitHub

Le projet ParallelTasks sur GitHub illustre l’utilisation de la propriété CloudPool.TaskSlotsPerNode .

Cette application console C# utilise la bibliothèque Batch .NET pour créer un pool avec un ou plusieurs nœuds de calcul. Il exécute un nombre configurable de tâches sur ces nœuds pour simuler une charge variable. La sortie de l’application indique quels nœuds ont exécuté chaque tâche. L’application fournit également un résumé des paramètres et de la durée du travail.

L’exemple suivant montre la partie récapitulative de la sortie à partir de deux exécutions différentes de l’exemple d’application ParallelTasks. Les durées de travail indiquées ici n’incluent pas le temps de création du pool, car chaque travail a été soumis à un pool créé précédemment dont les nœuds de calcul étaient dans l’état inactif au moment de l’envoi.

La première exécution de l’exemple d’application montre qu’avec un nœud unique dans le pool et le paramètre par défaut d’une tâche par nœud, la durée du travail est supérieure à 30 minutes.

Nodes: 1
Node size: large
Task slots per node: 1
Max slots per task: 1
Tasks: 32
Duration: 00:30:01.4638023

La deuxième exécution de l’exemple montre une diminution significative de la durée du travail. Cette réduction est due au fait que le pool a été configuré avec quatre tâches par nœud, ce qui permet l’exécution de tâches parallèles dans près d’un quart de temps.

Nodes: 1
Node size: large
Task slots per node: 4
Max slots per task: 1
Tasks: 32
Duration: 00:08:48.2423500

Étapes suivantes