跨区域训练不一定更慢:用 SageMaker HyperPod 与 Qumulo 解耦算力和数据

2026-09-25 22 预计阅读时间: 1 分钟
来源: aws.amazon.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

大模型训练通常默认“算力必须靠近数据”,否则跨区域延迟和带宽会拖慢 GPU。但 Amazon SageMaker HyperPod 与 Cloud Native Qumulo 展示了另一种部署方式:训练集保留在一个 AWS Region,训练集群部署在另一个 Region;经过短暂的 NeuralCache 预热后,远程集群的吞吐量可以追平计算与数据同区域部署的集群。

这项结果的价值不只是一次性能测试。它意味着团队可以分别选择数据落点和 GPU 容量,不必为了临时获得加速器而复制整套训练数据。

把“数据在哪里”和“GPU 在哪里”拆开

这种架构可以分成三层:

  • 数据区域:Cloud Native Qumulo 保存训练数据集,并提供统一文件命名空间。
  • 计算区域:SageMaker HyperPod 承载训练节点和 GPU,通过跨区域网络访问数据。
  • 缓存层:NeuralCache 在训练读取过程中逐步预热,减少后续 epoch 对远端读取路径的依赖。

数据路径可以概括为:

训练进程
  -> HyperPod 训练节点
  -> 跨区域网络
  -> Qumulo 文件系统
  -> 训练数据集

后续重复读取
  -> NeuralCache 命中
  -> 更稳定的训练吞吐量

这种拆分适合两类现实问题:一是训练数据受治理、存储成本或生产流程约束,不能随意迁移;二是目标区域的 GPU 容量不足,需要把训练任务放到另一个更容易获得算力的区域。

它也改变了容量规划方式。团队不再只能选择“复制数百 TB 数据”或“等待本区域 GPU”之一,而是可以先评估跨区域训练是否在预热后满足吞吐要求。

为什么不能只看第一次读取

跨区域训练的冷启动性能通常不是稳态性能。第一次读取数据时,请求需要经过远端存储和跨区域链路;当缓存逐步填充后,相同数据块的后续访问可能明显加快。

来源中的验证结果正是如此:远程集群经过短暂的 NeuralCache 预热后,达到了与同区域集群相当的吞吐量。不过,这个结论不应被理解为“所有跨区域训练天然无损”。最终表现仍取决于:

  • 数据集是否会在多个 epoch 中重复访问;
  • 样本大小和文件数量;
  • DataLoader 并发度与预取深度;
  • 跨区域链路的带宽、延迟和稳定性;
  • 缓存容量能否覆盖训练中的活跃数据集;
  • 训练是计算密集型,还是持续受存储吞吐限制。

例如,只扫描一次、数据量远大于缓存容量的训练任务,可能无法充分受益。相反,重复 epoch、样本访问具有局部性的数据集,更容易在预热后获得稳定吞吐。

用可重复测试区分冷启动和稳态吞吐

下面的 Python 脚本只依赖标准库,可以在训练节点上对挂载后的数据目录执行多轮并发读取。它不会模拟完整模型训练,但适合先观察第一轮与后续轮次之间的吞吐变化。

运行前,把 /mnt/qumulo/dataset 替换为实际的数据集挂载路径。为了避免意外扫描整个超大目录,示例默认最多读取 1,000 个文件。

#!/usr/bin/env python3
import argparse
import os
import time
from concurrent.futures import ThreadPoolExecutor


def collect_files(root: str, max_files: int) -> list[str]:
    paths = []
    for current, dirs, files in os.walk(root):
        dirs.sort()
        files.sort()
        for name in files:
            paths.append(os.path.join(current, name))
            if len(paths) >= max_files:
                return paths
    return paths


def read_file(path: str, block_size: int) -> int:
    total = 0
    with open(path, "rb", buffering=0) as handle:
        while True:
            chunk = handle.read(block_size)
            if not chunk:
                break
            total += len(chunk)
    return total


