你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

并发运行任务以最大程度地使用 Batch 计算节点

可以通过在每个节点上同时运行多个任务,在池中的少量计算节点上最大化资源使用率。

虽然某些方案最适合专用于单个任务的所有节点资源,但当多个任务共享这些资源时,某些工作负荷的作业时间可能会缩短,成本更低。 请考虑下列情形:

  • 对于能够共享数据的任务,最大限度减少数据传输。 通过将共享数据复制到较少的节点,然后在每个节点上并行执行任务,可以大幅降低数据传输费用。 如果要复制到每个节点的数据必须在地理区域之间传输,则尤其适用此策略。
  • 将在执行过程中仅在短时间内,以及在不同时间点,最大化内存使用量,以满足需要大量内存的任务。 可以采用更少的、但更大的计算节点,具有更多内存,以有效处理此类峰值。 这些节点在每个节点上并行运行多个任务,但每个任务可以在不同时间利用节点的丰富内存。
  • 在池中需要节点间通信时缓解节点数限制。 目前,为节点间通信配置的池限制为 50 个计算节点。 如果此类池中的每个节点能够并行执行任务,则可以同时执行多个任务。
  • 复制本地计算群集,例如首次将计算环境移到 Azure 时。 如果当前的本地解决方案为每个计算节点执行多个任务,则可以增加最大节点任务数,以更紧密地镜像该配置。

示例方案

例如,假设任务应用程序对 CPU 和内存有一定要求,此时 Standard_D1 节点就足够了。 但是,若要在所需时间内完成作业,需要其中 1,000 个节点。

与其使用一个 CPU 核心的Standard_D1节点,不如使用每个具有16个核心的Standard_D14节点,并启用并行任务执行。 可能可以使用 1/16 的节点,即只需使用 63 个节点,而无需使用 1,000 个节点。 如果每个节点需要大型应用程序文件或引用数据,作业持续时间和效率会得到提高,因为数据仅复制到 63 个节点。

启用并行任务执行

在池级别为并行任务执行配置计算节点。 使用 Batch .NET 库,在创建池时设置 CloudPool.TaskSlotsPerNode 属性。 如果使用 Batch REST API,请在创建池期间在请求正文中设置 taskSlotsPerNode 元素。

注释

只能在池创建时设置 taskSlotsPerNode 元素和 TaskSlotsPerNode 属性。 创建池后,无法修改它们。

Azure Batch 允许将每个节点的任务槽数量设置为最多为节点核心数量的 4 倍。 例如,如果池配置了大小为“大”(四个核心)的节点,则 taskSlotsPerNode 可以设置为 16。 但是,无论节点拥有多少个核心,每个节点的任务槽数不能超过 256 个。 有关每个节点大小的内核数的详细信息,请参阅 云服务(经典)的大小。 有关服务限制的详细信息,请参阅 Batch 服务配额和限制

小窍门

为池构造自动缩放公式时,请务必考虑 taskSlotsPerNode 值。 例如,计算 $RunningTasks 公式可能会由于每个节点任务数量的显著增加而受到重大影响。 有关详细信息,请参阅 创建用于在 Batch 池中缩放计算节点的自动公式

指定任务分布

启用并发任务时,请务必指定希望任务在池中的节点之间分布的方式。

通过使用 CloudPool.TaskSchedulingPolicy 属性,可以指定应跨池中的所有节点均匀分配任务(“分散”)。 或者,可以指定在将任务分配到池中的另一个节点之前,应向每个节点分配尽可能多的任务(“打包”)。

例如,考虑使用 CloudPool.TaskSlotsPerNode 值为 16 配置的Standard_D14节点池(在上一示例中)。 如果 CloudPool.TaskSchedulingPolicy 配置为 ComputeNodeFillTypePack,它将最大化地利用每个节点的所有 16 个核心,并允许 自动缩放池从池中删除未使用的节点(没有任何任务分配的节点)。 自动缩放可最大程度地减少资源使用情况,并节省资金。

为每个任务定义变量槽

