Skip to content

48. Communication Hotspots and Mitigation | 通信热点与缓解策略 ​

难度: Medium | 环境: CPU-first | 标签: 并行通信, 热点分析, 缓解策略 | 目标人群: 并行通信学习者

🚀 云端运行环境

本章节的实战代码可以点击以下链接在免费 GPU 算力平台上直接运行:

Open In ColabOpen In Studio (国内推荐:魔搭社区免费实例)


本节导读 ​

多卡程序出现通信等待时,不能只把单次耗时最大的 collective 当作热点。payload 决定一次传输有多重,同步频率决定它重复多少次,计算重叠决定其中多少时间真正暴露在关键路径上。本节再结合 rank 到达时间差,把这些证据转成可复测的缓解动作。

关键词: collective, wait time, hotspot, communication plan


前置阅读 ​

导语: 先从时间线中读出 collective、计算重叠和暴露通信时间;本节继续判断这些信号分别指向传输量、同步还是负载不均衡问题。


Step 1:从时间线建立热点证据 ​

通信总时长只能说明某类 collective 很忙,不能说明它一定在拖慢关键路径。判断热点时,需要同时观察以下信号:

证据它说明什么常见后续问题
暴露通信时间有多少通信没有被计算隐藏同步点是否过早、可重叠计算是否不足
payload 与同步频率单次搬运量和每 step 调用次数bucket、张量布局、路由流量或同步粒度是否过大
重叠比例计算覆盖通信的程度调度顺序是否让通信落在计算空档
rank 不均衡各 rank 到达或完成通信的差异;未采集时保持 unknownstage 切分、专家路由或数据分布是否失衡

通信热点识别总览

Step 2:识别热点属于哪种模式 ​

同样是通信慢,根因可能不同。先按证据归入模式,才能避免把所有问题都归结为带宽不足。

热点模式典型证据首要检查方向
传输量 / 路由压力payload 大,all_to_all 频繁或跨 rank token 多减少路由流量、调整专家或数据分组
暴露通信压力暴露时间高,重叠比例低,或小同步过于频繁延后同步、调整 bucket,寻找可重叠计算
rank 不均衡个别 rank 明显更晚到达或完成调整切分、专家容量或负载均衡策略

Step 3:选择缓解动作并设计复测 ​

缓解动作不是结论,而是下一轮对照实验的候选。每次调整都要保持 workload 不变,并记录它可能带来的新代价。下表中的 Dual Batch Overlap(DBO)用于说明 Serving 调度候选,本节 GPU 实验只验证 collective 与合成计算的重叠,不把结果写成 DBO 实测证据。

热点模式候选动作复测指标可能代价
传输量 / 路由压力减少跨 rank token、合并消息或调整分组payload、跨 rank 比例、吞吐路由质量、容量溢出或负载偏斜
暴露通信压力调整同步时机、bucket 或计算—通信 overlap同步次数、暴露时间、重叠比例、step time内存驻留、调度复杂度
rank 不均衡调整切分、专家容量或负载均衡最大 rank 耗时、负载偏斜、P95额外路由或通信开销
推理批次准备阻塞执行将批次拆成可交错的子批次,例如 Dual Batch Overlap(DBO)调度间隙、暴露通信、吞吐与尾延迟子批次过小会增加调度和同步开销

通信热点到缓解动作

Step 4:CPU 机制实现——热点归因与缓解建议 ​

题目区提供一组简化的 trace 事件。你需要先按 collective 汇总调用次数、payload、暴露时间、重叠与不均衡,再依据热点模式选择缓解动作,最后给出下一轮复测的决策。