def run_pass(paths: list[str], workers: int, block_size: int) -> tuple[int, float]:
    started = time.perf_counter()
    with ThreadPoolExecutor(max_workers=workers) as pool:
        sizes = list(pool.map(lambda p: read_file(p, block_size), paths))
    elapsed = time.perf_counter() - started
    return sum(sizes), elapsed


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("root")
    parser.add_argument("--passes", type=int, default=3)
    parser.add_argument("--workers", type=int, default=16)
    parser.add_argument("--max-files", type=int, default=1000)
    parser.add_argument("--block-mib", type=int, default=8)
    args = parser.parse_args()

    paths = collect_files(args.root, args.max_files)
    if not paths:
        raise SystemExit(f"No files found under {args.root}")

    print(f"files={len(paths)} workers={args.workers}")
    block_size = args.block_mib * 1024 * 1024

    for pass_number in range(1, args.passes + 1):
        byte_count, elapsed = run_pass(paths, args.workers, block_size)
        gib = byte_count / (1024 ** 3)
        throughput = gib / elapsed if elapsed else 0
        print(
            f"pass={pass_number} data={gib:.2f} GiB "
            f"time={elapsed:.2f}s throughput={throughput:.2f} GiB/s"
        )


if __name__ == "__main__":
    main()

保存为 bench_dataset.py 后执行:

python3 bench_dataset.py /mnt/qumulo/dataset \
  --passes 3 \
  --workers 32 \
  --max-files 2000 \
  --block-mib 8

建议分别在同区域集群和跨区域 HyperPod 集群上运行,并保证以下条件一致:

  1. 使用相同的数据文件清单,而不只是相同目录名;
  2. 使用相同实例类型、节点数量和客户端并发度;
  3. 分开记录第一轮冷读与后续稳态读取;
  4. 同时采集 GPU 利用率、训练 step 时间和网络吞吐;
  5. 至少重复三次,观察均值之外的抖动和尾部延迟。

需要注意,第二轮变快也可能受到 Linux 客户端页缓存影响,并不一定全部来自 NeuralCache。若要做严格归因,可以在独立节点上运行冷读测试,或按照团队的运维规范清理客户端缓存。不要在共享生产节点上直接执行 drop_caches,它会影响其他工作负载。

真正要比较的是训练效率,而不只是存储带宽

存储基准只能回答“数据能读多快”,无法完整回答“模型能训多快”。正式验证时,应把以下指标放在同一张时间线上:

  • 每秒处理的样本数或 token 数;
  • 单个训练 step 的平均耗时和 P95/P99;
  • GPU 利用率以及等待数据的时间;
  • 冷启动持续了多少 step 或多少分钟;
  • 跨区域传输量与对应成本;
  • 缓存命中稳定后是否仍出现周期性回源。

如果跨区域方案的稳态吞吐与同区域方案接近,但每次任务都需要较长预热,那么短任务可能仍不划算。对于运行数小时或数天的大规模训练,几分钟预热则可能是可以接受的交换条件。

上线前的决策清单

采用这类架构前,可以逐项确认:

  • 数据集是否会跨 epoch 重复访问,且工作集能被缓存有效覆盖;
  • 跨区域网络是否具备足够带宽、路由冗余和访问控制;
  • 数据治理规则是否允许从另一个区域读取数据;
  • 跨区域流量费用是否低于复制、同步和维护数据副本的成本;
  • Qumulo 或网络不可用时,训练任务如何重试、暂停或恢复;
  • 基准测试是否覆盖了冷启动、稳态和缓存失效三种状态;
  • 训练检查点是否采用了独立且可靠的持久化策略。

SageMaker HyperPod 与 Cloud Native Qumulo 的组合说明,数据重力不必完全决定训练算力的位置。更稳妥的落地方式不是假设跨区域访问没有代价,而是量化预热窗口、稳态吞吐、故障边界和流量成本,再决定是否用远程数据换取更灵活的 GPU 容量。


相关推荐