可以使用 CloudTask.RequiredSlots 属性定义任务,指定在计算节点上运行所需的槽数。 默认值为 1。 如果任务具有与计算节点上的资源使用情况关联的不同权重,则可以设置变量任务槽。 可变任务槽允许每个计算节点具有合理的并发运行任务数,而无需占用大量系统资源,例如 CPU 或内存。

例如,对于具有属性 taskSlotsPerNode = 8 的池,你可以使用 requiredSlots = 8 提交需要多核的 CPU 密集型任务,而其他任务可以设置为 requiredSlots = 1。 计划此混合工作负荷时,CPU 密集型任务在其计算节点上以独占方式运行,而其他任务在其他节点上可以同时运行(最多 8 个任务)。 混合工作负荷有助于在计算节点之间平衡工作负荷并提高资源使用效率。

确保没有将任务的 requiredSlots 指定为大于池的 taskSlotsPerNode,否则任务不会运行。 提交任务时,Batch 服务当前不会验证此冲突。 它不验证此冲突是因为作业在提交时可能未绑定池,也可能已通过禁用/重新启用更改为其他池。

小窍门

使用可变任务槽时,具有更多所需槽的大型任务可能会暂时无法计划,因为任何计算节点上都没有足够的槽可用,即使某些节点上仍有空闲槽。 可以提高这些任务的优先级,以增加它们在节点上竞争可用槽位的机会。

Batch 服务在无法安排任务运行时发出 TaskScheduleFailEvent,并在所需的槽位可用之前继续重试进行计划。 你可以侦听该事件以检测潜在的任务计划问题,并相应地进行缓解。

Batch .NET 示例

以下 Batch .NET API 代码片段演示如何为每个节点创建具有多个任务槽的池,以及如何提交具有所需槽的任务。

创建一个池,每个节点具有多个任务槽

此代码片段显示创建包含四个节点的池的请求,每个节点允许有四个任务槽。 它指定了一种任务调度策略,该策略是在将任务分配给池中的其它节点之前,先填满每个节点的任务。

有关使用 Batch .NET API 添加池的详细信息,请参阅 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();

创建具有必需槽的任务

此代码片段创建具有非默认 requiredSlots项的任务。 当计算节点上有足够的可用槽时,此任务将运行。

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

列出计算节点以及正在运行的任务和槽的计数

此代码片段列出池中的所有计算节点,并输出每个节点运行任务和任务槽的计数。

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

列出作业的任务计数

此代码段获取作业的任务计数,其中包含每个任务状态的任务和任务槽计数。

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

批量 REST 示例

以下 Batch REST API 代码片段演示如何为每个节点创建具有多个任务槽的池,以及如何提交具有所需槽的任务。

创建每个节点上具有多个任务槽的池

此代码片段显示创建一个池的请求,该池包含两个大型节点,每个节点最多包含四个任务。

有关使用 REST API 添加池的详细信息,请参阅 向帐户添加池

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

创建具有必需槽的任务

此代码片段显示添加具有非默认 requiredSlots任务的请求。 此任务仅在计算节点上有足够的可用槽时运行。

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

GitHub 上的代码示例

GitHub 上的 ParallelTasks 项目演示了 CloudPool.TaskSlotsPerNode 属性的使用。

此 C# 控制台应用程序使用 Batch .NET 库创建包含一个或多个计算节点的池。 它会在这些节点上执行可配置数量的任务来模拟变量负载。 应用程序的输出显示执行每个任务的节点。 该应用程序还提供作业参数和持续时间的摘要。

以下示例显示了 ParallelTasks 示例应用程序的两个不同运行输出的摘要部分。 此处显示的作业持续时间不包括池创建时间,因为每个作业都提交到以前创建的池,其计算节点在提交时处于 空闲 状态。

示例应用程序首次执行结果显示,在池中仅有一个节点并且每个节点默认设置为一个任务的情况下,作业持续时间超过30分钟。

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

示例的第二次运行显示作业持续时间显著减少。 这种减少是因为池为每个节点配置了四个任务,允许并行任务执行在近四分之一的时间内完成作业。

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

后续步骤