TODO函数 / 机制输入与关键变量输出与不变量测试关注点
1summarize_comm_hotspots:热点聚合collective、同步次数、暴露时间、payload、overlap、rank 不均衡同类 collective 先聚合;priority 必须是总暴露时间最大的项重复 collective 的频率、流量汇总与优先热点
2choose_comm_mitigation:动作匹配priority 的 payload、overlap、rank 不均衡返回策略、目标 collective 与复测字段;不平衡优先级最高路由、低 overlap 与不均衡分支
3recommend_comm_followup:实验决策priority、mitigation、暴露通信强度返回 accept / tune / collect_more_evidence 与下一轮字段缺少 trace、低暴露与高暴露对照
python
from typing import Any, Dict, List
python
def summarize_comm_hotspots(events: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    按 collective 汇总 trace 事件,并选择暴露通信时间最大的优先热点。

    每条事件至少包含 collective、exposed_comm_ms、payload_bytes、overlap_ratio
    和 rank_imbalance_ratio;第 47 节的聚合事件还会携带 sync_count 与
    total_comm_ms。对同一 collective,应累加同步次数、总通信时间、暴露时间
    和传输量,同时保留最差的重叠比例与最大的 rank 不均衡。
    """
    if not events:
        return {'by_collective': {}, 'priority': None, 'num_events': 0}

    by_collective: Dict[str, Dict[str, float]] = {}
    for event in events:
        collective = str(event['collective'])
        exposed_ms = float(event['exposed_comm_ms'])
        payload_bytes = int(event['payload_bytes'])
        overlap_ratio = float(event['overlap_ratio'])
        imbalance_value = event.get('rank_imbalance_ratio')
        imbalance_observed = bool(event.get('rank_imbalance_observed', imbalance_value is not None))
        imbalance_ratio = float(imbalance_value) if imbalance_observed else 0.0
        sync_count = int(event.get('sync_count', 1))
        total_comm_ms = float(event.get('total_comm_ms', exposed_ms))

        # TODO 1:为当前 collective 建立或取得统计桶,并更新同步频率等热点证据。
        # bucket = ???
        raise NotImplementedError

    # priority_collective = ???
    # priority = ???
    raise NotImplementedError


def choose_comm_mitigation(summary: Dict[str, Any]) -> Dict[str, Any]:
    """
    依据优先热点的模式选择缓解动作与下一轮复测指标。

    优先级为:rank 不均衡 > 低 overlap 的暴露通信 > all-to-all 路由流量。
    """
    priority = summary.get('priority')
    if priority is None:
        return {'strategy': 'collect_trace', 'target': None, 'next_measurements': ['rank_elapsed_ms']}

    # TODO 2:根据 rank_imbalance_ratio、overlap_ratio、collective 和 payload_bytes 决定策略。
    # strategy = ???
    # next_measurements = ???
    raise NotImplementedError


def recommend_comm_followup(summary: Dict[str, Any], mitigation: Dict[str, Any]) -> Dict[str, Any]:
    """
    根据热点严重程度形成 accept / tune / collect_more_evidence 决策。
    """
    priority = summary.get('priority')
    # TODO 3:无 trace、暴露通信较低、暴露通信明显三种情况分别给出决策和原因。
    # decision = ???
    # reason = ???
    raise NotImplementedError
python
def _comm_hotspot_fixture():
    """包含重复 all-reduce 与高流量 all-to-all 的简化 trace。"""
    return [
        {'collective': 'all_reduce', 'exposed_comm_ms': 8.0, 'payload_bytes': 1024, 'overlap_ratio': 0.60, 'rank_imbalance_ratio': 0.00},
        {'collective': 'all_reduce', 'exposed_comm_ms': 6.0, 'payload_bytes': 2048, 'overlap_ratio': 0.45, 'rank_imbalance_ratio': 0.05},
        {'collective': 'all_to_all', 'exposed_comm_ms': 20.0, 'payload_bytes': 65536, 'overlap_ratio': 0.60, 'rank_imbalance_ratio': 0.05},
        {'collective': 'broadcast', 'exposed_comm_ms': 5.0, 'payload_bytes': 512, 'overlap_ratio': 0.90, 'rank_imbalance_ratio': 0.00},
    ]

def _summary_with_priority(**priority):
    return {'by_collective': {priority['collective']: priority}, 'priority': priority, 'num_events': 1}

def test_collective_aggregation_and_priority():
    """重复 collective 必须先聚合,再按暴露通信时间选择热点。"""
    try:
        summary = summarize_comm_hotspots(_comm_hotspot_fixture())
        all_reduce = summary['by_collective']['all_reduce']
        assert all_reduce['count'] == 2
        assert all_reduce['sync_count'] == 2
        assert all_reduce['payload_bytes'] == 3072
        assert all_reduce['exposed_comm_ms'] == 14.0
        assert summary['priority']['collective'] == 'all_to_all'
        assert summary['priority']['exposed_comm_ms'] == 20.0
        exported = summarize_comm_hotspots([{
            'collective': 'all_reduce', 'sync_count': 3, 'total_comm_ms': 12.0,
            'exposed_comm_ms': 5.0, 'payload_bytes': 4096,
            'overlap_ratio': 7.0 / 12.0, 'rank_imbalance_ratio': 0.0,
        }])
        assert exported['priority']['sync_count'] == 3
        assert exported['priority']['total_comm_ms'] == 12.0
    except NotImplementedError:
        raise
    except (AttributeError, KeyError, NameError, TypeError, ValueError, AssertionError) as error:
        raise NotImplementedError('请先完成 TODO 1:按 collective 汇总热点证据。') from error

def test_mitigation_matches_hotspot_pattern():
    """路由流量、低 overlap 与 rank 不均衡应导向不同动作。"""
    try:
        routing = choose_comm_mitigation(summarize_comm_hotspots(_comm_hotspot_fixture()))
        assert routing['strategy'] == 'reduce_routing_traffic'

        exposed = _summary_with_priority(collective='all_reduce', exposed_comm_ms=16.0, payload_bytes=4096, overlap_ratio=0.10, rank_imbalance_ratio=0.00)
        assert choose_comm_mitigation(exposed)['strategy'] == 'increase_overlap_or_delay_sync'

        imbalanced = _summary_with_priority(collective='all_to_all', exposed_comm_ms=12.0, payload_bytes=8192, overlap_ratio=0.60, rank_imbalance_ratio=0.35)
        assert choose_comm_mitigation(imbalanced)['strategy'] == 'rebalance_ranks_or_experts'
    except NotImplementedError:
        raise
    except (AttributeError, KeyError, NameError, TypeError, ValueError, AssertionError) as error:
        raise NotImplementedError('请先完成 TODO 2:按热点模式选择缓解动作。') from error

def test_followup_decision():
    """决策应区分缺少证据、可接受与需要调优三种状态。"""
    try:
        empty = summarize_comm_hotspots([])
        assert recommend_comm_followup(empty, choose_comm_mitigation(empty))['decision'] == 'collect_more_evidence'

        low = _summary_with_priority(collective='broadcast', exposed_comm_ms=2.0, payload_bytes=512, overlap_ratio=0.90, rank_imbalance_ratio=0.00)
        assert recommend_comm_followup(low, choose_comm_mitigation(low))['decision'] == 'accept'

        high = summarize_comm_hotspots(_comm_hotspot_fixture())
        decision = recommend_comm_followup(high, choose_comm_mitigation(high))
        assert decision['decision'] == 'tune'
        assert 'dispatch_payload_bytes' in decision['next_measurements']
    except NotImplementedError:
        raise
    except (AttributeError, KeyError, NameError, TypeError, ValueError, AssertionError) as error:
        raise NotImplementedError('请先完成 TODO 3:形成下一轮复测决策。') from error

test_collective_aggregation_and_priority()
test_mitigation_matches_hotspot_pattern()
test_followup_decision()
print('✅ 通信热点归因、缓解动作与复测决策通过。')

🛑 STOP HERE 🛑









请先尝试自己完成代码并跑通测试。
如果你正在 Colab 中运行,并且遇到困难没有思路,可以向下滚动查看参考答案。










参考代码与解析 ​

代码 ​

python
def summarize_comm_hotspots(events: List[Dict[str, Any]]) -> Dict[str, Any]:
    """按 collective 聚合 trace 事件,并选择暴露通信时间最大的优先热点。"""
    if not events:
        return {'by_collective': {}, 'priority': None, 'num_events': 0}

    by_collective: Dict[str, Dict[str, float]] = {}
    for event in events:
        collective = str(event['collective'])
        exposed_ms = float(event['exposed_comm_ms'])
        payload_bytes = int(event['payload_bytes'])
        overlap_ratio = float(event['overlap_ratio'])
        imbalance_value = event.get('rank_imbalance_ratio')
        imbalance_observed = bool(event.get('rank_imbalance_observed', imbalance_value is not None))
        imbalance_ratio = float(imbalance_value) if imbalance_observed else 0.0
        sync_count = int(event.get('sync_count', 1))
        total_comm_ms = float(event.get('total_comm_ms', exposed_ms))

        # TODO 1:同类 collective 的事件要先合并,才能比较总暴露时间。
        bucket = by_collective.setdefault(
            collective,
            {'count': 0, 'sync_count': 0, 'total_comm_ms': 0.0, 'exposed_comm_ms': 0.0, 'payload_bytes': 0, 'overlap_ratio': 1.0, 'rank_imbalance_ratio': 0.0, 'rank_imbalance_observed': False},
        )
        bucket['count'] += 1
        bucket['sync_count'] += sync_count
        bucket['total_comm_ms'] += total_comm_ms
        bucket['exposed_comm_ms'] += exposed_ms
        bucket['payload_bytes'] += payload_bytes
        bucket['overlap_ratio'] = min(bucket['overlap_ratio'], overlap_ratio)
        bucket['rank_imbalance_ratio'] = max(bucket['rank_imbalance_ratio'], imbalance_ratio)
        bucket['rank_imbalance_observed'] = bucket['rank_imbalance_observed'] or imbalance_observed

    priority_collective, priority_bucket = max(
        by_collective.items(), key=lambda item: item[1]['exposed_comm_ms']
    )
    priority = {'collective': priority_collective, **priority_bucket}
    return {'by_collective': by_collective, 'priority': priority, 'num_events': len(events)}


def choose_comm_mitigation(summary: Dict[str, Any]) -> Dict[str, Any]:
    """依据优先热点的模式选择缓解动作与下一轮复测指标。"""
    priority = summary.get('priority')
    if priority is None:
        return {'strategy': 'collect_trace', 'target': None, 'next_measurements': ['rank_elapsed_ms', 'payload_bytes']}

    collective = priority['collective']
    if priority.get('rank_imbalance_observed', True) and priority['rank_imbalance_ratio'] > 0.20:
        return {'strategy': 'rebalance_ranks_or_experts', 'target': collective, 'next_measurements': ['rank_elapsed_ms', 'expert_load_skew', 'cross_rank_token_ratio']}
    if priority['overlap_ratio'] < 0.30 and priority['exposed_comm_ms'] > 0:
        return {'strategy': 'increase_overlap_or_delay_sync', 'target': collective, 'next_measurements': ['exposed_comm_ms', 'overlap_ratio', 'step_time_ms']}
    if collective == 'all_to_all' and priority['payload_bytes'] > 0:
        return {'strategy': 'reduce_routing_traffic', 'target': collective, 'next_measurements': ['dispatch_payload_bytes', 'cross_rank_token_ratio', 'throughput_tokens_per_s']}
    return {'strategy': 'inspect_collective_payload_and_schedule', 'target': collective, 'next_measurements': ['payload_bytes', 'exposed_comm_ms', 'rank_elapsed_ms']}


def recommend_comm_followup(summary: Dict[str, Any], mitigation: Dict[str, Any]) -> Dict[str, Any]:
    """根据热点严重程度形成 accept / tune / collect_more_evidence 决策。"""
    priority = summary.get('priority')
    if priority is None:
        return {'decision': 'collect_more_evidence', 'reason': '尚未采集到可比较的 collective trace。', 'next_measurements': mitigation['next_measurements']}
    if priority['exposed_comm_ms'] <= 5.0:
        return {'decision': 'accept', 'reason': '当前优先热点的暴露通信时间较低。', 'next_measurements': mitigation['next_measurements']}
    return {
        'decision': 'tune',
        'reason': f"{priority['collective']} 的暴露通信时间为 {priority['exposed_comm_ms']:.1f} ms。",
        'next_measurements': mitigation['next_measurements'],
    }

解析 ​

TODO 1:按 collective 汇总热点证据

  • 同一种 collective 可能在一次 step 中出现多次,应先累计 sync_count、暴露通信时间与 payload,再比较热点。
  • 重叠比例保留最差值、rank 不均衡保留最大值,避免平均值掩盖最差路径。

TODO 2:按热点模式选择动作

  • rank 不均衡优先指向切分、专家容量或负载均衡;低 overlap 指向同步时机与计算—通信重叠。
  • 高流量 all-to-all 则优先检查跨 rank token 和路由流量,并记录对应复测指标。

TODO 3:形成下一轮复测决策

  • 没有 trace 时先补证据;暴露通信较低时接受当前方案;暴露通信明显时进入调优。
  • 决策必须带上下一轮测量项,才能验证动作是否真的降低了关键路径通信。

Step 5:可选多 GPU 复测——通信与计算重叠 ​

CPU 时间线说明如何计算 overlap;本实验进一步验证异步 collective 是否真的缩短最大 rank 的 step 时间。它使用合成矩阵计算和真实 NCCL all_reduce,不代表完整训练框架已经实现同等重叠。

CPU 机制价值GPU 实验价值仍不能推出
验证 overlap 与暴露通信的计算口径比较串行调度和异步 collective—compute 调度真实模型 bucket、依赖关系和框架调度收益

5.1 环境与固定 workload ​

安装与双卡预检见通信专题 Quickstart。本单元固定通信 payload、矩阵计算量和重复协议,串行与 overlap 候选只改变调度关系。

python
RUN_MODE = 'dry_run'  # 默认 dry_run;双卡 Profile 预检通过后改为 real_gpu。
RUN_LABEL = 'default'  # 默认 default;正式复测改为唯一标签并保留历史 JSON。
ENVIRONMENT_PROFILE = 'PAR-CUDA-MECHANISM'  # 与 47 共用的双卡机制环境。
WORKLOAD_ID = 'par-overlap-allreduce-matmul'  # 串行与异步 All-Reduce—Matmul 配对实验。
WORLD_SIZE = 2  # NCCL rank 数;当前机制实验固定为两卡起步。
DTYPE = 'bfloat16'  # 保持通信 payload 与矩阵计算的数据类型一致。
PAYLOAD_ELEMENTS = 4 * 1024 * 1024  # 每个 rank 的 all-reduce 元素数。
COMPUTE_DIM = 2048  # 与通信并行执行的方阵乘法维度。
WARMUP = 3  # 排除首次初始化与 kernel 预热。
REPEATS = 10  # 收集重复 step,结果取最大 rank 平均时间。
RESULT_PATH = f'benchmarks/results/48_communication_overlap_{RUN_LABEL}.json'  # 运行标签进入文件名,避免覆盖历史结果。
print({'run_mode': RUN_MODE, 'profile': ENVIRONMENT_PROFILE, 'world_size': WORLD_SIZE, 'payload_elements': PAYLOAD_ELEMENTS, 'compute_dim': COMPUTE_DIM, 'result': RESULT_PATH})

5.2 执行串行与重叠调度 ​

python
# 只改变 collective 与 compute 的调度关系,其余 workload 保持不变。
if RUN_MODE == 'real_gpu':
    import subprocess
    command = [
        'torchrun', '--standalone', '--nproc_per_node', str(WORLD_SIZE),
        'tools/run_parallel_mechanism_benchmark.py', '--mode', 'comm_overlap',
        '--payload-elements', str(PAYLOAD_ELEMENTS), '--compute-dim', str(COMPUTE_DIM),
        '--dtype', DTYPE, '--warmup', str(WARMUP), '--repeats', str(REPEATS),
        '--run-label', RUN_LABEL, '--workload-id', WORKLOAD_ID,
        '--output', RESULT_PATH,
    ]
    subprocess.run(command, check=True)
else:
    print('dry_run:完成配置检查后,将 RUN_MODE 改为 real_gpu 再执行 5.2。')

5.3 读取结果 ​

python
import json
from pathlib import Path
result_path = Path(RESULT_PATH)
if result_path.exists():
    result = json.loads(result_path.read_text(encoding='utf-8'))
    required = {'workload', 'hardware', 'metrics', 'evidence_level', 'failure', 'decision'}
    missing = required - set(result)
    if missing:
        raise ValueError(f'结果 JSON 缺少字段:{sorted(missing)}')
    print(result)
else:
    print(f'尚无 overlap 结果:{RESULT_PATH}')

5.4 解释证据并形成决策 ​

串行与 overlap 使用相同 payload、计算量、dtype 和重复协议。只有 median 与 P95 都改善,且各 rank 时间没有出现新的长尾时,才能认为调度减少了暴露通信;合成矩阵结果不能替代真实训练 bucket 或 Serving 时间线。

观察项读取字段当前结果解释与动作
运行状态failure、environment_profile运行后填写失败时保留日志,不形成 overlap 结论
固定身份rows[].collective、rows[].payload_bytes_per_rank运行后填写必须与 47 的 collective 与 payload 口径一致
中位收益串行/重叠两行的 median_max_rank_ms、comparison.saved_ms运行后填写判断重叠是否缩短关键路径
尾部与 rank 差异两行的 p95_max_rank_ms、rank_mean_ms运行后填写P95 恶化或 rank 离散增大时继续调度与负载排查
证据出口comparison.overlap_gain_ratio、evidence_level、decision运行后填写该值是配对调度收益,不冒充真实 Trace 的暴露通信比例

相关阅读 ​

完成 collective、等待时间和缓解动作的判断后,可以继续用 NCCL 工具和分布式 benchmark 验证热点是否真正转化为收益。

Released under the MIT License.