Compartir a través de


Ejecutar tareas simultáneamente para maximizar el uso de nodos de proceso de Batch

Puede maximizar el uso de recursos en un número menor de nodos de proceso del grupo ejecutando más de una tarea simultáneamente en cada nodo.

Aunque algunos escenarios funcionan mejor con todos los recursos de un nodo dedicados a una sola tarea, ciertas cargas de trabajo pueden ver tiempos de trabajo más cortos y reducir los costos cuando varias tareas comparten esos recursos. Tenga en cuenta los siguientes escenarios:

  • Minimice la transferencia de datos para las tareas que pueden compartir datos. Puede reducir drásticamente los cargos de transferencia de datos copiando datos compartidos en un número menor de nodos y ejecutando tareas en paralelo en cada nodo. Esta estrategia se aplica especialmente si los datos que se van a copiar en cada nodo deben transferirse entre regiones geográficas.
  • Maximice el uso de memoria para las tareas que requieren una gran cantidad de memoria, pero solo durante breves períodos de tiempo y en momentos variables durante la ejecución. Puede emplear menos nodos de proceso, pero más grandes, con más memoria para controlar eficazmente estos picos. Estos nodos tienen varias tareas que se ejecutan en paralelo en cada nodo, pero cada tarea puede aprovechar la memoria abundante de los nodos en momentos diferentes.
  • Mitigue los límites de número de nodo cuando se requiere comunicación entre nodos dentro de un grupo. Actualmente, los grupos configurados para la comunicación entre nodos están limitados a 50 nodos de proceso. Si cada nodo de este grupo puede ejecutar tareas en paralelo, se puede ejecutar simultáneamente un mayor número de tareas.
  • Replique un clúster de proceso local, como cuando mueva por primera vez un entorno de proceso a Azure. Si la solución local actual ejecuta varias tareas por nodo de proceso, puede aumentar el número máximo de tareas de nodo para reflejar mejor esa configuración.

Escenario de ejemplo

Por ejemplo, imagine una aplicación de tareas con requisitos de CPU y memoria de forma que Standard_D1 nodos sean suficientes. Sin embargo, para finalizar el trabajo en el tiempo necesario, se necesitan 1000 nodos.

En lugar de usar Standard_D1 nodos que tienen un núcleo de CPU, puede usar Standard_D14 nodos con 16 núcleos cada uno y habilitar la ejecución de tareas paralelas. Podría usar 16 veces menos nodos en lugar de 1000 nodos, solo se necesitarían 63. Si se requieren archivos de aplicación grandes o datos de referencia para cada nodo, se mejoran la duración y la eficacia del trabajo, ya que los datos se copian en solo 63 nodos.

Habilitación de la ejecución de tareas paralelas

Los nodos de proceso se configuran para la ejecución de tareas paralelas en el nivel de grupo. Con la biblioteca .NET de Batch, establezca la propiedad CloudPool.TaskSlotsPerNode al crear un grupo. Si usa la API REST de Batch, establezca el elemento taskSlotsPerNode en el cuerpo de la solicitud durante la creación del grupo.

Nota:

Puede establecer el taskSlotsPerNode elemento y la propiedad TaskSlotsPerNode solo en el momento de creación del grupo. No se pueden modificar después de haber creado el grupo.

Azure Batch permite establecer ranuras de tareas por nodo hasta (4 veces) el número de núcleos de nodo. Por ejemplo, si el grupo está configurado con nodos de tamaño "Grande" (cuatro núcleos), taskSlotsPerNode puede establecerse en 16. Sin embargo, independientemente del número de núcleos que tiene el nodo, no puede tener más de 256 ranuras de tareas por nodo. Para detalles sobre el número de núcleos de cada uno de los tamaños de nodo, consulte Sizes for Cloud Services (classic) Para más información sobre los límites de servicio, consulte Límites y cuotas de servicio de Batch.

Sugerencia

Asegúrese de tener en cuenta el valor taskSlotsPerNode al construir una fórmula de escalado automático para el grupo. Por ejemplo, una fórmula que evalúa $RunningTasks podría verse afectada drásticamente por un aumento de tareas por nodo. Para más información, consulte Creación de una fórmula automática para escalar nodos de proceso en un grupo de Batch.

Especificar la distribución de tareas

Al habilitar tareas simultáneas, es importante especificar cómo desea que las tareas se distribuyan entre los nodos del grupo.

Mediante la propiedad CloudPool.TaskSchedulingPolicy , puede especificar que las tareas se deben asignar uniformemente entre todos los nodos del grupo ("propagación"). O bien, puede especificar que se deben asignar tantas tareas como sea posible a cada nodo antes de pasar al siguiente nodo del grupo (técnica de "empaquetado").

Por ejemplo, considere el grupo de nodos de Standard_D14 (en el ejemplo anterior) configurado con un valor CloudPool.TaskSlotsPerNode de 16. Si CloudPool.TaskSchedulingPolicy está configurado con un ComputeNodeFillType de Pack, maximizaría el uso de los 16 núcleos de cada nodo y permitiría que un grupo de escalado automático quite nodos no usados (nodos sin ninguna tarea asignada) del grupo. El escalado automático minimiza el uso de recursos y puede ahorrar dinero.

Definición de ranuras variables por tarea

Una tarea se puede definir con la propiedad CloudTask.RequiredSlots , especificando cuántas ranuras requiere para ejecutarse en un nodo de proceso. El valor predeterminado es 1. Puede establecer ranuras de tareas variables si las tareas tienen diferentes pesos asociados con su uso de recursos en el nodo de proceso. Las ranuras de tareas variables permiten que cada nodo de proceso tenga un número razonable de tareas en ejecución simultáneas sin sobrecargar recursos del sistema como CPU o memoria.

