已关闭
--cpu-affinity 在容器内绑核会给 sched_setaffinity 传空集合,子进程直接 OSError 退出 #28
崇理战队创建于  8月13日关闭于  8月20日
崇理战队
8月13日 创建

在容器里带 cpu_affinity 跑压测,子进程起来就挂,报的是 OSError: [Errno 22] Invalid argument。追下来是拓扑探测失败之后兜底逻辑没兜住,最后给 sched_setaffinity 传了个空 list。

链路

ascend_npu_burn/runtime/engine.py:71-78

def subprocess_func(index, input_queues, result_queue, args, run_devices):
    setup_logger(args.log_level)
    try:
        set_cpu_flag = False
        if args.cpu_affinity and not set_cpu_flag:
            cpu_affinity = get_cpu_by_device_id(run_devices[index])
            # 0表示当前进程
            os.sched_setaffinity(0, cpu_affinity)  # pylint: disable=E1101

ascend_npu_burn/common/utils.py:209-216

def get_cpu_by_device_id(device_id: int) -> List[int]:
    """根据设备ID获取对应的CPU列表"""
    numa_topology = get_npu_numa_topology()
    cpu_topology = get_numa_configs()
    for numa_id, npu_ids in numa_topology.items():
        if device_id in npu_ids:
            return cpu_topology[numa_id]
    return []

os.sched_setaffinity(0, []) 传空集合是非法的,必抛 OSError: [Errno 22] Invalid argument。这里返回 [] 的路径没有任何保护,直接就送进去了。

什么时候会返回空

get_npu_numa_topology() 靠 lspci 列 19e5 设备来建拓扑(utils.py:162-183)。压测工具的典型运行环境是容器,而昇腾的基础镜像里一般不装 pciutils,_find_command_path('lspci') 返回 None,回落到 /usr/bin/lspci 又不存在,Popen 抛 FileNotFoundError,走到 198-204 行的兜底:

    except Exception as e:
        logger.warning(f"Could not detect NPU topology automatically: {e}")
        # 假设标准的 8 卡机器,前 4 张在 Node 0,后 4 张在 Node 1
        numa_to_npus = defaultdict(list)  # 重置
        for i in range(8):
            node = i // 4
            numa_to_npus[node].append(i)

兜底把卡数写死成 8。于是:

  • 16 卡的 A3 超节点、或者用户 -d 指定了 8 号及以上的卡,device_id in npu_ids 永远不成立 → 返回 [] → 子进程 OSError 退出;
  • 就算卡号在 0-7 内没崩,这个映射也纯粹是猜的,实际拓扑不是 0-3 / 4-7 的机器会把进程绑到错误的 NUMA 节点上。

还有一个索引错配

return cpu_topology[numa_id] 是拿 NUMA node 号当 list 下标用的,但 get_numa_configs() 返回的 list 未必是按 NUMA node 组织的:

  • 正常路径(utils.py:104-119)按 range(node_count) 逐个 append,下标 == node 号,对得上;
  • 但 121-142 行的兜底分支是 for s_id in range(sockets_count) 按 socket 建的,长度 = socket 数。

鲲鹏 920 这类每 socket 2 个 NUMA node 的机器,兜底后 list 长度是 2,而 NPU 读到的 numa_node 可能是 2 或 3,cpu_topology[3] 直接 IndexError;即使不越界,socket 和 NUMA node 也不是一个东西,绑出来的核是错的。

影响

绑核这个功能本来是为了避免跨 NUMA 访存、让压测压出真实算力。现在的情况是:

  • 崩的场景还算好,至少能看见;
  • 不崩但绑错的场景更麻烦——跨 NUMA 访存会让 matmul 这类带宽敏感的用例算力掉下来,而这个工具的用途恰恰是根据算力判断硬件是否故障,等于把好卡判成坏卡。而且 logger.warning 之后就静默降级了,报告里看不出拓扑是猜的。

建议

  1. get_cpu_by_device_id 拿不到有效 CPU 列表时不要返回 [] 让调用方去踩,或者调用方判空后跳过绑核并打一条明确的 warning:
            cpu_affinity = get_cpu_by_device_id(run_devices[index])
            if cpu_affinity:
                os.sched_setaffinity(0, cpu_affinity)
            else:
                logger.warning(
                    f"device {run_devices[index]} 未匹配到 NUMA 拓扑,跳过绑核,"
                    f"本轮性能数据不可用于故障判定"
                )
  1. 拓扑探测优先读 /sys/class/devdrv/(或 npu-smi info -m)而不是依赖容器里未必存在的 lspci;实在探测不到时,兜底不要硬编码 8 卡,按实际 run_devices 的最大卡号来生成,或者干脆放弃绑核。

  2. cpu_topology[numa_id] 改成显式的 dict 映射,别用 list 下标去承载 NUMA node 号;兜底分支按 socket 建 list 的语义也和调用方的假设不一致,建议统一。

顺带

engine.py:74-75 的 set_cpu_flag 定义完就是 False,if args.cpu_affinity and not set_cpu_flag 里的 not set_cpu_flag 恒真,这个变量后面也没再赋过值。看着像是想做"只绑一次",但子进程里本来就只走一次,是不是可以直接删掉?

likedislike
xiangjie10成员
8月13日 评论:

👋 您好,感谢向 MindCluster-AscendNPUBurn 提交 Issue!
🎉 我们已收到您的反馈,感谢你对开源社区的支持!

📅 处理时效 维护团队将在工作日 24 小时内查看并回复您的问题。
🔍 自助排查(推荐优先查看) 在等待回复期间,您可以先查阅仓库README以及历史 Issue 中相似问题的解决方案,多数问题可快速解决。
💡 为了更快定位问题,请您确保 Issue 包含:

  • 清晰的问题描述
  • 可复现的操作步骤
  • 相关日志、截图或环境信息
    我们会尽快跟进,感谢您的理解与配合!
likedislike
xiangjie10成员
8月13日 评论:

/label add triaged

likedislike
ascend-robotascend-robot成员
8月13日 添加了label:triaged
MMilchstraße成员
8月13日 将 yangpeng197 设为负责人
yangpeng197成员
8月18日 评论:

/label add feature

likedislike
ascend-robotascend-robot成员
8月18日 添加了label:feature
Yyangpeng197成员
8月18日 关联了pull request:【Issue】add empty cpu_affinity check
ascend-robotascend-robot成员
8月20日 关闭了 issue
ascend-robotascend-robot成员
8月20日 添加了label:resolved
Xxiangjie10成员
9月9日 issue状态由 TODO 改变为 DONE