Por ejemplo, en el caso de un grupo con la propiedad taskSlotsPerNode = 8, puede enviar tareas de uso intensivo de CPU que requieren varios núcleos con requiredSlots = 8, y otras tareas con requiredSlots = 1. Cuando se programa esta carga de trabajo mixta, las tareas que consumen mucha CPU se ejecutan exclusivamente en sus nodos de proceso, mientras que otras tareas se pueden ejecutar simultáneamente (hasta ocho tareas a la vez) en otros nodos. La carga de trabajo mixta le ayuda a equilibrar la carga de trabajo entre nodos de proceso y a mejorar la eficacia del uso de recursos.

Asegúrese de no especificar un valor de requiredSlots para la tarea que sea mayor que el valor de taskSlotsPerNode para el grupo, de lo contrario la tarea no se ejecutará. El servicio Batch no valida actualmente este conflicto al enviar tareas. Esto se debe a que es posible que el trabajo no tenga ningún grupo enlazado al momento del envío o a que puede cambiar a un grupo diferente si se deshabilita o se vuelve a habilitar.

Sugerencia

Al usar ranuras de tareas variables, es posible que las tareas grandes con más ranuras necesarias no se puedan programar temporalmente porque no hay suficientes ranuras disponibles en cualquier nodo de proceso, incluso cuando todavía hay ranuras inactivas en algunos nodos. Puede subir la prioridad del trabajo para estas tareas con el fin de aumentar su oportunidad de competir por las ranuras disponibles en los nodos.

El servicio Batch emite taskScheduleFailEvent cuando no se puede programar una tarea para ejecutarse y sigue intentando volver a intentar la programación hasta que estén disponibles las ranuras necesarias. Puede escuchar ese evento para detectar posibles problemas de programación de tareas y mitigarlos en consecuencia.

Ejemplo Batch de .NET

Los siguientes fragmentos de código de la API Batch .NET muestran cómo crear un pool con múltiples slots de tareas por nodo y cómo enviar una tarea con los slots requeridos.

Creación de un grupo con varias ranuras de tareas por nodo

Este fragmento de código muestra una solicitud para crear un grupo que contiene cuatro nodos, con cuatro ranuras de tareas permitidas por nodo. Especifica una directiva de programación de tareas que rellena cada nodo con tareas antes de asignar tareas a otro nodo del grupo.

Para obtener más información sobre cómo agregar grupos mediante la API de .NET de Batch, consulte 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();

Creación de una tarea con las ranuras necesarias

Este fragmento de código crea una tarea con un valor no predeterminado requiredSlots. Esta tarea se ejecuta cuando hay suficientes ranuras libres disponibles en un nodo de proceso.

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

Listar nodos de cómputo con recuentos para tareas en ejecución y ranuras

Este fragmento de código enumera todos los nodos de cómputo del conjunto e imprime las cuentas de tareas en ejecución y ranuras de tareas por nodo.

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

Enumeración de recuentos de tarea para el trabajo

Este fragmento de código obtiene los recuentos de tareas para el trabajo, lo que incluye los recuentos de tareas y de ranuras de tarea por estado de tarea.

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}");

Ejemplo de Batch REST

Los siguientes fragmentos de código de la API REST de Batch muestran cómo crear un grupo con varias ranuras de tareas por nodo y cómo enviar una tarea con ranuras necesarias.

Creación de un grupo con varias ranuras de tareas por nodo

Este fragmento de código muestra una solicitud para crear un grupo que contiene dos nodos grandes con un máximo de cuatro tareas por nodo.

Para obtener más información sobre cómo agregar grupos mediante la API REST, consulte Adición de un grupo a una cuenta.

{
  "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,
}

Crear una tarea con campos obligatorios

Este fragmento de código muestra una solicitud para agregar una tarea con un valor no predeterminado requiredSlots. Esta tarea solo se ejecuta cuando hay suficientes ranuras libres disponibles en el nodo de proceso.

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

Ejemplo de código en GitHub

El proyecto ParallelTasks en GitHub muestra el uso de la propiedad CloudPool.TaskSlotsPerNode .

Esta aplicación de consola de C# usa la biblioteca Batch .NET para crear un grupo con uno o varios nodos de cálculo. Ejecuta un número configurable de tareas en esos nodos para simular una carga variable. La salida de la aplicación muestra qué nodos ejecutaron cada tarea. La aplicación también proporciona un resumen de los parámetros y la duración del trabajo.

En el ejemplo siguiente se muestra la parte de resumen de la salida de dos ejecuciones diferentes de la aplicación de ejemplo ParallelTasks. Las duraciones de trabajo que se muestran aquí no incluyen el tiempo de creación del grupo, ya que cada trabajo se envió a un grupo creado previamente cuyos nodos de proceso estaban en estado Inactivo en el momento del envío.

La primera ejecución de la aplicación de ejemplo muestra que, con un solo nodo en el grupo y la configuración predeterminada de una tarea por nodo, la duración del trabajo supera los 30 minutos.

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

La segunda ejecución del ejemplo muestra una disminución significativa de la duración del trabajo. Esta reducción se debe a que el grupo se configuró con cuatro tareas por nodo, lo que permite que la ejecución de tareas paralelas complete el trabajo en casi un trimestre del tiempo.

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

Pasos siguientes