已合并
同步开源代码 #3121
c15029001705创建于 6月26日
同步开源代码 #3121
已合并
c15029001705创建于 6月26日
127 个文件变更+5504-2650
Mtest/hccl_vm/CMakeLists.txt+37-10
@@ -85,19 +85,46 @@ include(cmake/third_party/cli11.cmake)
85include(cmake/third_party/spdlog.cmake)85include(cmake/third_party/spdlog.cmake)
86include(cmake/third_party/sqlite3.cmake)86include(cmake/third_party/sqlite3.cmake)
87 87 
88+include_directories(
89+ ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
90+ ${CMAKE_SOURCE_DIR}/third_party
91+ ${CMAKE_SOURCE_DIR}/third_party/yaml-cpp/include
92+)
93+ 
88function(config_hcclvm_install)94function(config_hcclvm_install)
89 install(DIRECTORY DESTINATION "data")95 install(DIRECTORY DESTINATION "data")
90- install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/asset/"96+ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/config/"
91- DESTINATION "."97+ DESTINATION "config")
92- # 文件的权限 (755)98+ install(FILES
93- FILE_PERMISSIONS99+ "${CMAKE_CURRENT_SOURCE_DIR}/asset/allocate_eid.sh"
94- OWNER_READ OWNER_WRITE OWNER_EXECUTE 100+ "${CMAKE_CURRENT_SOURCE_DIR}/asset/generate_cluster_topo.sh"
95- GROUP_READ GROUP_EXECUTE 101+ "${CMAKE_CURRENT_SOURCE_DIR}/asset/generate_server_topo.sh"
102+ "${CMAKE_CURRENT_SOURCE_DIR}/asset/hccl_config.sh"
103+ DESTINATION "script"
104+ PERMISSIONS
105+ OWNER_READ OWNER_WRITE OWNER_EXECUTE
106+ GROUP_READ GROUP_EXECUTE
96 WORLD_READ WORLD_EXECUTE107 WORLD_READ WORLD_EXECUTE
97- # 目录的权限 (755)108+ )
109+ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/asset/cluster_model/config/cluster"
110+ DESTINATION "config"
98 DIRECTORY_PERMISSIONS111 DIRECTORY_PERMISSIONS
99- OWNER_READ OWNER_WRITE OWNER_EXECUTE 112+ OWNER_READ OWNER_WRITE OWNER_EXECUTE
100- GROUP_READ GROUP_EXECUTE 113+ GROUP_READ GROUP_EXECUTE
114+ WORLD_READ WORLD_EXECUTE
115+ )
116+ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/asset/cluster_model/config/server_or_pod"
117+ DESTINATION "config"
118+ DIRECTORY_PERMISSIONS
119+ OWNER_READ OWNER_WRITE OWNER_EXECUTE
120+ GROUP_READ GROUP_EXECUTE
121+ WORLD_READ WORLD_EXECUTE
122+ )
123+ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/asset/cluster_model/topo_meta"
124+ DESTINATION "config"
125+ DIRECTORY_PERMISSIONS
126+ OWNER_READ OWNER_WRITE OWNER_EXECUTE
127+ GROUP_READ GROUP_EXECUTE
101 WORLD_READ WORLD_EXECUTE128 WORLD_READ WORLD_EXECUTE
102 )129 )
103endfunction()130endfunction()
@@ -116,7 +143,7 @@ include(CPack)
116if(BUILD_DEVICE_ARM)143if(BUILD_DEVICE_ARM)
117 message(STATUS "BUILD_DEVICE_ARM start")144 message(STATUS "BUILD_DEVICE_ARM start")
118 set(DEVICE_ARM_BUILD_DIR ${CMAKE_SOURCE_DIR}/build_device)145 set(DEVICE_ARM_BUILD_DIR ${CMAKE_SOURCE_DIR}/build_device)
119- set(DEVICE_ARM_INSTALL_DIR ${CMAKE_SOURCE_DIR}/hccl_vm_install/device)146+ set(DEVICE_ARM_INSTALL_DIR ${CMAKE_SOURCE_DIR}/hccl_vm_install)
120 include(ExternalProject)147 include(ExternalProject)
121 ExternalProject_Add(device_arm_external148 ExternalProject_Add(device_arm_external
122 SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/device_arm149 SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/device_arm
Mtest/hccl_vm/README.md+66-22
@@ -4,6 +4,8 @@
4 4 
5HCCL-VM是面向华为昇腾NPU卡的高性能集合通信的虚拟执行环境,该工具旨在无真实昇腾硬件的条件下,实现HCCL集合通信算子的开发和功能验证。5HCCL-VM是面向华为昇腾NPU卡的高性能集合通信的虚拟执行环境,该工具旨在无真实昇腾硬件的条件下,实现HCCL集合通信算子的开发和功能验证。
6 6 
7+![hccl-vm GIF](docs/hccl-vm.gif)
8+ 
7## 2. 前置依赖9## 2. 前置依赖
8 10 
9| | |11| | |
@@ -40,13 +42,13 @@ cd /home/workspace
40# 2. 下载依赖源码42# 2. 下载依赖源码
41git clone https://gitcode.com/cann/hccl.git43git clone https://gitcode.com/cann/hccl.git
42git clone https://gitcode.com/cann/hcomm.git44git clone https://gitcode.com/cann/hcomm.git
43-git clone https://gitcode.com/zhupc158/CheckerL2.git
44 45 
45# 3. 安装第三方依赖46# 3. 安装第三方依赖
46sudo apt-get update47sudo apt-get update
47sudo apt install build-essential cmake libsqlite3-dev rdma-core libibverbs-dev pkg-config gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user-static binfmt-support48sudo apt install build-essential cmake libsqlite3-dev rdma-core libibverbs-dev pkg-config gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user-static binfmt-support
48 49 
49-# 4. 编译HCCL-VM工具50+# 4. 编译HCCL-VM工具,下载hcomm代码之后,工具源码所在路径:/home/workspace/hcomm/test/hccl_vm
51+cd /home/workspace/hcomm/test/hccl_vm
50source /home/Ascend/cann/set_env.sh52source /home/Ascend/cann/set_env.sh
51export HCCL_CODE_HOME=/home/workspace/hccl53export HCCL_CODE_HOME=/home/workspace/hccl
52export HCOMM_CODE_HOME=/home/workspace/hcomm54export HCOMM_CODE_HOME=/home/workspace/hcomm
@@ -65,18 +67,22 @@ export HCOMM_CODE_HOME=/home/workspace/hcomm
65 67 
66```bash68```bash
67# 进入工具安装目录69# 进入工具安装目录
68-cd /home/workspace/CheckerL2/hccl_vm_install70+cd /home/workspace/hcomm/test/hccl_vm/hccl_vm_install
69source /home/Ascend/cann/set_env.sh71source /home/Ascend/cann/set_env.sh
70export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH72export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH
71-export RANK_TABLE_FILE=$(pwd)/ranktable.json73+export RANK_TABLE_FILE=$(pwd)/data/ranktable.json
72export HCCL_OP_EXPANSION_MODE="CCU_SCHED"74export HCCL_OP_EXPANSION_MODE="CCU_SCHED"
73```75```
74 76 
752. 执行772. 执行
76 78 
79+ 
77```bash80```bash
78# 进入工具安装目录81# 进入工具安装目录
79-cd /home/workspace/CheckerL2/hccl_vm_install82+cd /home/workspace/hcomm/test/hccl_vm/hccl_vm_install
83+ 
84+# 需要进入到新的bin文件目录下执行hccl-vm
85+cd ./bin
80 86 
81# 选择昇腾集群拓扑配置文件,启动工具,初始化集群环境,进入工具命令行87# 选择昇腾集群拓扑配置文件,启动工具,初始化集群环境,进入工具命令行
82./hccl-vm start ascend950_cluster_32_server_normal.yaml88./hccl-vm start ascend950_cluster_32_server_normal.yaml
@@ -106,7 +112,7 @@ AICPU展开模式需要将算法展开步骤放到设备侧执行,因此hccl-v
1061. HCCL设备侧符号编译、安装、拷贝等1121. HCCL设备侧符号编译、安装、拷贝等
107 113 
108```bash114```bash
109-cd /home/workspace/CheckerL2115+cd /home/workspace/hcomm/test/hccl_vm/
110bash ./build_pkg.sh116bash ./build_pkg.sh
111```117```
112 118 
@@ -114,10 +120,10 @@ bash ./build_pkg.sh
114 120 
115```bash121```bash
116# 进入工具安装目录122# 进入工具安装目录
117-cd /home/workspace/CheckerL2/hccl_vm_install123+cd /home/workspace/hcomm/test/hccl_vm/hccl_vm_install
118source /home/Ascend/cann/set_env.sh124source /home/Ascend/cann/set_env.sh
119export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH125export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH
120-export RANK_TABLE_FILE=$(pwd)/ranktable.json126+export RANK_TABLE_FILE=$(pwd)/data/ranktable.json
121export HCCL_OP_EXPANSION_MODE="AI_CPU"127export HCCL_OP_EXPANSION_MODE="AI_CPU"
122```128```
123 129 
@@ -125,7 +131,10 @@ export HCCL_OP_EXPANSION_MODE="AI_CPU"
125 131 
126```bash132```bash
127# 进入工具安装目录133# 进入工具安装目录
128-cd /home/workspace/CheckerL2/hccl_vm_install134+cd /home/workspace/hcomm/test/hccl_vm/hccl_vm_install
135+ 
136+# 需要进入到新的bin文件目录下执行hccl-vm
137+cd ./bin
129 138 
130# 选择昇腾集群拓扑配置文件,启动工具,初始化集群环境,进入工具命令行139# 选择昇腾集群拓扑配置文件,启动工具,初始化集群环境,进入工具命令行
131./hccl-vm start ascend950_cluster_32_server_normal.yaml140./hccl-vm start ascend950_cluster_32_server_normal.yaml
@@ -154,10 +163,10 @@ cd /home/workspace/CheckerL2/hccl_vm_install
154 163 
155```bash164```bash
156# 进入工具安装目录165# 进入工具安装目录
157-cd /home/workspace/CheckerL2/hccl_vm_install166+cd /home/workspace/hcomm/test/hccl_vm/hccl_vm_install
158source /home/Ascend/cann/set_env.sh167source /home/Ascend/cann/set_env.sh
159export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH168export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH
160-export RANK_TABLE_FILE=$(pwd)/ranktable.json169+export RANK_TABLE_FILE=$(pwd)/data/ranktable.json
161export HCCL_OP_EXPANSION_MODE="AIV"170export HCCL_OP_EXPANSION_MODE="AIV"
162```171```
163 172 
@@ -165,7 +174,10 @@ export HCCL_OP_EXPANSION_MODE="AIV"
165 174 
166```bash175```bash
167# 进入工具安装目录176# 进入工具安装目录
168-cd /home/workspace/CheckerL2/hccl_vm_install177+cd /home/workspace/hcomm/test/hccl_vm/hccl_vm_install
178+ 
179+# 需要进入到新的bin文件目录下执行hccl-vm
180+cd ./bin
169 181 
170# 选择昇腾集群拓扑配置文件,启动工具,初始化集群环境,进入工具命令行182# 选择昇腾集群拓扑配置文件,启动工具,初始化集群环境,进入工具命令行
171./hccl-vm start ascend950_cluster_32_server_normal.yaml183./hccl-vm start ascend950_cluster_32_server_normal.yaml
@@ -373,7 +385,7 @@ server_list:
373 385 
374用户在昇腾集群环境中,需要根据待执行算子所需的通信域不同,选择不同的通信域配置文件。386用户在昇腾集群环境中,需要根据待执行算子所需的通信域不同,选择不同的通信域配置文件。
375 387 
376-工具提供了hccl-vm mock-comm命令读取和配置算子通信域配置文件。通信域配置文件格式为yaml,路径为hccl_vm_install/cluster_model/topo_meta。若目录中没有对应的通信域配置文件,则用户需要先创建一个。388+工具提供了hccl-vm mock-comm命令读取和配置算子通信域配置文件。通信域配置文件格式为yaml,路径为hccl_vm_install/config/topo_meta。若目录中没有对应的通信域配置文件,则用户需要先创建一个。
377 389 
378hccl-vm工具支持非对称拓扑通信域配置。如下所示:390hccl-vm工具支持非对称拓扑通信域配置。如下所示:
379 391 
@@ -539,19 +551,37 @@ remove_files_by_prefix() {
539 return 0551 return 0
540}552}
541 553 
542-# 清理执行目录的冗余文件554+# 清理 data/ 目录的冗余文件(CCU 模式下生成的临时文件)
543-remove_files_by_prefix "sqe_info_rank_"555+cd "${HCCL_VM_INSTALL_DIR}/data" 2>/dev/null && {
544-remove_files_by_prefix "mc_instr_info_rank_"556+ remove_files_by_prefix "sqe_info_rank_"
545-rm -f "all_rank_input_output.txt"557+ remove_files_by_prefix "mc_instr_info_rank_"
558+ rm -f "all_rank_input_output.txt"
559+ cd "${HCCL_VM_INSTALL_DIR}"
560+}
546 561 
547# 设置CANN环境变量562# 设置CANN环境变量
548source /home/Ascend/cann/set_env.sh563source /home/Ascend/cann/set_env.sh
549 564 
565+# 关闭hccl心跳功能
566+export HCCL_DFS_CONFIG=cluster_heartbeat:off
567+ 
568+# 设置 HCCL-VM 安装路径,基于脚本自身位置推断(兼容 bin/ 与 script/ 子目录)
569+_INSTALL_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
570+case "$(basename "${_INSTALL_SCRIPT_DIR}")" in
571+ bin|script)
572+ export HCCL_VM_INSTALL_DIR="$(dirname "${_INSTALL_SCRIPT_DIR}")"
573+ ;;
574+ *)
575+ export HCCL_VM_INSTALL_DIR="${_INSTALL_SCRIPT_DIR}"
576+ ;;
577+esac
578+unset _INSTALL_SCRIPT_DIR
579+ 
550# 配置LD_LIBRARY_PATH580# 配置LD_LIBRARY_PATH
551export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH581export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH
552 582 
553-# 设置 ranktable.json文件路径583+# 设置 ranktable.json文件路径 (与 mock-comm 生成路径保持一致)
554-export RANK_TABLE_FILE=$(pwd)/ranktable.json584+export RANK_TABLE_FILE=${HCCL_VM_INSTALL_DIR}/data/ranktable.json
555 585 
556# 设置日志级别586# 设置日志级别
557export ASCEND_GLOBAL_LOG_LEVEL=1587export ASCEND_GLOBAL_LOG_LEVEL=1
@@ -620,7 +650,8 @@ Checker插件,即算法分析器插件:功能是将hccl生成的所有task
620Checker插件正处于新旧交替阶段,Checker V3为原Checker的重构版,主要提高了校验性能,在默认情况下将会运行新Checker(Checker V3),可以通过修改Checker的`manifest.json`文件中的配置参数进行调整。650Checker插件正处于新旧交替阶段,Checker V3为原Checker的重构版,主要提高了校验性能,在默认情况下将会运行新Checker(Checker V3),可以通过修改Checker的`manifest.json`文件中的配置参数进行调整。
621 651 
622```bash652```bash
623-# 配置文件位于 /pathto/hccl_vm_install/plugin/validate/checker/manifest.json653+ 
654+# 配置文件位于 /pathto/hccl_vm_install/plugin/checker/manifest.json
624 655 
625{656{
626 "name": "checker", // Checker插件名657 "name": "checker", // Checker插件名
@@ -646,7 +677,7 @@ Checker插件正处于新旧交替阶段,Checker V3为原Checker的重构版
646```json677```json
647{678{
648 "version": "2.0",679 "version": "2.0",
649- "topo_file_path": "/home/myuser/workspace/CheckerL2/hccl_vm_install/topo.json"680+ "topo_file_path": "/home/myuser/workspace/CheckerL2/hccl_vm_install/data/topo.json"
650}681}
651```682```
652 683 
@@ -711,6 +742,19 @@ data_size(Bytes): | aveg_time(us): | alg_bandwidth(GB/s): | check_result:
711```742```
712 743 
713---744---
745+### 4.10 大块内存复用(仅校验模式)
746+ 
747+仅校验模式用于大规模集群仅运行 Checker 校验的场景。开启后,单块 200MB 到 4GB 的大内存申请复用同一块 4GB 共享区 `HcclCommPool`,各 rank 共享、允许互相覆盖,以此大幅降低 `/dev/shm` 占用。此时大块内容不保证正确,仅适用于不读取缓冲区数据的 Checker V3 校验链路,需要数值正确的结果时请勿开启。
748+ 
749+仅校验模式是会话级开关,在 `start` 子命令后追加 `--check-only` 显式开启;不加时为默认的普通模式,大块走真实独立分配,正确性无损。小于 200MB 的申请始终走真实分配,单块大于 4GB 在仅校验模式下直接报错拒绝。仅校验模式与 Runner 不互斥,但在仅校验模式开启时安装 Runner,大块复用仍会生效、可能覆盖 Runner 数据,工具会打印告警。
750+ 
751+```bash
752+ 
753+# 启动工具时开启仅校验模式
754+./hccl-vm start ascend950_cluster_32_server_normal.yaml --check-only
755+```
756+ 
757+***
714 758 
715## 5 附录759## 5 附录
716 760 
@@ -742,4 +786,4 @@ data_size(Bytes): | aveg_time(us): | alg_bandwidth(GB/s): | check_result:
742---786---
743 787 
744**文档版本**:v1.0788**文档版本**:v1.0
745-**最后更新**:2026-06-22789+**最后更新**:2026-06-26
Mtest/hccl_vm/asset/allocate_eid.sh+2-2
@@ -51,8 +51,8 @@ warn_msg() {
51}51}
52 52 
53SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"53SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
54-BASE_DIR="${SCRIPT_DIR}"54+BASE_DIR="$(dirname "${SCRIPT_DIR}")"
55-OUTPUT_DIR="${BASE_DIR}/cluster_model/network/cluster"55+OUTPUT_DIR="${BASE_DIR}/config/network/cluster"
56 56 
57show_usage() {57show_usage() {
58 cat << EOF58 cat << EOF
Mtest/hccl_vm/asset/cluster_model/config/cluster/ascend950_cluster_128_server_normal.yaml+0-0
The file is empty
Mtest/hccl_vm/asset/cluster_model/topo_meta/12_2_4.yaml+0-0
The file is empty
Mtest/hccl_vm/asset/generate_cluster_topo.sh+7-7
@@ -46,15 +46,15 @@ warn_msg() {
46}46}
47 47 
48SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"48SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
49-BASE_DIR="${SCRIPT_DIR}"49+BASE_DIR="$(dirname "${SCRIPT_DIR}")"
50 50 
51-CLUSTER_CONFIG_DIR="${BASE_DIR}/cluster_model/config/cluster"51+CLUSTER_CONFIG_DIR="${BASE_DIR}/config/cluster"
52-SUPERPOD_CONFIG_DIR="${BASE_DIR}/cluster_model/config/super_pod"52+SUPERPOD_CONFIG_DIR="${BASE_DIR}/config/super_pod"
53-SERVER_CONFIG_DIR="${BASE_DIR}/cluster_model/config/server_or_pod"53+SERVER_CONFIG_DIR="${BASE_DIR}/config/server_or_pod"
54SERVER_TOPO_BANK_DIR="${BASE_DIR}/topo_bank/server_topo_bank"54SERVER_TOPO_BANK_DIR="${BASE_DIR}/topo_bank/server_topo_bank"
55-GENERATE_TOPO_SCRIPT="${BASE_DIR}/generate_server_topo.sh"55+GENERATE_TOPO_SCRIPT="${SCRIPT_DIR}/generate_server_topo.sh"
56-ALLOCATE_EID_SCRIPT="${BASE_DIR}/allocate_eid.sh"56+ALLOCATE_EID_SCRIPT="${SCRIPT_DIR}/allocate_eid.sh"
57-DEFAULT_OUTPUT_DIR="${BASE_DIR}/cluster_model/network/cluster"57+DEFAULT_OUTPUT_DIR="${BASE_DIR}/config/network/cluster"
58 58 
59resolve_path() {59resolve_path() {
60 local input="$1"60 local input="$1"
Mtest/hccl_vm/asset/generate_server_topo.sh+3-3
@@ -54,10 +54,10 @@ warn_msg() {
54}54}
55 55 
56SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"56SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
57-BASE_DIR="${SCRIPT_DIR}"57+BASE_DIR="$(dirname "${SCRIPT_DIR}")"
58 58 
59-SERVER_CONFIG_DIR="${BASE_DIR}/cluster_model/config/server_or_pod"59+SERVER_CONFIG_DIR="${BASE_DIR}/config/server_or_pod"
60-DEFAULT_OUTPUT_DIR="${BASE_DIR}/cluster_model/network/server_or_pod"60+DEFAULT_OUTPUT_DIR="${BASE_DIR}/config/network/server_or_pod"
61 61 
62resolve_path() {62resolve_path() {
63 local input="$1"63 local input="$1"
Mtest/hccl_vm/asset/hccl_config.sh+13-7
@@ -41,14 +41,20 @@ rm -f "all_rank_input_output.txt"
41# 设置CANN环境变量41# 设置CANN环境变量
42source /home/teamserver/workspace/Ascend/cann/set_env.sh42source /home/teamserver/workspace/Ascend/cann/set_env.sh
43 43 
44-# 设置 HCCL-VM 安装路径44+# 设置 HCCL-VM 安装路径,基于脚本自身位置推断(兼容 bin/ 与 script/ 子目录)
45-export HCCL_VM_INSTALL_DIR=$(pwd)45+_INSTALL_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
46+case "$(basename "${_INSTALL_SCRIPT_DIR}")" in
47+ bin|script)
48+ export HCCL_VM_INSTALL_DIR="$(dirname "${_INSTALL_SCRIPT_DIR}")"
49+ ;;
50+ *)
51+ export HCCL_VM_INSTALL_DIR="${_INSTALL_SCRIPT_DIR}"
52+ ;;
53+esac
54+unset _INSTALL_SCRIPT_DIR
46 55 
47-# LD_LIBRARY_PATH56+# ranktable.json文件路径 (与 mock-comm 生成路径保持一致)
48-export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$ASCEND_HOME_PATH/devlib:$LD_LIBRARY_PATH57+export RANK_TABLE_FILE=${HCCL_VM_INSTALL_DIR}/data/ranktable.json
49- 
50-# 设置 ranktable.json文件路径
51-export RANK_TABLE_FILE=${HCCL_VM_INSTALL_DIR}/ranktable.json
52 58 
53# 设置日志级别59# 设置日志级别
54export ASCEND_GLOBAL_LOG_LEVEL=160export ASCEND_GLOBAL_LOG_LEVEL=1
Mtest/hccl_vm/build.sh+2-0
@@ -7,6 +7,8 @@
7# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.7# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8# See LICENSE in the root of the software repository for the full text of the License.8# See LICENSE in the root of the software repository for the full text of the License.
9 9 
10+set -e
11+ 
10function usage() {12function usage() {
11 echo "Usage:"13 echo "Usage:"
12 echo " sh build.sh [-h | --help]"14 echo " sh build.sh [-h | --help]"
Mtest/hccl_vm/build_pkg.sh+1-1
@@ -166,7 +166,7 @@ echo "正在拷贝驱动依赖库文件..."
166 166 
167# 1.定义源路径和目标路径167# 1.定义源路径和目标路径
168SOURCE_DIR="$ASCEND_INSTALL_PATH/cann/x86_64-linux/devlib/device"168SOURCE_DIR="$ASCEND_INSTALL_PATH/cann/x86_64-linux/devlib/device"
169-TARGET_DIR="$HCCL_VM_PATH/hccl_vm_install/device/lib64"169+TARGET_DIR="$HCCL_VM_PATH/hccl_vm_install/lib/aarch64"
170 170 
171# 2.检查目标目录是否存在,不存在则创建171# 2.检查目标目录是否存在,不存在则创建
172if [ ! -d "$TARGET_DIR" ]; then172if [ ! -d "$TARGET_DIR" ]; then
Atest/hccl_vm/config/log_config.yaml+39-0
@@ -0,0 +1,39 @@
1+hccl_vm:
2+ console_level: 2
3+ file_level: 1
4+ max_file_size: 52428800
5+ max_files: 65535
6+ file_path: logs
7+ file_base_name: app_log
8+ file_suffix: .log
9+ enable_compress: false
10+ 
11+proxy:
12+ console_level: 2
13+ file_level: 1
14+ max_file_size: 52428800
15+ max_files: 65535
16+ file_path: logs/proxy
17+ file_base_name: app_log
18+ file_suffix: .log
19+ enable_compress: false
20+ 
21+runner:
22+ console_level: 6
23+ file_level: 2
24+ max_file_size: 52428800
25+ max_files: 65535
26+ file_path: logs/runner
27+ file_base_name: app_log
28+ file_suffix: .log
29+ enable_compress: false
30+ 
31+checker:
32+ console_level: 2
33+ file_level: 0
34+ max_file_size: 52428800
35+ max_files: 65535
36+ file_path: logs/checker
37+ file_base_name: app_log
38+ file_suffix: .log
39+ enable_compress: false
Atest/hccl_vm/docs/insightV3_guide.md+328-0
@@ -0,0 +1,328 @@
1+# HVRM Insight V3 使用指南
2+ 
3+## 1. 概述
4+ 
5+HVRM Insight V3 当前主要支持 `DAGView` 任务图查看。本文档围绕 DAGView 介绍数据准备、页面访问、数据集选择、DAG 图浏览和节点检索等常用操作。
6+ 
7+其他页面及高级联动能力暂未实现。
8+ 
9+---
10+ 
11+## 2. 前置准备
12+ 
13+使用 Insight V3 前,请确认 Checker 已完成运行,并已生成 Insight 数据。
14+ 
15+如需配置 Checker 数据输出,请确认 Checker 配置文件中已开启 Insight dump:
16+ 
17+```json
18+# 配置文件位于 /pathto/hccl_vm_install/plugin/checker/manifest.json
19+ 
20+{
21+ ...
22+ "setting": { // Checker插件配置项
23+ ...
24+ "enable_insight_dump": true, // 是否启用可视化数据输出(默认为关闭状态)
25+ "enable_memory_snapshot_dump": false // 是否启用可视化内存快照数据输出(默认关闭,仅支持老Checker,需要先开启可视化数据输出"enable_insight_dump")
26+ }
27+}
28+```
29+ 
30+DAGView 至少需要以下数据文件:
31+ 
32+```text
33+<dataset_name>/
34+├── manifest.json
35+└── graph/
36+ ├── graph.msgpack
37+ └── layout.msgpack
38+```
39+ 
40+其中,`manifest.json` 用于读取数据集元信息,`graph.msgpack``layout.msgpack` 用于绘制 DAG 任务图。
41+ 
42+---
43+ 
44+## 3. 编译与安装 Insight 插件
45+ 
46+Insight V3 当前前端源码位于:
47+ 
48+```text
49+{hccl_vm目录}/src/plugin/insight/frontend_v3
50+```
51+ 
52+在安装 Insight 插件前,请先完成前端编译,再执行 HCCL VM 的构建安装流程。
53+ 
54+推荐步骤如下:
55+ 
56+1. 进入前端目录:
57+ 
58+```bash
59+cd {hccl_vm目录}/src/plugin/insight/frontend_v3
60+```
61+ 
62+2. 安装前端依赖:
63+ 
64+```bash
65+npm install
66+```
67+ 
68+3. 编译前端产物:
69+ 
70+```bash
71+npm run build
72+```
73+ 
74+前端编译完成后,会生成 `src/plugin/insight/dist/` 目录。
75+ 
76+4. 回到 HCCL_VM 根目录,执行 `build.sh` 编译并安装 hccl-vm:
77+ 
78+```bash
79+cd {HCCL_VM目录}
80+bash build.sh --package-path <ASCEND_CANN_PATH> --hcomm-path <HCOMM_CODE_PATH>
81+```
82+ 
83+如需打包安装目录,可附加 `--pkg`。若当前构建场景要求 AICPU / AIV / FULL 模式,可按项目常规流程追加 `--aicpu``--aiv``--full` 参数。
84+ 
85+---
86+ 
87+## 4. Insight 插件配置与启停
88+ 
89+Insight 插件配置文件位于:
90+ 
91+```text
92+hccl_vm_install/plugin/visualization/insight/manifest.json
93+```
94+ 
95+当前默认配置如下:
96+ 
97+```json
98+{
99+ "name": "insight",
100+ "version": "1.0.0",
101+ "entry": "python3 server.py",
102+ "dependency": {
103+ "min_core_version": "1.0.0"
104+ },
105+ "setting": {
106+ "dist_path": "./dist",
107+ "data_path": "../../checker/data/insight",
108+ "topo_config_path": "../../../../../asset/cluster_model/config/cluster",
109+ "port": 8080
110+ }
111+}
112+```
113+ 
114+常用字段说明如下:
115+ 
116+| 字段 | 说明 |
117+|------|------|
118+| `entry` | Insight 插件启动命令,当前通过 `python3 server.py` 启动服务 |
119+| `setting.dist_path` | 前端静态资源目录,默认读取 `./dist` |
120+| `setting.data_path` | Insight 数据目录,默认指向 Checker 输出的 `data/insight` |
121+| `setting.topo_config_path` | 集群拓扑配置目录 |
122+| `setting.port` | Insight 服务端口,默认 `8080` |
123+ 
124+若需要修改端口或数据目录,可直接编辑该 `manifest.json` 中的 `setting` 字段。
125+ 
126+插件安装与卸载命令如下:
127+ 
128+```bash
129+# 安装并启动 Insight 插件
130+hccl-vm plugin install @insight
131+ 
132+# 卸载 Insight 插件
133+hccl-vm plugin uninstall @insight
134+```
135+ 
136+---
137+ 
138+## 5. 打开 Insight
139+ 
140+若 Insight 服务已启动,请直接在浏览器中打开服务地址,例如:
141+ 
142+```text
143+http://localhost:8080
144+```
145+ 
146+若需要通过插件启动 Insight,可执行:
147+ 
148+```bash
149+hccl-vm plugin install @insight
150+```
151+ 
152+插件启动后,终端会输出访问地址。请在浏览器中打开该地址进入 Insight 页面。
153+ 
154+![启动 Insight 并打开页面](insight_image/V3_image/launch.gif)
155+ 
156+---
157+ 
158+## 6. 选择数据集
159+ 
160+打开 Insight 后,默认进入 `总览` 页面。请按以下步骤选择需要分析的数据集:
161+ 
162+1. 在中间的数据集表格中查看数据集列表。
163+2. 点击目标数据集。
164+3. 在左侧 Rank 树中确认需要查看的 Rank。
165+4. 如 DAG 图规模较大,可先减少 Rank 选择范围。
166+5. 点击右侧 `进入关联视图`
167+ 
168+![选择数据集并进入关联视图](insight_image/V3_image/dagView_V3.gif)
169+ 
170+建议首次分析时先选择少量 Rank,确认分析方向后再逐步扩大 Rank 范围。
171+ 
172+---
173+ 
174+## 7. 进入 DAGView
175+ 
176+进入 `关联` 页面后,重点查看下半部分的 `DAGView 任务视图`
177+ 
178+页面主要区域如下:
179+ 
180+| 区域 | 功能 |
181+|------|------|
182+| 左侧面板 | 选择 Rank、搜索节点 |
183+| 中间下方 | 展示 DAG 任务图 |
184+| 右侧详情栏 | 查看当前节点详情 |
185+ 
186+若上方内存视图为空,不影响下方 DAGView 的查看和使用。
187+ 
188+---
189+ 
190+## 8. 查看 DAG 任务图
191+ 
192+DAG 任务图由泳道、节点和箭头组成:
193+ 
194+| 元素 | 说明 |
195+|------|------|
196+| 泳道 | 表示一个执行队列,通常显示为 `Rank / Stream / Queue` |
197+| 节点 | 表示一个任务,例如数据搬运、Reduce、Record、Wait 等 |
198+| 箭头 | 表示任务之间的依赖关系 |
199+| Loop 虚线框 | 表示一个 Loop 区域,会用虚线框圈住该 Loop 内相关节点,并在框外显示 `Loop` 标记 |
200+ 
201+常见节点类型如下:
202+ 
203+| 节点类型 | 含义 |
204+|----------|------|
205+| `TRANS_MEM` | 数据搬运任务 |
206+| `REDUCE` | Reduce 任务 |
207+| `RECORD` | Notify record 任务 |
208+| `WAIT` | Notify wait 任务 |
209+| `CCU_GRAPH` | CCU 图任务 |
210+| `AIV_GRAPH` | AIV 图任务 |
211+ 
212+推荐查看方式:
213+ 
214+1. 按箭头方向查看任务依赖顺序。
215+2. 点击关注的任务节点。
216+3. 在右侧详情栏查看该节点所属的 Rank、Stream、Queue 及节点详细信息。
217+4. 继续查看该节点的父节点和子节点,追踪上下游依赖。
218+ 
219+关于 Loop 展示,建议额外关注以下信息:
220+ 
221+1. 若 DAG 中存在 Loop,页面会自动在任务图上叠加 Loop 虚线框。
222+2. 虚线框会覆盖该 Loop 的起点、终点及循环体内部节点,便于快速识别循环边界。
223+3. 点击 Loop 胶囊后,可直接定位到对应的 Loop Start 节点。
224+4. 选中 Loop 内部节点后,右侧详情栏会展示该节点所属的 Loop 信息和嵌套链路。
225+ 
226+![在 DAGView 中浏览任务图并查看节点详情](insight_image/V3_image/dagView_V3_2.gif)
227+ 
228+---
229+ 
230+## 9. 画布操作
231+ 
232+DAGView 画布支持以下操作:
233+ 
234+| 操作 | 说明 |
235+|------|------|
236+| 移动画布 | 拖拽空白区域 |
237+| 放大 / 缩小 | 使用鼠标滚轮,或点击右下角 `+` / `-` |
238+| 重置缩放 | 点击右下角百分比按钮 |
239+| 选中节点 | 点击目标节点 |
240+ 
241+当图规模较大时,建议先缩小 Rank 选择范围,再放大局部区域查看依赖。
242+ 
243+---
244+ 
245+## 10. 查看节点详情
246+ 
247+点击 DAG 节点后,右侧详情栏会展示节点信息。常用信息如下:
248+ 
249+| 区块 | 说明 |
250+|------|------|
251+| 节点概览 | 节点 ID、任务类型、Rank、Stream、Queue |
252+| Loop 信息 | 当前节点所属 Loop、Loop 次数、指令范围、Loop 边界等 |
253+| 父节点 | 当前节点依赖的上游节点 |
254+| 子节点 | 依赖当前节点的下游节点 |
255+| 节点语义 | Notify、Task 元数据、Memory Slices 等 |
256+| 原始 JSON | 当前节点的完整原始信息 |
257+ 
258+排查依赖关系时,建议优先查看 `父节点``子节点`;排查循环结构时,可重点查看 `Loop 信息`;排查数据搬运相关问题时,可重点查看 `Memory Slices`
259+ 
260+其中 `Memory Slices` 区块有以下展示规则:
261+ 
262+1. 普通内存 Slice 会显示 `rank / type / offset / size`
263+2. 若 Slice 属于 `MS_CCU`,Insight V3 会优先显示 `MSID`,而不是底层抽象 offset。
264+3. 对于 batch 类任务,若存在多个 `MS_CCU` Slice,会自动合并为一个汇总卡片。
265+4. 汇总卡片会显示类似 `共计使用8个MSID` 的说明。
266+5. 展开 `MSID 明细` 后,可逐项查看每个 `MSID` 对应的 `id``size`
267+ 
268+---
269+ 
270+## 11. 搜索节点
271+ 
272+左侧 `搜索` 面板可用于快速定位 DAG 节点。
273+ 
274+支持的搜索字段如下:
275+ 
276+| 字段 | 使用场景 |
277+|------|----------|
278+| `taskId` | 已知节点 ID 时使用 |
279+| `taskType` | 按任务类型查找,例如 `TRANS_MEM` |
280+| `notifyId` | 按 notify id 查找 |
281+ 
282+操作步骤:
283+ 
284+1. 在左侧搜索面板选择搜索字段。
285+2. 输入完整关键词。
286+3. 点击搜索结果。
287+4. DAGView 会自动定位并选中对应节点。
288+ 
289+当前搜索为精确匹配。若未找到结果,请确认关键词是否完整,以及目标 Rank 是否已被勾选。
290+ 
291+---
292+ 
293+## 12. 推荐分析流程
294+ 
295+建议按以下流程进行 DAGView 分析:
296+ 
297+1.`总览` 页面选择数据集。
298+2. 在左侧 Rank 树中选择少量 Rank。
299+3. 点击 `进入关联视图`
300+4. 在 DAGView 中查看整体节点和依赖箭头。
301+5. 点击关注节点并查看右侧详情。
302+6. 通过父节点和子节点追踪上下游依赖。
303+7. 使用搜索功能快速定位已知节点。
304+ 
305+---
306+ 
307+## 13. 常见问题
308+ 
309+### 页面没有数据
310+ 
311+请确认 Insight 服务地址是否正确,并确认 Checker 已生成 Insight 数据。
312+ 
313+### 进入关联页后 DAG 为空
314+ 
315+请确认数据集中存在以下文件:
316+ 
317+```text
318+graph/graph.msgpack
319+graph/layout.msgpack
320+```
321+ 
322+### DAG 图过于密集
323+ 
324+建议先减少左侧 Rank 勾选数量,只查看部分 Rank,再放大局部区域进行分析。
325+ 
326+### 搜索不到节点
327+ 
328+请确认搜索关键词完整,并确认目标节点所在 Rank 已被勾选。
Mtest/hccl_vm/docs/insight_guide.md+17-1
@@ -18,7 +18,7 @@ HVRM Insight 是一个分布式算子可视化分析工具,用于对 HCCL 集
18 18 
19HVRM Insight 依赖 Checker 插件输出的数据文件。使用前,请确保已完整运行过 Checker,并在 Checker 的配置文件中开启以下两个数据输出开关:19HVRM Insight 依赖 Checker 插件输出的数据文件。使用前,请确保已完整运行过 Checker,并在 Checker 的配置文件中开启以下两个数据输出开关:
20 20 
21-Checker配置文件默认位于`/path/to/hccl_vm_install/plugin/validate/checker/manifest.json`21+Checker配置文件默认位于`/path/to/hccl_vm_install/plugin/checker/manifest.json`
22 22 
23```json23```json
24"setting": {24"setting": {
@@ -83,14 +83,20 @@ python3 serve.py
83 83 
84浏览所有数据集,点击选择目标数据集,查看算子信息和统计数据,然后进入关联分析页面。84浏览所有数据集,点击选择目标数据集,查看算子信息和统计数据,然后进入关联分析页面。
85 85 
86+![Dashboard 操作演示](insight_image/dashboard.gif)
87+ 
86### 场景二:联动分析内存与任务图(MemView)88### 场景二:联动分析内存与任务图(MemView)
87 89 
88通过内存时间线和 DAG 任务图的联动,定位某个 Step 的内存操作、查看节点详情、追溯数据来源。90通过内存时间线和 DAG 任务图的联动,定位某个 Step 的内存操作、查看节点详情、追溯数据来源。
89 91 
92+![MemView 操作演示](insight_image/memView.gif)
93+ 
90### 场景三:查看报错并跳转到问题现场(Analytic)94### 场景三:查看报错并跳转到问题现场(Analytic)
91 95 
92在 Analytic 页面或 MemView 左侧报错列表中查看错误详情,然后一键跳转到关联的 DAG 节点或内存上下文。96在 Analytic 页面或 MemView 左侧报错列表中查看错误详情,然后一键跳转到关联的 DAG 节点或内存上下文。
93 97 
98+![错误定位操作演示](insight_image/error.gif)
99+ 
94---100---
95 101 
96## 页面详解102## 页面详解
@@ -103,8 +109,12 @@ Dashboard 是使用工具的起点,页面从左到右分为三个区域:
103- **中间**:数据集列表。点击某一行选中数据集,左侧和右侧同步刷新109- **中间**:数据集列表。点击某一行选中数据集,左侧和右侧同步刷新
104- **右侧**:选中数据集的详情面板,展示统计信息和跳转按钮110- **右侧**:选中数据集的详情面板,展示统计信息和跳转按钮
105 111 
112+![Dashboard 总览](insight_image/dashboard_overview.png)
113+ 
106选中数据集后,右侧展示详细统计:114选中数据集后,右侧展示详细统计:
107 115 
116+![Dashboard 已选数据集](insight_image/dashboard_selected.png)
117+ 
108详情面板底部提供两个跳转入口:118详情面板底部提供两个跳转入口:
109 119 
110- **`进入关联视图`**:跳转到 MemView,带入当前数据集和 Rank 选择120- **`进入关联视图`**:跳转到 MemView,带入当前数据集和 Rank 选择
@@ -114,6 +124,8 @@ Dashboard 是使用工具的起点,页面从左到右分为三个区域:
114 124 
115MemView 将内存时间线和 DAG 任务图放在同一视图中联动展示,是排查问题的核心页面。125MemView 将内存时间线和 DAG 任务图放在同一视图中联动展示,是排查问题的核心页面。
116 126 
127+![MemView 总览](insight_image/memview_overview.png)
128+ 
117**页面布局:**129**页面布局:**
118 130 
119- **左侧**:Rank 选择树、报错列表(Issues)、搜索面板131- **左侧**:Rank 选择树、报错列表(Issues)、搜索面板
@@ -121,6 +133,8 @@ MemView 将内存时间线和 DAG 任务图放在同一视图中联动展示,
121- **下半区**:DAG 任务图 —— 按 Rank / Stream 泳道展示任务节点和依赖关系133- **下半区**:DAG 任务图 —— 按 Rank / Stream 泳道展示任务节点和依赖关系
122- **右侧**:详情栏 —— 展示当前选中 Step 或节点的详细信息134- **右侧**:详情栏 —— 展示当前选中 Step 或节点的详细信息
123 135 
136+![DAG 节点视图](insight_image/memview_dag_node.png)
137+ 
124#### 核心联动机制:Step、Rank、Task 三者联动138#### 核心联动机制:Step、Rank、Task 三者联动
125 139 
126- 在时间线上选中一个 Step → DAG 同步高亮对应节点140- 在时间线上选中一个 Step → DAG 同步高亮对应节点
@@ -144,6 +158,8 @@ MemView 将内存时间线和 DAG 任务图放在同一视图中联动展示,
144 158 
145Analytic 集中展示校验过程中发现的所有错误,页面分为三个区域:159Analytic 集中展示校验过程中发现的所有错误,页面分为三个区域:
146 160 
161+![Analytic 报错详情](insight_image/analytic_issue_detail.png)
162+ 
147- **左侧**:数据集与 Rank 选择163- **左侧**:数据集与 Rank 选择
148- **中间**:报错列表,每条错误展示标题、严重程度标签(颜色区分)、关键字段摘要和原始错误码164- **中间**:报错列表,每条错误展示标题、严重程度标签(颜色区分)、关键字段摘要和原始错误码
149- **右侧**:选中错误的详情面板,包括错误基本信息、涉及的 Rank、关联节点信息、Slice 详情(精确定位数据不一致的内存位置)、补充信息和原始 JSON165- **右侧**:选中错误的详情面板,包括错误基本信息、涉及的 Rank、关联节点信息、Slice 详情(精确定位数据不一致的内存位置)、补充信息和原始 JSON
Atest/hccl_vm/docs/insight_image/V3_image/dagView_V3.gif+3-0
@@ -0,0 +1,3 @@
1+version https://git-lfs.github.com/spec/v1
2+oid sha256:9bd6fdabd019c274e01ca1565d627a5d2f179ad5f2f5f61e639b3685547ea47b
3+size 4795196
Atest/hccl_vm/docs/insight_image/V3_image/dagView_V3_2.gif+3-0
@@ -0,0 +1,3 @@
1+version https://git-lfs.github.com/spec/v1
2+oid sha256:656143fa919b0da1d15929c2031d6144222afe7266a34045f0f4f8f1158a9e6e
3+size 3951263
Atest/hccl_vm/docs/insight_image/V3_image/launch.gif+3-0
@@ -0,0 +1,3 @@
1+version https://git-lfs.github.com/spec/v1
2+oid sha256:6eedaa1041718bdb503535db9b47d7fe66294dc73748c0da9767e5d4b6c296d7
3+size 2843499
Mtest/hccl_vm/include/runnerdb/db_sim_runner_common.h+1-1
@@ -35,6 +35,6 @@ uint32_t GetAICoreCount(uint64_t deviceId);
35uint32_t GetVectorCoreCount(uint64_t deviceId);35uint32_t GetVectorCoreCount(uint64_t deviceId);
36int GetRankIdByDeviceId(uint32_t deviceId);36int GetRankIdByDeviceId(uint32_t deviceId);
37bool ResetAllDeviceLogicId();37bool ResetAllDeviceLogicId();
38-bool GetRankIdByMPI(uint32_t &rankId);38+bool GetRankIdByMPI(uint32_t &rankId, uint64_t &serverId);
39}39}
40#endif // _SIM_RUNNER_COMMOM_H_40#endif // _SIM_RUNNER_COMMOM_H_
Mtest/hccl_vm/include/runnerdb/db_sim_sqlite_db.h+3-0
@@ -89,6 +89,7 @@ private:
89 sim::SqliteTable<sim::RaJetty> m_raJettyTbl;89 sim::SqliteTable<sim::RaJetty> m_raJettyTbl;
90 sim::SqliteTable<sim::RaJfc> m_raJfcTbl;90 sim::SqliteTable<sim::RaJfc> m_raJfcTbl;
91 sim::SqliteTable<sim::RaCr> m_raCrTbl;91 sim::SqliteTable<sim::RaCr> m_raCrTbl;
92+ sim::SqliteTable<sim::RunModeConfig> m_runModeConfigTbl;
92 93 
93 std::mutex m_lazyMutex;94 std::mutex m_lazyMutex;
94 std::unordered_map<std::type_index, sim::TableBase*> m_tableMap;95 std::unordered_map<std::type_index, sim::TableBase*> m_tableMap;
@@ -154,6 +155,7 @@ public:
154 , m_raJettyTbl(m_db.GetDb(), "RaJetty")155 , m_raJettyTbl(m_db.GetDb(), "RaJetty")
155 , m_raJfcTbl(m_db.GetDb(), "RaJfc")156 , m_raJfcTbl(m_db.GetDb(), "RaJfc")
156 , m_raCrTbl(m_db.GetDb(), "RaCr")157 , m_raCrTbl(m_db.GetDb(), "RaCr")
158+ , m_runModeConfigTbl(m_db.GetDb(), "RunModeConfig")
157 {159 {
158 RegisterTable(m_serverTbl, "Server");160 RegisterTable(m_serverTbl, "Server");
159 RegisterTable(m_hostTbl, "Host");161 RegisterTable(m_hostTbl, "Host");
@@ -206,6 +208,7 @@ public:
206 RegisterTable(m_raJettyTbl, "RaJetty");208 RegisterTable(m_raJettyTbl, "RaJetty");
207 RegisterTable(m_raJfcTbl, "RaJfc");209 RegisterTable(m_raJfcTbl, "RaJfc");
208 RegisterTable(m_raCrTbl, "RaCr");210 RegisterTable(m_raCrTbl, "RaCr");
211+ RegisterTable(m_runModeConfigTbl, "RunModeConfig");
209 }212 }
210 213 
211 SimRunnerSqliteDB(const SimRunnerSqliteDB&) = delete;214 SimRunnerSqliteDB(const SimRunnerSqliteDB&) = delete;
Mtest/hccl_vm/include/runnerdb/sim_models.h+5-0
@@ -26,6 +26,11 @@ typedef struct {
26 char version[16];26 char version[16];
27} Server;27} Server;
28 28 
29+typedef struct {
30+ uint64_t id; // PK
31+ uint8_t mode; // 0=normal,1=check-only
32+} RunModeConfig;
33+ 
29typedef struct {34typedef struct {
30 uint64_t id; // PK35 uint64_t id; // PK
31 uint64_t server_id;36 uint64_t server_id;
Mtest/hccl_vm/include/runnerdb/sim_op_db_types.h+1-1
@@ -20,7 +20,7 @@ enum class DbType {
20};20};
21struct DBConfig {21struct DBConfig {
22 DbType type = DbType::SQLITE3;22 DbType type = DbType::SQLITE3;
23- std::string dbPath = "./hccl_vm_data.db";23+ std::string dbPath = "data/hccl_vm_data.db";
24 std::string host;24 std::string host;
25 uint16_t port = 0;25 uint16_t port = 0;
26 std::string user;26 std::string user;
Atest/hccl_vm/include/sim_common_api.h+29-0
@@ -0,0 +1,29 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef SIM_COMMON_API_H
12+#define SIM_COMMON_API_H
13+ 
14+#include <string>
15+ 
16+class InstallPath {
17+public:
18+ // 获取 hccl-vm-install 根目录的绝对路径
19+ static const std::string& GetHcclVmInstallAbsPath();
20+ 
21+ // 把相对路径解析为基于 install root 的绝对路径
22+ // @param relPath 相对路径(如 "data/hccl_vm_data.db")
23+ // @note - 已是绝对路径(以 '/' 开头)则原样返回
24+ // - 以 "./" 开头也原样返回
25+ // - 空字符串原样返回
26+ static std::string ResolveToInstallRoot(const std::string& relPath);
27+};
28+ 
29+#endif // SIM_COMMON_API_H
Mtest/hccl_vm/include/sim_log.h+2-0
@@ -16,6 +16,7 @@
16#include <iostream>16#include <iostream>
17#include <memory>17#include <memory>
18#include <sstream>18#include <sstream>
19+#include <sys/types.h>
19 20 
20#include "spdlog/spdlog.h"21#include "spdlog/spdlog.h"
21 22 
@@ -75,6 +76,7 @@ struct LogConfig
75 bool enableCompress{false};76 bool enableCompress{false};
76};77};
77 78 
79+LogConfig LoadLogConfig(const std::string& process_name);
78void InitLogger(const LogConfig& config);80void InitLogger(const LogConfig& config);
79void FlushLog();81void FlushLog();
80void DeInitLogger();82void DeInitLogger();
Atest/hccl_vm/include/sim_yaml_config.h+22-0
@@ -0,0 +1,22 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef SIM_YAML_CONFIG_H
12+#define SIM_YAML_CONFIG_H
13+ 
14+#include <map>
15+#include <string>
16+ 
17+// 解析yaml文件
18+bool LoadYamlStringMap(const std::string& yamlPath,
19+ const std::string& nodeName,
20+ std::map<std::string, std::string>& out);
21+ 
22+#endif // SIM_YAML_CONFIG_H
Atest/hccl_vm/include/store/store_sim_comm_pool_policy.h+42-0
@@ -0,0 +1,42 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef SIM_COMM_POOL_POLICY_H
12+#define SIM_COMM_POOL_POLICY_H
13+ 
14+#include <cstddef>
15+ 
16+namespace sim {
17+// HcclCommPool 复用区的准入策略,主机侧与设备侧共用的纯判定。阈值与上界集中在此定义。
18+struct CommPoolPolicy {
19+ static constexpr size_t kBigBlockThreshold = 200ULL * 1024 * 1024; // 200MB,对齐 CCL buffer 默认
20+ // 复用区规格 4GB,大块可申请和可寻址的上界(含等于)。超过 4GB 由 ExceedsCeiling 报错。
21+ static constexpr size_t kPoolSize = 4ULL * 1024 * 1024 * 1024; // 4GB
22+ 
23+ // 复用区共享内存名。
24+ static constexpr const char* kPoolName = "HcclCommPool";
25+ 
26+ // 本次申请是否应引流到复用区:仅校验模式开且 threshold <= size <= poolSize。
27+ static bool ShouldRedirect(size_t size, bool checkOnlyMode,
28+ size_t threshold = kBigBlockThreshold,
29+ size_t poolSize = kPoolSize)
30+ {
31+ return checkOnlyMode && size >= threshold && size <= poolSize;
32+ }
33+ 
34+ // 本次申请是否超出复用区上界、应报错:仅仅校验模式下拦 size > poolSize。
35+ static bool ExceedsCeiling(size_t size, bool checkOnlyMode, size_t poolSize = kPoolSize)
36+ {
37+ return checkOnlyMode && size > poolSize;
38+ }
39+};
40+} // namespace sim
41+ 
42+#endif // SIM_COMM_POOL_POLICY_H
Mtest/hccl_vm/include/store/store_sim_device_memory_manager.h+1-1
@@ -51,7 +51,7 @@ public:
51 51 
52 // 获取host地址52 // 获取host地址
53 void* GetHostPtrByDevPtr(void* devPtr);53 void* GetHostPtrByDevPtr(void* devPtr);
54- 54+ 
55private:55private:
56 // 私有构造函数56 // 私有构造函数
57 DeviceMemoryManager();57 DeviceMemoryManager();
Atest/hccl_vm/include/store/store_sim_run_mode.h+22-0
@@ -0,0 +1,22 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef STORE_SIM_RUN_MODE_H
12+#define STORE_SIM_RUN_MODE_H
13+ 
14+namespace sim {
15+// 读 DB 的 RunModeConfig,返回是否仅校验模式,不缓存。表为空返回 false。
16+bool ProbeCheckOnlyMode();
17+ 
18+// 进程内只读一次并缓存结果。
19+bool IsCheckOnlyMode();
20+} // namespace sim
21+ 
22+#endif // STORE_SIM_RUN_MODE_H
Mtest/hccl_vm/install.sh+2-2
@@ -298,12 +298,12 @@ run_test() {
298 sudo bash -c "cat > /etc/hccl_rootinfo.json << EOF298 sudo bash -c "cat > /etc/hccl_rootinfo.json << EOF
299{299{
300 \"version\": \"2.0\",300 \"version\": \"2.0\",
301- \"topo_file_path\": \"${CHECKER_PATH}/hccl_vm_install/topo.json\"301+ \"topo_file_path\": \"${CHECKER_PATH}/hccl_vm_install/data/topo.json\"
302}302}
303EOF"303EOF"
304 else304 else
305 info "更新hccl_rootinfo.json文件..."305 info "更新hccl_rootinfo.json文件..."
306- sudo sed -i -E 's|"topo_file_path"\s*:\s*".*"|"topo_file_path": "${CHECKER_PATH}/hccl_vm_install/topo.json"|g' /etc/hccl_rootinfo.json306+ sudo sed -i -E 's|"topo_file_path"\s*:\s*".*"|"topo_file_path": "${CHECKER_PATH}/hccl_vm_install/data/topo.json"|g' /etc/hccl_rootinfo.json
307 fi307 fi
308 308
309 # 配置环境变量309 # 配置环境变量
Mtest/hccl_vm/src/CMakeLists.txt+14-5
@@ -71,13 +71,22 @@ target_include_directories(hccl-vm PUBLIC
71 71 
72target_link_libraries(hccl-vm72target_link_libraries(hccl-vm
73 PRIVATE73 PRIVATE
74- common74+ -Wl,--whole-archive
75- runnerdb
76- store
77- log
78 cmd75 cmd
76+ common
77+ store
78+ topo
79+ loader
80+ runnerdb
79 modeldb81 modeldb
82+ log
83+ -Wl,--no-whole-archive
84+ dl
85+ rt
80 sqlite386 sqlite3
87+ yaml-cpp::yaml-cpp
88+ CLI11::CLI11
89+ JSON::JSON
81)90)
82 91 
83target_compile_options(hccl-vm PRIVATE92target_compile_options(hccl-vm PRIVATE
@@ -107,7 +116,7 @@ if(TARGET third_party_yaml_cpp)
107endif()116endif()
108 117 
109install(TARGETS hccl-vm118install(TARGETS hccl-vm
110- DESTINATION "."119+ DESTINATION "bin"
111 # 文件的权限 (755)120 # 文件的权限 (755)
112 PERMISSIONS 121 PERMISSIONS
113 OWNER_READ OWNER_WRITE OWNER_EXECUTE 122 OWNER_READ OWNER_WRITE OWNER_EXECUTE
Mtest/hccl_vm/src/cmd/CMakeLists.txt+4-5
@@ -28,7 +28,7 @@ target_include_directories(cmd PRIVATE
28 ${CMAKE_CURRENT_SOURCE_DIR}/../../include/topo28 ${CMAKE_CURRENT_SOURCE_DIR}/../../include/topo
29 ${CMAKE_CURRENT_SOURCE_DIR}/../../include/store29 ${CMAKE_CURRENT_SOURCE_DIR}/../../include/store
30 ${CMAKE_CURRENT_SOURCE_DIR}/../../include30 ${CMAKE_CURRENT_SOURCE_DIR}/../../include
31- ${CMAKE_CURRENT_SOURCE_DIR}/../../third_party31+ ${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/nlohmann_json
32 32 
33 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc33 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc
34 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/runtime34 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/runtime
@@ -54,12 +54,11 @@ target_link_libraries(cmd PRIVATE
54 runnerdb54 runnerdb
55 store55 store
56 topo56 topo
57+ CLI11::CLI11
58+ yaml-cpp::yaml-cpp
59+ JSON::JSON
57)60)
58 61 
59-target_link_libraries(cmd PRIVATE CLI11::CLI11)
60-target_link_libraries(cmd PRIVATE yaml-cpp::yaml-cpp)
61-target_link_libraries(cmd PRIVATE JSON::JSON)
62- 
63if(TARGET third_party_yaml_cpp)62if(TARGET third_party_yaml_cpp)
64 add_dependencies(cmd third_party_yaml_cpp)63 add_dependencies(cmd third_party_yaml_cpp)
65endif()64endif()
Mtest/hccl_vm/src/cmd/cmd_base_utils.cc+70-40
@@ -30,6 +30,7 @@
30#include <stdexcept>30#include <stdexcept>
31#include <string>31#include <string>
32#include <sys/file.h>32#include <sys/file.h>
33+#include <sys/mman.h>
33#include <sys/stat.h>34#include <sys/stat.h>
34#include <sys/wait.h>35#include <sys/wait.h>
35#include <thread>36#include <thread>
@@ -41,10 +42,13 @@
41#include "cmd_base.h"42#include "cmd_base.h"
42#include "cmd_base_utils_internal.h"43#include "cmd_base_utils_internal.h"
43#include "store_dump_shm_data.h"44#include "store_dump_shm_data.h"
45+#include "store_sim_comm_pool_policy.h"
46+#include "store_sim_run_mode.h"
44#include "sim_data_dump.h"47#include "sim_data_dump.h"
45#include "sim_log.h"48#include "sim_log.h"
46#include "runnerdb/modeldb/db_hccl_op_db_ops.h"49#include "runnerdb/modeldb/db_hccl_op_db_ops.h"
47#include "store_sim_memory_manager.h"50#include "store_sim_memory_manager.h"
51+#include "store_sim_device_memory_manager.h"
48#include "sim_process_syncer.h"52#include "sim_process_syncer.h"
49#include "db_sim_runner_common.h"53#include "db_sim_runner_common.h"
50#include "db_sim_runner_db.h"54#include "db_sim_runner_db.h"
@@ -327,7 +331,11 @@ std::string GetBinLocation() {
327 if (ec) { 331 if (ec) {
328 throw std::runtime_error("read_symlink failed: " + ec.message()); 332 throw std::runtime_error("read_symlink failed: " + ec.message());
329 } 333 }
330- return exePath.parent_path().string(); 334+ fs::path binDir = exePath.parent_path();
335+ if (binDir.filename() == "bin") {
336+ return binDir.parent_path().string();
337+ }
338+ return binDir.string();
331} 339}
332 340 
333std::string ArgvToString(int argc, char *argv[]) { 341std::string ArgvToString(int argc, char *argv[]) {
@@ -396,7 +404,7 @@ std::string FileInModelDir(const std::string& fileName) {
396 } 404 }
397 return ""; 405 return "";
398 } 406 }
399- std::string filePath = GetBinLocation() + "/cluster_model/topo_meta/" + fileName + ".yaml"; 407+ std::string filePath = GetBinLocation() + "/config/topo_meta/" + fileName + ".yaml";
400 auto fileExistd = [&]()->bool { 408 auto fileExistd = [&]()->bool {
401 std::ifstream f(filePath.c_str()); 409 std::ifstream f(filePath.c_str());
402 return f.good(); 410 return f.good();
@@ -415,12 +423,12 @@ std::string GenerateClusterTopo(const std::string& topoFileName) {
415 topoName.erase(topoName.find(".yaml"), 5); 423 topoName.erase(topoName.find(".yaml"), 5);
416 } 424 }
417 425 
418- std::string generateShellPath = GetBinLocation() + "/generate_cluster_topo.sh"; 426+ std::string generateShellPath = GetBinLocation() + "/script/generate_cluster_topo.sh";
419 if (!fs::exists(generateShellPath)) { 427 if (!fs::exists(generateShellPath)) {
420 HCCL_VM_ERROR("[HVM] generate_cluster_topo.sh not found: {}", generateShellPath); 428 HCCL_VM_ERROR("[HVM] generate_cluster_topo.sh not found: {}", generateShellPath);
421 return "[HVM] generate_cluster_topo.sh not found: " + generateShellPath; 429 return "[HVM] generate_cluster_topo.sh not found: " + generateShellPath;
422 } 430 }
423- std::string clusterConfigFilePath = GetBinLocation() + "/cluster_model/config/cluster/" + topoName + ".yaml"; 431+ std::string clusterConfigFilePath = GetBinLocation() + "/config/cluster/" + topoName + ".yaml";
424 if (!fs::exists(clusterConfigFilePath)) { 432 if (!fs::exists(clusterConfigFilePath)) {
425 HCCL_VM_ERROR("[HVM] cluster config file not found: {}", clusterConfigFilePath); 433 HCCL_VM_ERROR("[HVM] cluster config file not found: {}", clusterConfigFilePath);
426 return "[HVM] cluster config file not found: " + clusterConfigFilePath; 434 return "[HVM] cluster config file not found: " + clusterConfigFilePath;
@@ -465,12 +473,21 @@ void ShowModel() {
465 return; 473 return;
466} 474}
467 475 
468-HcclVmResult InitHvmEnv(const std::string& configClusterDir, uint32_t level) 476+HcclVmResult InitHvmEnv(const std::string& configClusterDir, uint32_t level, bool checkOnlyMode)
469-{ 477+{
470- HCCL_VM_INFO("Enter InitHvmEnv: {}", configClusterDir); 478+ HCCL_VM_INFO("Enter InitHvmEnv: {}", configClusterDir);
471- // 启动仿真环境 479+ // 启动仿真环境
472- // 创建用于Host-Device通信的共享内存 480+ // 创建用于Host-Device通信的共享内存
473- void *shmptr =sim::MemoryManager::GetInstance().AllocMemByName("HcclAicpuData", sizeof(HcclAicpuData)); 481+ // 仅校验模式才创建大块复用区 HcclCommPool,须在其它共享内存创建前,仅一次。
482+ if (checkOnlyMode) {
483+ void* commPool = sim::MemoryManager::GetInstance().AllocMemByName(
484+ sim::CommPoolPolicy::kPoolName, sim::CommPoolPolicy::kPoolSize);
485+ if (commPool == nullptr) {
486+ HCCL_VM_ERROR("[HVM] create HcclCommPool fail");
487+ return HcclVmResult::HCCL_SIM_HOST_ERROR_CMD;
488+ }
489+ }
490+ void *shmptr =sim::MemoryManager::GetInstance().AllocMemByName("HcclAicpuData", sizeof(HcclAicpuData));
474 if (shmptr == nullptr) { 491 if (shmptr == nullptr) {
475 HCCL_VM_ERROR("[HVM] Alloc Shared Memory fail "); 492 HCCL_VM_ERROR("[HVM] Alloc Shared Memory fail ");
476 return HcclVmResult::HCCL_SIM_HOST_ERROR_CMD; 493 return HcclVmResult::HCCL_SIM_HOST_ERROR_CMD;
@@ -498,7 +515,7 @@ HcclVmResult InitHvmCommEnv(const TopoMeta& topoMeta, const std::string& configF
498{ 515{
499 // 通信域已初始化,则无需重复初始化 516 // 通信域已初始化,则无需重复初始化
500 if (AscendClusterTopoParser::GetInstance().GetClusterStatus() == HvmClusterStatus::COMM_DOMAIN_INIT_DONE) { 517 if (AscendClusterTopoParser::GetInstance().GetClusterStatus() == HvmClusterStatus::COMM_DOMAIN_INIT_DONE) {
501- HCCL_VM_ERROR("[{}] communication domain already initialized", __func__); 518+ HCCL_VM_ERROR("communication domain already initialized");
502 return HcclVmResult::HCCL_SIM_E_INTERNAL; 519 return HcclVmResult::HCCL_SIM_E_INTERNAL;
503 } 520 }
504 HcclVmResult ret; 521 HcclVmResult ret;
@@ -508,7 +525,7 @@ HcclVmResult InitHvmCommEnv(const TopoMeta& topoMeta, const std::string& configF
508 ret = AscendClusterTopoParser::GetInstance().InitCommunicationDomain(topoMeta, false); 525 ret = AscendClusterTopoParser::GetInstance().InitCommunicationDomain(topoMeta, false);
509 } else { 526 } else {
510 HCCL_VM_INFO("mock-comm cmd config rank table file, config by ranktable.json"); 527 HCCL_VM_INFO("mock-comm cmd config rank table file, config by ranktable.json");
511- std::string clusterInfo = GetBinLocation() + "/ranktable.json"; 528+ std::string clusterInfo = GetBinLocation() + "/data/ranktable.json";
512 std::ifstream f(clusterInfo.c_str()); 529 std::ifstream f(clusterInfo.c_str());
513 if (!f.good()) { 530 if (!f.good()) {
514 HCCL_VM_ERROR("ranktable.json file not found: {}", clusterInfo); 531 HCCL_VM_ERROR("ranktable.json file not found: {}", clusterInfo);
@@ -539,14 +556,20 @@ HcclVmResult HcclVmExit()
539 const fs::path backupDir = fs::path(g_binDir) / "data" / MakeDataBackupTimestamp();556 const fs::path backupDir = fs::path(g_binDir) / "data" / MakeDataBackupTimestamp();
540 BackupAivTaskFiles(backupDir);557 BackupAivTaskFiles(backupDir);
541 558 
542- HCCL_VM_INFO("start Destroy ALL Resources."); 559+ HCCL_VM_INFO("start Destroy ALL Resources.");
543- int ret1 = system("sudo rm -fr /dev/shm/* 2>/dev/null"); 560+ // 仅校验模式才解除主进程对复用区 HcclCommPool 的映射,普通模式从未建池。
561+ // 这里只解主进程自己这一份,共享内存文件何时真正删除由跨进程引用计数决定,
562+ // rank 还在用时不会删;异常退出未走到这里的,由下面的 rm 兜底删除。
563+ if (sim::IsCheckOnlyMode()) {
564+ sim::MemoryManager::GetInstance().FreeMemByName(sim::CommPoolPolicy::kPoolName);
565+ }
566+ int ret1 = system("sudo rm -fr /dev/shm/* 2>/dev/null");
544 ret1 |= system("sudo rm -fr /tmp/hccl_sim.db* 2>/dev/null"); 567 ret1 |= system("sudo rm -fr /tmp/hccl_sim.db* 2>/dev/null");
545 return ret; 568 return ret;
546} 569}
547 570 
548-HcclVmResult InstallUserPlugin(std::string argStr) { 571+HcclVmResult InstallUserPlugin(std::string argStr) {
549- // 处理插件tag和路径 572+ // 处理插件tag和路径
550 HcclVmResult ret {HcclVmResult::HCCL_SIM_HOST_ERROR_CMD}; 573 HcclVmResult ret {HcclVmResult::HCCL_SIM_HOST_ERROR_CMD};
551 if (argStr.empty() || argStr[0] != '@') { 574 if (argStr.empty() || argStr[0] != '@') {
552 HCCL_VM_ERROR("[HVM] plugin tag should start with '@', invalid tag: {}", argStr); 575 HCCL_VM_ERROR("[HVM] plugin tag should start with '@', invalid tag: {}", argStr);
@@ -556,14 +579,19 @@ HcclVmResult InstallUserPlugin(std::string argStr) {
556 579 
557 // 注册插件 580 // 注册插件
558 HcclPluginManager &pluginManager = HcclPluginManager::GetInstance(); 581 HcclPluginManager &pluginManager = HcclPluginManager::GetInstance();
559- ret = pluginManager.RegisterPlugin(argStr); 582+ ret = pluginManager.RegisterPlugin(argStr);
560- if (ret != HcclVmResult::HCCL_SIM_SUCCESS) { 583+ if (ret != HcclVmResult::HCCL_SIM_SUCCESS) {
561- HCCL_VM_ERROR("[HVM] Install plugin [{}] failed", argStr); 584+ HCCL_VM_ERROR("[HVM] Install plugin [{}] failed", argStr);
562- return ret; 585+ return ret;
563- } 586+ }
564 587 
565- return HcclVmResult::HCCL_SIM_HOST_SUCCESS_CMD; 588+ // Runner 时若仅校验模式开着,复用池仍在、大块仍会引流,可能覆盖 Runner 真实数据,告警提示。
566-} 589+ if (argStr == "runner" && sim::IsCheckOnlyMode()) {
590+ HCCL_VM_WARN("[HVM] check-only mode on while runner installed; big-block contents are not guaranteed");
591+ }
592+ 
593+ return HcclVmResult::HCCL_SIM_HOST_SUCCESS_CMD;
594+}
567 595 
568HcclVmResult RunUserPlugin(std::string argStr) { 596HcclVmResult RunUserPlugin(std::string argStr) {
569 nlohmann::json j; 597 nlohmann::json j;
@@ -598,17 +626,18 @@ HcclVmResult UninstallUserPlugin(std::string argStr) {
598 lastTag.erase(lastTag.begin()); 626 lastTag.erase(lastTag.begin());
599 pluginTags.push_back(lastTag); 627 pluginTags.push_back(lastTag);
600 628 
601- HcclPluginManager &pluginManager = HcclPluginManager::GetInstance(); 629+ HcclPluginManager &pluginManager = HcclPluginManager::GetInstance();
602- auto rets = pluginManager.StopPlugins(pluginTags); 630+ auto rets = pluginManager.StopPlugins(pluginTags);
603- for (int i = 0; i < pluginTags.size(); ++i) {
604- if (rets[i] != HcclVmResult::HCCL_SIM_SUCCESS) {
605- HCCL_VM_ERROR("[HVM] plugin Uninstall fail : {}", pluginTags[i]);
606- return rets[i];
607- }
608- }
609 631 
610- return HcclVmResult::HCCL_SIM_HOST_SUCCESS_CMD; 632+ for (int i = 0; i < pluginTags.size(); ++i) {
611-} 633+ if (rets[i] != HcclVmResult::HCCL_SIM_SUCCESS) {
634+ HCCL_VM_ERROR("[HVM] plugin Uninstall fail : {}", pluginTags[i]);
635+ return rets[i];
636+ }
637+ }
638+ 
639+ return HcclVmResult::HCCL_SIM_HOST_SUCCESS_CMD;
640+}
612 641 
613void ShowUserPlugin() { 642void ShowUserPlugin() {
614 std::vector<std::string> listPlugins{}; 643 std::vector<std::string> listPlugins{};
@@ -654,8 +683,8 @@ HcclVmResult StartHvmCmd() {
654 683 
655 // Child Process(Bash) 684 // Child Process(Bash)
656 // 劫持库存在性判断 685 // 劫持库存在性判断
657- std::string hcclVmbin = g_binDir + "/hccl-vm"; 686+ std::string hcclVmbin = g_binDir + "/bin/hccl-vm";
658- std::string proxyPath = g_binDir + "/libhccl_proxy_level" + std::to_string(g_hcclVmLevel) + ".so"; 687+ std::string proxyPath = g_binDir + "/lib/x86_64/libhccl_proxy_level" + std::to_string(g_hcclVmLevel) + ".so";
659 if (!fs::exists(proxyPath)) { 688 if (!fs::exists(proxyPath)) {
660 HCCL_VM_ERROR("[HVM] [ERROR] proxy hacking .so not found {}, please check your proxy hacking .so:" 689 HCCL_VM_ERROR("[HVM] [ERROR] proxy hacking .so not found {}, please check your proxy hacking .so:"
661 "1. Whether the hook library has been successfully built and installed. 2. Whether the simulation level matches the proxy hook library version. Current simulation level: {}, Default simulation level: 2" 690 "1. Whether the hook library has been successfully built and installed. 2. Whether the simulation level matches the proxy hook library version. Current simulation level: {}, Default simulation level: 2"
@@ -711,14 +740,14 @@ HcclVmResult StartHvmCmd() {
711 } else if (pid == 0) { 740 } else if (pid == 0) {
712 setenv(HVM_BASH_ENV_KEY.c_str(), g_binDir.c_str(), 1); 741 setenv(HVM_BASH_ENV_KEY.c_str(), g_binDir.c_str(), 1);
713 setenv("LD_PRELOAD", proxyPath.c_str(), 1); 742 setenv("LD_PRELOAD", proxyPath.c_str(), 1);
743+ setenv("HCCL_VM_INSTALL_ROOT", g_binDir.c_str(), 1);
714 execv("/bin/bash", bashArgv); 744 execv("/bin/bash", bashArgv);
715- perror("bash execv failed");
716 exit(1); 745 exit(1);
717 } else { 746 } else {
718 // Parent Process (Host) 747 // Parent Process (Host)
719 // 等待 bash 结束 (阻塞等待,保持Host存活) 748 // 等待 bash 结束 (阻塞等待,保持Host存活)
720 int status; 749 int status;
721- waitpid(pid, &status, 0); 750+ waitpid(pid, &status, 0);
722 auto ret = HcclVmExit(); 751 auto ret = HcclVmExit();
723 HCCL_VM_INFO("[HVM] Shell exited. Host shutting down."); 752 HCCL_VM_INFO("[HVM] Shell exited. Host shutting down.");
724 } 753 }
@@ -1088,7 +1117,7 @@ HcclVmResult ClearDbTables()
1088 const fs::path backupDir = fs::path(g_binDir) / "data" / MakeDataBackupTimestamp();1117 const fs::path backupDir = fs::path(g_binDir) / "data" / MakeDataBackupTimestamp();
1089 BackupBinFiles(backupDir);1118 BackupBinFiles(backupDir);
1090 BackupAivTaskFiles(backupDir);1119 BackupAivTaskFiles(backupDir);
1091- BackupDatabase("./hccl_vm_data.db"); 1120+ BackupDatabase(g_binDir + "/data/hccl_vm_data.db");
1092 1121 
1093 // 1. 清空 OpDbOps (SQLite) 中的静态表数据 1122 // 1. 清空 OpDbOps (SQLite) 中的静态表数据
1094 std::vector<std::string> staticTables = { 1123 std::vector<std::string> staticTables = {
@@ -1135,7 +1164,7 @@ HcclVmResult ClearDbTables()
1135 RunnerDB::DeleteAll<sim::RaSocket>();1164 RunnerDB::DeleteAll<sim::RaSocket>();
1136 RunnerDB::DeleteAll<sim::RaSocketPair>();1165 RunnerDB::DeleteAll<sim::RaSocketPair>();
1137 1166 
1138- CleanShmFilesByPrefix({"DEV", "ra_socket_"});1167+ CleanShmFilesByPrefix({"DEV", "ra_sock_"});
1139 1168 
1140 sim::ProcessSyncer syncer; 1169 sim::ProcessSyncer syncer;
1141 syncer.Reset(); 1170 syncer.Reset();
@@ -1162,7 +1191,8 @@ HcclVmResult HcclVmResetCommDomain()
1162HcclVmResult CopyFile(const std::string& clusterDir) {1191HcclVmResult CopyFile(const std::string& clusterDir) {
1163 // 1. 拼接源文件和目标文件路径1192 // 1. 拼接源文件和目标文件路径
1164 auto srcPath = clusterDir + "/superpod0/server0/topo.json";1193 auto srcPath = clusterDir + "/superpod0/server0/topo.json";
1165- auto destPath = GetBinLocation() + "/topo.json";1194+ fs::create_directories(fs::path(GetBinLocation() + "/data"));
1195+ auto destPath = GetBinLocation() + "/data/topo.json";
1166 1196 
1167 // 显式提示:文件已存在,将覆盖1197 // 显式提示:文件已存在,将覆盖
1168 if (fs::exists(destPath)) {1198 if (fs::exists(destPath)) {
Mtest/hccl_vm/src/cmd/cmd_base_utils.h+1-1
@@ -72,7 +72,7 @@ void RemoveFromLDPreload(const std::string& targetValue);
72void ShowModel();72void ShowModel();
73 73 
74// init hvm env74// init hvm env
75-HcclVmResult InitHvmEnv(const std::string& configClusterDir, uint32_t level);75+HcclVmResult InitHvmEnv(const std::string& configClusterDir, uint32_t level, bool checkOnlyMode);
76HcclVmResult InitHvmCommEnv(const TopoMeta& topoMeta, const std::string& configFileName, uint32_t level);76HcclVmResult InitHvmCommEnv(const TopoMeta& topoMeta, const std::string& configFileName, uint32_t level);
77HcclVmResult HcclVmExit();77HcclVmResult HcclVmExit();
78HcclVmResult CopyFile(const std::string& clusterDir);78HcclVmResult CopyFile(const std::string& clusterDir);
Mtest/hccl_vm/src/cmd/cmd_cluster_model_utils.cc+1-1
@@ -38,7 +38,7 @@ bool SetEnvValue(const char* key, const std::string& value)
38bool ParseYamlTopoImpl(const std::string& fileName, TopoMeta& topo)38bool ParseYamlTopoImpl(const std::string& fileName, TopoMeta& topo)
39{39{
40 try {40 try {
41- std::string filePath = GetBinLocation() + "/cluster_model/topo_meta/" + fileName + ".yaml";41+ std::string filePath = GetBinLocation() + "/config/topo_meta/" + fileName + ".yaml";
42 YAML::Node root = YAML::LoadFile(filePath);42 YAML::Node root = YAML::LoadFile(filePath);
43 43 
44 if (!root["meta"]) {44 if (!root["meta"]) {
Mtest/hccl_vm/src/cmd/subcmd/subcmd_run.cc+14-3
@@ -11,8 +11,10 @@
11#include <string>11#include <string>
12 12 
13#include "cmd_base_utils.h"13#include "cmd_base_utils.h"
14+#include "db_sim_runner_db.h"
14#include "sim_common_defs.h"15#include "sim_common_defs.h"
15#include "sim_log.h"16#include "sim_log.h"
17+#include "sim_models.h"
16#include "subcmd_run.h"18#include "subcmd_run.h"
17 19 
18namespace HcclSim {20namespace HcclSim {
@@ -21,6 +23,8 @@ void RunCommand::Setup(CLI::App& app) {
21 23 
22 sub_oneShot->add_option("configFile", configClusterName, "加载昇腾集群组网配置目录")->required()->check(GenerateClusterTopo);24 sub_oneShot->add_option("configFile", configClusterName, "加载昇腾集群组网配置目录")->required()->check(GenerateClusterTopo);
23 sub_oneShot->add_option("--level", g_hcclVmLevel, "设置模拟等级, 当前支持等级为 1 和 2, 默认模拟等级 2 ");25 sub_oneShot->add_option("--level", g_hcclVmLevel, "设置模拟等级, 当前支持等级为 1 和 2, 默认模拟等级 2 ");
26+ sub_oneShot->add_flag("--check-only", checkOnlyMode,
27+ "仅校验模式:大块(200MB-4GB)内存申请复用同一块 4GB 共享区,内容不保证正确,换取内存节省");
24 sub_oneShot->allow_extras(true);28 sub_oneShot->allow_extras(true);
25 29
26 sub_oneShot->callback([this, &app]() { Execute(app); });30 sub_oneShot->callback([this, &app]() { Execute(app); });
@@ -38,8 +42,15 @@ void RunCommand::Execute(CLI::App& app) {
38 return;42 return;
39 }43 }
40 HCCL_VM_INFO("[HVM] Initializing: Model={}, Level={}", configClusterName, g_hcclVmLevel);44 HCCL_VM_INFO("[HVM] Initializing: Model={}, Level={}", configClusterName, g_hcclVmLevel);
41- auto clusterDir = GetBinLocation() + "/cluster_model/network/cluster/" + configClusterName;45+ auto clusterDir = GetBinLocation() + "/config/network/cluster/" + configClusterName;
42- auto ret = InitHvmEnv(clusterDir, g_hcclVmLevel);46+ 
47+ sim::RunModeConfig runMode{};
48+ runMode.mode = checkOnlyMode ? 1 : 0;
49+ RunnerDB::DeleteAll<sim::RunModeConfig>();
50+ RunnerDB::Add<sim::RunModeConfig>(runMode);
51+ HCCL_VM_INFO("[HVM] run mode: {}", checkOnlyMode ? "check-only" : "normal");
52+ 
53+ auto ret = InitHvmEnv(clusterDir, g_hcclVmLevel, checkOnlyMode);
43 if (ret != HcclVmResult::HCCL_SIM_HOST_SUCCESS_CMD) {54 if (ret != HcclVmResult::HCCL_SIM_HOST_SUCCESS_CMD) {
44 HCCL_VM_ERROR("[HVM] Failed to initialize simulation environment. Cleaning up environment.");55 HCCL_VM_ERROR("[HVM] Failed to initialize simulation environment. Cleaning up environment.");
45 auto cleanRet = HcclVmExit();56 auto cleanRet = HcclVmExit();
@@ -50,7 +61,7 @@ void RunCommand::Execute(CLI::App& app) {
50 }61 }
51 CstyleCmd syscmd(leftargvs);62 CstyleCmd syscmd(leftargvs);
52 HCCL_VM_INFO("[HVM] one_shot mode, executing: {}", syscmd.cmd());63 HCCL_VM_INFO("[HVM] one_shot mode, executing: {}", syscmd.cmd());
53- std::string proxyPath = GetBinLocation() + "/libhccl_proxy_level2.so";64+ std::string proxyPath = GetBinLocation() + "/lib/x86_64/libhccl_proxy_level2.so";
54 setenv("LD_PRELOAD", proxyPath.c_str(), 1);65 setenv("LD_PRELOAD", proxyPath.c_str(), 1);
55 int sysRet = std::system(syscmd.cmd().c_str()); // system() 会阻塞当前进程直到子命令结束66 int sysRet = std::system(syscmd.cmd().c_str()); // system() 会阻塞当前进程直到子命令结束
56 if (sysRet != 0) {67 if (sysRet != 0) {
Mtest/hccl_vm/src/cmd/subcmd/subcmd_run.h+1-0文件内容审核中,请稍后刷新重试
Mtest/hccl_vm/src/cmd/subcmd/subcmd_start.cc+14-4
@@ -12,8 +12,10 @@
12#include <string>12#include <string>
13 13 
14#include "cmd_base_utils.h"14#include "cmd_base_utils.h"
15+#include "db_sim_runner_db.h"
15#include "sim_common_defs.h"16#include "sim_common_defs.h"
16#include "sim_log.h"17#include "sim_log.h"
18+#include "sim_models.h"
17#include "subcmd_start.h"19#include "subcmd_start.h"
18 20 
19namespace HcclSim {21namespace HcclSim {
@@ -22,7 +24,9 @@ void StartCommand::Setup(CLI::App& app) {
22 24 
23 sub_start->add_option("configCluster", configClusterName, "加载昇腾集群组网配置目录")->required()->check(GenerateClusterTopo);25 sub_start->add_option("configCluster", configClusterName, "加载昇腾集群组网配置目录")->required()->check(GenerateClusterTopo);
24 sub_start->add_option("--level", g_hcclVmLevel, "设置模拟等级, 当前支持等级为 1 和 2, 默认模拟等级 2 ");26 sub_start->add_option("--level", g_hcclVmLevel, "设置模拟等级, 当前支持等级为 1 和 2, 默认模拟等级 2 ");
25- 27+ sub_start->add_flag("--check-only", checkOnlyMode,
28+ "仅校验模式:大块(200MB-4GB)内存申请复用同一块 4GB 共享区,内容不保证正确,换取内存节省");
29+ 
26 sub_start->callback([this]() { Execute(); });30 sub_start->callback([this]() { Execute(); });
27}31}
28 32 
@@ -36,16 +40,22 @@ void StartCommand::Execute() {
36 }40 }
37 41 
38 HCCL_VM_INFO("[HVM] Initializing: Model={}, Level={}", configClusterName, g_hcclVmLevel);42 HCCL_VM_INFO("[HVM] Initializing: Model={}, Level={}", configClusterName, g_hcclVmLevel);
39- auto clusterDir = GetBinLocation() + "/cluster_model/network/cluster/" + configClusterName;43+ auto clusterDir = GetBinLocation() + "/config/network/cluster/" + configClusterName;
40 44 
41 // 拷贝topo.json文件到执行目录45 // 拷贝topo.json文件到执行目录
42 auto ret = CopyFile(clusterDir);46 auto ret = CopyFile(clusterDir);
43 if (ret != HcclVmResult::HCCL_SIM_SUCCESS) {47 if (ret != HcclVmResult::HCCL_SIM_SUCCESS) {
44- HCCL_VM_ERROR("[{}] copy topo.json file failed", __func__);48+ HCCL_VM_ERROR("copy topo.json file failed");
45 return;49 return;
46 }50 }
47 51 
48- ret = InitHvmEnv(clusterDir, g_hcclVmLevel);52+ sim::RunModeConfig runMode{};
53+ runMode.mode = checkOnlyMode ? 1 : 0;
54+ RunnerDB::DeleteAll<sim::RunModeConfig>();
55+ RunnerDB::Add<sim::RunModeConfig>(runMode);
56+ HCCL_VM_INFO("[HVM] run mode: {}", checkOnlyMode ? "check-only" : "normal");
57+ 
58+ ret = InitHvmEnv(clusterDir, g_hcclVmLevel, checkOnlyMode);
49 if (ret != HcclVmResult::HCCL_SIM_HOST_SUCCESS_CMD) {59 if (ret != HcclVmResult::HCCL_SIM_HOST_SUCCESS_CMD) {
50 HCCL_VM_ERROR("[HVM] Failed to initialize simulation environment. Cleaning up environment.");60 HCCL_VM_ERROR("[HVM] Failed to initialize simulation environment. Cleaning up environment.");
51 auto cleanRet = HcclVmExit();61 auto cleanRet = HcclVmExit();
Mtest/hccl_vm/src/cmd/subcmd/subcmd_start.h+1-0文件内容审核中,请稍后刷新重试
Mtest/hccl_vm/src/common/CMakeLists.txt+2-25
@@ -8,9 +8,10 @@
8 8 
9file(GLOB_RECURSE HCCL_SOURCES "*.cc")9file(GLOB_RECURSE HCCL_SOURCES "*.cc")
10 10 
11-add_library(common SHARED ${HCCL_SOURCES})11+add_library(common STATIC ${HCCL_SOURCES})
12 12 
13target_compile_options(common PRIVATE13target_compile_options(common PRIVATE
14+ -fPIC
14 -Werror15 -Werror
15 -fno-common16 -fno-common
16 -fno-strict-aliasing17 -fno-strict-aliasing
@@ -55,27 +56,3 @@ target_link_libraries(common PRIVATE
55 log56 log
56 runnerdb57 runnerdb
57)58)
58- 
59-install(TARGETS common
60- DESTINATION "."
61- PERMISSIONS
62- OWNER_READ OWNER_WRITE OWNER_EXECUTE
63- GROUP_READ GROUP_EXECUTE
64- WORLD_READ WORLD_EXECUTE
65-)
66- 
67-install(TARGETS common
68- DESTINATION "plugin/validate/runner"
69- PERMISSIONS
70- OWNER_READ OWNER_WRITE OWNER_EXECUTE
71- GROUP_READ GROUP_EXECUTE
72- WORLD_READ WORLD_EXECUTE
73-)
74- 
75-install(TARGETS common
76- DESTINATION "plugin/validate/checker"
77- PERMISSIONS
78- OWNER_READ OWNER_WRITE OWNER_EXECUTE
79- GROUP_READ GROUP_EXECUTE
80- WORLD_READ WORLD_EXECUTE
81-)
Mtest/hccl_vm/src/device_arm/CMakeLists.txt+7-4
@@ -49,8 +49,8 @@ target_include_directories(sqlite3 PRIVATE
49)49)
50install(TARGETS sqlite350install(TARGETS sqlite3
51 RUNTIME DESTINATION bin51 RUNTIME DESTINATION bin
52- LIBRARY DESTINATION lib52+ LIBRARY DESTINATION lib/aarch64
53- ARCHIVE DESTINATION lib53+ ARCHIVE DESTINATION lib/aarch64
54)54)
55 55 
56add_executable(device56add_executable(device
@@ -62,6 +62,8 @@ add_executable(device
62 ${HCCL_VM_SRC}/store/store_sim_shm_ops.cc62 ${HCCL_VM_SRC}/store/store_sim_shm_ops.cc
63 ${HCCL_VM_SRC}/runnerdb/tracedb/db_sim_sqlite_db.cc63 ${HCCL_VM_SRC}/runnerdb/tracedb/db_sim_sqlite_db.cc
64 ${HCCL_VM_SRC}/utils/sim_log.cc64 ${HCCL_VM_SRC}/utils/sim_log.cc
65+ ${HCCL_VM_SRC}/utils/sim_yaml_config.cc
66+ ${HCCL_VM_SRC}/utils/sim_common_api.cc
65 ${HCCL_VM_SRC}/proxy/log_stub.cc67 ${HCCL_VM_SRC}/proxy/log_stub.cc
66 ${HCCL_VM_SRC}/runnerdb/tracedb/db_sim_op_db_ops.cc68 ${HCCL_VM_SRC}/runnerdb/tracedb/db_sim_op_db_ops.cc
67 ${HCCL_VM_SRC}/runnerdb/modeldb/db_hccl_op_db_ops.cc69 ${HCCL_VM_SRC}/runnerdb/modeldb/db_hccl_op_db_ops.cc
@@ -127,6 +129,7 @@ target_compile_options(device PRIVATE
127 -g129 -g
128 -D_GLIBCXX_USE_CXX11_ABI=0130 -D_GLIBCXX_USE_CXX11_ABI=0
129 -DCANN_VERSION_NUM=${HCCL_CANN_VERSION_NUM}131 -DCANN_VERSION_NUM=${HCCL_CANN_VERSION_NUM}
132+ -DNO_YAML_CONFIG
130)133)
131 134 
132target_link_options(device PUBLIC "-Wl,--export-dynamic")135target_link_options(device PUBLIC "-Wl,--export-dynamic")
@@ -135,8 +138,8 @@ add_dependencies(device sqlite3)
135 138 
136install(TARGETS device139install(TARGETS device
137 RUNTIME DESTINATION bin140 RUNTIME DESTINATION bin
138- LIBRARY DESTINATION lib141+ LIBRARY DESTINATION lib/aarch64
139- ARCHIVE DESTINATION lib142+ ARCHIVE DESTINATION lib/aarch64
140)143)
141 144 
142 145 
Mtest/hccl_vm/src/device_arm/aicpu_args_stub.h+3-3
@@ -16,9 +16,9 @@ constexpr uint32_t UNIQUEID_HEADER_OFFSET = 152;
16constexpr uint32_t UNIQUEID_HEADER_SIZE = 20;16constexpr uint32_t UNIQUEID_HEADER_SIZE = 20;
17constexpr uint32_t COMMON_DATA_SIZE = 8;17constexpr uint32_t COMMON_DATA_SIZE = 8;
18constexpr uint32_t NOTIFY_ID_SIZE = 8;18constexpr uint32_t NOTIFY_ID_SIZE = 8;
19-constexpr uint32_t NOTIFY_BUFFER_SIZE = 24;19+constexpr uint32_t NOTIFY_BUFFER_SIZE = 28;
20-constexpr uint32_t LOCAL_BUFFER_SIZE = 24;20+constexpr uint32_t LOCAL_BUFFER_SIZE = 28;
21-constexpr uint32_t REMOTE_BUFFER_SIZE = 24;21+constexpr uint32_t REMOTE_BUFFER_SIZE = 28;
22constexpr uint32_t HCOMID_MAX_SIZE = 128;22constexpr uint32_t HCOMID_MAX_SIZE = 128;
23 23 
24struct HDCommunicateParams {24struct HDCommunicateParams {
Mtest/hccl_vm/src/device_arm/hccl_device_pub.cc+1-1
@@ -210,7 +210,7 @@ void SignalHandler(int signum)
210 }210 }
211 211
212 printf("========================================\n");212 printf("========================================\n");
213- printf("Resolve: aarch64-linux-gnu-addr2line -e ./device/bin/device -C -f <addr>\n");213+ printf("Resolve: aarch64-linux-gnu-addr2line -e ./bin/device -C -f <addr>\n");
214 printf("========================================\n\n");214 printf("========================================\n\n");
215 215 
216 // 4. 刷新缓冲区,确保信息在进程退出前输出216 // 4. 刷新缓冲区,确保信息在进程退出前输出
Mtest/hccl_vm/src/device_arm/hccl_kernel_executor.cc+2-2
@@ -126,9 +126,9 @@ bool InitKernelFuncHandle()
126 if (gLibsLoaded) {126 if (gLibsLoaded) {
127 return true;127 return true;
128 }128 }
129- std::string libDir = "./device/lib/"; // device编译产物install路径129+ const char* installDir = getenv("HCCL_VM_INSTALL_DIR");
130+ std::string libDir = installDir ? std::string(installDir) + "/lib/aarch64/" : "./lib/aarch64/";
130 gSlogHandle = LoadLibrary(libDir, "libslog.so");131 gSlogHandle = LoadLibrary(libDir, "libslog.so");
131- libDir = "./device/lib64/"; // device依赖库路径
132 gCsecHandle = LoadLibrary(libDir, "libc_sec.so");132 gCsecHandle = LoadLibrary(libDir, "libc_sec.so");
133 gHcclKerHandle = LoadLibrary(libDir, "libscatter_aicpu_kernel.so");133 gHcclKerHandle = LoadLibrary(libDir, "libscatter_aicpu_kernel.so");
134 gHcommHandle = LoadLibrary(libDir, "libccl_kernel.so");134 gHcommHandle = LoadLibrary(libDir, "libccl_kernel.so");
Mtest/hccl_vm/src/device_arm/main.cc+1-1
@@ -26,7 +26,7 @@ namespace sim {
26 26 
27int main(int argc, char *argv[])27int main(int argc, char *argv[])
28{28{
29- LogConfig config;29+ LogConfig config = LoadLogConfig("device_aarch64");
30 InitLogger(config);30 InitLogger(config);
31 31 
32 RegisterSignalHandler();32 RegisterSignalHandler();
Mtest/hccl_vm/src/device_arm/proxy/CMakeLists.txt+10-6文件内容可能有违规信息
Mtest/hccl_vm/src/device_arm/proxy/device_sqe_parse_stub.cc+2-1
@@ -395,7 +395,8 @@ std::map<uint32_t, HcclDataType> DavidUbDataTypeMap = {
395 {0x4, HcclDataType::HCCL_DATA_TYPE_UINT16},395 {0x4, HcclDataType::HCCL_DATA_TYPE_UINT16},
396 {0x5, HcclDataType::HCCL_DATA_TYPE_UINT32},396 {0x5, HcclDataType::HCCL_DATA_TYPE_UINT32},
397 {0x6, HcclDataType::HCCL_DATA_TYPE_FP16},397 {0x6, HcclDataType::HCCL_DATA_TYPE_FP16},
398- {0x7, HcclDataType::HCCL_DATA_TYPE_FP32}398+ {0x7, HcclDataType::HCCL_DATA_TYPE_FP32},
399+ {0x8, HcclDataType::HCCL_DATA_TYPE_BFP16}
399};400};
400 401 
401HcclDataType ParseUbDataTypeDavid(uint32_t type)402HcclDataType ParseUbDataTypeDavid(uint32_t type)
Mtest/hccl_vm/src/main.cc+5-5
@@ -19,7 +19,9 @@
19 19 
20void envInit()20void envInit()
21{21{
22- int ret3 = system("sudo rm -fr ./hccl_vm_data.db ./hccl_vm_data.db-wal ./hccl_vm_data.db-shm 2>/dev/null");22+ setenv("HCCL_VM_INSTALL_ROOT", GetBinLocation().c_str(), 1);
23+ std::string dbPrefix = GetBinLocation() + "/data/hccl_vm_data.db";
24+ int ret3 = system(("sudo rm -fr " + dbPrefix + " " + dbPrefix + "-wal " + dbPrefix + "-shm 2>/dev/null").c_str());
23 int ret1 = system("sudo rm -fr /dev/shm/* 2>/dev/null");25 int ret1 = system("sudo rm -fr /dev/shm/* 2>/dev/null");
24 int ret2 = system("sudo rm -fr /tmp/hccl_sim.db* 2>/dev/null");26 int ret2 = system("sudo rm -fr /tmp/hccl_sim.db* 2>/dev/null");
25 if (ret1 != 0 || ret2 != 0) {27 if (ret1 != 0 || ret2 != 0) {
@@ -31,15 +33,13 @@ void envInit()
31 33 
32int main(int argc, char *argv[])34int main(int argc, char *argv[])
33{35{
34- LogConfig config;
35- config.fileBaseName = "hccl_vm";
36- InitLogger(config);
37- 
38 const char* envCheck = std::getenv(HVM_BASH_ENV_KEY.c_str());36 const char* envCheck = std::getenv(HVM_BASH_ENV_KEY.c_str());
39 37 
40 if (envCheck != nullptr) {38 if (envCheck != nullptr) {
41 StartHostClient(argc, argv);39 StartHostClient(argc, argv);
42 } else {40 } else {
41+ LogConfig config = LoadLogConfig("hccl_vm");
42+ InitLogger(config);
43 envInit();43 envInit();
44 std::string cmd = ArgvToString(argc, argv);44 std::string cmd = ArgvToString(argc, argv);
45 if (argc == 1) {45 if (argc == 1) {
Mtest/hccl_vm/src/plugin/CMakeLists.txt+1-1
@@ -26,7 +26,7 @@ add_subdirectory(checker)
26#)26#)
27 27 
28install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/insight"28install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/insight"
29- DESTINATION "plugin/visualize"29+ DESTINATION "plugin/visualization"
30 # 文件的权限 (755)30 # 文件的权限 (755)
31 FILE_PERMISSIONS31 FILE_PERMISSIONS
32 OWNER_READ OWNER_WRITE OWNER_EXECUTE 32 OWNER_READ OWNER_WRITE OWNER_EXECUTE
Mtest/hccl_vm/src/plugin/checker/CMakeLists.txt+11-9
@@ -46,6 +46,7 @@ target_include_directories(checker PUBLIC
46 46 
47 ${CMAKE_SOURCE_DIR}/include47 ${CMAKE_SOURCE_DIR}/include
48 ${CMAKE_SOURCE_DIR}/include/runnerdb48 ${CMAKE_SOURCE_DIR}/include/runnerdb
49+ ${CMAKE_SOURCE_DIR}/third_party
49 ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json50 ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
50)51)
51 52 
@@ -54,15 +55,16 @@ target_sources(checker PRIVATE
54)55)
55 56 
56target_link_libraries(checker PRIVATE 57target_link_libraries(checker PRIVATE
57- ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/lib64/libc_sec.so
58- pthread
59 log58 log
60 loader59 loader
61-)60+ runnerdb
62- 61+ modeldb
63-set_target_properties(checker PROPERTIES62+ store
64- INSTALL_RPATH "$ORIGIN"63+ common
65- BUILD_WITH_INSTALL_RPATH TRUE64+ ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/lib64/libc_sec.so
65+ pthread
66+ yaml-cpp::yaml-cpp
67+ sqlite3
66)68)
67 69 
68set_target_properties(checker PROPERTIES POSITION_INDEPENDENT_CODE ON)70set_target_properties(checker PROPERTIES POSITION_INDEPENDENT_CODE ON)
@@ -81,7 +83,7 @@ target_compile_options(checker PRIVATE
81)83)
82 84 
83install(TARGETS checker85install(TARGETS checker
84- DESTINATION "plugin/validate/checker"86+ DESTINATION "plugin/checker"
85 # 文件的权限 (755)87 # 文件的权限 (755)
86 PERMISSIONS88 PERMISSIONS
87 OWNER_READ OWNER_WRITE OWNER_EXECUTE89 OWNER_READ OWNER_WRITE OWNER_EXECUTE
@@ -90,7 +92,7 @@ install(TARGETS checker
90 )92 )
91 93 
92install(FILES "manifest.json"94install(FILES "manifest.json"
93- DESTINATION "plugin/validate/checker"95+ DESTINATION "plugin/checker"
94 # 文件的权限 (755)96 # 文件的权限 (755)
95 PERMISSIONS97 PERMISSIONS
96 OWNER_READ OWNER_WRITE OWNER_EXECUTE98 OWNER_READ OWNER_WRITE OWNER_EXECUTE
Mtest/hccl_vm/src/plugin/checker/main.cc+1-4
@@ -546,10 +546,7 @@ void ProcessCommand(const std::string& line) {
546}546}
547 547 
548int main() {548int main() {
549- LogConfig config;549+ LogConfig config = LoadLogConfig("checker");
550- config.fileBaseName = "checker";
551- config.consoleLevel = 2;
552- config.fileLevel = 0;
553 InitLogger(config);550 InitLogger(config);
554 551 
555 HCCL_VM_INFO("[Checker] Plugin process active. Listening for commands...");552 HCCL_VM_INFO("[Checker] Plugin process active. Listening for commands...");
Mtest/hccl_vm/src/plugin/checker/src/dfx/ccu_error_handler.cc+1-1
@@ -72,7 +72,7 @@ void CcuErrorHandler::GetCcuErrorMsg(int32_t deviceId, const ParaCcu &ccuTaskPar
72 const auto missionContext = GetCcuMissionContext(deviceId, ccuTaskParam.dieId, ccuTaskParam.execMissionId);72 const auto missionContext = GetCcuMissionContext(deviceId, ccuTaskParam.dieId, ccuTaskParam.execMissionId);
73 const uint16_t missionStatus = missionContext.GetStatus();73 const uint16_t missionStatus = missionContext.GetStatus();
74 if (missionStatus == 0) {74 if (missionStatus == 0) {
75- HCCL_INFO("[CcuErrorHandler][%s] no err found, mission status is 0, deviceId[%d], dieId[%u], execMissionId[%u]",75+ HCCL_INFO("[CcuErrorHandler] no err found, mission status is 0, deviceId[%d], dieId[%u], execMissionId[%u]",
76 __func__, deviceId, static_cast<u32>(ccuTaskParam.dieId), static_cast<u32>(ccuTaskParam.execMissionId));76 __func__, deviceId, static_cast<u32>(ccuTaskParam.dieId), static_cast<u32>(ccuTaskParam.execMissionId));
77 return;77 return;
78 }78 }
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator/ccu_task_transform_instruct_a5.cc+1-1文件内容审核中,请稍后刷新重试
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator_v3/aiv_graph_generator_v3/aiv_task_transform_v3.cc+1036-4
@@ -11,6 +11,7 @@
11#include "aiv_task_transform_v3.h"11#include "aiv_task_transform_v3.h"
12 12 
13#include <algorithm>13#include <algorithm>
14+#include <array>
14#include <chrono>15#include <chrono>
15#include <deque>16#include <deque>
16#include <limits>17#include <limits>
@@ -19,6 +20,7 @@
19#include <queue>20#include <queue>
20#include <set>21#include <set>
21#include <sstream>22#include <sstream>
23+#include <unordered_map>
22#include <utility>24#include <utility>
23 25 
24#include "aiv_snapshot_json_loader_v3.h"26#include "aiv_snapshot_json_loader_v3.h"
@@ -50,9 +52,21 @@ struct AivLaunchContext {
50 NodeId endNodeId{INVALID_NODE_ID};52 NodeId endNodeId{INVALID_NODE_ID};
51 std::map<uint32_t, AivNodeRecord> taskIdToNode;53 std::map<uint32_t, AivNodeRecord> taskIdToNode;
52 std::vector<NodeId> internalNodeIds;54 std::vector<NodeId> internalNodeIds;
55+ std::set<NodeId> inactiveNodeIds;
56+ std::unordered_map<NodeId, std::vector<uint32_t>> cpGmMergeSourceTaskIds;
57+ uint32_t nextSyntheticTaskId{0};
53 size_t setWaitEdgeCount{0};58 size_t setWaitEdgeCount{0};
54 size_t pipeBarrierMergeCount{0};59 size_t pipeBarrierMergeCount{0};
55 size_t syncAllMergeCount{0};60 size_t syncAllMergeCount{0};
61+ size_t taskJsonTotalTaskCount{0};
62+ size_t dagNodeCountBeforeCpGmMerge{0};
63+ size_t dagNodeCountAfterCpGmMerge{0};
64+ size_t cpGmLoopMergeCount{0};
65+ size_t cpGmMergedIterationCount{0};
66+ size_t cpGmMergedOriginalNodeCount{0};
67+ size_t cpGmGeneratedNodeCount{0};
68+ size_t cpGmInactiveNodeCount{0};
69+ size_t cpGmLoopSkipCount{0};
56};70};
57 71 
58struct SetWaitKey {72struct SetWaitKey {
@@ -116,6 +130,13 @@ struct MergeGroup {
116 uint32_t syncRound{std::numeric_limits<uint32_t>::max()};130 uint32_t syncRound{std::numeric_limits<uint32_t>::max()};
117};131};
118 132 
133+using CpGmIter = std::array<TaskNode *, 6>;
134+ 
135+struct CpGmLoopGather {
136+ std::vector<CpGmIter> templateLoop;
137+ std::array<TaskNode *, 6> merged{};
138+};
139+ 
119uint64_t ElapsedNs(AivExpandClock::time_point start, AivExpandClock::time_point end)140uint64_t ElapsedNs(AivExpandClock::time_point start, AivExpandClock::time_point end)
120{141{
121 return static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count());142 return static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count());
@@ -278,6 +299,11 @@ HcclResult AddEdge(TaskGraphGeneratorV3 *graph, NodeId parent, NodeId child)
278 return graph->AddEdge(parent, child);299 return graph->AddEdge(parent, child);
279}300}
280 301 
302+bool IsInactive(const AivLaunchContext &ctx, NodeId nodeId)
303+{
304+ return ctx.inactiveNodeIds.count(nodeId) != 0;
305+}
306+ 
281HcclResult RewireGroupMember(TaskGraphGeneratorV3 *graph, NodeId memberNodeId, NodeId mergeNodeId)307HcclResult RewireGroupMember(TaskGraphGeneratorV3 *graph, NodeId memberNodeId, NodeId mergeNodeId)
282{308{
283 if (graph == nullptr) {309 if (graph == nullptr) {
@@ -565,6 +591,995 @@ HcclResult AddSetWaitFlagEdges(AivLaunchContext &ctx)
565 return HCCL_SUCCESS;591 return HCCL_SUCCESS;
566}592}
567 593 
594+size_t CountAivSnapshotTasks(const AivRuntimeTaskSnapshotV3 &snapshot)
595+{
596+ size_t count = 0;
597+ for (const auto &block : snapshot.blocks) {
598+ count += block.scalarTasks.size();
599+ count += block.mte2Tasks.size();
600+ count += block.mte3Tasks.size();
601+ }
602+ return count;
603+}
604+ 
605+uint32_t FindMaxTaskId(const AivRuntimeTaskSnapshotV3 &snapshot)
606+{
607+ uint32_t maxTaskId = 0;
608+ for (const auto &block : snapshot.blocks) {
609+ auto update = [&maxTaskId](const std::vector<AivRuntimeTaskV3> &tasks) {
610+ for (const auto &task : tasks) {
611+ maxTaskId = std::max(maxTaskId, task.taskId);
612+ }
613+ };
614+ update(block.scalarTasks);
615+ update(block.mte2Tasks);
616+ update(block.mte3Tasks);
617+ }
618+ return maxTaskId;
619+}
620+ 
621+size_t CountReachableActiveInternalNodes(const AivLaunchContext &ctx)
622+{
623+ if (ctx.graph == nullptr || ctx.headNodeId == INVALID_NODE_ID) {
624+ return 0;
625+ }
626+ std::set<NodeId> internalSet(ctx.internalNodeIds.begin(), ctx.internalNodeIds.end());
627+ std::set<NodeId> visited;
628+ std::queue<NodeId> nodeQueue;
629+ nodeQueue.push(ctx.headNodeId);
630+ visited.insert(ctx.headNodeId);
631+ 
632+ while (!nodeQueue.empty()) {
633+ const NodeId nodeId = nodeQueue.front();
634+ nodeQueue.pop();
635+ const TaskNode *node = ctx.graph->GetNode(nodeId);
636+ if (node == nullptr) {
637+ continue;
638+ }
639+ for (const TaskNode *child : node->GetChildren()) {
640+ if (child == nullptr) {
641+ continue;
642+ }
643+ const NodeId childId = child->GetNodeId();
644+ if (internalSet.count(childId) == 0 || IsInactive(ctx, childId)) {
645+ continue;
646+ }
647+ if (visited.insert(childId).second) {
648+ nodeQueue.push(childId);
649+ }
650+ }
651+ }
652+ return visited.size();
653+}
654+ 
655+bool IsCpGmExternalMemType(MemType memType)
656+{
657+ return memType == MemType::INPUT || memType == MemType::CCL || memType == MemType::OUTPUT ||
658+ memType == MemType::FLAG_AIV;
659+}
660+ 
661+bool SameSliceIdentity(const MemSlice &lhs, const MemSlice &rhs)
662+{
663+ return lhs.rankId == rhs.rankId && lhs.memType == rhs.memType;
664+}
665+ 
666+bool IsContinuousAfter(const MemSlice &prev, const MemSlice &next)
667+{
668+ return SameSliceIdentity(prev, next) && next.offset == prev.offset + prev.len;
669+}
670+ 
671+bool SliceExactEqual(const MemSlice &lhs, const MemSlice &rhs)
672+{
673+ return lhs.rankId == rhs.rankId && lhs.memType == rhs.memType && lhs.offset == rhs.offset && lhs.len == rhs.len;
674+}
675+ 
676+bool IntervalsOverlap(const MemSlice &lhs, const MemSlice &rhs)
677+{
678+ if (!SameSliceIdentity(lhs, rhs)) {
679+ return false;
680+ }
681+ return lhs.offset < rhs.offset + rhs.len && rhs.offset < lhs.offset + lhs.len;
682+}
683+ 
684+std::vector<MemSlice> MergeMemSliceIntervals(std::vector<MemSlice> slices)
685+{
686+ if (slices.empty()) {
687+ return {};
688+ }
689+ std::sort(slices.begin(), slices.end(), [](const MemSlice &lhs, const MemSlice &rhs) {
690+ if (lhs.rankId != rhs.rankId) {
691+ return lhs.rankId < rhs.rankId;
692+ }
693+ if (lhs.memType != rhs.memType) {
694+ return static_cast<uint32_t>(lhs.memType) < static_cast<uint32_t>(rhs.memType);
695+ }
696+ if (lhs.offset != rhs.offset) {
697+ return lhs.offset < rhs.offset;
698+ }
699+ return lhs.len < rhs.len;
700+ });
701+ 
702+ std::vector<MemSlice> merged;
703+ merged.push_back(slices.front());
704+ for (size_t index = 1; index < slices.size(); ++index) {
705+ MemSlice &last = merged.back();
706+ const MemSlice &cur = slices[index];
707+ if (!SameSliceIdentity(last, cur)) {
708+ merged.push_back(cur);
709+ continue;
710+ }
711+ const uint64_t lastEnd = last.offset + last.len;
712+ const uint64_t curEnd = cur.offset + cur.len;
713+ if (cur.offset <= lastEnd) {
714+ last.len = std::max(lastEnd, curEnd) - last.offset;
715+ continue;
716+ }
717+ merged.push_back(cur);
718+ }
719+ return merged;
720+}
721+ 
722+bool BuildContinuousMergedSlices(const std::vector<MemSlice> &slices, std::vector<MemSlice> &merged)
723+{
724+ merged.clear();
725+ if (slices.empty()) {
726+ return false;
727+ }
728+ MemSlice cur = slices.front();
729+ for (size_t index = 1; index < slices.size(); ++index) {
730+ if (!IsContinuousAfter(cur, slices[index])) {
731+ return false;
732+ }
733+ cur.len += slices[index].len;
734+ }
735+ merged.push_back(cur);
736+ return true;
737+}
738+ 
739+bool GetDataSlices(const TaskNode *node, MemSlice &src, MemSlice &dst, uint8_t &dataType, uint8_t &reduceOp)
740+{
741+ if (node == nullptr) {
742+ return false;
743+ }
744+ if (node->GetType() == TaskType::TRANS_MEM) {
745+ const auto *task = dynamic_cast<const TaskTransMem *>(node);
746+ if (task == nullptr) {
747+ return false;
748+ }
749+ src = task->GetSrc();
750+ dst = task->GetDst();
751+ dataType = 0;
752+ reduceOp = 0;
753+ return true;
754+ }
755+ if (node->GetType() == TaskType::REDUCE) {
756+ const auto *task = dynamic_cast<const TaskReduce *>(node);
757+ if (task == nullptr || task->GetSrcs().size() != 1U) {
758+ return false;
759+ }
760+ src = task->GetSrc();
761+ dst = task->GetDst();
762+ dataType = task->GetDataType();
763+ reduceOp = task->GetReduceOp();
764+ return true;
765+ }
766+ return false;
767+}
768+ 
769+bool IsValidCpGmDataNode(const TaskNode *node, uint32_t pipe, bool ubAsDst)
770+{
771+ MemSlice src;
772+ MemSlice dst;
773+ uint8_t dataType = 0;
774+ uint8_t reduceOp = 0;
775+ if (!GetDataSlices(node, src, dst, dataType, reduceOp)) {
776+ return false;
777+ }
778+ if (node->GetPosition().pipe != pipe) {
779+ return false;
780+ }
781+ const MemSlice &ubSlice = ubAsDst ? dst : src;
782+ const MemSlice &externalSlice = ubAsDst ? src : dst;
783+ return ubSlice.memType == MemType::UB_AIV && IsCpGmExternalMemType(externalSlice.memType);
784+}
785+ 
786+bool MatchPipeEventNode(const TaskNode *node, TaskType type, uint32_t curPipe, uint32_t srcPipe, uint32_t dstPipe)
787+{
788+ if (node == nullptr || node->GetType() != type || node->GetPosition().pipe != curPipe) {
789+ return false;
790+ }
791+ const AivPipeEvent *event = nullptr;
792+ if (type == TaskType::AIV_SET_FLAG) {
793+ const auto *setFlag = dynamic_cast<const TaskAivSetFlag *>(node);
794+ event = setFlag == nullptr ? nullptr : &setFlag->GetEvent();
795+ } else {
796+ const auto *waitFlag = dynamic_cast<const TaskAivWaitFlag *>(node);
797+ event = waitFlag == nullptr ? nullptr : &waitFlag->GetEvent();
798+ }
799+ return event != nullptr && event->curPipe == curPipe && event->srcPipe == srcPipe && event->dstPipe == dstPipe;
800+}
801+ 
802+const AivPipeEvent *GetPipeEvent(const TaskNode *node)
803+{
804+ if (node == nullptr) {
805+ return nullptr;
806+ }
807+ if (node->GetType() == TaskType::AIV_SET_FLAG) {
808+ const auto *setFlag = dynamic_cast<const TaskAivSetFlag *>(node);
809+ return setFlag == nullptr ? nullptr : &setFlag->GetEvent();
810+ }
811+ if (node->GetType() == TaskType::AIV_WAIT_FLAG) {
812+ const auto *waitFlag = dynamic_cast<const TaskAivWaitFlag *>(node);
813+ return waitFlag == nullptr ? nullptr : &waitFlag->GetEvent();
814+ }
815+ return nullptr;
816+}
817+ 
818+bool SamePositionScope(const TaskPosition &lhs, const TaskPosition &rhs)
819+{
820+ return lhs.rankId == rhs.rankId && lhs.launchIdx == rhs.launchIdx && lhs.blockId == rhs.blockId;
821+}
822+ 
823+bool SameEventExceptTaskId(const TaskNode *lhsNode, const TaskNode *rhsNode)
824+{
825+ if (lhsNode == nullptr || rhsNode == nullptr || lhsNode->GetType() != rhsNode->GetType()) {
826+ return false;
827+ }
828+ const AivPipeEvent *lhs = GetPipeEvent(lhsNode);
829+ const AivPipeEvent *rhs = GetPipeEvent(rhsNode);
830+ if (lhs == nullptr || rhs == nullptr) {
831+ return false;
832+ }
833+ return lhs->rankId == rhs->rankId && lhs->launchIdx == rhs->launchIdx && lhs->blockId == rhs->blockId &&
834+ lhs->curPipe == rhs->curPipe && lhs->srcPipe == rhs->srcPipe && lhs->dstPipe == rhs->dstPipe &&
835+ lhs->eventId == rhs->eventId;
836+}
837+ 
838+bool SameSetWaitEvent(const TaskNode *setNode, const TaskNode *waitNode)
839+{
840+ const AivPipeEvent *setEvent = GetPipeEvent(setNode);
841+ const AivPipeEvent *waitEvent = GetPipeEvent(waitNode);
842+ if (setEvent == nullptr || waitEvent == nullptr) {
843+ return false;
844+ }
845+ return setEvent->rankId == waitEvent->rankId && setEvent->launchIdx == waitEvent->launchIdx &&
846+ setEvent->blockId == waitEvent->blockId && setEvent->srcPipe == waitEvent->srcPipe &&
847+ setEvent->dstPipe == waitEvent->dstPipe && setEvent->eventId == waitEvent->eventId;
848+}
849+ 
850+bool SameDataTypeAndOp(const TaskNode *lhsNode, const TaskNode *rhsNode)
851+{
852+ if (lhsNode == nullptr || rhsNode == nullptr || lhsNode->GetType() != rhsNode->GetType()) {
853+ return false;
854+ }
855+ if (lhsNode->GetType() != TaskType::REDUCE) {
856+ return true;
857+ }
858+ MemSlice lhsSrc;
859+ MemSlice lhsDst;
860+ uint8_t lhsDataType = 0;
861+ uint8_t lhsReduceOp = 0;
862+ MemSlice rhsSrc;
863+ MemSlice rhsDst;
864+ uint8_t rhsDataType = 0;
865+ uint8_t rhsReduceOp = 0;
866+ return GetDataSlices(lhsNode, lhsSrc, lhsDst, lhsDataType, lhsReduceOp) &&
867+ GetDataSlices(rhsNode, rhsSrc, rhsDst, rhsDataType, rhsReduceOp) &&
868+ lhsDataType == rhsDataType && lhsReduceOp == rhsReduceOp;
869+}
870+ 
871+bool DataSliceShapeValid(const CpGmIter &iter)
872+{
873+ MemSlice t0Src;
874+ MemSlice t0Dst;
875+ MemSlice t3Src;
876+ MemSlice t3Dst;
877+ uint8_t dataType = 0;
878+ uint8_t reduceOp = 0;
879+ if (!GetDataSlices(iter[0], t0Src, t0Dst, dataType, reduceOp) ||
880+ !GetDataSlices(iter[3], t3Src, t3Dst, dataType, reduceOp)) {
881+ return false;
882+ }
883+ if (t0Dst.memType != MemType::UB_AIV || t3Src.memType != MemType::UB_AIV) {
884+ return false;
885+ }
886+ if (!IsCpGmExternalMemType(t0Src.memType) || !IsCpGmExternalMemType(t3Dst.memType)) {
887+ return false;
888+ }
889+ if (t0Dst.len != t3Src.len) {
890+ return false;
891+ }
892+ return true;
893+}
894+ 
895+bool SameUbLayout(const CpGmIter &iter)
896+{
897+ MemSlice t0Src;
898+ MemSlice t0Dst;
899+ MemSlice t3Src;
900+ MemSlice t3Dst;
901+ uint8_t dataType = 0;
902+ uint8_t reduceOp = 0;
903+ if (!GetDataSlices(iter[0], t0Src, t0Dst, dataType, reduceOp) ||
904+ !GetDataSlices(iter[3], t3Src, t3Dst, dataType, reduceOp)) {
905+ return false;
906+ }
907+ return SliceExactEqual(t0Dst, t3Src);
908+}
909+ 
910+bool SameExternalIdentityAsFirst(const CpGmIter &first, const CpGmIter &iter)
911+{
912+ MemSlice firstT0Src;
913+ MemSlice firstT0Dst;
914+ MemSlice firstT3Src;
915+ MemSlice firstT3Dst;
916+ MemSlice curT0Src;
917+ MemSlice curT0Dst;
918+ MemSlice curT3Src;
919+ MemSlice curT3Dst;
920+ uint8_t dataType = 0;
921+ uint8_t reduceOp = 0;
922+ if (!GetDataSlices(first[0], firstT0Src, firstT0Dst, dataType, reduceOp) ||
923+ !GetDataSlices(first[3], firstT3Src, firstT3Dst, dataType, reduceOp) ||
924+ !GetDataSlices(iter[0], curT0Src, curT0Dst, dataType, reduceOp) ||
925+ !GetDataSlices(iter[3], curT3Src, curT3Dst, dataType, reduceOp)) {
926+ return false;
927+ }
928+ return SameSliceIdentity(firstT0Src, curT0Src) && SameSliceIdentity(firstT3Dst, curT3Dst);
929+}
930+ 
931+bool ExternalSlicesContinuous(const CpGmLoopGather &gather)
932+{
933+ if (gather.templateLoop.empty()) {
934+ return false;
935+ }
936+ std::vector<MemSlice> t0Srcs;
937+ std::vector<MemSlice> t3Dsts;
938+ t0Srcs.reserve(gather.templateLoop.size());
939+ t3Dsts.reserve(gather.templateLoop.size());
940+ for (const CpGmIter &iter : gather.templateLoop) {
941+ MemSlice t0Src;
942+ MemSlice t0Dst;
943+ MemSlice t3Src;
944+ MemSlice t3Dst;
945+ uint8_t dataType = 0;
946+ uint8_t reduceOp = 0;
947+ if (!GetDataSlices(iter[0], t0Src, t0Dst, dataType, reduceOp) ||
948+ !GetDataSlices(iter[3], t3Src, t3Dst, dataType, reduceOp)) {
949+ return false;
950+ }
951+ t0Srcs.push_back(t0Src);
952+ t3Dsts.push_back(t3Dst);
953+ }
954+ std::vector<MemSlice> merged;
955+ return BuildContinuousMergedSlices(t0Srcs, merged) && BuildContinuousMergedSlices(t3Dsts, merged);
956+}
957+ 
958+bool HasValidSyntheticTaskIdBudget(const AivLaunchContext &ctx)
959+{
960+ const uint32_t maxTaskId = std::numeric_limits<uint32_t>::max();
961+ return ctx.nextSyntheticTaskId <= maxTaskId - 6U;
962+}
963+ 
964+bool CanMergeCpGmRun(const AivLaunchContext &ctx, const CpGmLoopGather &gather)
965+{
966+ if (!HasValidSyntheticTaskIdBudget(ctx)) {
967+ return false;
968+ }
969+ return ExternalSlicesContinuous(gather);
970+}
971+ 
972+TaskNode *FindOnlyChildByType(const AivLaunchContext &ctx, const TaskNode *node, TaskType type)
973+{
974+ if (node == nullptr) {
975+ return nullptr;
976+ }
977+ TaskNode *matched = nullptr;
978+ for (TaskNode *child : node->GetChildren()) {
979+ if (child == nullptr || IsInactive(ctx, child->GetNodeId()) || child->GetType() != type) {
980+ continue;
981+ }
982+ if (matched != nullptr) {
983+ return nullptr;
984+ }
985+ matched = child;
986+ }
987+ return matched;
988+}
989+ 
990+TaskNode *FindOnlyWaitChildForSet(const AivLaunchContext &ctx, const TaskNode *setNode)
991+{
992+ if (setNode == nullptr) {
993+ return nullptr;
994+ }
995+ TaskNode *matched = nullptr;
996+ for (TaskNode *child : setNode->GetChildren()) {
997+ if (child == nullptr || IsInactive(ctx, child->GetNodeId()) || child->GetType() != TaskType::AIV_WAIT_FLAG ||
998+ !SameSetWaitEvent(setNode, child)) {
999+ continue;
1000+ }
1001+ if (matched != nullptr) {
1002+ return nullptr;
1003+ }
1004+ matched = child;
1005+ }
1006+ return matched;
1007+}
1008+ 
1009+TaskNode *FindOnlySetParentForWait(const AivLaunchContext &ctx, const TaskNode *waitNode)
1010+{
1011+ if (waitNode == nullptr) {
1012+ return nullptr;
1013+ }
1014+ TaskNode *matched = nullptr;
1015+ for (TaskNode *parent : waitNode->GetParents()) {
1016+ if (parent == nullptr || IsInactive(ctx, parent->GetNodeId()) || parent->GetType() != TaskType::AIV_SET_FLAG ||
1017+ !SameSetWaitEvent(parent, waitNode)) {
1018+ continue;
1019+ }
1020+ if (matched != nullptr) {
1021+ return nullptr;
1022+ }
1023+ matched = parent;
1024+ }
1025+ return matched;
1026+}
1027+ 
1028+bool HasActiveChild(const AivLaunchContext &ctx, const TaskNode *parent, const TaskNode *target)
1029+{
1030+ if (parent == nullptr || target == nullptr) {
1031+ return false;
1032+ }
1033+ const NodeId targetId = target->GetNodeId();
1034+ if (IsInactive(ctx, targetId)) {
1035+ return false;
1036+ }
1037+ for (const TaskNode *child : parent->GetChildren()) {
1038+ if (child != nullptr && child->GetNodeId() == targetId && !IsInactive(ctx, child->GetNodeId())) {
1039+ return true;
1040+ }
1041+ }
1042+ return false;
1043+}
1044+ 
1045+bool MatchCpGmTemplateFromNode(TaskNode *start, const AivLaunchContext &ctx, CpGmIter &iter)
1046+{
1047+ if (start == nullptr || IsInactive(ctx, start->GetNodeId())) {
1048+ return false;
1049+ }
1050+ iter = CpGmIter{};
1051+ iter[0] = start;
1052+ if (!IsValidCpGmDataNode(iter[0], PIPE_MTE2, true)) {
1053+ return false;
1054+ }
1055+ iter[1] = FindOnlyChildByType(ctx, iter[0], TaskType::AIV_SET_FLAG);
1056+ iter[2] = FindOnlyWaitChildForSet(ctx, iter[1]);
1057+ iter[3] = FindOnlyChildByType(ctx, iter[2], TaskType::TRANS_MEM);
1058+ if (iter[3] == nullptr) {
1059+ iter[3] = FindOnlyChildByType(ctx, iter[2], TaskType::REDUCE);
1060+ }
1061+ iter[4] = FindOnlyChildByType(ctx, iter[3], TaskType::AIV_SET_FLAG);
1062+ iter[5] = FindOnlyWaitChildForSet(ctx, iter[4]);
1063+ for (TaskNode *node : iter) {
1064+ if (node == nullptr || IsInactive(ctx, node->GetNodeId())) {
1065+ return false;
1066+ }
1067+ }
1068+ 
1069+ if (!HasActiveChild(ctx, iter[1], iter[5])) {
1070+ return false;
1071+ }
1072+ 
1073+ if (FindOnlySetParentForWait(ctx, iter[2]) != iter[1]) {
1074+ return false;
1075+ }
1076+ if (FindOnlySetParentForWait(ctx, iter[5]) != iter[4]) {
1077+ return false;
1078+ }
1079+ 
1080+ if (!IsValidCpGmDataNode(iter[0], PIPE_MTE2, true) ||
1081+ !MatchPipeEventNode(iter[1], TaskType::AIV_SET_FLAG, PIPE_MTE2, PIPE_MTE2, PIPE_MTE3) ||
1082+ !MatchPipeEventNode(iter[2], TaskType::AIV_WAIT_FLAG, PIPE_MTE3, PIPE_MTE2, PIPE_MTE3) ||
1083+ !IsValidCpGmDataNode(iter[3], PIPE_MTE3, false) ||
1084+ !MatchPipeEventNode(iter[4], TaskType::AIV_SET_FLAG, PIPE_MTE3, PIPE_MTE3, PIPE_MTE2) ||
1085+ !MatchPipeEventNode(iter[5], TaskType::AIV_WAIT_FLAG, PIPE_MTE2, PIPE_MTE3, PIPE_MTE2)) {
1086+ return false;
1087+ }
1088+ if (!SameSetWaitEvent(iter[1], iter[2]) || !SameSetWaitEvent(iter[4], iter[5])) {
1089+ return false;
1090+ }
1091+ const TaskPosition &position = iter[0]->GetPosition();
1092+ for (TaskNode *node : iter) {
1093+ if (!SamePositionScope(position, node->GetPosition())) {
1094+ return false;
1095+ }
1096+ }
1097+ return true;
1098+}
1099+ 
1100+bool ValidateCpGmRun(const CpGmLoopGather &gather)
1101+{
1102+ if (gather.templateLoop.size() <= 1U) {
1103+ return false;
1104+ }
1105+ const CpGmIter &first = gather.templateLoop.front();
1106+ for (const CpGmIter &iter : gather.templateLoop) {
1107+ if (iter[0]->GetType() != first[0]->GetType() || iter[3]->GetType() != first[3]->GetType()) {
1108+ return false;
1109+ }
1110+ if (!DataSliceShapeValid(iter) || !SameUbLayout(iter) || !SameExternalIdentityAsFirst(first, iter)) {
1111+ return false;
1112+ }
1113+ for (size_t idx : {1U, 2U, 4U, 5U}) {
1114+ if (!SameEventExceptTaskId(first[idx], iter[idx])) {
1115+ return false;
1116+ }
1117+ }
1118+ for (size_t idx : {0U, 3U}) {
1119+ if (!SameDataTypeAndOp(first[idx], iter[idx])) {
1120+ return false;
1121+ }
1122+ }
1123+ }
1124+ return true;
1125+}
1126+ 
1127+bool CollectCpGmBlockTopo(const AivLaunchContext &ctx, uint32_t blockId, std::vector<NodeId> &topo)
1128+{
1129+ topo.clear();
1130+ std::set<NodeId> blockNodes;
1131+ for (NodeId nodeId : ctx.internalNodeIds) {
1132+ if (IsInactive(ctx, nodeId)) {
1133+ continue;
1134+ }
1135+ const TaskNode *node = ctx.graph->GetNode(nodeId);
1136+ if (node == nullptr || node->GetPosition().blockId != blockId) {
1137+ continue;
1138+ }
1139+ const uint32_t pipe = node->GetPosition().pipe;
1140+ if (pipe == PIPE_MTE2 || pipe == PIPE_MTE3) {
1141+ blockNodes.insert(nodeId);
1142+ }
1143+ }
1144+ if (blockNodes.empty()) {
1145+ return true;
1146+ }
1147+ 
1148+ std::map<NodeId, uint32_t> indegree;
1149+ std::map<NodeId, std::vector<NodeId>> children;
1150+ for (NodeId nodeId : blockNodes) {
1151+ indegree[nodeId] = 0;
1152+ }
1153+ for (NodeId nodeId : blockNodes) {
1154+ const TaskNode *node = ctx.graph->GetNode(nodeId);
1155+ if (node == nullptr) {
1156+ return false;
1157+ }
1158+ for (const TaskNode *child : node->GetChildren()) {
1159+ if (child == nullptr) {
1160+ return false;
1161+ }
1162+ const NodeId childId = child->GetNodeId();
1163+ if (blockNodes.count(childId) == 0) {
1164+ continue;
1165+ }
1166+ children[nodeId].push_back(childId);
1167+ ++indegree[childId];
1168+ }
1169+ }
1170+ 
1171+ auto lessByTaskId = [&ctx](NodeId lhs, NodeId rhs) {
1172+ const TaskNode *lhsNode = ctx.graph->GetNode(lhs);
1173+ const TaskNode *rhsNode = ctx.graph->GetNode(rhs);
1174+ const uint32_t lhsTaskId = lhsNode == nullptr ? std::numeric_limits<uint32_t>::max() :
1175+ lhsNode->GetPosition().taskId;
1176+ const uint32_t rhsTaskId = rhsNode == nullptr ? std::numeric_limits<uint32_t>::max() :
1177+ rhsNode->GetPosition().taskId;
1178+ if (lhsTaskId != rhsTaskId) {
1179+ return lhsTaskId > rhsTaskId;
1180+ }
1181+ return lhs > rhs;
1182+ };
1183+ std::priority_queue<NodeId, std::vector<NodeId>, decltype(lessByTaskId)> ready(lessByTaskId);
1184+ for (const auto &entry : indegree) {
1185+ if (entry.second == 0) {
1186+ ready.push(entry.first);
1187+ }
1188+ }
1189+ while (!ready.empty()) {
1190+ const NodeId nodeId = ready.top();
1191+ ready.pop();
1192+ topo.push_back(nodeId);
1193+ for (NodeId childId : children[nodeId]) {
1194+ auto iter = indegree.find(childId);
1195+ if (iter == indegree.end()) {
1196+ continue;
1197+ }
1198+ if (--iter->second == 0) {
1199+ ready.push(childId);
1200+ }
1201+ }
1202+ }
1203+ return topo.size() == blockNodes.size();
1204+}
1205+ 
1206+uint32_t AllocateSyntheticTaskId(AivLaunchContext &ctx)
1207+{
1208+ return ctx.nextSyntheticTaskId++;
1209+}
1210+ 
1211+TaskPosition MakeSyntheticPosition(AivLaunchContext &ctx, const TaskNode *source)
1212+{
1213+ TaskPosition position = source == nullptr ? TaskPosition{} : source->GetPosition();
1214+ position.taskId = AllocateSyntheticTaskId(ctx);
1215+ return position;
1216+}
1217+ 
1218+std::unique_ptr<TaskNode> CloneSyntheticSyncNode(const TaskNode *source, const TaskPosition &position)
1219+{
1220+ if (source == nullptr) {
1221+ return nullptr;
1222+ }
1223+ if (source->GetType() == TaskType::AIV_SET_FLAG) {
1224+ const auto *setFlag = dynamic_cast<const TaskAivSetFlag *>(source);
1225+ if (setFlag == nullptr) {
1226+ return nullptr;
1227+ }
1228+ AivPipeEvent event = setFlag->GetEvent();
1229+ event.taskId = position.taskId;
1230+ return std::make_unique<TaskAivSetFlag>(event);
1231+ }
1232+ if (source->GetType() == TaskType::AIV_WAIT_FLAG) {
1233+ const auto *waitFlag = dynamic_cast<const TaskAivWaitFlag *>(source);
1234+ if (waitFlag == nullptr) {
1235+ return nullptr;
1236+ }
1237+ AivPipeEvent event = waitFlag->GetEvent();
1238+ event.taskId = position.taskId;
1239+ return std::make_unique<TaskAivWaitFlag>(event);
1240+ }
1241+ return nullptr;
1242+}
1243+ 
1244+std::vector<uint32_t> CollectSourceTaskIds(const CpGmLoopGather &gather, size_t column)
1245+{
1246+ std::vector<uint32_t> taskIds;
1247+ taskIds.reserve(gather.templateLoop.size());
1248+ for (const CpGmIter &iter : gather.templateLoop) {
1249+ taskIds.push_back(iter[column]->GetPosition().taskId);
1250+ }
1251+ return taskIds;
1252+}
1253+ 
1254+std::unique_ptr<TaskNode> BuildBatchNodeForColumn(const CpGmLoopGather &gather, size_t column)
1255+{
1256+ std::vector<MemSlice> srcs;
1257+ std::vector<MemSlice> dsts;
1258+ srcs.reserve(gather.templateLoop.size());
1259+ dsts.reserve(gather.templateLoop.size());
1260+ 
1261+ const TaskType taskType = gather.templateLoop.front()[column]->GetType();
1262+ uint8_t dataType = 0;
1263+ uint8_t reduceOp = 0;
1264+ for (const CpGmIter &iter : gather.templateLoop) {
1265+ MemSlice src;
1266+ MemSlice dst;
1267+ uint8_t curDataType = 0;
1268+ uint8_t curReduceOp = 0;
1269+ if (!GetDataSlices(iter[column], src, dst, curDataType, curReduceOp)) {
1270+ return nullptr;
1271+ }
1272+ if (iter == gather.templateLoop.front()) {
1273+ dataType = curDataType;
1274+ reduceOp = curReduceOp;
1275+ }
1276+ srcs.push_back(src);
1277+ dsts.push_back(dst);
1278+ }
1279+ 
1280+ std::vector<MemSlice> mergedSrcs;
1281+ std::vector<MemSlice> mergedDsts;
1282+ const bool srcIsUb = !srcs.empty() && srcs.front().memType == MemType::UB_AIV;
1283+ const bool dstIsUb = !dsts.empty() && dsts.front().memType == MemType::UB_AIV;
1284+ if (srcIsUb) {
1285+ mergedSrcs = MergeMemSliceIntervals(srcs);
1286+ } else if (!BuildContinuousMergedSlices(srcs, mergedSrcs)) {
1287+ return nullptr;
1288+ }
1289+ if (dstIsUb) {
1290+ mergedDsts = MergeMemSliceIntervals(dsts);
1291+ } else if (!BuildContinuousMergedSlices(dsts, mergedDsts)) {
1292+ return nullptr;
1293+ }
1294+ 
1295+ if (taskType == TaskType::TRANS_MEM) {
1296+ auto batch = std::make_unique<TaskBatchTransMem>(ProtocolType::SDMA);
1297+ batch->SetSrcMemSlices(std::move(srcs));
1298+ batch->SetDstMemSlices(std::move(dsts));
1299+ batch->SetMergedSrcMemSlices(std::move(mergedSrcs));
1300+ batch->SetMergedDstMemSlices(std::move(mergedDsts));
1301+ return batch;
1302+ }
1303+ if (taskType == TaskType::REDUCE) {
1304+ auto batch = std::make_unique<TaskBatchReduce>(dataType, reduceOp, ProtocolType::SDMA);
1305+ std::vector<std::vector<MemSlice>> srcGroups;
1306+ srcGroups.reserve(srcs.size());
1307+ for (const MemSlice &src : srcs) {
1308+ srcGroups.push_back({src});
1309+ }
1310+ std::vector<std::vector<MemSlice>> mergedSrcGroups;
1311+ if (mergedDsts.size() == 1U && !mergedSrcs.empty()) {
1312+ mergedSrcGroups.push_back(mergedSrcs);
1313+ } else {
1314+ for (const MemSlice &src : srcs) {
1315+ mergedSrcGroups.push_back({src});
1316+ }
1317+ mergedDsts = dsts;
1318+ }
1319+ batch->SetSrcMemSlices(std::move(srcGroups));
1320+ batch->SetDstMemSlices(std::move(dsts));
1321+ batch->SetMergedSrcMemSlices(std::move(mergedSrcGroups));
1322+ batch->SetMergedDstMemSlices(std::move(mergedDsts));
1323+ return batch;
1324+ }
1325+ return nullptr;
1326+}
1327+ 
1328+HcclResult AppendCpGmMergedNode(AivLaunchContext &ctx, CpGmLoopGather &gather, size_t column,
1329+ std::unique_ptr<TaskNode> node, const TaskPosition &position)
1330+{
1331+ if (node == nullptr) {
1332+ return HCCL_E_INTERNAL;
1333+ }
1334+ NodeId nodeId = INVALID_NODE_ID;
1335+ HcclResult ret = ctx.graph->AppendGeneratedNode(std::move(node), position, nodeId);
1336+ if (ret != HCCL_SUCCESS) {
1337+ return ret;
1338+ }
1339+ ctx.internalNodeIds.push_back(nodeId);
1340+ gather.merged[column] = ctx.graph->GetNode(nodeId);
1341+ ctx.cpGmMergeSourceTaskIds[nodeId] = CollectSourceTaskIds(gather, column);
1342+ ++ctx.cpGmGeneratedNodeCount;
1343+ return HCCL_SUCCESS;
1344+}
1345+ 
1346+bool CollectCpGmBoundaryNodes(const CpGmLoopGather &gather, std::set<NodeId> &runNodes,
1347+ std::set<std::pair<NodeId, size_t>> &externalParentEdges,
1348+ std::set<std::pair<size_t, NodeId>> &externalChildEdges)
1349+{
1350+ runNodes.clear();
1351+ externalParentEdges.clear();
1352+ externalChildEdges.clear();
1353+ for (const CpGmIter &iter : gather.templateLoop) {
1354+ for (TaskNode *node : iter) {
1355+ runNodes.insert(node->GetNodeId());
1356+ }
1357+ }
1358+ const NodeId firstNodeId = gather.templateLoop.front()[0]->GetNodeId();
1359+ const NodeId firstMte3NodeId = gather.templateLoop.front()[2]->GetNodeId();
1360+ const NodeId lastMte3NodeId = gather.templateLoop.back()[4]->GetNodeId();
1361+ const NodeId lastMte2NodeId = gather.templateLoop.back()[5]->GetNodeId();
1362+ for (const CpGmIter &iter : gather.templateLoop) {
1363+ for (TaskNode *node : iter) {
1364+ for (TaskNode *parent : node->GetParents()) {
1365+ if (parent == nullptr) {
1366+ return false;
1367+ }
1368+ const NodeId parentId = parent->GetNodeId();
1369+ if (runNodes.count(parentId) == 0) {
1370+ const NodeId nodeId = node->GetNodeId();
1371+ if (nodeId == firstNodeId) {
1372+ externalParentEdges.insert({parentId, 0U});
1373+ } else if (nodeId == firstMte3NodeId) {
1374+ externalParentEdges.insert({parentId, 2U});
1375+ } else {
1376+ return false;
1377+ }
1378+ }
1379+ }
1380+ for (TaskNode *child : node->GetChildren()) {
1381+ if (child == nullptr) {
1382+ return false;
1383+ }
1384+ const NodeId childId = child->GetNodeId();
1385+ if (runNodes.count(childId) == 0) {
1386+ const NodeId nodeId = node->GetNodeId();
1387+ if (nodeId == lastMte3NodeId) {
1388+ externalChildEdges.insert({4U, childId});
1389+ } else if (nodeId == lastMte2NodeId) {
1390+ externalChildEdges.insert({5U, childId});
1391+ } else {
1392+ return false;
1393+ }
1394+ }
1395+ }
1396+ }
1397+ }
1398+ return true;
1399+}
1400+ 
1401+HcclResult RemoveRunEdges(AivLaunchContext &ctx, const std::set<NodeId> &runNodes)
1402+{
1403+ std::vector<std::pair<NodeId, NodeId>> edges;
1404+ for (NodeId nodeId : runNodes) {
1405+ TaskNode *node = ctx.graph->GetNode(nodeId);
1406+ if (node == nullptr) {
1407+ return HCCL_E_PTR;
1408+ }
1409+ for (TaskNode *parent : node->GetParents()) {
1410+ if (parent != nullptr) {
1411+ edges.push_back({parent->GetNodeId(), nodeId});
1412+ }
1413+ }
1414+ for (TaskNode *child : node->GetChildren()) {
1415+ if (child != nullptr) {
1416+ edges.push_back({nodeId, child->GetNodeId()});
1417+ }
1418+ }
1419+ }
1420+ std::sort(edges.begin(), edges.end());
1421+ edges.erase(std::unique(edges.begin(), edges.end()), edges.end());
1422+ for (const auto &edge : edges) {
1423+ HcclResult ret = ctx.graph->RemoveEdge(edge.first, edge.second);
1424+ if (ret != HCCL_SUCCESS) {
1425+ return ret;
1426+ }
1427+ }
1428+ return HCCL_SUCCESS;
1429+}
1430+ 
1431+HcclResult ReplaceCpGmRun(AivLaunchContext &ctx, CpGmLoopGather &gather)
1432+{
1433+ if (!ValidateCpGmRun(gather) || !CanMergeCpGmRun(ctx, gather)) {
1434+ ++ctx.cpGmLoopSkipCount;
1435+ return HCCL_SUCCESS;
1436+ }
1437+ 
1438+ std::set<NodeId> runNodes;
1439+ std::set<std::pair<NodeId, size_t>> externalParentEdges;
1440+ std::set<std::pair<size_t, NodeId>> externalChildEdges;
1441+ if (!CollectCpGmBoundaryNodes(gather, runNodes, externalParentEdges, externalChildEdges)) {
1442+ ++ctx.cpGmLoopSkipCount;
1443+ return HCCL_SUCCESS;
1444+ }
1445+ 
1446+ HcclResult ret = AppendCpGmMergedNode(ctx, gather, 0, BuildBatchNodeForColumn(gather, 0),
1447+ MakeSyntheticPosition(ctx, gather.templateLoop.front()[0]));
1448+ if (ret != HCCL_SUCCESS) {
1449+ return ret;
1450+ }
1451+ for (size_t column : {1U, 2U}) {
1452+ const TaskPosition position = MakeSyntheticPosition(ctx, gather.templateLoop.front()[column]);
1453+ ret = AppendCpGmMergedNode(ctx, gather, column,
1454+ CloneSyntheticSyncNode(gather.templateLoop.front()[column], position), position);
1455+ if (ret != HCCL_SUCCESS) {
1456+ return ret;
1457+ }
1458+ }
1459+ ret = AppendCpGmMergedNode(ctx, gather, 3, BuildBatchNodeForColumn(gather, 3),
1460+ MakeSyntheticPosition(ctx, gather.templateLoop.front()[3]));
1461+ if (ret != HCCL_SUCCESS) {
1462+ return ret;
1463+ }
1464+ for (size_t column : {4U, 5U}) {
1465+ const TaskPosition position = MakeSyntheticPosition(ctx, gather.templateLoop.front()[column]);
1466+ ret = AppendCpGmMergedNode(ctx, gather, column,
1467+ CloneSyntheticSyncNode(gather.templateLoop.front()[column], position), position);
1468+ if (ret != HCCL_SUCCESS) {
1469+ return ret;
1470+ }
1471+ }
1472+ 
1473+ ret = RemoveRunEdges(ctx, runNodes);
1474+ if (ret != HCCL_SUCCESS) {
1475+ return ret;
1476+ }
1477+ for (const auto &edge : externalParentEdges) {
1478+ TaskNode *target = gather.merged[edge.second];
1479+ if (target == nullptr) {
1480+ return HCCL_E_INTERNAL;
1481+ }
1482+ ret = AddEdge(ctx.graph, edge.first, target->GetNodeId());
1483+ if (ret != HCCL_SUCCESS) {
1484+ return ret;
1485+ }
1486+ }
1487+ const std::array<std::pair<size_t, size_t>, 6> internalEdges{{{0U, 1U}, {1U, 5U}, {1U, 2U}, {2U, 3U},
1488+ {3U, 4U}, {4U, 5U}}};
1489+ for (const auto &edge : internalEdges) {
1490+ TaskNode *source = gather.merged[edge.first];
1491+ TaskNode *target = gather.merged[edge.second];
1492+ if (source == nullptr || target == nullptr) {
1493+ return HCCL_E_INTERNAL;
1494+ }
1495+ ret = AddEdge(ctx.graph, source->GetNodeId(), target->GetNodeId());
1496+ if (ret != HCCL_SUCCESS) {
1497+ return ret;
1498+ }
1499+ }
1500+ for (const auto &edge : externalChildEdges) {
1501+ TaskNode *source = gather.merged[edge.first];
1502+ if (source == nullptr) {
1503+ return HCCL_E_INTERNAL;
1504+ }
1505+ ret = AddEdge(ctx.graph, source->GetNodeId(), edge.second);
1506+ if (ret != HCCL_SUCCESS) {
1507+ return ret;
1508+ }
1509+ }
1510+ for (NodeId nodeId : runNodes) {
1511+ ctx.inactiveNodeIds.insert(nodeId);
1512+ }
1513+ ++ctx.cpGmLoopMergeCount;
1514+ ctx.cpGmMergedIterationCount += gather.templateLoop.size();
1515+ ctx.cpGmMergedOriginalNodeCount += runNodes.size();
1516+ ctx.cpGmInactiveNodeCount += runNodes.size();
1517+ return HCCL_SUCCESS;
1518+}
1519+ 
1520+HcclResult FlushCpGmGather(AivLaunchContext &ctx, CpGmLoopGather &gather)
1521+{
1522+ if (gather.templateLoop.size() > 1U) {
1523+ HcclResult ret = ReplaceCpGmRun(ctx, gather);
1524+ if (ret != HCCL_SUCCESS) {
1525+ return ret;
1526+ }
1527+ }
1528+ gather = CpGmLoopGather{};
1529+ return HCCL_SUCCESS;
1530+}
1531+ 
1532+HcclResult MergeCpGM2GMLoops(AivLaunchContext &ctx)
1533+{
1534+ ctx.dagNodeCountBeforeCpGmMerge = CountReachableActiveInternalNodes(ctx);
1535+ for (const auto &block : ctx.snapshot.blocks) {
1536+ std::vector<NodeId> topo;
1537+ if (!CollectCpGmBlockTopo(ctx, block.blockIdx, topo)) {
1538+ HCCL_VM_WARN("[AivTaskTransformV3][CpGM2GMMerge] Skip block due to topo failure, rank={}, launch={}, "
1539+ "block={}, file={}", ctx.placeholder->GetRankId(), ctx.placeholder->GetLaunchIdx(), block.blockIdx,
1540+ ctx.snapshot.filePath);
1541+ continue;
1542+ }
1543+ 
1544+ CpGmLoopGather gather;
1545+ std::set<NodeId> consumed; // 记录节点已经作为某个完整模板的一部分被识别过
1546+ for (size_t index = 0; index < topo.size(); ++index) {
1547+ CpGmIter iter{};
1548+ TaskNode *node = ctx.graph->GetNode(topo[index]);
1549+ if (node == nullptr) {
1550+ continue;
1551+ }
1552+ if (consumed.count(node->GetNodeId()) != 0) {
1553+ continue;
1554+ }
1555+ if (MatchCpGmTemplateFromNode(node, ctx, iter)) {
1556+ gather.templateLoop.push_back(iter);
1557+ for (TaskNode *matchedNode : iter) {
1558+ consumed.insert(matchedNode->GetNodeId());
1559+ }
1560+ continue;
1561+ }
1562+ HcclResult ret = FlushCpGmGather(ctx, gather);
1563+ if (ret != HCCL_SUCCESS) {
1564+ return ret;
1565+ }
1566+ }
1567+ HcclResult ret = FlushCpGmGather(ctx, gather);
1568+ if (ret != HCCL_SUCCESS) {
1569+ return ret;
1570+ }
1571+ }
1572+ ctx.dagNodeCountAfterCpGmMerge = CountReachableActiveInternalNodes(ctx);
1573+ HCCL_VM_INFO("[AivTaskTransformV3][CpGM2GMMerge] rank={}, launch={}, taskJsonTotalTaskCount={}, "
1574+ "dagNodeCountBeforeMerge={}, dagNodeCountAfterMerge={}, cpGmLoopMergeCount={}, "
1575+ "cpGmMergedIterationCount={}, cpGmMergedOriginalNodeCount={}, cpGmGeneratedNodeCount={}, "
1576+ "cpGmInactiveNodeCount={}", ctx.placeholder->GetRankId(), ctx.placeholder->GetLaunchIdx(),
1577+ ctx.taskJsonTotalTaskCount, ctx.dagNodeCountBeforeCpGmMerge, ctx.dagNodeCountAfterCpGmMerge,
1578+ ctx.cpGmLoopMergeCount, ctx.cpGmMergedIterationCount, ctx.cpGmMergedOriginalNodeCount,
1579+ ctx.cpGmGeneratedNodeCount, ctx.cpGmInactiveNodeCount);
1580+ return HCCL_SUCCESS;
1581+}
1582+ 
568class DisjointSet {1583class DisjointSet {
569public:1584public:
570 explicit DisjointSet(size_t size) : parent_(size), rank_(size, 0)1585 explicit DisjointSet(size_t size) : parent_(size), rank_(size, 0)
@@ -781,11 +1796,11 @@ HcclResult MergeSyncAllGroups(AivLaunchContext &ctx)
781 group.syncRound, memberNodeId, ctx.snapshot.filePath);1796 group.syncRound, memberNodeId, ctx.snapshot.filePath);
782 return HCCL_E_INTERNAL;1797 return HCCL_E_INTERNAL;
783 }1798 }
784- const TaskPosition &location = syncAll->GetInfo().taskLoc;1799+ const TaskPosition &taskLoc = syncAll->GetInfo().taskLoc;
785- if (!members.insert({location.blockId, location.pipe}).second) {1800+ if (!members.insert({taskLoc.blockId, taskLoc.pipe}).second) {
786 HCCL_VM_ERROR("[AivTaskTransformV3] Duplicate SyncAll member, rank={}, launch={}, syncRound={}, "1801 HCCL_VM_ERROR("[AivTaskTransformV3] Duplicate SyncAll member, rank={}, launch={}, syncRound={}, "
787 "block={}, pipe={}, file={}", ctx.placeholder->GetRankId(), ctx.placeholder->GetLaunchIdx(),1802 "block={}, pipe={}, file={}", ctx.placeholder->GetRankId(), ctx.placeholder->GetLaunchIdx(),
788- group.syncRound, location.blockId, location.pipe, ctx.snapshot.filePath);1803+ group.syncRound, taskLoc.blockId, taskLoc.pipe, ctx.snapshot.filePath);
789 return HCCL_E_INTERNAL;1804 return HCCL_E_INTERNAL;
790 }1805 }
791 }1806 }
@@ -854,7 +1869,8 @@ HcclResult ConnectCurrentTailsToEnd(AivLaunchContext &ctx)
854 return HCCL_E_PTR;1869 return HCCL_E_PTR;
855 }1870 }
856 const NodeId childId = child->GetNodeId();1871 const NodeId childId = child->GetNodeId();
857- if (internalSet.count(childId) == 0 || reachableInternalNodes.count(childId) != 0) {1872+ if (internalSet.count(childId) == 0 || IsInactive(ctx, childId) ||
1873+ reachableInternalNodes.count(childId) != 0) {
858 continue;1874 continue;
859 }1875 }
860 reachableInternalNodes.insert(childId);1876 reachableInternalNodes.insert(childId);
@@ -955,6 +1971,10 @@ HcclResult ExpandOneAivGraph(TaskGraphGeneratorV3 *graph, StorageManager *storag
955 if (ret != HCCL_SUCCESS) {1971 if (ret != HCCL_SUCCESS) {
956 return ret;1972 return ret;
957 }1973 }
1974+ ctx.taskJsonTotalTaskCount = CountAivSnapshotTasks(ctx.snapshot);
1975+ const uint32_t maxTaskId = FindMaxTaskId(ctx.snapshot);
1976+ ctx.nextSyntheticTaskId = maxTaskId == std::numeric_limits<uint32_t>::max() ?
1977+ std::numeric_limits<uint32_t>::max() : maxTaskId + 1U;
958 1978 
959 const TaskPosition basePosition = aivGraph->GetPosition();1979 const TaskPosition basePosition = aivGraph->GetPosition();
960 ret = graph->AppendGeneratedNode(std::make_unique<TaskStart>(BoundaryType::AIV_SUB_GRAPH), basePosition,1980 ret = graph->AppendGeneratedNode(std::make_unique<TaskStart>(BoundaryType::AIV_SUB_GRAPH), basePosition,
@@ -973,6 +1993,10 @@ HcclResult ExpandOneAivGraph(TaskGraphGeneratorV3 *graph, StorageManager *storag
973 if (ret != HCCL_SUCCESS) {1993 if (ret != HCCL_SUCCESS) {
974 return ret;1994 return ret;
975 }1995 }
1996+ ret = MergeCpGM2GMLoops(ctx);
1997+ if (ret != HCCL_SUCCESS) {
1998+ return ret;
1999+ }
976 ret = MergePipeBarrierGroups(ctx);2000 ret = MergePipeBarrierGroups(ctx);
977 if (ret != HCCL_SUCCESS) {2001 if (ret != HCCL_SUCCESS) {
978 return ret;2002 return ret;
@@ -1001,6 +2025,14 @@ HcclResult ExpandOneAivGraph(TaskGraphGeneratorV3 *graph, StorageManager *storag
1001 output.setWaitEdgeCount += ctx.setWaitEdgeCount;2025 output.setWaitEdgeCount += ctx.setWaitEdgeCount;
1002 output.pipeBarrierMergeCount += ctx.pipeBarrierMergeCount;2026 output.pipeBarrierMergeCount += ctx.pipeBarrierMergeCount;
1003 output.syncAllMergeCount += ctx.syncAllMergeCount;2027 output.syncAllMergeCount += ctx.syncAllMergeCount;
2028+ output.taskJsonTotalTaskCount += ctx.taskJsonTotalTaskCount;
2029+ output.dagNodeCountBeforeCpGmMerge += ctx.dagNodeCountBeforeCpGmMerge;
2030+ output.dagNodeCountAfterCpGmMerge += ctx.dagNodeCountAfterCpGmMerge;
2031+ output.cpGmLoopMergeCount += ctx.cpGmLoopMergeCount;
2032+ output.cpGmMergedIterationCount += ctx.cpGmMergedIterationCount;
2033+ output.cpGmMergedOriginalNodeCount += ctx.cpGmMergedOriginalNodeCount;
2034+ output.cpGmGeneratedNodeCount += ctx.cpGmGeneratedNodeCount;
2035+ output.cpGmInactiveNodeCount += ctx.cpGmInactiveNodeCount;
1004 return HCCL_SUCCESS;2036 return HCCL_SUCCESS;
1005}2037}
1006 2038 
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator_v3/aiv_graph_generator_v3/aiv_task_transform_v3.h+8-0文件内容审核中,请稍后刷新重试
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator_v3/ccu_graph_generator_v3/ccu_task_transform_instruct_a5_v3.cc+1-1
@@ -468,7 +468,7 @@ HcclResult TransformTransLocMemToLocMemInstr(const CcuRep::CcuInstr *instr, CcuG
468 CHK_RET(StorageManager::GetInstance().GetSlice(srcAddr, len, srcSlice));468 CHK_RET(StorageManager::GetInstance().GetSlice(srcAddr, len, srcSlice));
469 CHK_RET(StorageManager::GetInstance().GetSlice(dstAddr, len, dstSlice));469 CHK_RET(StorageManager::GetInstance().GetSlice(dstAddr, len, dstSlice));
470 470 
471- HCCL_VM_DEBUG("zhf-locmem2locmem: rankId={}, src={}, dst={}, size={}",471+ HCCL_VM_DEBUG("locmem2locmem: rankId={}, src={}, dst={}, size={}",
472 rankId, srcSlice.Describe(), dstSlice.Describe(), len);472 rankId, srcSlice.Describe(), dstSlice.Describe(), len);
473 473 
474 AddLocalCopy(rankId, queId, curCcuTask, srcSlice, dstSlice);474 AddLocalCopy(rankId, queId, curCcuTask, srcSlice, dstSlice);
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator_v3/ccu_graph_generator_v3/ccu_task_transform_v3.cc+3-3
@@ -332,9 +332,9 @@ void SetNodeQueueIdV3(TaskNode *node, QueueId queueId)
332 return;332 return;
333 }333 }
334 334 
335- TaskLocation location = node->GetLocation();335+ TaskLocation taskLoc = node->GetLocation();
336- location.queueId = queueId;336+ taskLoc.queueId = queueId;
337- node->SetLocation(location);337+ node->SetLocation(taskLoc);
338 338 
339 if (!node->HasCcuTrace()) {339 if (!node->HasCcuTrace()) {
340 return;340 return;
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator_v3/task_graph_generator_v3.cc+16-3
@@ -517,14 +517,27 @@ HcclResult TaskGraphGeneratorV3::ExpandAivSubGraphs()
517 stats.pipeBarrierMergeCount = result.pipeBarrierMergeCount;517 stats.pipeBarrierMergeCount = result.pipeBarrierMergeCount;
518 stats.syncAllMergeCount = result.syncAllMergeCount;518 stats.syncAllMergeCount = result.syncAllMergeCount;
519 stats.sendRecvEdgeCount = result.sendRecvEdgeCount;519 stats.sendRecvEdgeCount = result.sendRecvEdgeCount;
520+ stats.taskJsonTotalTaskCount = result.taskJsonTotalTaskCount;
521+ stats.dagNodeCountBeforeCpGmMerge = result.dagNodeCountBeforeCpGmMerge;
522+ stats.dagNodeCountAfterCpGmMerge = result.dagNodeCountAfterCpGmMerge;
523+ stats.cpGmLoopMergeCount = result.cpGmLoopMergeCount;
524+ stats.cpGmMergedIterationCount = result.cpGmMergedIterationCount;
525+ stats.cpGmMergedOriginalNodeCount = result.cpGmMergedOriginalNodeCount;
526+ stats.cpGmGeneratedNodeCount = result.cpGmGeneratedNodeCount;
527+ stats.cpGmInactiveNodeCount = result.cpGmInactiveNodeCount;
520 stats.totalExpandNs = result.expandNs;528 stats.totalExpandNs = result.expandNs;
521 aivExpandStats_ = stats;529 aivExpandStats_ = stats;
522 HCCL_VM_INFO("[TaskGraphGeneratorV3][ExpandAivSubGraphs] AIV subgraph expansion finished, aivGraphCount={}, "530 HCCL_VM_INFO("[TaskGraphGeneratorV3][ExpandAivSubGraphs] AIV subgraph expansion finished, aivGraphCount={}, "
523 "internalNodeCount={}, setWaitEdgeCount={}, pipeBarrierMergeCount={}, syncAllMergeCount={}, "531 "internalNodeCount={}, setWaitEdgeCount={}, pipeBarrierMergeCount={}, syncAllMergeCount={}, "
524- "sendRecvEdgeCount={}, ubBufferSize={}, flagBufferSize={}, expandTotalMs={}", stats.graphCount,532+ "sendRecvEdgeCount={}, taskJsonTotalTaskCount={}, dagNodeCountBeforeCpGmMerge={}, "
525- stats.internalNodeCount,533+ "dagNodeCountAfterCpGmMerge={}, cpGmLoopMergeCount={}, cpGmMergedIterationCount={}, "
534+ "cpGmMergedOriginalNodeCount={}, cpGmGeneratedNodeCount={}, cpGmInactiveNodeCount={}, "
535+ "ubBufferSize={}, flagBufferSize={}, expandTotalMs={}", stats.graphCount, stats.internalNodeCount,
526 stats.setWaitEdgeCount, stats.pipeBarrierMergeCount, stats.syncAllMergeCount, stats.sendRecvEdgeCount,536 stats.setWaitEdgeCount, stats.pipeBarrierMergeCount, stats.syncAllMergeCount, stats.sendRecvEdgeCount,
527- g_checkerAivUbBufferSize, g_checkerAivFlagBufferSize, stats.totalExpandNs / 1000000ULL);537+ stats.taskJsonTotalTaskCount, stats.dagNodeCountBeforeCpGmMerge, stats.dagNodeCountAfterCpGmMerge,
538+ stats.cpGmLoopMergeCount, stats.cpGmMergedIterationCount, stats.cpGmMergedOriginalNodeCount,
539+ stats.cpGmGeneratedNodeCount, stats.cpGmInactiveNodeCount, g_checkerAivUbBufferSize,
540+ g_checkerAivFlagBufferSize, stats.totalExpandNs / 1000000ULL);
528 return HCCL_SUCCESS;541 return HCCL_SUCCESS;
529}542}
530 543 
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator_v3/task_graph_generator_v3.h+8-0
@@ -39,6 +39,14 @@ struct AivExpandStats {
39 size_t pipeBarrierMergeCount{0};39 size_t pipeBarrierMergeCount{0};
40 size_t syncAllMergeCount{0};40 size_t syncAllMergeCount{0};
41 size_t sendRecvEdgeCount{0};41 size_t sendRecvEdgeCount{0};
42+ size_t taskJsonTotalTaskCount{0};
43+ size_t dagNodeCountBeforeCpGmMerge{0};
44+ size_t dagNodeCountAfterCpGmMerge{0};
45+ size_t cpGmLoopMergeCount{0};
46+ size_t cpGmMergedIterationCount{0};
47+ size_t cpGmMergedOriginalNodeCount{0};
48+ size_t cpGmGeneratedNodeCount{0};
49+ size_t cpGmInactiveNodeCount{0};
42 uint64_t totalExpandNs{0};50 uint64_t totalExpandNs{0};
43};51};
44 52 
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator_v3/task_graph_semantic_check_v3.cc+33-21
@@ -16,6 +16,7 @@
16#include <queue>16#include <queue>
17#include <set>17#include <set>
18#include <sstream>18#include <sstream>
19+#include <string>
19#include <unordered_map>20#include <unordered_map>
20#include <utility>21#include <utility>
21#include <vector>22#include <vector>
@@ -54,6 +55,7 @@ struct SliceOpPairV3 {
54 SliceOpV3 op{SliceOpV3::OVERRIDE};55 SliceOpV3 op{SliceOpV3::OVERRIDE};
55 HcclReduceOp reduceType{HCCL_REDUCE_RESERVED};56 HcclReduceOp reduceType{HCCL_REDUCE_RESERVED};
56 u64 index{0};57 u64 index{0};
58+ bool useBatchIndex{false};
57 59 
58 std::string Describe() const60 std::string Describe() const
59 {61 {
@@ -65,6 +67,7 @@ struct SliceOpPairV3 {
65 << ", launch=" << position.launchIdx << ", block=" << position.blockId67 << ", launch=" << position.launchIdx << ", block=" << position.blockId
66 << ", pipe=" << position.pipe << ", taskId=" << position.taskId68 << ", pipe=" << position.pipe << ", taskId=" << position.taskId
67 << ", index=" << index << ", op=" << static_cast<uint32_t>(op)69 << ", index=" << index << ", op=" << static_cast<uint32_t>(op)
70+ << ", useBatchIndex=" << useBatchIndex
68 << ", reduceType=" << static_cast<uint32_t>(reduceType) << "}";71 << ", reduceType=" << static_cast<uint32_t>(reduceType) << "}";
69 return os.str();72 return os.str();
70 }73 }
@@ -92,7 +95,9 @@ struct MsKey {
92struct AivMemoryKey {95struct AivMemoryKey {
93 RankId rankId{INVALID_RANK_ID};96 RankId rankId{INVALID_RANK_ID};
94 MemType memType{MemType::INVALID};97 MemType memType{MemType::INVALID};
95- u64 aivUbIdx{0};98+ u64 aivUBIdx{0};
99+ u64 batchIndex{0};
100+ bool isBatchUB{false};
96 101 
97 bool operator<(const AivMemoryKey &rhs) const102 bool operator<(const AivMemoryKey &rhs) const
98 {103 {
@@ -102,7 +107,13 @@ struct AivMemoryKey {
102 if (memType != rhs.memType) {107 if (memType != rhs.memType) {
103 return static_cast<uint32_t>(memType) < static_cast<uint32_t>(rhs.memType);108 return static_cast<uint32_t>(memType) < static_cast<uint32_t>(rhs.memType);
104 }109 }
105- return aivUbIdx < rhs.aivUbIdx;110+ if (aivUBIdx != rhs.aivUBIdx) {
111+ return aivUBIdx < rhs.aivUBIdx;
112+ }
113+ if (batchIndex != rhs.batchIndex) {
114+ return batchIndex < rhs.batchIndex;
115+ }
116+ return isBatchUB < rhs.isBatchUB;
106 }117 }
107};118};
108 119 
@@ -126,7 +137,7 @@ struct SemanticState {
126 u64 outputSize{0};137 u64 outputSize{0};
127 std::map<RankId, InternalRankMemorySemantics> mem;138 std::map<RankId, InternalRankMemorySemantics> mem;
128 std::map<MsKey, InternalBufferSemanticMap> ms;139 std::map<MsKey, InternalBufferSemanticMap> ms;
129- std::map<AivMemoryKey, InternalBufferSemanticMap> scopedMem;140+ std::map<AivMemoryKey, InternalBufferSemanticMap> AivDevMem;
130};141};
131 142 
132BufferType ConvertMemTypeToBufferType(MemType memType)143BufferType ConvertMemTypeToBufferType(MemType memType)
@@ -195,33 +206,33 @@ u64 MakeAivUbIdx(const TaskPosition &position)
195 return (position.launchIdx << 32U) | static_cast<u64>(position.blockId);206 return (position.launchIdx << 32U) | static_cast<u64>(position.blockId);
196}207}
197 208 
198-u64 GetSemanticIndex(const MemSlice &slice, const TaskPosition &position, u64 fallbackIndex)209+AivMemoryKey MakeAivMemoryKey(RankId rankId, MemType memType, const TaskPosition &position, u64 batchIndex,
210+ bool isBatchUB)
199{211{
200- if (slice.memType == MemType::UB_AIV) {212+ if (memType == MemType::FLAG_AIV) {
201- return MakeAivUbIdx(position);213+ return AivMemoryKey{rankId, memType, 0U, 0U, false};
202 }214 }
203- if (slice.memType == MemType::FLAG_AIV) {215+ const bool effectiveIsBatchUB = memType == MemType::UB_AIV && isBatchUB;
204- return 0;216+ return AivMemoryKey{rankId, memType, MakeAivUbIdx(position), effectiveIsBatchUB ? batchIndex : 0U,
205- }217+ effectiveIsBatchUB};
206- return fallbackIndex;
207}218}
208 219 
209// 普通内存直接按 rank/bufferType 取语义表;MS 则额外按 batch 下标拆桶,220// 普通内存直接按 rank/bufferType 取语义表;MS 则额外按 batch 下标拆桶,
210// 从而把“同一个 ms 在不同 batch item 复用”的语义分开记录。221// 从而把“同一个 ms 在不同 batch item 复用”的语义分开记录。
211InternalBufferSemanticMap &GetBufferSemanticMap(SemanticState &state, RankId rankId, MemType memType, u64 offset,222InternalBufferSemanticMap &GetBufferSemanticMap(SemanticState &state, RankId rankId, MemType memType, u64 offset,
212- u64 index)223+ const TaskPosition &position, u64 index, bool useBatchIndex)
213{224{
214 if (memType == MemType::MS_CCU) {225 if (memType == MemType::MS_CCU) {
215 return state.ms[MakeMsKey(rankId, offset, index)];226 return state.ms[MakeMsKey(rankId, offset, index)];
216 }227 }
217 if (memType == MemType::UB_AIV || memType == MemType::FLAG_AIV) {228 if (memType == MemType::UB_AIV || memType == MemType::FLAG_AIV) {
218- return state.scopedMem[AivMemoryKey{rankId, memType, index}];229+ return state.AivDevMem[MakeAivMemoryKey(rankId, memType, position, index, useBatchIndex)];
219 }230 }
220 return state.mem[rankId][ConvertMemTypeToBufferType(memType)];231 return state.mem[rankId][ConvertMemTypeToBufferType(memType)];
221}232}
222 233 
223const InternalBufferSemanticMap &GetBufferSemanticMap(const SemanticState &state, RankId rankId, MemType memType,234const InternalBufferSemanticMap &GetBufferSemanticMap(const SemanticState &state, RankId rankId, MemType memType,
224- u64 offset, u64 index)235+ u64 offset, const TaskPosition &position, u64 index, bool useBatchIndex)
225{236{
226 static const InternalBufferSemanticMap empty;237 static const InternalBufferSemanticMap empty;
227 if (memType == MemType::MS_CCU) {238 if (memType == MemType::MS_CCU) {
@@ -229,8 +240,8 @@ const InternalBufferSemanticMap &GetBufferSemanticMap(const SemanticState &state
229 return iter == state.ms.end() ? empty : iter->second;240 return iter == state.ms.end() ? empty : iter->second;
230 }241 }
231 if (memType == MemType::UB_AIV || memType == MemType::FLAG_AIV) {242 if (memType == MemType::UB_AIV || memType == MemType::FLAG_AIV) {
232- const auto iter = state.scopedMem.find(AivMemoryKey{rankId, memType, index});243+ const auto iter = state.AivDevMem.find(MakeAivMemoryKey(rankId, memType, position, index, useBatchIndex));
233- return iter == state.scopedMem.end() ? empty : iter->second;244+ return iter == state.AivDevMem.end() ? empty : iter->second;
234 }245 }
235 const auto rankIter = state.mem.find(rankId);246 const auto rankIter = state.mem.find(rankId);
236 if (rankIter == state.mem.end()) {247 if (rankIter == state.mem.end()) {
@@ -556,8 +567,8 @@ void ApplyOverrideSemantics(const SliceOpPairV3 &pair, SemanticState &state,
556 const std::vector<const BufferSemantic *> &srcSemantics)567 const std::vector<const BufferSemantic *> &srcSemantics)
557{568{
558 InternalBufferSemanticMap &dstSemantics =569 InternalBufferSemanticMap &dstSemantics =
559- GetBufferSemanticMap(state, pair.dstRank, pair.dst.memType, pair.dst.offset,570+ GetBufferSemanticMap(state, pair.dstRank, pair.dst.memType, pair.dst.offset, pair.position, pair.index,
560- GetSemanticIndex(pair.dst, pair.position, pair.index));571+ pair.useBatchIndex);
561 const u64 dstStartAddr = pair.dst.offset;572 const u64 dstStartAddr = pair.dst.offset;
562 const u64 dstEndAddr = dstStartAddr + pair.dst.len;573 const u64 dstEndAddr = dstStartAddr + pair.dst.len;
563 SplitBufferSemantic(dstSemantics, dstStartAddr);574 SplitBufferSemantic(dstSemantics, dstStartAddr);
@@ -623,8 +634,8 @@ HcclResult ApplyReduceSemantic(const SliceOpPairV3 &pair, SemanticState &state,
623 const u64 dstEndAddr = ApplyOffsetDelta(srcEndAddr, dstSrcOffset);634 const u64 dstEndAddr = ApplyOffsetDelta(srcEndAddr, dstSrcOffset);
624 635 
625 InternalBufferSemanticMap &dstSemantics =636 InternalBufferSemanticMap &dstSemantics =
626- GetBufferSemanticMap(state, pair.dstRank, pair.dst.memType, pair.dst.offset,637+ GetBufferSemanticMap(state, pair.dstRank, pair.dst.memType, pair.dst.offset, pair.position, pair.index,
627- GetSemanticIndex(pair.dst, pair.position, pair.index));638+ pair.useBatchIndex);
628 SplitBufferSemantic(dstSemantics, dstStartAddr);639 SplitBufferSemantic(dstSemantics, dstStartAddr);
629 SplitBufferSemantic(dstSemantics, dstEndAddr);640 SplitBufferSemantic(dstSemantics, dstEndAddr);
630 641 
@@ -691,7 +702,7 @@ HcclResult LoadSliceOpPairSrcSemantics(const SliceOpPairV3 &pair, const Semantic
691 }702 }
692 703 
693 const InternalBufferSemanticMap &srcSemanticsMap = GetBufferSemanticMap(state, pair.srcRank, pair.src.memType,704 const InternalBufferSemanticMap &srcSemanticsMap = GetBufferSemanticMap(state, pair.srcRank, pair.src.memType,
694- pair.src.offset, GetSemanticIndex(pair.src, pair.position, pair.index));705+ pair.src.offset, pair.position, pair.index, pair.useBatchIndex);
695 std::vector<const BufferSemantic *> srcViews;706 std::vector<const BufferSemantic *> srcViews;
696 CollectOverlappingBufferSemantics(srcSemanticsMap, pair.src.offset, pair.src.offset + pair.src.len, srcViews);707 CollectOverlappingBufferSemantics(srcSemanticsMap, pair.src.offset, pair.src.offset + pair.src.len, srcViews);
697 708 
@@ -787,7 +798,7 @@ void GetSliceOpPairs(const TaskNode *node, std::vector<SliceOpPairV3> &pairs)
787 for (size_t index = 0; index < count; ++index) {798 for (size_t index = 0; index < count; ++index) {
788 pairs.push_back(SliceOpPairV3{srcs[index].rankId, dsts[index].rankId, srcs[index], dsts[index],799 pairs.push_back(SliceOpPairV3{srcs[index].rankId, dsts[index].rankId, srcs[index], dsts[index],
789 node->GetPosition(), SliceOpV3::OVERRIDE, HCCL_REDUCE_RESERVED,800 node->GetPosition(), SliceOpV3::OVERRIDE, HCCL_REDUCE_RESERVED,
790- static_cast<u64>(index)});801+ static_cast<u64>(index), true});
791 }802 }
792 return;803 return;
793 }804 }
@@ -811,6 +822,7 @@ void GetSliceOpPairs(const TaskNode *node, std::vector<SliceOpPairV3> &pairs)
811 pair.op = SliceOpV3::REDUCE;822 pair.op = SliceOpV3::REDUCE;
812 pair.reduceType = static_cast<HcclReduceOp>(task->GetReduceOp());823 pair.reduceType = static_cast<HcclReduceOp>(task->GetReduceOp());
813 pair.index = static_cast<u64>(index);824 pair.index = static_cast<u64>(index);
825+ pair.useBatchIndex = true;
814 pairs.push_back(std::move(pair));826 pairs.push_back(std::move(pair));
815 }827 }
816 }828 }
Mtest/hccl_vm/src/plugin/checker/src/framework/task_graph_generator_v3/task_graph_single_task_check_v3.cc+11-2
@@ -519,15 +519,24 @@ HcclResult CheckTwoSliceOverlap(const TaskNode *node, const MemSlice &lhs, const
519 }519 }
520 if (lhs.len > std::numeric_limits<uint64_t>::max() - lhs.offset ||520 if (lhs.len > std::numeric_limits<uint64_t>::max() - lhs.offset ||
521 rhs.len > std::numeric_limits<uint64_t>::max() - rhs.offset) {521 rhs.len > std::numeric_limits<uint64_t>::max() - rhs.offset) {
522- HCCL_VM_ERROR("[TaskGraphSingleTaskCheckV3] Memory slice overflow, node={}, lhs={}, rhs={}",522+ HCCL_VM_ERROR("[TaskGraphSingleTaskCheckV3] Memory slice overflow, node={}, sliceA={}, sliceB={}",
523 node->Describe(), DescribeMemSlice(lhs), DescribeMemSlice(rhs));523 node->Describe(), DescribeMemSlice(lhs), DescribeMemSlice(rhs));
524 return HCCL_E_PARA;524 return HCCL_E_PARA;
525 }525 }
526 526 
527+ if (node->HasCcuTrace()) {
528+ // CCU mode下,允许src == dst
529+ if (lhs.rankId == rhs.rankId && IsSameMemoryType(lhs, rhs) && lhs.offset == rhs.offset && lhs.len == rhs.len) {
530+ HCCL_VM_WARN("[TaskGraphSingleTaskCheckV3] MemSlice are same (src == dst), which may affect performance, {}, node={}, sliceA={}, sliceB={}",
531+ DescribePosition(node->GetPosition()), node->Describe(), DescribeMemSlice(lhs), DescribeMemSlice(rhs));
532+ return HCCL_SUCCESS;
533+ }
534+ }
535+ 
527 const bool conflictCase1 = lhs.offset >= rhs.offset && lhs.offset < (rhs.offset + rhs.len);536 const bool conflictCase1 = lhs.offset >= rhs.offset && lhs.offset < (rhs.offset + rhs.len);
528 const bool conflictCase2 = rhs.offset >= lhs.offset && rhs.offset < (lhs.offset + lhs.len);537 const bool conflictCase2 = rhs.offset >= lhs.offset && rhs.offset < (lhs.offset + lhs.len);
529 if (conflictCase1 || conflictCase2) {538 if (conflictCase1 || conflictCase2) {
530- HCCL_VM_ERROR("[TaskGraphSingleTaskCheckV3] Slice conflict, {}, node={}, lhs={}, rhs={}",539+ HCCL_VM_ERROR("[TaskGraphSingleTaskCheckV3] Slice conflict, {}, node={}, sliceA={}, sliceB={}",
531 DescribePosition(node->GetPosition()), node->Describe(), DescribeMemSlice(lhs), DescribeMemSlice(rhs));540 DescribePosition(node->GetPosition()), node->Describe(), DescribeMemSlice(lhs), DescribeMemSlice(rhs));
532 return HCCL_E_INTERNAL;541 return HCCL_E_INTERNAL;
533 }542 }
Mtest/hccl_vm/src/plugin/hccl_plugin.cc+7-7
@@ -74,10 +74,10 @@ HcclVmResult HcclPlugin::Start()
74{74{
75 std::string entryCmd = m_manifest.value(HcclPlugin::Manifest::pluginEntry, "");75 std::string entryCmd = m_manifest.value(HcclPlugin::Manifest::pluginEntry, "");
76 if (entryCmd.empty()) {76 if (entryCmd.empty()) {
77- HCCL_VM_INFO("[HcclPlugin::{}] Empty Entry Command", __func__);77+ HCCL_VM_INFO("Empty Entry Command");
78 return HcclVmResult::HCCL_SIM_E_PARA;78 return HcclVmResult::HCCL_SIM_E_PARA;
79 }79 }
80- HCCL_VM_INFO("[HcclPlugin::{}] Starting plugin [{}]", __func__, GetTag());80+ HCCL_VM_INFO("Starting plugin [{}]", GetTag());
81 int32_t fds[2];81 int32_t fds[2];
82 if (pipe(fds) == -1) {82 if (pipe(fds) == -1) {
83 return HcclVmResult::HCCL_SIM_E_INTERNAL;83 return HcclVmResult::HCCL_SIM_E_INTERNAL;
@@ -127,14 +127,14 @@ HcclVmResult HcclPlugin::Start()
127 127 
128 if (res == 0) {128 if (res == 0) {
129 // 情况 A: 子进程正常运行中(这是我们预期的)129 // 情况 A: 子进程正常运行中(这是我们预期的)
130- HCCL_VM_INFO("[HcclPlugin::{}] Plugin [{}] started", __func__, GetTag());130+ HCCL_VM_INFO("Plugin [{}] started", GetTag());
131 return HcclVmResult::HCCL_SIM_SUCCESS;131 return HcclVmResult::HCCL_SIM_SUCCESS;
132 } else if (res == m_pid) {132 } else if (res == m_pid) {
133 // 情况 B: 子进程已经退出,通常是 execvp 失败133 // 情况 B: 子进程已经退出,通常是 execvp 失败
134 // 检查退出码,如果是通过 _exit(EXIT_FAILURE) 退出的134 // 检查退出码,如果是通过 _exit(EXIT_FAILURE) 退出的
135 if (WIFEXITED(status)) {135 if (WIFEXITED(status)) {
136 int exitCode = WEXITSTATUS(status);136 int exitCode = WEXITSTATUS(status);
137- HCCL_VM_ERROR("[HcclPlugin::{}] Plugin [{}] failed to start with code [{}].", __func__, GetTag(), exitCode);137+ HCCL_VM_ERROR("Plugin [{}] failed to start with code [{}].", GetTag(), exitCode);
138 }138 }
139 139 
140 // 清理现场,防止后续逻辑误以为子进程还在140 // 清理现场,防止后续逻辑误以为子进程还在
@@ -211,13 +211,13 @@ HcclVmResult HcclPlugin::Stop()
211 211 
212 // 4. 超时后不强制 kill,而是输出告警信息212 // 4. 超时后不强制 kill,而是输出告警信息
213 if (!exited) {213 if (!exited) {
214- HCCL_VM_ERROR("[HcclPlugin::{}] Plugin [{}] detected as not exiting normally.", __func__, GetTag());214+ HCCL_VM_ERROR("Plugin [{}] detected as not exiting normally.", GetTag());
215- HCCL_VM_ERROR("[HcclPlugin::{}] [ACTION REQUIRED] Please manually check or terminate PID: {:d}", __func__, m_pid);215+ HCCL_VM_ERROR("[ACTION REQUIRED] Please manually check or terminate PID: {:d}", m_pid);
216 216
217 // 既然无法回收,我们将该 PID 记录在日志后放弃管理217 // 既然无法回收,我们将该 PID 记录在日志后放弃管理
218 // 防止析构函数再次产生误判218 // 防止析构函数再次产生误判
219 } else {219 } else {
220- HCCL_VM_INFO("[HcclPlugin::{}] [{}] exited successfully.", __func__, GetTag());220+ HCCL_VM_INFO("Plugin [{}] exited successfully.", GetTag());
221 }221 }
222 222 
223 m_pid = -1;223 m_pid = -1;
Mtest/hccl_vm/src/plugin/hccl_virtual_runtime_main.cc+1-4
@@ -255,10 +255,7 @@ int main(int argc, char* argv[])
255 (void) argc;255 (void) argc;
256 (void) argv;256 (void) argv;
257 init_lock();257 init_lock();
258- LogConfig config;258+ LogConfig config = LoadLogConfig("runner");
259- config.fileBaseName = "runner";
260- config.consoleLevel = 6;
261- config.fileLevel = 2;
262 InitLogger(config);259 InitLogger(config);
263 HcclSim::StorageManager& storage = HcclSim::StorageManager::GetInstance();260 HcclSim::StorageManager& storage = HcclSim::StorageManager::GetInstance();
264 storage.SetDataId("runner");261 storage.SetDataId("runner");
Mtest/hccl_vm/src/plugin/insight/frontend/src/pages/MemViewPage.vue+899-899
@@ -1,26 +1,26 @@
1<script setup>1<script setup>
2import { computed, nextTick, onMounted, onBeforeUnmount, ref, watch } from 'vue'2import { computed, nextTick, onMounted, onBeforeUnmount, ref, watch } from 'vue'
3-import {3+import {
4- CaretLeft,4+ CaretLeft,
5- CaretRight,5+ CaretRight,
6- DArrowLeft,6+ DArrowLeft,
7- DArrowRight,7+ DArrowRight,
8- Search,8+ Search,
9- VideoPause,9+ VideoPause,
10- VideoPlay,10+ VideoPlay,
11- WarningFilled,11+ WarningFilled,
12-} from '@element-plus/icons-vue'12+} from '@element-plus/icons-vue'
13import sidebarHandle from '../assets/sidebar-handle.svg'13import sidebarHandle from '../assets/sidebar-handle.svg'
14import sidebarHandleExpand from '../assets/sidebar-handle-expand.svg'14import sidebarHandleExpand from '../assets/sidebar-handle-expand.svg'
15import { useInsightDatasetState } from '../composables/useInsightDatasetState'15import { useInsightDatasetState } from '../composables/useInsightDatasetState'
16import { usePageNavigationState } from '../composables/usePageNavigationState'16import { usePageNavigationState } from '../composables/usePageNavigationState'
17import { useMemViewTimeline } from '../composables/useMemViewTimeline'17import { useMemViewTimeline } from '../composables/useMemViewTimeline'
18-import { taskOpsAtStep } from '../utils/memviewIndex.js'18+import { taskOpsAtStep } from '../utils/memviewIndex.js'
19-import { fetchValidationIssues } from '../utils/analyticIssues.js'19+import { fetchValidationIssues } from '../utils/analyticIssues.js'
20-import {20+import {
21- DEFAULT_MEMVIEW_STAGE,21+ DEFAULT_MEMVIEW_STAGE,
22- normalizeStreamCollection,22+ normalizeStreamCollection,
23-} from '../utils/memviewDag.js'23+} from '../utils/memviewDag.js'
24import DashboardSidebar from '../components/dashboard/DashboardSidebar.vue'24import DashboardSidebar from '../components/dashboard/DashboardSidebar.vue'
25import MemViewDagPanel from '../components/memview/MemViewDagPanel.vue'25import MemViewDagPanel from '../components/memview/MemViewDagPanel.vue'
26import MemViewInspectorPanel from '../components/memview/MemViewInspectorPanel.vue'26import MemViewInspectorPanel from '../components/memview/MemViewInspectorPanel.vue'
@@ -29,57 +29,57 @@ import MemViewOverviewPanel from '../components/memview/MemViewOverviewPanel.vue
29const CENTER_TOP_MIN_SIZE = 25029const CENTER_TOP_MIN_SIZE = 250
30const CENTER_TOP_MAX_SIZE = 62030const CENTER_TOP_MAX_SIZE = 620
31const CENTER_TOP_DEFAULT_RATIO = 0.631const CENTER_TOP_DEFAULT_RATIO = 0.6
32-const CENTER_BOTTOM_MIN_SIZE = 22032+const CENTER_BOTTOM_MIN_SIZE = 220
33-const CENTER_COLLAPSED_SIZE = 3633+const CENTER_COLLAPSED_SIZE = 36
34-const CENTER_COLLAPSE_THRESHOLD = 7234+const CENTER_COLLAPSE_THRESHOLD = 72
35-const SEARCH_FIELD_OPTIONS = [35+const SEARCH_FIELD_OPTIONS = [
36- { label: 'taskId', value: 'taskId' },36+ { label: 'taskId', value: 'taskId' },
37- { label: 'taskType', value: 'taskType' },37+ { label: 'taskType', value: 'taskType' },
38- { label: 'notifyId', value: 'notifyId' },38+ { label: 'notifyId', value: 'notifyId' },
39-]39+]
40- 40+ 
41-const sidebarCollapsed = ref(false)41+const sidebarCollapsed = ref(false)
42-const recordCollapsed = ref(false)42+const recordCollapsed = ref(false)
43-const leftPanelSize = ref(376)43+const leftPanelSize = ref(376)
44-const rightPanelSize = ref(420)44+const rightPanelSize = ref(420)
45const centerPaneState = ref('normal')45const centerPaneState = ref('normal')
46const centerAvailableHeight = ref(0)46const centerAvailableHeight = ref(0)
47const centerTopSize = ref(360)47const centerTopSize = ref(360)
48const currentGraphNodeIds = ref([])48const currentGraphNodeIds = ref([])
49-const currentGraphStage = ref(DEFAULT_MEMVIEW_STAGE)49+const currentGraphStage = ref(DEFAULT_MEMVIEW_STAGE)
50-const pendingStageNavigation = ref(null)50+const pendingStageNavigation = ref(null)
51-const pendingExternalNavigation = ref(null)51+const pendingExternalNavigation = ref(null)
52-const issueHighlightedNodeIds = ref(null)52+const issueHighlightedNodeIds = ref(null)
53-const datasetIssuePayload = ref({ issueCount: 0, issues: [] })53+const datasetIssuePayload = ref({ issueCount: 0, issues: [] })
54-const selectedIssueId = ref(null)54+const selectedIssueId = ref(null)
55-const inspectorRef = ref(null)55+const inspectorRef = ref(null)
56-const centerSplitRef = ref(null)56+const centerSplitRef = ref(null)
57-const layoutRefreshTimers = []57+const layoutRefreshTimers = []
58-const playbackPlaying = ref(false)58+const playbackPlaying = ref(false)
59-const issuePanelSections = ref(['issues'])59+const issuePanelSections = ref(['issues'])
60-const searchPanelSections = ref(['search'])60+const searchPanelSections = ref(['search'])
61-const searchField = ref('taskId')61+const searchField = ref('taskId')
62-const searchQuery = ref('')62+const searchQuery = ref('')
63-const dagFocusStep = ref(null)63+const dagFocusStep = ref(null)
64-let centerResizeObserver = null64+let centerResizeObserver = null
65-let playbackTimer = 065+let playbackTimer = 0
66-let issueLoadToken = 066+let issueLoadToken = 0
67- 67+ 
68-const overviewEmptyText = computed(() => {68+const overviewEmptyText = computed(() => {
69- if (loadingDetail.value) return 'Loading data...'69+ if (loadingDetail.value) return 'Loading data...'
70- if (selectedDataset.value && memorySnapshotCount.value === 0) {70+ if (selectedDataset.value && memorySnapshotCount.value === 0) {
71- return '当前数据集没有内存快照。'71+ return '当前数据集没有内存快照。'
72- }72+ }
73- return EMPTY_TEXT73+ return EMPTY_TEXT
74-})74+})
75- 75+ 
76-const dagEmptyText = computed(() => {76+const dagEmptyText = computed(() => {
77- if (loadingDetail.value) return 'Loading data...'77+ if (loadingDetail.value) return 'Loading data...'
78- if (selectedDataset.value && !datasetDetail.value?.graph?.length) {78+ if (selectedDataset.value && !datasetDetail.value?.graph?.length) {
79- return '当前数据集没有 DAG 数据。'79+ return '当前数据集没有 DAG 数据。'
80- }80+ }
81- return EMPTY_TEXT81+ return EMPTY_TEXT
82-})82+})
83 83 
84const {84const {
85 EMPTY_TEXT,85 EMPTY_TEXT,
@@ -87,11 +87,11 @@ const {
87 selectedDataset,87 selectedDataset,
88 selectedDatasetName,88 selectedDatasetName,
89 selectedRankKeys,89 selectedRankKeys,
90- loadingList,90+ loadingList,
91- loadingDetail,91+ loadingDetail,
92- hasData,92+ hasData,
93- memorySnapshotCount,93+ memorySnapshotCount,
94- selectedRankCount,94+ selectedRankCount,
95 treeData,95 treeData,
96 ensureDatasetsLoaded,96 ensureDatasetsLoaded,
97 selectDataset,97 selectDataset,
@@ -99,7 +99,7 @@ const {
99 formatCount,99 formatCount,
100} = useInsightDatasetState()100} = useInsightDatasetState()
101 101 
102-const { pendingMemViewNavigation, clearMemViewNavigation } = usePageNavigationState()102+const { pendingMemViewNavigation, clearMemViewNavigation } = usePageNavigationState()
103 103 
104const {104const {
105 clampedCurrentStep,105 clampedCurrentStep,
@@ -124,14 +124,14 @@ const {
124 124 
125const renderedLeftPanelSize = computed(() => (sidebarCollapsed.value ? 28 : leftPanelSize.value))125const renderedLeftPanelSize = computed(() => (sidebarCollapsed.value ? 28 : leftPanelSize.value))
126const renderedRightPanelSize = computed(() => (recordCollapsed.value ? 28 : rightPanelSize.value))126const renderedRightPanelSize = computed(() => (recordCollapsed.value ? 28 : rightPanelSize.value))
127-const topPaneCollapsed = computed(() => centerPaneState.value === 'top-collapsed')127+const topPaneCollapsed = computed(() => centerPaneState.value === 'top-collapsed')
128-const bottomPaneCollapsed = computed(() => centerPaneState.value === 'bottom-collapsed')128+const bottomPaneCollapsed = computed(() => centerPaneState.value === 'bottom-collapsed')
129-const issues = computed(() => datasetIssuePayload.value?.issues ?? [])129+const issues = computed(() => datasetIssuePayload.value?.issues ?? [])
130-const issueCount = computed(() => datasetIssuePayload.value?.issueCount ?? issues.value.length)130+const issueCount = computed(() => datasetIssuePayload.value?.issueCount ?? issues.value.length)
131-const selectedIssue = computed(131+const selectedIssue = computed(
132- () => issues.value.find((issue) => issue.issueId === selectedIssueId.value) ?? issues.value[0] ?? null,132+ () => issues.value.find((issue) => issue.issueId === selectedIssueId.value) ?? issues.value[0] ?? null,
133-)133+)
134-const renderedCenterTopSize = computed(() => {134+const renderedCenterTopSize = computed(() => {
135 if (topPaneCollapsed.value) return CENTER_COLLAPSED_SIZE135 if (topPaneCollapsed.value) return CENTER_COLLAPSED_SIZE
136 if (bottomPaneCollapsed.value) {136 if (bottomPaneCollapsed.value) {
137 return Math.max(CENTER_COLLAPSED_SIZE, resolveCenterAvailableHeight() - CENTER_COLLAPSED_SIZE)137 return Math.max(CENTER_COLLAPSED_SIZE, resolveCenterAvailableHeight() - CENTER_COLLAPSED_SIZE)
@@ -247,9 +247,9 @@ watch(
247 },247 },
248)248)
249 249 
250-watch(250+watch(
251- availableGraphStages,251+ availableGraphStages,
252- (stages) => {252+ (stages) => {
253 if (!stages.length) {253 if (!stages.length) {
254 currentGraphStage.value = ''254 currentGraphStage.value = ''
255 return255 return
@@ -258,45 +258,45 @@ watch(
258 if (stages.includes(currentGraphStage.value)) return258 if (stages.includes(currentGraphStage.value)) return
259 currentGraphStage.value = stages.includes(DEFAULT_MEMVIEW_STAGE) ? DEFAULT_MEMVIEW_STAGE : stages[0]259 currentGraphStage.value = stages.includes(DEFAULT_MEMVIEW_STAGE) ? DEFAULT_MEMVIEW_STAGE : stages[0]
260 },260 },
261- { immediate: true },261+ { immediate: true },
262-)262+)
263- 263+ 
264-watch(264+watch(
265- selectedDatasetName,265+ selectedDatasetName,
266- async (datasetName) => {266+ async (datasetName) => {
267- const token = ++issueLoadToken267+ const token = ++issueLoadToken
268- if (!datasetName) {268+ if (!datasetName) {
269- datasetIssuePayload.value = { issueCount: 0, issues: [] }269+ datasetIssuePayload.value = { issueCount: 0, issues: [] }
270- return270+ return
271- }271+ }
272- 272+ 
273- const payload = await fetchValidationIssues(datasetName)273+ const payload = await fetchValidationIssues(datasetName)
274- if (token === issueLoadToken) {274+ if (token === issueLoadToken) {
275- datasetIssuePayload.value = payload275+ datasetIssuePayload.value = payload
276- }276+ }
277- },277+ },
278- { immediate: true },278+ { immediate: true },
279-)279+)
280- 280+ 
281-watch(281+watch(
282- issues,282+ issues,
283- (nextIssues) => {283+ (nextIssues) => {
284- if (!nextIssues.length) {284+ if (!nextIssues.length) {
285- selectedIssueId.value = null285+ selectedIssueId.value = null
286- return286+ return
287- }287+ }
288- 288+ 
289- const currentIssueExists = nextIssues.some((issue) => issue.issueId === selectedIssueId.value)289+ const currentIssueExists = nextIssues.some((issue) => issue.issueId === selectedIssueId.value)
290- if (!currentIssueExists) {290+ if (!currentIssueExists) {
291- selectedIssueId.value = nextIssues[0].issueId291+ selectedIssueId.value = nextIssues[0].issueId
292- }292+ }
293- },293+ },
294- { immediate: true },294+ { immediate: true },
295-)295+)
296- 296+ 
297-watch(297+watch(
298- () => pendingMemViewNavigation.value?.token ?? 0,298+ () => pendingMemViewNavigation.value?.token ?? 0,
299- async () => {299+ async () => {
300 const request = pendingMemViewNavigation.value300 const request = pendingMemViewNavigation.value
301 if (!request) return301 if (!request) return
302 302 
@@ -334,303 +334,303 @@ watch(
334 { immediate: true },334 { immediate: true },
335)335)
336 336 
337-const selectedNodeStep = computed(() => {337+const selectedNodeStep = computed(() => {
338- const node = selectedNode.value338+ const node = selectedNode.value
339- return resolveStepForNode(node)339+ return resolveStepForNode(node)
340-})340+})
341- 
342-const selectedRankIdSet = computed(() => {
343- const ids = new Set()
344- for (const key of selectedRankKeys.value) {
345- const match = String(key).match(/^rank-(\d+)$/)
346- if (match) {
347- ids.add(Number(match[1]))
348- }
349- }
350- return ids
351-})
352- 
353-const selectedNodeTaskOps = computed(() => {
354- const node = selectedNode.value
355- const step = selectedNodeStep.value
356- if (!node?.id || !indexes.value || !Number.isInteger(step)) return []
357- return taskOpsAtStep(indexes.value, step).filter((op) => taskOpMatchesSelectedNode(node, op))
358-})
359- 
360-const selectedSearchFieldLabel = computed(
361- () => SEARCH_FIELD_OPTIONS.find((option) => option.value === searchField.value)?.label ?? searchField.value,
362-)
363- 
364-const selectedRankSearchNodes = computed(() => {
365- if (!selectedRankIdSet.value.size) return []
366- 
367- const registry = nodeRegistry.value
368- if (!(registry instanceof Map) || !registry.size) return []
369- 
370- const seen = new Map()
371- for (const node of registry.values()) {
372- if (!node?.id || seen.has(node.id)) continue
373- if (Number.isInteger(node.rankId) && !selectedRankIdSet.value.has(node.rankId)) {
374- continue
375- }
376- if (!Number.isInteger(node.rankId)) {
377- continue
378- }
379- seen.set(node.id, node)
380- }
381- 
382- return [...seen.values()].sort((left, right) => {
383- const leftRank = Number.isInteger(left.rankId) ? left.rankId : Number.POSITIVE_INFINITY
384- const rightRank = Number.isInteger(right.rankId) ? right.rankId : Number.POSITIVE_INFINITY
385- if (leftRank !== rightRank) return leftRank - rightRank
386- 
387- const leftQueue = Number.isInteger(left.queueId) ? left.queueId : Number.POSITIVE_INFINITY
388- const rightQueue = Number.isInteger(right.queueId) ? right.queueId : Number.POSITIVE_INFINITY
389- if (leftQueue !== rightQueue) return leftQueue - rightQueue
390- 
391- const leftPos = Number.isInteger(left.pos) ? left.pos : Number.isInteger(left.slotIndex) ? left.slotIndex : Number.POSITIVE_INFINITY
392- const rightPos = Number.isInteger(right.pos) ? right.pos : Number.isInteger(right.slotIndex) ? right.slotIndex : Number.POSITIVE_INFINITY
393- if (leftPos !== rightPos) return leftPos - rightPos
394- 
395- return String(left.id).localeCompare(String(right.id))
396- })
397-})
398- 
399-const normalizedSearchQuery = computed(() => searchQuery.value.trim())
400- 
401-function collectSearchNodesFromRegistry(registry) {
402- if (!(registry instanceof Map) || !registry.size) return []
403- 
404- const roots = [...registry.values()].filter((node) => node?.id && !node.isPlaceholder && Number.isInteger(node.rankId))
405- 
406- const collected = []
407- const seenIds = new Set()
408- 
409- const pushNode = (node, context = {}) => {
410- if (!node?.id || seenIds.has(node.id)) return
411- if (!Number.isInteger(node.rankId)) return
412- 
413- seenIds.add(node.id)
414- collected.push({
415- ...node,
416- searchParentId: context.parentId ?? '',
417- searchDepth: Number.isInteger(context.depth) ? context.depth : 0,
418- searchPath: Array.isArray(context.path) ? context.path : [],
419- searchRootId: context.rootId ?? node.id,
420- })
421- }
422- 
423- const walkSubgraph = (entries, context) => {
424- if (!Array.isArray(entries)) return
425- for (const entry of entries) {
426- if (!entry || typeof entry !== 'object') continue
427- const nestedNodes = normalizeStreamCollection([entry], context.rankId, {
428- stageName: currentGraphStage.value || DEFAULT_MEMVIEW_STAGE,
429- topLevel: false,
430- }).flatMap((stream) => stream.nodes)
431- 
432- for (const nestedNode of nestedNodes) {
433- const nestedContext = {
434- rootId: context.rootId,
435- parentId: context.parentId,
436- depth: context.depth + 1,
437- path: [...context.path, entry.node_id ?? entry.id ?? entry.task_id ?? nestedNode.id],
438- }
439- pushNode(nestedNode, nestedContext)
440- const nestedSubGraph = nestedNode?.rawNode?.task?.task_data?.sub_graph
441- if (Array.isArray(nestedSubGraph) && nestedSubGraph.length) {
442- walkSubgraph(nestedSubGraph.flat(), {
443- rankId: nestedNode.rankId,
444- rootId: context.rootId,
445- parentId: nestedNode.id,
446- depth: nestedContext.depth,
447- path: nestedContext.path,
448- })
449- }
450- }
451- }
452- }
453- 
454- for (const rootNode of roots) {
455- pushNode(rootNode, {
456- rootId: rootNode.id,
457- parentId: '',
458- depth: 0,
459- path: [rootNode.id],
460- })
461- 
462- const subGraph = rootNode?.rawNode?.task?.task_data?.sub_graph
463- if (Array.isArray(subGraph) && subGraph.length) {
464- walkSubgraph(subGraph.flat(), {
465- rankId: rootNode.rankId,
466- rootId: rootNode.id,
467- parentId: rootNode.id,
468- depth: 0,
469- path: [rootNode.id],
470- })
471- }
472- }
473- 
474- return collected
475-}
476- 
477-function isExactSearchMatch(node, field, query) {
478- if (!node || !query) return false
479- const normalizedQuery = String(query).trim().toLowerCase()
480- if (!normalizedQuery) return false
481- 
482- if (field === 'taskType') {
483- const value = String(node.taskType ?? node.rawNode?.task?.task_type ?? node.rawNode?.task_type ?? '').trim().toLowerCase()
484- return value === normalizedQuery
485- }
486- 
487- if (field === 'notifyId') {
488- const value = normalizeSearchFieldValue(
489- node.notifyId ??
490- node.notify_id ??
491- node.taskData?.notifyId ??
492- node.taskData?.notify_id ??
493- node.rawNode?.notifyId ??
494- node.rawNode?.notify_id ??
495- node.rawNode?.task?.notifyId ??
496- node.rawNode?.task?.notify_id,
497- )
498- return value === normalizedQuery
499- }
500- 
501- const values = resolveSearchNodeIds(node)
502- return values.some((value) => String(value).trim().toLowerCase() === normalizedQuery)
503-}
504- 
505-function resolveSearchNodeIds(node) {
506- const ids = new Set()
507- const push = (value) => {
508- if (typeof value === 'string' && value.trim()) {
509- ids.add(value.trim())
510- }
511- }
512- 
513- push(node?.id)
514- push(node?.rawNode?.node_id)
515- push(node?.rawNode?.id)
516- push(node?.rawNode?.task_id)
517- return [...ids]
518-}
519- 
520-function sortSearchNodes(nodes) {
521- return [...nodes].sort((left, right) => {
522- const leftRank = Number.isInteger(left.rankId) ? left.rankId : Number.POSITIVE_INFINITY
523- const rightRank = Number.isInteger(right.rankId) ? right.rankId : Number.POSITIVE_INFINITY
524- if (leftRank !== rightRank) return leftRank - rightRank
525- 
526- const leftQueue = Number.isInteger(left.queueId) ? left.queueId : Number.POSITIVE_INFINITY
527- const rightQueue = Number.isInteger(right.queueId) ? right.queueId : Number.POSITIVE_INFINITY
528- if (leftQueue !== rightQueue) return leftQueue - rightQueue
529- 
530- const leftPos = Number.isInteger(left.pos) ? left.pos : Number.isInteger(left.slotIndex) ? left.slotIndex : Number.POSITIVE_INFINITY
531- const rightPos = Number.isInteger(right.pos) ? right.pos : Number.isInteger(right.slotIndex) ? right.slotIndex : Number.POSITIVE_INFINITY
532- if (leftPos !== rightPos) return leftPos - rightPos
533- 
534- return String(left.id).localeCompare(String(right.id))
535- })
536-}
537- 
538-const searchResults = computed(() => {
539- const query = normalizedSearchQuery.value
540- if (!query) return []
541- 
542- const field = searchField.value
543- const allSearchNodes = collectSearchNodesFromRegistry(nodeRegistry.value)
544- const matched = allSearchNodes.filter((node) => isExactSearchMatch(node, field, query))
545- 
546- return sortSearchNodes(matched)
547- .map((node) => ({
548- node,
549- taskId: node.id,
550- taskType: String(node.taskType ?? '--').replaceAll('_', ' '),
551- rank: Number.isInteger(node.rankId) ? node.rankId : '--',
552- queue: Number.isInteger(node.queueId) ? node.queueId : '--',
553- pos: Number.isInteger(node.pos) ? node.pos : Number.isInteger(node.slotIndex) ? node.slotIndex : '--',
554- location: formatSearchLocation(node),
555- notifyId: resolveSearchFieldDisplayValue(node, 'notifyId'),
556- }))
557-})
558- 
559-const canResolveRelation = computed(() => (nodeId) => Boolean(resolveNodeNavigationTarget(nodeId)))
560-const datasetIssueNodeIds = computed(() => {
561- const issues = datasetIssuePayload.value?.issues ?? []
562- const registry = nodeRegistry.value
563- if (!(registry instanceof Map) || !registry.size || !issues.length) return null
564- 
565- const ids = new Set()
566- const nodes = [...new Set([...registry.values()].filter(Boolean))]
567- for (const issue of issues) {
568- for (const node of nodes) {
569- if (issueMatchesNode(issue, node)) {
570- ids.add(node.id)
571- }
572- }
573- }
574- 
575- return ids.size ? ids : null
576-})
577- 
578-const dagIssueNodeIds = computed(() => {
579- const ids = new Set()
580- datasetIssueNodeIds.value?.forEach((nodeId) => ids.add(nodeId))
581- issueHighlightedNodeIds.value?.forEach?.((nodeId) => ids.add(nodeId))
582- return ids.size ? ids : null
583-})
584 341 
585-function handleTreeCheck(checkedKeys) {342+const selectedRankIdSet = computed(() => {
586- setSelectedRankKeys(checkedKeys)343+ const ids = new Set()
587-}344+ for (const key of selectedRankKeys.value) {
588- 345+ const match = String(key).match(/^rank-(\d+)$/)
589-function issueMatchesNode(issue, node) {346+ if (match) {
590- if (!issue || !node?.id) return false347+ ids.add(Number(match[1]))
591- 348+ }
592- const targetIds = issueTargetNodeIds(issue)349+ }
593- const lookupIds = new Set(resolveNodeLookupIds(node))350+ return ids
594- if (targetIds.some((nodeId) => lookupIds.has(nodeId) || node.mappingKey === nodeId)) {351+})
595- return true352+ 
596- }353+const selectedNodeTaskOps = computed(() => {
597- 354+ const node = selectedNode.value
598- const target = issue.dagTarget ?? {}355+ const step = selectedNodeStep.value
599- const detail = issue.rawDetail ?? {}356+ if (!node?.id || !indexes.value || !Number.isInteger(step)) return []
600- const rankId = firstIssueInteger(target.rankId, issue.primaryRankId, detail.rank_id, detail.task_rank, detail.peer_rank)357+ return taskOpsAtStep(indexes.value, step).filter((op) => taskOpMatchesSelectedNode(node, op))
601- const queueId = firstIssueInteger(target.queueId, issue.queueId, detail.queue_id)358+})
602- const slotIndex = firstIssueInteger(target.slotIndex, detail.task_pos, detail.pos)359+ 
603- if (!Number.isInteger(rankId) || !Number.isInteger(queueId) || !Number.isInteger(slotIndex)) return false360+const selectedSearchFieldLabel = computed(
604- 361+ () => SEARCH_FIELD_OPTIONS.find((option) => option.value === searchField.value)?.label ?? searchField.value,
605- const nodeSlot = Number.isInteger(node.pos) ? node.pos : node.slotIndex362+)
606- const nodeQueue = Number.isInteger(node.queueId) ? node.queueId : node.streamId363+ 
607- return node.rankId === rankId && nodeQueue === queueId && nodeSlot === slotIndex364+const selectedRankSearchNodes = computed(() => {
608-}365+ if (!selectedRankIdSet.value.size) return []
609- 366+ 
610-function issueTargetNodeIds(issue) {367+ const registry = nodeRegistry.value
611- const ids = []368+ if (!(registry instanceof Map) || !registry.size) return []
612- const push = (value) => {369+ 
613- if (typeof value === 'string' && value.trim()) ids.push(value.trim())370+ const seen = new Map()
614- }371+ for (const node of registry.values()) {
615- 372+ if (!node?.id || seen.has(node.id)) continue
616- push(issue?.dagTarget?.nodeId)373+ if (Number.isInteger(node.rankId) && !selectedRankIdSet.value.has(node.rankId)) {
617- push(issue?.taskNode?.nodeId)374+ continue
618- push(issue?.rawDetail?.task_id)375+ }
619- issue?.relatedNodeAnchors?.forEach((anchor) => push(anchor?.nodeId))376+ if (!Number.isInteger(node.rankId)) {
620- 377+ continue
621- return [...new Set(ids)]378+ }
622-}379+ seen.set(node.id, node)
623- 380+ }
624-function firstIssueInteger(...values) {381+ 
625- for (const value of values) {382+ return [...seen.values()].sort((left, right) => {
626- if (Number.isInteger(value)) return value383+ const leftRank = Number.isInteger(left.rankId) ? left.rankId : Number.POSITIVE_INFINITY
627- if (typeof value === 'string' && value.trim()) {384+ const rightRank = Number.isInteger(right.rankId) ? right.rankId : Number.POSITIVE_INFINITY
628- const numeric = Number(value)385+ if (leftRank !== rightRank) return leftRank - rightRank
629- if (Number.isInteger(numeric)) return numeric386+ 
630- }387+ const leftQueue = Number.isInteger(left.queueId) ? left.queueId : Number.POSITIVE_INFINITY
631- }388+ const rightQueue = Number.isInteger(right.queueId) ? right.queueId : Number.POSITIVE_INFINITY
632- return null389+ if (leftQueue !== rightQueue) return leftQueue - rightQueue
633-}390+ 
391+ const leftPos = Number.isInteger(left.pos) ? left.pos : Number.isInteger(left.slotIndex) ? left.slotIndex : Number.POSITIVE_INFINITY
392+ const rightPos = Number.isInteger(right.pos) ? right.pos : Number.isInteger(right.slotIndex) ? right.slotIndex : Number.POSITIVE_INFINITY
393+ if (leftPos !== rightPos) return leftPos - rightPos
394+ 
395+ return String(left.id).localeCompare(String(right.id))
396+ })
397+})
398+ 
399+const normalizedSearchQuery = computed(() => searchQuery.value.trim())
400+ 
401+function collectSearchNodesFromRegistry(registry) {
402+ if (!(registry instanceof Map) || !registry.size) return []
403+ 
404+ const roots = [...registry.values()].filter((node) => node?.id && !node.isPlaceholder && Number.isInteger(node.rankId))
405+ 
406+ const collected = []
407+ const seenIds = new Set()
408+ 
409+ const pushNode = (node, context = {}) => {
410+ if (!node?.id || seenIds.has(node.id)) return
411+ if (!Number.isInteger(node.rankId)) return
412+ 
413+ seenIds.add(node.id)
414+ collected.push({
415+ ...node,
416+ searchParentId: context.parentId ?? '',
417+ searchDepth: Number.isInteger(context.depth) ? context.depth : 0,
418+ searchPath: Array.isArray(context.path) ? context.path : [],
419+ searchRootId: context.rootId ?? node.id,
420+ })
421+ }
422+ 
423+ const walkSubgraph = (entries, context) => {
424+ if (!Array.isArray(entries)) return
425+ for (const entry of entries) {
426+ if (!entry || typeof entry !== 'object') continue
427+ const nestedNodes = normalizeStreamCollection([entry], context.rankId, {
428+ stageName: currentGraphStage.value || DEFAULT_MEMVIEW_STAGE,
429+ topLevel: false,
430+ }).flatMap((stream) => stream.nodes)
431+ 
432+ for (const nestedNode of nestedNodes) {
433+ const nestedContext = {
434+ rootId: context.rootId,
435+ parentId: context.parentId,
436+ depth: context.depth + 1,
437+ path: [...context.path, entry.node_id ?? entry.id ?? entry.task_id ?? nestedNode.id],
438+ }
439+ pushNode(nestedNode, nestedContext)
440+ const nestedSubGraph = nestedNode?.rawNode?.task?.task_data?.sub_graph
441+ if (Array.isArray(nestedSubGraph) && nestedSubGraph.length) {
442+ walkSubgraph(nestedSubGraph.flat(), {
443+ rankId: nestedNode.rankId,
444+ rootId: context.rootId,
445+ parentId: nestedNode.id,
446+ depth: nestedContext.depth,
447+ path: nestedContext.path,
448+ })
449+ }
450+ }
451+ }
452+ }
453+ 
454+ for (const rootNode of roots) {
455+ pushNode(rootNode, {
456+ rootId: rootNode.id,
457+ parentId: '',
458+ depth: 0,
459+ path: [rootNode.id],
460+ })
461+ 
462+ const subGraph = rootNode?.rawNode?.task?.task_data?.sub_graph
463+ if (Array.isArray(subGraph) && subGraph.length) {
464+ walkSubgraph(subGraph.flat(), {
465+ rankId: rootNode.rankId,
466+ rootId: rootNode.id,
467+ parentId: rootNode.id,
468+ depth: 0,
469+ path: [rootNode.id],
470+ })
471+ }
472+ }
473+ 
474+ return collected
475+}
476+ 
477+function isExactSearchMatch(node, field, query) {
478+ if (!node || !query) return false
479+ const normalizedQuery = String(query).trim().toLowerCase()
480+ if (!normalizedQuery) return false
481+ 
482+ if (field === 'taskType') {
483+ const value = String(node.taskType ?? node.rawNode?.task?.task_type ?? node.rawNode?.task_type ?? '').trim().toLowerCase()
484+ return value === normalizedQuery
485+ }
486+ 
487+ if (field === 'notifyId') {
488+ const value = normalizeSearchFieldValue(
489+ node.notifyId ??
490+ node.notify_id ??
491+ node.taskData?.notifyId ??
492+ node.taskData?.notify_id ??
493+ node.rawNode?.notifyId ??
494+ node.rawNode?.notify_id ??
495+ node.rawNode?.task?.notifyId ??
496+ node.rawNode?.task?.notify_id,
497+ )
498+ return value === normalizedQuery
499+ }
500+ 
501+ const values = resolveSearchNodeIds(node)
502+ return values.some((value) => String(value).trim().toLowerCase() === normalizedQuery)
503+}
504+ 
505+function resolveSearchNodeIds(node) {
506+ const ids = new Set()
507+ const push = (value) => {
508+ if (typeof value === 'string' && value.trim()) {
509+ ids.add(value.trim())
510+ }
511+ }
512+ 
513+ push(node?.id)
514+ push(node?.rawNode?.node_id)
515+ push(node?.rawNode?.id)
516+ push(node?.rawNode?.task_id)
517+ return [...ids]
518+}
519+ 
520+function sortSearchNodes(nodes) {
521+ return [...nodes].sort((left, right) => {
522+ const leftRank = Number.isInteger(left.rankId) ? left.rankId : Number.POSITIVE_INFINITY
523+ const rightRank = Number.isInteger(right.rankId) ? right.rankId : Number.POSITIVE_INFINITY
524+ if (leftRank !== rightRank) return leftRank - rightRank
525+ 
526+ const leftQueue = Number.isInteger(left.queueId) ? left.queueId : Number.POSITIVE_INFINITY
527+ const rightQueue = Number.isInteger(right.queueId) ? right.queueId : Number.POSITIVE_INFINITY
528+ if (leftQueue !== rightQueue) return leftQueue - rightQueue
529+ 
530+ const leftPos = Number.isInteger(left.pos) ? left.pos : Number.isInteger(left.slotIndex) ? left.slotIndex : Number.POSITIVE_INFINITY
531+ const rightPos = Number.isInteger(right.pos) ? right.pos : Number.isInteger(right.slotIndex) ? right.slotIndex : Number.POSITIVE_INFINITY
532+ if (leftPos !== rightPos) return leftPos - rightPos
533+ 
534+ return String(left.id).localeCompare(String(right.id))
535+ })
536+}
537+ 
538+const searchResults = computed(() => {
539+ const query = normalizedSearchQuery.value
540+ if (!query) return []
541+ 
542+ const field = searchField.value
543+ const allSearchNodes = collectSearchNodesFromRegistry(nodeRegistry.value)
544+ const matched = allSearchNodes.filter((node) => isExactSearchMatch(node, field, query))
545+ 
546+ return sortSearchNodes(matched)
547+ .map((node) => ({
548+ node,
549+ taskId: node.id,
550+ taskType: String(node.taskType ?? '--').replaceAll('_', ' '),
551+ rank: Number.isInteger(node.rankId) ? node.rankId : '--',
552+ queue: Number.isInteger(node.queueId) ? node.queueId : '--',
553+ pos: Number.isInteger(node.pos) ? node.pos : Number.isInteger(node.slotIndex) ? node.slotIndex : '--',
554+ formatLoc: formatSearchLocation(node),
555+ notifyId: resolveSearchFieldDisplayValue(node, 'notifyId'),
556+ }))
557+})
558+ 
559+const canResolveRelation = computed(() => (nodeId) => Boolean(resolveNodeNavigationTarget(nodeId)))
560+const datasetIssueNodeIds = computed(() => {
561+ const issues = datasetIssuePayload.value?.issues ?? []
562+ const registry = nodeRegistry.value
563+ if (!(registry instanceof Map) || !registry.size || !issues.length) return null
564+ 
565+ const ids = new Set()
566+ const nodes = [...new Set([...registry.values()].filter(Boolean))]
567+ for (const issue of issues) {
568+ for (const node of nodes) {
569+ if (issueMatchesNode(issue, node)) {
570+ ids.add(node.id)
571+ }
572+ }
573+ }
574+ 
575+ return ids.size ? ids : null
576+})
577+ 
578+const dagIssueNodeIds = computed(() => {
579+ const ids = new Set()
580+ datasetIssueNodeIds.value?.forEach((nodeId) => ids.add(nodeId))
581+ issueHighlightedNodeIds.value?.forEach?.((nodeId) => ids.add(nodeId))
582+ return ids.size ? ids : null
583+})
584+ 
585+function handleTreeCheck(checkedKeys) {
586+ setSelectedRankKeys(checkedKeys)
587+}
588+ 
589+function issueMatchesNode(issue, node) {
590+ if (!issue || !node?.id) return false
591+ 
592+ const targetIds = issueTargetNodeIds(issue)
593+ const lookupIds = new Set(resolveNodeLookupIds(node))
594+ if (targetIds.some((nodeId) => lookupIds.has(nodeId) || node.mappingKey === nodeId)) {
595+ return true
596+ }
597+ 
598+ const target = issue.dagTarget ?? {}
599+ const detail = issue.rawDetail ?? {}
600+ const rankId = firstIssueInteger(target.rankId, issue.primaryRankId, detail.rank_id, detail.task_rank, detail.peer_rank)
601+ const queueId = firstIssueInteger(target.queueId, issue.queueId, detail.queue_id)
602+ const slotIndex = firstIssueInteger(target.slotIndex, detail.task_pos, detail.pos)
603+ if (!Number.isInteger(rankId) || !Number.isInteger(queueId) || !Number.isInteger(slotIndex)) return false
604+ 
605+ const nodeSlot = Number.isInteger(node.pos) ? node.pos : node.slotIndex
606+ const nodeQueue = Number.isInteger(node.queueId) ? node.queueId : node.streamId
607+ return node.rankId === rankId && nodeQueue === queueId && nodeSlot === slotIndex
608+}
609+ 
610+function issueTargetNodeIds(issue) {
611+ const ids = []
612+ const push = (value) => {
613+ if (typeof value === 'string' && value.trim()) ids.push(value.trim())
614+ }
615+ 
616+ push(issue?.dagTarget?.nodeId)
617+ push(issue?.taskNode?.nodeId)
618+ push(issue?.rawDetail?.task_id)
619+ issue?.relatedNodeAnchors?.forEach((anchor) => push(anchor?.nodeId))
620+ 
621+ return [...new Set(ids)]
622+}
623+ 
624+function firstIssueInteger(...values) {
625+ for (const value of values) {
626+ if (Number.isInteger(value)) return value
627+ if (typeof value === 'string' && value.trim()) {
628+ const numeric = Number(value)
629+ if (Number.isInteger(numeric)) return numeric
630+ }
631+ }
632+ return null
633+}
634 634 
635function handleLeftPanelResize(size) {635function handleLeftPanelResize(size) {
636 if (!sidebarCollapsed.value && typeof size === 'number') {636 if (!sidebarCollapsed.value && typeof size === 'number') {
@@ -681,206 +681,206 @@ function expandCenterPane(target) {
681 notifyDagLayoutChange()681 notifyDagLayoutChange()
682}682}
683 683 
684-function handleNodeSelect(node) {684+function handleNodeSelect(node) {
685- focusNode(node, { follow: true, sync: true })685+ focusNode(node, { follow: true, sync: true })
686-}686+}
687- 687+ 
688-async function handleIssueSelect(issue) {688+async function handleIssueSelect(issue) {
689- if (!issue) return689+ if (!issue) return
690- 690+ 
691- selectedIssueId.value = issue.issueId691+ selectedIssueId.value = issue.issueId
692- 692+ 
693- const target = resolveIssueNodeTarget(issue)693+ const target = resolveIssueNodeTarget(issue)
694- const step = resolveIssueNavigationStep(issue)694+ const step = resolveIssueNavigationStep(issue)
695- const rankIds = resolveNavigationRankIds(issue)695+ const rankIds = resolveNavigationRankIds(issue)
696- const stageName = issue?.dagTarget?.stageName ?? issue?.memTarget?.stageName ?? ''696+ const stageName = issue?.dagTarget?.stageName ?? issue?.memTarget?.stageName ?? ''
697- const hasNodeTarget = Boolean(target?.nodeId)697+ const hasNodeTarget = Boolean(target?.nodeId)
698- const hasStepTarget = Number.isInteger(step)698+ const hasStepTarget = Number.isInteger(step)
699- 699+ 
700- if (!hasNodeTarget && !hasStepTarget && !rankIds.length) {700+ if (!hasNodeTarget && !hasStepTarget && !rankIds.length) {
701- return701+ return
702- }702+ }
703- 703+ 
704- if (rankIds.length) {704+ if (rankIds.length) {
705- setSelectedRankKeys(rankIds.map((rankId) => `rank-${rankId}`))705+ setSelectedRankKeys(rankIds.map((rankId) => `rank-${rankId}`))
706- }706+ }
707- 707+ 
708- if (stageName && stageName !== currentGraphStage.value) {708+ if (stageName && stageName !== currentGraphStage.value) {
709- handleStageUpdate(stageName)709+ handleStageUpdate(stageName)
710- }710+ }
711- 711+ 
712- if (hasNodeTarget) {712+ if (hasNodeTarget) {
713- pendingExternalNavigation.value = {713+ pendingExternalNavigation.value = {
714- source: 'analytic',714+ source: 'analytic',
715- targetView: 'dag',715+ targetView: 'dag',
716- stageName: stageName || currentGraphStage.value,716+ stageName: stageName || currentGraphStage.value,
717- lookupId: target.nodeId,717+ lookupId: target.nodeId,
718- nodeId: target.nodeId,718+ nodeId: target.nodeId,
719- rankId: target.rankId,719+ rankId: target.rankId,
720- queueId: target.queueId,720+ queueId: target.queueId,
721- slotIndex: target.slotIndex,721+ slotIndex: target.slotIndex,
722- taskType: target.taskType,722+ taskType: target.taskType,
723- }723+ }
724- 724+ 
725- await nextTick()725+ await nextTick()
726- resolvePendingExternalNavigation()726+ resolvePendingExternalNavigation()
727- return727+ return
728- }728+ }
729- 729+ 
730- if (hasStepTarget) {730+ if (hasStepTarget) {
731- focusStep(step, { smoothScroll: false })731+ focusStep(step, { smoothScroll: false })
732- }732+ }
733-}733+}
734- 734+ 
735-function handleSearchResultSelect(result) {735+function handleSearchResultSelect(result) {
736- if (!result?.node) return736+ if (!result?.node) return
737- handleNavigate({737+ handleNavigate({
738- stageName: result.node.stageName || currentGraphStage.value || '',738+ stageName: result.node.stageName || currentGraphStage.value || '',
739- lookupId: result.node.id,739+ lookupId: result.node.id,
740- containerLookupId:740+ containerLookupId:
741- result.node.searchParentId && result.node.searchParentId !== result.node.id741+ result.node.searchParentId && result.node.searchParentId !== result.node.id
742- ? result.node.searchParentId742+ ? result.node.searchParentId
743- : result.node.searchRootId && result.node.searchRootId !== result.node.id743+ : result.node.searchRootId && result.node.searchRootId !== result.node.id
744- ? result.node.searchRootId744+ ? result.node.searchRootId
745- : '',745+ : '',
746- })746+ })
747-}747+}
748- 748+ 
749-function resolveNavigationRankIds(issue) {749+function resolveNavigationRankIds(issue) {
750- if (issue?.relatedRankIds?.length) {750+ if (issue?.relatedRankIds?.length) {
751- return issue.relatedRankIds751+ return issue.relatedRankIds
752- }752+ }
753- 753+ 
754- return selectedRankKeys.value754+ return selectedRankKeys.value
755 .map((key) => {755 .map((key) => {
756 const match = String(key).match(/^rank-(\d+)$/)756 const match = String(key).match(/^rank-(\d+)$/)
757 return match ? Number(match[1]) : null757 return match ? Number(match[1]) : null
758 })758 })
759 .filter((rankId) => Number.isInteger(rankId))759 .filter((rankId) => Number.isInteger(rankId))
760-}760+}
761- 
762-function resolveIssueNavigationStep(issue) {
763- const primaryStep = issue?.primaryRelatedStep
764- if (Number.isInteger(primaryStep?.snapshotStep)) return primaryStep.snapshotStep
765- 
766- const taskNode = issue?.taskNode
767- if (Number.isInteger(taskNode?.globalStep)) return taskNode.globalStep
768- if (Number.isInteger(taskNode?.localStep)) return taskNode.localStep
769- 
770- const stage = typeof issue?.stage === 'string' ? issue.stage : ''
771- const stageMatch = stage.match(/^step_(\d+)_/)
772- if (stageMatch) return Number(stageMatch[1])
773- 
774- return null
775-}
776- 
777-function resolveIssueNodeTarget(issue) {
778- const relatedNode = issue?.primaryRelatedNode ?? issue?.taskNode
779- if (relatedNode?.nodeId || (Number.isInteger(relatedNode?.rankId) && Number.isInteger(relatedNode?.queueId))) {
780- return {
781- nodeId: relatedNode?.nodeId ?? '',
782- rankId: Number.isInteger(relatedNode?.rankId) ? relatedNode.rankId : issue?.primaryRankId ?? null,
783- queueId: Number.isInteger(relatedNode?.queueId) ? relatedNode.queueId : issue?.queueId ?? null,
784- slotIndex: Number.isInteger(relatedNode?.pos) ? relatedNode.pos : null,
785- taskType: relatedNode?.taskType ?? issue?.taskType ?? '',
786- }
787- }
788- 
789- if (!issue?.dagTarget) {
790- return {
791- nodeId: '',
792- rankId: null,
793- queueId: null,
794- slotIndex: null,
795- taskType: '',
796- }
797- }
798- 
799- return {
800- nodeId: issue.dagTarget.nodeId ?? '',
801- rankId:
802- Number.isInteger(issue?.dagTarget?.queueId) || Number.isInteger(issue?.dagTarget?.slotIndex) || issue?.dagTarget?.nodeId
803- ? issue.dagTarget.rankId ?? issue?.primaryRankId ?? null
804- : null,
805- queueId: issue.dagTarget.queueId ?? issue?.queueId ?? null,
806- slotIndex: issue?.dagTarget?.slotIndex ?? null,
807- taskType: issue?.dagTarget?.taskType ?? issue?.taskType ?? '',
808- }
809-}
810- 
811-function resolveSearchFieldValue(node, field) {
812- if (!node) return ''
813- const rawNode = node.rawNode ?? {}
814- const taskData = node.taskData ?? rawNode?.task?.task_data ?? rawNode?.task_data ?? {}
815- 
816- switch (field) {
817- case 'taskType':
818- return String(node.taskType ?? rawNode?.task?.task_type ?? rawNode?.task_type ?? '').toLowerCase()
819- case 'notifyId':
820- return normalizeSearchFieldValue(
821- node.notifyId ??
822- node.notify_id ??
823- taskData.notifyId ??
824- taskData.notify_id ??
825- rawNode.notifyId ??
826- rawNode.notify_id ??
827- rawNode?.task?.notifyId ??
828- rawNode?.task?.notify_id,
829- )
830- case 'taskId':
831- default:
832- return String(node.id ?? rawNode?.node_id ?? rawNode?.id ?? '').toLowerCase()
833- }
834-}
835- 
836-function resolveSearchFieldDisplayValue(node, field) {
837- if (!node) return '--'
838- const rawNode = node.rawNode ?? {}
839- const taskData = node.taskData ?? rawNode?.task?.task_data ?? rawNode?.task_data ?? {}
840- 
841- switch (field) {
842- case 'taskType':
843- return String(node.taskType ?? rawNode?.task?.task_type ?? rawNode?.task_type ?? '--').replaceAll('_', ' ')
844- case 'notifyId':
845- return formatSearchFieldDisplayValue(
846- node.notifyId ??
847- node.notify_id ??
848- taskData.notifyId ??
849- taskData.notify_id ??
850- rawNode.notifyId ??
851- rawNode.notify_id ??
852- rawNode?.task?.notifyId ??
853- rawNode?.task?.notify_id,
854- )
855- case 'taskId':
856- default:
857- return formatSearchFieldDisplayValue(node.id ?? rawNode?.node_id ?? rawNode?.id)
858- }
859-}
860- 
861-function formatSearchLocation(node) {
862- const rank = Number.isInteger(node?.rankId) ? `rank ${node.rankId}` : 'rank --'
863- const queue = Number.isInteger(node?.queueId) ? `queue ${node.queueId}` : 'queue --'
864- const posValue = Number.isInteger(node?.pos)
865- ? node.pos
866- : Number.isInteger(node?.slotIndex)
867- ? node.slotIndex
868- : null
869- const pos = Number.isInteger(posValue) ? `pos ${posValue}` : 'pos --'
870- return `${rank} / ${queue} / ${pos}`
871-}
872- 
873-function normalizeSearchFieldValue(value) {
874- if (value === null || value === undefined || value === '') return ''
875- return String(value).toLowerCase()
876-}
877- 
878-function formatSearchFieldDisplayValue(value) {
879- if (value === null || value === undefined || value === '') return '--'
880- return String(value)
881-}
882 761 
883-function handleStageUpdate(nextStage) {762+function resolveIssueNavigationStep(issue) {
763+ const primaryStep = issue?.primaryRelatedStep
764+ if (Number.isInteger(primaryStep?.snapshotStep)) return primaryStep.snapshotStep
765+ 
766+ const taskNode = issue?.taskNode
767+ if (Number.isInteger(taskNode?.globalStep)) return taskNode.globalStep
768+ if (Number.isInteger(taskNode?.localStep)) return taskNode.localStep
769+ 
770+ const stage = typeof issue?.stage === 'string' ? issue.stage : ''
771+ const stageMatch = stage.match(/^step_(\d+)_/)
772+ if (stageMatch) return Number(stageMatch[1])
773+ 
774+ return null
775+}
776+ 
777+function resolveIssueNodeTarget(issue) {
778+ const relatedNode = issue?.primaryRelatedNode ?? issue?.taskNode
779+ if (relatedNode?.nodeId || (Number.isInteger(relatedNode?.rankId) && Number.isInteger(relatedNode?.queueId))) {
780+ return {
781+ nodeId: relatedNode?.nodeId ?? '',
782+ rankId: Number.isInteger(relatedNode?.rankId) ? relatedNode.rankId : issue?.primaryRankId ?? null,
783+ queueId: Number.isInteger(relatedNode?.queueId) ? relatedNode.queueId : issue?.queueId ?? null,
784+ slotIndex: Number.isInteger(relatedNode?.pos) ? relatedNode.pos : null,
785+ taskType: relatedNode?.taskType ?? issue?.taskType ?? '',
786+ }
787+ }
788+ 
789+ if (!issue?.dagTarget) {
790+ return {
791+ nodeId: '',
792+ rankId: null,
793+ queueId: null,
794+ slotIndex: null,
795+ taskType: '',
796+ }
797+ }
798+ 
799+ return {
800+ nodeId: issue.dagTarget.nodeId ?? '',
801+ rankId:
802+ Number.isInteger(issue?.dagTarget?.queueId) || Number.isInteger(issue?.dagTarget?.slotIndex) || issue?.dagTarget?.nodeId
803+ ? issue.dagTarget.rankId ?? issue?.primaryRankId ?? null
804+ : null,
805+ queueId: issue.dagTarget.queueId ?? issue?.queueId ?? null,
806+ slotIndex: issue?.dagTarget?.slotIndex ?? null,
807+ taskType: issue?.dagTarget?.taskType ?? issue?.taskType ?? '',
808+ }
809+}
810+ 
811+function resolveSearchFieldValue(node, field) {
812+ if (!node) return ''
813+ const rawNode = node.rawNode ?? {}
814+ const taskData = node.taskData ?? rawNode?.task?.task_data ?? rawNode?.task_data ?? {}
815+ 
816+ switch (field) {
817+ case 'taskType':
818+ return String(node.taskType ?? rawNode?.task?.task_type ?? rawNode?.task_type ?? '').toLowerCase()
819+ case 'notifyId':
820+ return normalizeSearchFieldValue(
821+ node.notifyId ??
822+ node.notify_id ??
823+ taskData.notifyId ??
824+ taskData.notify_id ??
825+ rawNode.notifyId ??
826+ rawNode.notify_id ??
827+ rawNode?.task?.notifyId ??
828+ rawNode?.task?.notify_id,
829+ )
830+ case 'taskId':
831+ default:
832+ return String(node.id ?? rawNode?.node_id ?? rawNode?.id ?? '').toLowerCase()
833+ }
834+}
835+ 
836+function resolveSearchFieldDisplayValue(node, field) {
837+ if (!node) return '--'
838+ const rawNode = node.rawNode ?? {}
839+ const taskData = node.taskData ?? rawNode?.task?.task_data ?? rawNode?.task_data ?? {}
840+ 
841+ switch (field) {
842+ case 'taskType':
843+ return String(node.taskType ?? rawNode?.task?.task_type ?? rawNode?.task_type ?? '--').replaceAll('_', ' ')
844+ case 'notifyId':
845+ return formatSearchFieldDisplayValue(
846+ node.notifyId ??
847+ node.notify_id ??
848+ taskData.notifyId ??
849+ taskData.notify_id ??
850+ rawNode.notifyId ??
851+ rawNode.notify_id ??
852+ rawNode?.task?.notifyId ??
853+ rawNode?.task?.notify_id,
854+ )
855+ case 'taskId':
856+ default:
857+ return formatSearchFieldDisplayValue(node.id ?? rawNode?.node_id ?? rawNode?.id)
858+ }
859+}
860+ 
861+function formatSearchLocation(node) {
862+ const rank = Number.isInteger(node?.rankId) ? `rank ${node.rankId}` : 'rank --'
863+ const queue = Number.isInteger(node?.queueId) ? `queue ${node.queueId}` : 'queue --'
864+ const posValue = Number.isInteger(node?.pos)
865+ ? node.pos
866+ : Number.isInteger(node?.slotIndex)
867+ ? node.slotIndex
868+ : null
869+ const pos = Number.isInteger(posValue) ? `pos ${posValue}` : 'pos --'
870+ return `${rank} / ${queue} / ${pos}`
871+}
872+ 
873+function normalizeSearchFieldValue(value) {
874+ if (value === null || value === undefined || value === '') return ''
875+ return String(value).toLowerCase()
876+}
877+ 
878+function formatSearchFieldDisplayValue(value) {
879+ if (value === null || value === undefined || value === '') return '--'
880+ return String(value)
881+}
882+ 
883+function handleStageUpdate(nextStage) {
884 if (typeof nextStage !== 'string' || !nextStage || nextStage === currentGraphStage.value) return884 if (typeof nextStage !== 'string' || !nextStage || nextStage === currentGraphStage.value) return
885 885 
886 const mappedTarget = selectedNode.value?.nodeMappings?.find((item) => item?.stageName === nextStage) ?? null886 const mappedTarget = selectedNode.value?.nodeMappings?.find((item) => item?.stageName === nextStage) ?? null
@@ -937,55 +937,55 @@ function handleNavigate(target) {
937 return937 return
938 }938 }
939 939 
940- if (resolvedTarget.containerNode) {940+ if (resolvedTarget.containerNode) {
941- enterSubgraph(resolvedTarget.containerNode)941+ enterSubgraph(resolvedTarget.containerNode)
942- } else if (subgraphNode.value) {942+ } else if (subgraphNode.value) {
943- exitSubgraph()943+ exitSubgraph()
944- }944+ }
945 945 
946 registerNodes([...resolvedTarget.path, resolvedTarget.node])946 registerNodes([...resolvedTarget.path, resolvedTarget.node])
947 focusNode(resolvedTarget.node, { follow: true, sync: true })947 focusNode(resolvedTarget.node, { follow: true, sync: true })
948-}948+}
949- 949+ 
950-function handleFocusStep(step) {950+function handleFocusStep(step) {
951- enterDagFocus(step)951+ enterDagFocus(step)
952- focusStep(step, { smoothScroll: true })952+ focusStep(step, { smoothScroll: true })
953-}953+}
954- 954+ 
955-function handleStepUpdate(step) {955+function handleStepUpdate(step) {
956- if (!Number.isFinite(step)) return956+ if (!Number.isFinite(step)) return
957- enterDagFocus(step)957+ enterDagFocus(step)
958- focusStep(step, { smoothScroll: false })958+ focusStep(step, { smoothScroll: false })
959-}959+}
960- 960+ 
961-function handleOverviewStepUpdate(step) {961+function handleOverviewStepUpdate(step) {
962- if (!Number.isFinite(step)) return962+ if (!Number.isFinite(step)) return
963- selectedNode.value = null963+ selectedNode.value = null
964- enterDagFocus(step)964+ enterDagFocus(step)
965- focusStep(step, { smoothScroll: false })965+ focusStep(step, { smoothScroll: false })
966-}966+}
967- 967+ 
968-async function handleBufferSelect(selection) {968+async function handleBufferSelect(selection) {
969- const step = Number(selection?.step)969+ const step = Number(selection?.step)
970 const rankId = Number(selection?.rankId)970 const rankId = Number(selection?.rankId)
971 const bufferId = Number(selection?.bufferId)971 const bufferId = Number(selection?.bufferId)
972- if (!Number.isInteger(step) || !Number.isInteger(rankId) || !Number.isInteger(bufferId)) return972+ if (!Number.isInteger(step) || !Number.isInteger(rankId) || !Number.isInteger(bufferId)) return
973- 973+ 
974- selectedNode.value = null974+ selectedNode.value = null
975- enterDagFocus(step)975+ enterDagFocus(step)
976- focusStep(step, { smoothScroll: false })976+ focusStep(step, { smoothScroll: false })
977- await nextTick()977+ await nextTick()
978- inspectorRef.value?.selectLayout?.(rankId, bufferId)978+ inspectorRef.value?.selectLayout?.(rankId, bufferId)
979-}979+}
980- 980+ 
981-function enterDagFocus(step) {981+function enterDagFocus(step) {
982- if (!Number.isFinite(step)) return982+ if (!Number.isFinite(step)) return
983- dagFocusStep.value = Math.max(0, Math.floor(step))983+ dagFocusStep.value = Math.max(0, Math.floor(step))
984-}984+}
985- 985+ 
986-function exitDagFocus() {986+function exitDagFocus() {
987- dagFocusStep.value = null987+ dagFocusStep.value = null
988-}988+}
989 989 
990function handleTotalStepsUpdate(total) {990function handleTotalStepsUpdate(total) {
991 setTotalSteps(total)991 setTotalSteps(total)
@@ -1392,19 +1392,19 @@ function findNodeInDescendants(rootNode, targetId, visited, path = []) {
1392 return null1392 return null
1393}1393}
1394 1394 
1395-function iterateSubgraphEntries(streamCollection, fallbackRankId) {1395+function iterateSubgraphEntries(streamCollection, fallbackRankId) {
1396- return normalizeStreamCollection(streamCollection, fallbackRankId, {1396+ return normalizeStreamCollection(streamCollection, fallbackRankId, {
1397- stageName: DEFAULT_MEMVIEW_STAGE,1397+ stageName: DEFAULT_MEMVIEW_STAGE,
1398- topLevel: false,1398+ topLevel: false,
1399- }).flatMap((stream) =>1399+ }).flatMap((stream) =>
1400- stream.nodes.map((node) => ({1400+ stream.nodes.map((node) => ({
1401- rawNode: node.rawNode,1401+ rawNode: node.rawNode,
1402- node,1402+ node,
1403- })),1403+ })),
1404- )1404+ )
1405-}1405+}
1406- 1406+ 
1407-watch(1407+watch(
1408 () => [selectedDatasetName.value, selectedRankKeys.value.join(',')],1408 () => [selectedDatasetName.value, selectedRankKeys.value.join(',')],
1409 () => {1409 () => {
1410 resetTimeline()1410 resetTimeline()
@@ -1436,35 +1436,35 @@ watch(
1436 },1436 },
1437)1437)
1438 1438 
1439-watch(1439+watch(
1440- () => [playbackVisibleSteps.value.length, playbackVisibleIndex.value],1440+ () => [playbackVisibleSteps.value.length, playbackVisibleIndex.value],
1441- ([stepCount, currentIndex]) => {1441+ ([stepCount, currentIndex]) => {
1442- if (!playbackPlaying.value) return1442+ if (!playbackPlaying.value) return
1443- if (!stepCount || currentIndex >= stepCount - 1) {1443+ if (!stepCount || currentIndex >= stepCount - 1) {
1444- stopPlayback()1444+ stopPlayback()
1445- }1445+ }
1446- },1446+ },
1447-)1447+)
1448- 1448+ 
1449-watch(1449+watch(
1450- () => [selectedDatasetName.value, selectedRankKeys.value.join(','), currentGraphStage.value],1450+ () => [selectedDatasetName.value, selectedRankKeys.value.join(','), currentGraphStage.value],
1451- () => {1451+ () => {
1452- if (Number.isInteger(dagFocusStep.value)) {1452+ if (Number.isInteger(dagFocusStep.value)) {
1453- exitDagFocus()1453+ exitDagFocus()
1454- }1454+ }
1455- },1455+ },
1456-)1456+)
1457- 1457+ 
1458-watch(1458+watch(
1459- clampedCurrentStep,1459+ clampedCurrentStep,
1460- (step) => {1460+ (step) => {
1461- if (!Number.isInteger(dagFocusStep.value)) return1461+ if (!Number.isInteger(dagFocusStep.value)) return
1462- if (dagFocusStep.value !== step) {1462+ if (dagFocusStep.value !== step) {
1463- dagFocusStep.value = step1463+ dagFocusStep.value = step
1464- }1464+ }
1465- },1465+ },
1466- { immediate: true },1466+ { immediate: true },
1467-)1467+)
1468 1468 
1469onMounted(() => {1469onMounted(() => {
1470 centerTopSize.value = resolveDefaultCenterTopSize()1470 centerTopSize.value = resolveDefaultCenterTopSize()
@@ -1499,190 +1499,190 @@ onBeforeUnmount(() => {
1499 :resizable="!sidebarCollapsed"1499 :resizable="!sidebarCollapsed"
1500 @update:size="handleLeftPanelResize"1500 @update:size="handleLeftPanelResize"
1501 >1501 >
1502- <aside class="dashboard-sidebar-shell" :class="{ 'is-collapsed': sidebarCollapsed }">1502+ <aside class="dashboard-sidebar-shell" :class="{ 'is-collapsed': sidebarCollapsed }">
1503- <DashboardSidebar1503+ <DashboardSidebar
1504- :selected-rank-count="selectedRankCount"1504+ :selected-rank-count="selectedRankCount"
1505- :selected-dataset="selectedDataset"1505+ :selected-dataset="selectedDataset"
1506- :tree-data="treeData"1506+ :tree-data="treeData"
1507- :checked-keys="selectedRankKeys"1507+ :checked-keys="selectedRankKeys"
1508- :format-count="formatCount"1508+ :format-count="formatCount"
1509- :empty-text="loadingList ? 'Loading...' : EMPTY_TEXT"1509+ :empty-text="loadingList ? 'Loading...' : EMPTY_TEXT"
1510- @tree-check="handleTreeCheck"1510+ @tree-check="handleTreeCheck"
1511- >1511+ >
1512- <template #after-rank>1512+ <template #after-rank>
1513- <el-collapse1513+ <el-collapse
1514- v-model="issuePanelSections"1514+ v-model="issuePanelSections"
1515- class="dashboard-sidebar-collapse memview-issue-collapse"1515+ class="dashboard-sidebar-collapse memview-issue-collapse"
1516- >1516+ >
1517- <el-collapse-item name="issues" class="dashboard-subpanel analytic-list-panel">1517+ <el-collapse-item name="issues" class="dashboard-subpanel analytic-list-panel">
1518- <template #title>1518+ <template #title>
1519- <div class="dashboard-panel-toggle">1519+ <div class="dashboard-panel-toggle">
1520- <span class="dashboard-panel-toggle__title">1520+ <span class="dashboard-panel-toggle__title">
1521- <el-icon><WarningFilled /></el-icon>1521+ <el-icon><WarningFilled /></el-icon>
1522- <span>报错列表</span>1522+ <span>报错列表</span>
1523- </span>1523+ </span>
1524- <span class="dashboard-panel-toggle__meta">1524+ <span class="dashboard-panel-toggle__meta">
1525- <el-tag size="small" type="danger">{{ issueCount }} 条</el-tag>1525+ <el-tag size="small" type="danger">{{ issueCount }} 条</el-tag>
1526- </span>1526+ </span>
1527- </div>1527+ </div>
1528- </template>1528+ </template>
1529- 1529+ 
1530- <div v-if="loadingIssues" class="dashboard-empty-wrap analytic-list-empty">1530+ <div v-if="loadingIssues" class="dashboard-empty-wrap analytic-list-empty">
1531- <el-empty description="正在读取 issues.msgpack..." />1531+ <el-empty description="正在读取 issues.msgpack..." />
1532- </div>1532+ </div>
1533- 1533+ 
1534- <div v-else-if="issues.length" class="analytic-issue-list">1534+ <div v-else-if="issues.length" class="analytic-issue-list">
1535- <button1535+ <button
1536- v-for="issue in issues"1536+ v-for="issue in issues"
1537- :key="issue.issueId"1537+ :key="issue.issueId"
1538- type="button"1538+ type="button"
1539- class="analytic-issue-row"1539+ class="analytic-issue-row"
1540- :class="{ 'is-active': selectedIssue?.issueId === issue.issueId }"1540+ :class="{ 'is-active': selectedIssue?.issueId === issue.issueId }"
1541- @click="handleIssueSelect(issue)"1541+ @click="handleIssueSelect(issue)"
1542- >1542+ >
1543- <span class="analytic-issue-row__icon" :class="`is-${issue.severityType}`">1543+ <span class="analytic-issue-row__icon" :class="`is-${issue.severityType}`">
1544- <el-icon><WarningFilled /></el-icon>1544+ <el-icon><WarningFilled /></el-icon>
1545- </span>1545+ </span>
1546- <div class="analytic-issue-row__content">1546+ <div class="analytic-issue-row__content">
1547- <div class="analytic-issue-row__head">1547+ <div class="analytic-issue-row__head">
1548- <strong>{{ issue.title }}</strong>1548+ <strong>{{ issue.title }}</strong>
1549- <div class="analytic-issue-row__head-tags">1549+ <div class="analytic-issue-row__head-tags">
1550- <el-tag size="small" :type="issue.severityType">{{ issue.severityLabel }}</el-tag>1550+ <el-tag size="small" :type="issue.severityType">{{ issue.severityLabel }}</el-tag>
1551- </div>1551+ </div>
1552- </div>1552+ </div>
1553- <div class="analytic-issue-row__body">1553+ <div class="analytic-issue-row__body">
1554- <div v-if="issue.listFieldRows.primaryRow.length" class="analytic-issue-row__field-stack">1554+ <div v-if="issue.listFieldRows.primaryRow.length" class="analytic-issue-row__field-stack">
1555- <div1555+ <div
1556- v-for="field in issue.listFieldRows.primaryRow"1556+ v-for="field in issue.listFieldRows.primaryRow"
1557- :key="`primary-${issue.issueId}-${field.label}`"1557+ :key="`primary-${issue.issueId}-${field.label}`"
1558- class="analytic-issue-row__field"1558+ class="analytic-issue-row__field"
1559- >1559+ >
1560- <span>{{ field.label }}</span>1560+ <span>{{ field.label }}</span>
1561- <strong>{{ field.value }}</strong>1561+ <strong>{{ field.value }}</strong>
1562- </div>1562+ </div>
1563- </div>1563+ </div>
1564- <div1564+ <div
1565- v-if="issue.listFieldRows.secondaryRow.length"1565+ v-if="issue.listFieldRows.secondaryRow.length"
1566- class="analytic-issue-row__field-stack is-secondary"1566+ class="analytic-issue-row__field-stack is-secondary"
1567- >1567+ >
1568- <div1568+ <div
1569- v-for="field in issue.listFieldRows.secondaryRow"1569+ v-for="field in issue.listFieldRows.secondaryRow"
1570- :key="`secondary-${issue.issueId}-${field.label}`"1570+ :key="`secondary-${issue.issueId}-${field.label}`"
1571- class="analytic-issue-row__field"1571+ class="analytic-issue-row__field"
1572- >1572+ >
1573- <span>{{ field.label }}</span>1573+ <span>{{ field.label }}</span>
1574- <strong>{{ field.value }}</strong>1574+ <strong>{{ field.value }}</strong>
1575- </div>1575+ </div>
1576- </div>1576+ </div>
1577- </div>1577+ </div>
1578- <span class="analytic-issue-row__code">{{ issue.code }}</span>1578+ <span class="analytic-issue-row__code">{{ issue.code }}</span>
1579- </div>1579+ </div>
1580- </button>1580+ </button>
1581- </div>1581+ </div>
1582- 1582+ 
1583- <div v-else class="dashboard-empty-wrap analytic-list-empty">1583+ <div v-else class="dashboard-empty-wrap analytic-list-empty">
1584- <el-empty description="当前数据集没有 issue 记录。" />1584+ <el-empty description="当前数据集没有 issue 记录。" />
1585- </div>1585+ </div>
1586- </el-collapse-item>1586+ </el-collapse-item>
1587- </el-collapse>1587+ </el-collapse>
1588- 1588+ 
1589- <el-collapse1589+ <el-collapse
1590- v-model="searchPanelSections"1590+ v-model="searchPanelSections"
1591- class="dashboard-sidebar-collapse memview-search-collapse"1591+ class="dashboard-sidebar-collapse memview-search-collapse"
1592- >1592+ >
1593- <el-collapse-item name="search" class="dashboard-subpanel memview-search-panel">1593+ <el-collapse-item name="search" class="dashboard-subpanel memview-search-panel">
1594- <template #title>1594+ <template #title>
1595- <div class="dashboard-panel-toggle">1595+ <div class="dashboard-panel-toggle">
1596- <span class="dashboard-panel-toggle__title">1596+ <span class="dashboard-panel-toggle__title">
1597- <el-icon><Search /></el-icon>1597+ <el-icon><Search /></el-icon>
1598- <span>搜索</span>1598+ <span>搜索</span>
1599- </span>1599+ </span>
1600- <span class="dashboard-panel-toggle__meta">1600+ <span class="dashboard-panel-toggle__meta">
1601- <el-tag size="small" type="info">{{ selectedSearchFieldLabel }}</el-tag>1601+ <el-tag size="small" type="info">{{ selectedSearchFieldLabel }}</el-tag>
1602- <el-tag size="small" type="success">1602+ <el-tag size="small" type="success">
1603- {{ normalizedSearchQuery ? `${searchResults.length} results` : `${selectedRankCount} ranks` }}1603+ {{ normalizedSearchQuery ? `${searchResults.length} results` : `${selectedRankCount} ranks` }}
1604- </el-tag>1604+ </el-tag>
1605- </span>1605+ </span>
1606- </div>1606+ </div>
1607- </template>1607+ </template>
1608- 1608+ 
1609- <section class="memview-sidebar-search">1609+ <section class="memview-sidebar-search">
1610- <div class="memview-sidebar-search__controls">1610+ <div class="memview-sidebar-search__controls">
1611- <el-select1611+ <el-select
1612- v-model="searchField"1612+ v-model="searchField"
1613- class="memview-sidebar-search__field-select"1613+ class="memview-sidebar-search__field-select"
1614- aria-label="Search field"1614+ aria-label="Search field"
1615- >1615+ >
1616- <el-option1616+ <el-option
1617- v-for="option in SEARCH_FIELD_OPTIONS"1617+ v-for="option in SEARCH_FIELD_OPTIONS"
1618- :key="option.value"1618+ :key="option.value"
1619- :label="option.label"1619+ :label="option.label"
1620- :value="option.value"1620+ :value="option.value"
1621- />1621+ />
1622- </el-select>1622+ </el-select>
1623- 1623+ 
1624- <el-input1624+ <el-input
1625- v-model="searchQuery"1625+ v-model="searchQuery"
1626- class="memview-sidebar-search__input"1626+ class="memview-sidebar-search__input"
1627- clearable1627+ clearable
1628- placeholder="Enter keyword"1628+ placeholder="Enter keyword"
1629- aria-label="Search keyword"1629+ aria-label="Search keyword"
1630- />1630+ />
1631- </div>1631+ </div>
1632- 1632+ 
1633- <div class="memview-sidebar-search__meta">1633+ <div class="memview-sidebar-search__meta">
1634- <span>Selected ranks: {{ selectedRankCount }}</span>1634+ <span>Selected ranks: {{ selectedRankCount }}</span>
1635- <span>Results: {{ searchResults.length }}</span>1635+ <span>Results: {{ searchResults.length }}</span>
1636- </div>1636+ </div>
1637- 1637+ 
1638- <div1638+ <div
1639- v-if="normalizedSearchQuery && searchResults.length"1639+ v-if="normalizedSearchQuery && searchResults.length"
1640- class="memview-sidebar-search__results"1640+ class="memview-sidebar-search__results"
1641- >1641+ >
1642- <button1642+ <button
1643- v-for="result in searchResults"1643+ v-for="result in searchResults"
1644- :key="result.node.id"1644+ :key="result.node.id"
1645- type="button"1645+ type="button"
1646- class="memview-sidebar-search-card"1646+ class="memview-sidebar-search-card"
1647- :class="{ 'is-active': selectedNode?.id === result.node.id }"1647+ :class="{ 'is-active': selectedNode?.id === result.node.id }"
1648- @click="handleSearchResultSelect(result)"1648+ @click="handleSearchResultSelect(result)"
1649- >1649+ >
1650- <div class="memview-sidebar-search-card__head">1650+ <div class="memview-sidebar-search-card__head">
1651- <strong>{{ result.taskId }}</strong>1651+ <strong>{{ result.taskId }}</strong>
1652- <el-tag size="small" type="info">{{ result.taskType }}</el-tag>1652+ <el-tag size="small" type="info">{{ result.taskType }}</el-tag>
1653- </div>1653+ </div>
1654- 1654+ 
1655- <div class="memview-sidebar-search-card__grid">1655+ <div class="memview-sidebar-search-card__grid">
1656- <div class="memview-sidebar-search-card__field memview-sidebar-search-card__field--wide">1656+ <div class="memview-sidebar-search-card__field memview-sidebar-search-card__field--wide">
1657- <span>rank / queue / pos</span>1657+ <span>rank / queue / pos</span>
1658- <strong>{{ result.location }}</strong>1658+ <strong>{{ result.formatLoc }}</strong>
1659- </div>1659+ </div>
1660- <div1660+ <div
1661- v-if="searchField === 'notifyId'"1661+ v-if="searchField === 'notifyId'"
1662- class="memview-sidebar-search-card__field memview-sidebar-search-card__field--wide"1662+ class="memview-sidebar-search-card__field memview-sidebar-search-card__field--wide"
1663- >1663+ >
1664- <span>notifyId</span>1664+ <span>notifyId</span>
1665- <strong>{{ result.notifyId }}</strong>1665+ <strong>{{ result.notifyId }}</strong>
1666- </div>1666+ </div>
1667- </div>1667+ </div>
1668- </button>1668+ </button>
1669- </div>1669+ </div>
1670- 1670+ 
1671- <div v-else-if="normalizedSearchQuery" class="memview-sidebar-search__empty">1671+ <div v-else-if="normalizedSearchQuery" class="memview-sidebar-search__empty">
1672- <el-empty description="No matching tasks" :image-size="72" />1672+ <el-empty description="No matching tasks" :image-size="72" />
1673- </div>1673+ </div>
1674- 1674+ 
1675- <div v-else class="memview-sidebar-search__hint">1675+ <div v-else class="memview-sidebar-search__hint">
1676- Choose taskId, taskType, or notifyId and enter a keyword to search within the selected ranks.1676+ Choose taskId, taskType, or notifyId and enter a keyword to search within the selected ranks.
1677- </div>1677+ </div>
1678- </section>1678+ </section>
1679- </el-collapse-item>1679+ </el-collapse-item>
1680- </el-collapse>1680+ </el-collapse>
1681- </template>1681+ </template>
1682- </DashboardSidebar>1682+ </DashboardSidebar>
1683- 1683+ 
1684- <button1684+ <button
1685- v-if="!sidebarCollapsed"1685+ v-if="!sidebarCollapsed"
1686 type="button"1686 type="button"
1687 class="dashboard-sidebar-toggle dashboard-sidebar-toggle--collapse"1687 class="dashboard-sidebar-toggle dashboard-sidebar-toggle--collapse"
1688 aria-label="Collapse sidebar"1688 aria-label="Collapse sidebar"
@@ -1727,7 +1727,7 @@ onBeforeUnmount(() => {
1727 :selected-rank-keys="selectedRankKeys"1727 :selected-rank-keys="selectedRankKeys"
1728 :current-step="clampedCurrentStep"1728 :current-step="clampedCurrentStep"
1729 :display-step-items="displayStepItems"1729 :display-step-items="displayStepItems"
1730- :empty-text="overviewEmptyText"1730+ :empty-text="overviewEmptyText"
1731 @update:current-step="handleOverviewStepUpdate"1731 @update:current-step="handleOverviewStepUpdate"
1732 @update:total-steps="handleTotalStepsUpdate"1732 @update:total-steps="handleTotalStepsUpdate"
1733 @buffer-select="handleBufferSelect"1733 @buffer-select="handleBufferSelect"
@@ -1743,28 +1743,28 @@ onBeforeUnmount(() => {
1743 >1743 >
1744 DAGView 任务视图1744 DAGView 任务视图
1745 </button>1745 </button>
1746- <MemViewDagPanel1746+ <MemViewDagPanel
1747- v-else1747+ v-else
1748- :dataset-name="selectedDatasetName"1748+ :dataset-name="selectedDatasetName"
1749- :graph-groups="datasetDetail?.graph ?? []"1749+ :graph-groups="datasetDetail?.graph ?? []"
1750- :stage-name="currentGraphStage"1750+ :stage-name="currentGraphStage"
1751- :selected-rank-keys="selectedRankKeys"1751+ :selected-rank-keys="selectedRankKeys"
1752- :selected-node-id="selectedNode?.id ?? ''"1752+ :selected-node-id="selectedNode?.id ?? ''"
1753- :focus-step="dagFocusStep"1753+ :focus-step="dagFocusStep"
1754- :issue-node-ids="dagIssueNodeIds"1754+ :issue-node-ids="dagIssueNodeIds"
1755- :subgraph-node="subgraphNode"1755+ :subgraph-node="subgraphNode"
1756- :current-step="clampedCurrentStep"1756+ :current-step="clampedCurrentStep"
1757- :total-steps="totalSteps"1757+ :total-steps="totalSteps"
1758- :display-step-items="displayStepItems"1758+ :display-step-items="displayStepItems"
1759- :empty-text="dagEmptyText"1759+ :empty-text="dagEmptyText"
1760 @node-select="handleNodeSelect"1760 @node-select="handleNodeSelect"
1761 @update:stage-name="handleStageUpdate"1761 @update:stage-name="handleStageUpdate"
1762 @update:current-step="handleStepUpdate"1762 @update:current-step="handleStepUpdate"
1763- @update:total-steps="handleTotalStepsUpdate"1763+ @update:total-steps="handleTotalStepsUpdate"
1764- @nodes-loaded="handleNodesLoaded"1764+ @nodes-loaded="handleNodesLoaded"
1765- @graph-node-ids-change="handleGraphNodeIdsChange"1765+ @graph-node-ids-change="handleGraphNodeIdsChange"
1766- @close-focus-view="exitDagFocus"1766+ @close-focus-view="exitDagFocus"
1767- />1767+ />
1768 </el-splitter-panel>1768 </el-splitter-panel>
1769 </el-splitter>1769 </el-splitter>
1770 </div>1770 </div>
Mtest/hccl_vm/src/plugin/insight/frontend_v3/src/pages/MemViewPage.vue+898-898
@@ -1,26 +1,26 @@
1<script setup>1<script setup>
2import { computed, nextTick, onMounted, onBeforeUnmount, ref, watch } from 'vue'2import { computed, nextTick, onMounted, onBeforeUnmount, ref, watch } from 'vue'
3-import {3+import {
4- CaretLeft,4+ CaretLeft,
5- CaretRight,5+ CaretRight,
6- DArrowLeft,6+ DArrowLeft,
7- DArrowRight,7+ DArrowRight,
8- Search,8+ Search,
9- VideoPause,9+ VideoPause,
10- VideoPlay,10+ VideoPlay,
11- WarningFilled,11+ WarningFilled,
12-} from '@element-plus/icons-vue'12+} from '@element-plus/icons-vue'
13import sidebarHandle from '../assets/sidebar-handle.svg'13import sidebarHandle from '../assets/sidebar-handle.svg'
14import sidebarHandleExpand from '../assets/sidebar-handle-expand.svg'14import sidebarHandleExpand from '../assets/sidebar-handle-expand.svg'
15import { useInsightDatasetState } from '../composables/useInsightDatasetState'15import { useInsightDatasetState } from '../composables/useInsightDatasetState'
16import { usePageNavigationState } from '../composables/usePageNavigationState'16import { usePageNavigationState } from '../composables/usePageNavigationState'
17import { useMemViewTimeline } from '../composables/useMemViewTimeline'17import { useMemViewTimeline } from '../composables/useMemViewTimeline'
18-import { taskOpsAtStep } from '../utils/memviewIndex.js'18+import { taskOpsAtStep } from '../utils/memviewIndex.js'
19-import { fetchValidationIssues } from '../utils/analyticIssues.js'19+import { fetchValidationIssues } from '../utils/analyticIssues.js'
20-import {20+import {
21- DEFAULT_MEMVIEW_STAGE,21+ DEFAULT_MEMVIEW_STAGE,
22- normalizeStreamCollection,22+ normalizeStreamCollection,
23-} from '../utils/memviewDag.js'23+} from '../utils/memviewDag.js'
24import DashboardSidebar from '../components/dashboard/DashboardSidebar.vue'24import DashboardSidebar from '../components/dashboard/DashboardSidebar.vue'
25import MemViewDagPanel from '../components/memview/MemViewDagPanel.vue'25import MemViewDagPanel from '../components/memview/MemViewDagPanel.vue'
26import MemViewInspectorPanel from '../components/memview/MemViewInspectorPanel.vue'26import MemViewInspectorPanel from '../components/memview/MemViewInspectorPanel.vue'
@@ -29,58 +29,58 @@ import MemViewOverviewPanel from '../components/memview/MemViewOverviewPanel.vue
29const CENTER_TOP_MIN_SIZE = 25029const CENTER_TOP_MIN_SIZE = 250
30const CENTER_TOP_MAX_SIZE = 62030const CENTER_TOP_MAX_SIZE = 620
31const CENTER_TOP_DEFAULT_RATIO = 0.631const CENTER_TOP_DEFAULT_RATIO = 0.6
32-const CENTER_BOTTOM_MIN_SIZE = 22032+const CENTER_BOTTOM_MIN_SIZE = 220
33-const CENTER_COLLAPSED_SIZE = 3633+const CENTER_COLLAPSED_SIZE = 36
34-const CENTER_COLLAPSE_THRESHOLD = 7234+const CENTER_COLLAPSE_THRESHOLD = 72
35-const SEARCH_FIELD_OPTIONS = [35+const SEARCH_FIELD_OPTIONS = [
36- { label: 'taskId', value: 'taskId' },36+ { label: 'taskId', value: 'taskId' },
37- { label: 'taskType', value: 'taskType' },37+ { label: 'taskType', value: 'taskType' },
38- { label: 'notifyId', value: 'notifyId' },38+ { label: 'notifyId', value: 'notifyId' },
39-]39+]
40- 40+ 
41-const sidebarCollapsed = ref(false)41+const sidebarCollapsed = ref(false)
42-const recordCollapsed = ref(false)42+const recordCollapsed = ref(false)
43-const leftPanelSize = ref(376)43+const leftPanelSize = ref(376)
44-const rightPanelSize = ref(420)44+const rightPanelSize = ref(420)
45const centerPaneState = ref('normal')45const centerPaneState = ref('normal')
46const centerAvailableHeight = ref(0)46const centerAvailableHeight = ref(0)
47const centerTopSize = ref(360)47const centerTopSize = ref(360)
48const currentGraphNodeIds = ref([])48const currentGraphNodeIds = ref([])
49-const currentGraphStage = ref(DEFAULT_MEMVIEW_STAGE)49+const currentGraphStage = ref(DEFAULT_MEMVIEW_STAGE)
50-const pendingStageNavigation = ref(null)50+const pendingStageNavigation = ref(null)
51-const pendingExternalNavigation = ref(null)51+const pendingExternalNavigation = ref(null)
52-const issueHighlightedNodeIds = ref(null)52+const issueHighlightedNodeIds = ref(null)
53-const datasetIssuePayload = ref({ issueCount: 0, issues: [] })53+const datasetIssuePayload = ref({ issueCount: 0, issues: [] })
54-const selectedIssueId = ref(null)54+const selectedIssueId = ref(null)
55-const inspectorRef = ref(null)55+const inspectorRef = ref(null)
56-const centerSplitRef = ref(null)56+const centerSplitRef = ref(null)
57-const layoutRefreshTimers = []57+const layoutRefreshTimers = []
58-const playbackPlaying = ref(false)58+const playbackPlaying = ref(false)
59-const issuePanelSections = ref(['issues'])59+const issuePanelSections = ref(['issues'])
60-const searchPanelSections = ref(['search'])60+const searchPanelSections = ref(['search'])
61-const searchField = ref('taskId')61+const searchField = ref('taskId')
62-const searchQuery = ref('')62+const searchQuery = ref('')
63-let centerResizeObserver = null63+let centerResizeObserver = null
64-let playbackTimer = 064+let playbackTimer = 0
65-let issueLoadToken = 065+let issueLoadToken = 0
66- 66+ 
67-const hasMemoryData = computed(() => (datasetDetail.value?.memory?.length ?? 0) > 0)67+const hasMemoryData = computed(() => (datasetDetail.value?.memory?.length ?? 0) > 0)
68- 68+ 
69-const overviewEmptyText = computed(() => {69+const overviewEmptyText = computed(() => {
70- if (loadingDetail.value) return 'Loading data...'70+ if (loadingDetail.value) return 'Loading data...'
71- if (selectedDataset.value && !hasMemoryData.value) {71+ if (selectedDataset.value && !hasMemoryData.value) {
72- return '当前数据集只有 DAG 数据,内存时间线暂不可用。'72+ return '当前数据集只有 DAG 数据,内存时间线暂不可用。'
73- }73+ }
74- return EMPTY_TEXT74+ return EMPTY_TEXT
75-})75+})
76- 76+ 
77-const dagEmptyText = computed(() => {77+const dagEmptyText = computed(() => {
78- if (loadingDetail.value) return 'Loading data...'78+ if (loadingDetail.value) return 'Loading data...'
79- if (selectedDataset.value && !datasetDetail.value?.graph?.length) {79+ if (selectedDataset.value && !datasetDetail.value?.graph?.length) {
80- return '当前数据集没有 DAG 数据。'80+ return '当前数据集没有 DAG 数据。'
81- }81+ }
82- return EMPTY_TEXT82+ return EMPTY_TEXT
83-})83+})
84 84 
85const {85const {
86 EMPTY_TEXT,86 EMPTY_TEXT,
@@ -88,18 +88,18 @@ const {
88 selectedDataset,88 selectedDataset,
89 selectedDatasetName,89 selectedDatasetName,
90 selectedRankKeys,90 selectedRankKeys,
91- loadingList,91+ loadingList,
92- loadingDetail,92+ loadingDetail,
93- hasData,93+ hasData,
94- memorySnapshotCount,94+ memorySnapshotCount,
95- selectedRankCount,95+ selectedRankCount,
96- treeData,96+ treeData,
97- ensureDatasetsLoaded,97+ ensureDatasetsLoaded,
98- selectDataset,98+ selectDataset,
99- setSelectedRankKeys,99+ setSelectedRankKeys,
100-} = useInsightDatasetState()100+} = useInsightDatasetState()
101 101 
102-const { pendingMemViewNavigation, clearMemViewNavigation } = usePageNavigationState()102+const { pendingMemViewNavigation, clearMemViewNavigation } = usePageNavigationState()
103 103 
104const {104const {
105 clampedCurrentStep,105 clampedCurrentStep,
@@ -124,14 +124,14 @@ const {
124 124 
125const renderedLeftPanelSize = computed(() => (sidebarCollapsed.value ? 28 : leftPanelSize.value))125const renderedLeftPanelSize = computed(() => (sidebarCollapsed.value ? 28 : leftPanelSize.value))
126const renderedRightPanelSize = computed(() => (recordCollapsed.value ? 28 : rightPanelSize.value))126const renderedRightPanelSize = computed(() => (recordCollapsed.value ? 28 : rightPanelSize.value))
127-const topPaneCollapsed = computed(() => centerPaneState.value === 'top-collapsed')127+const topPaneCollapsed = computed(() => centerPaneState.value === 'top-collapsed')
128-const bottomPaneCollapsed = computed(() => centerPaneState.value === 'bottom-collapsed')128+const bottomPaneCollapsed = computed(() => centerPaneState.value === 'bottom-collapsed')
129-const issues = computed(() => datasetIssuePayload.value?.issues ?? [])129+const issues = computed(() => datasetIssuePayload.value?.issues ?? [])
130-const issueCount = computed(() => datasetIssuePayload.value?.issueCount ?? issues.value.length)130+const issueCount = computed(() => datasetIssuePayload.value?.issueCount ?? issues.value.length)
131-const selectedIssue = computed(131+const selectedIssue = computed(
132- () => issues.value.find((issue) => issue.issueId === selectedIssueId.value) ?? issues.value[0] ?? null,132+ () => issues.value.find((issue) => issue.issueId === selectedIssueId.value) ?? issues.value[0] ?? null,
133-)133+)
134-const renderedCenterTopSize = computed(() => {134+const renderedCenterTopSize = computed(() => {
135 if (topPaneCollapsed.value) return CENTER_COLLAPSED_SIZE135 if (topPaneCollapsed.value) return CENTER_COLLAPSED_SIZE
136 if (bottomPaneCollapsed.value) {136 if (bottomPaneCollapsed.value) {
137 return Math.max(CENTER_COLLAPSED_SIZE, resolveCenterAvailableHeight() - CENTER_COLLAPSED_SIZE)137 return Math.max(CENTER_COLLAPSED_SIZE, resolveCenterAvailableHeight() - CENTER_COLLAPSED_SIZE)
@@ -247,9 +247,9 @@ watch(
247 },247 },
248)248)
249 249 
250-watch(250+watch(
251- availableGraphStages,251+ availableGraphStages,
252- (stages) => {252+ (stages) => {
253 if (!stages.length) {253 if (!stages.length) {
254 currentGraphStage.value = ''254 currentGraphStage.value = ''
255 return255 return
@@ -258,45 +258,45 @@ watch(
258 if (stages.includes(currentGraphStage.value)) return258 if (stages.includes(currentGraphStage.value)) return
259 currentGraphStage.value = stages.includes(DEFAULT_MEMVIEW_STAGE) ? DEFAULT_MEMVIEW_STAGE : stages[0]259 currentGraphStage.value = stages.includes(DEFAULT_MEMVIEW_STAGE) ? DEFAULT_MEMVIEW_STAGE : stages[0]
260 },260 },
261- { immediate: true },261+ { immediate: true },
262-)262+)
263- 263+ 
264-watch(264+watch(
265- selectedDatasetName,265+ selectedDatasetName,
266- async (datasetName) => {266+ async (datasetName) => {
267- const token = ++issueLoadToken267+ const token = ++issueLoadToken
268- if (!datasetName) {268+ if (!datasetName) {
269- datasetIssuePayload.value = { issueCount: 0, issues: [] }269+ datasetIssuePayload.value = { issueCount: 0, issues: [] }
270- return270+ return
271- }271+ }
272- 272+ 
273- const payload = await fetchValidationIssues(datasetName)273+ const payload = await fetchValidationIssues(datasetName)
274- if (token === issueLoadToken) {274+ if (token === issueLoadToken) {
275- datasetIssuePayload.value = payload275+ datasetIssuePayload.value = payload
276- }276+ }
277- },277+ },
278- { immediate: true },278+ { immediate: true },
279-)279+)
280- 280+ 
281-watch(281+watch(
282- issues,282+ issues,
283- (nextIssues) => {283+ (nextIssues) => {
284- if (!nextIssues.length) {284+ if (!nextIssues.length) {
285- selectedIssueId.value = null285+ selectedIssueId.value = null
286- return286+ return
287- }287+ }
288- 288+ 
289- const currentIssueExists = nextIssues.some((issue) => issue.issueId === selectedIssueId.value)289+ const currentIssueExists = nextIssues.some((issue) => issue.issueId === selectedIssueId.value)
290- if (!currentIssueExists) {290+ if (!currentIssueExists) {
291- selectedIssueId.value = nextIssues[0].issueId291+ selectedIssueId.value = nextIssues[0].issueId
292- }292+ }
293- },293+ },
294- { immediate: true },294+ { immediate: true },
295-)295+)
296- 296+ 
297-watch(297+watch(
298- () => pendingMemViewNavigation.value?.token ?? 0,298+ () => pendingMemViewNavigation.value?.token ?? 0,
299- async () => {299+ async () => {
300 const request = pendingMemViewNavigation.value300 const request = pendingMemViewNavigation.value
301 if (!request) return301 if (!request) return
302 302 
@@ -334,314 +334,314 @@ watch(
334 { immediate: true },334 { immediate: true },
335)335)
336 336 
337-const selectedNodeStep = computed(() => {337+const selectedNodeStep = computed(() => {
338- const node = selectedNode.value338+ const node = selectedNode.value
339- return resolveStepForNode(node)339+ return resolveStepForNode(node)
340-})340+})
341- 
342-const selectedRankIdSet = computed(() => {
343- const ids = new Set()
344- for (const key of selectedRankKeys.value) {
345- const match = String(key).match(/^rank-(\d+)$/)
346- if (match) {
347- ids.add(Number(match[1]))
348- }
349- }
350- return ids
351-})
352- 
353-const selectedNodeTaskOps = computed(() => {
354- const node = selectedNode.value
355- const step = selectedNodeStep.value
356- if (!node?.id || !indexes.value || !Number.isInteger(step)) return []
357- return taskOpsAtStep(indexes.value, step).filter((op) => taskOpMatchesSelectedNode(node, op))
358-})
359- 
360-const selectedSearchFieldLabel = computed(
361- () => SEARCH_FIELD_OPTIONS.find((option) => option.value === searchField.value)?.label ?? searchField.value,
362-)
363- 
364-const selectedRankSearchNodes = computed(() => {
365- if (!selectedRankIdSet.value.size) return []
366- 
367- const registry = nodeRegistry.value
368- if (!(registry instanceof Map) || !registry.size) return []
369- 
370- const seen = new Map()
371- for (const node of registry.values()) {
372- if (!node?.id || seen.has(node.id)) continue
373- if (Number.isInteger(node.rankId) && !selectedRankIdSet.value.has(node.rankId)) {
374- continue
375- }
376- if (!Number.isInteger(node.rankId)) {
377- continue
378- }
379- seen.set(node.id, node)
380- }
381- 
382- return [...seen.values()].sort((left, right) => {
383- const leftRank = Number.isInteger(left.rankId) ? left.rankId : Number.POSITIVE_INFINITY
384- const rightRank = Number.isInteger(right.rankId) ? right.rankId : Number.POSITIVE_INFINITY
385- if (leftRank !== rightRank) return leftRank - rightRank
386- 
387- const leftQueue = Number.isInteger(left.queueId) ? left.queueId : Number.POSITIVE_INFINITY
388- const rightQueue = Number.isInteger(right.queueId) ? right.queueId : Number.POSITIVE_INFINITY
389- if (leftQueue !== rightQueue) return leftQueue - rightQueue
390- 
391- const leftPos = Number.isInteger(left.pos) ? left.pos : Number.isInteger(left.slotIndex) ? left.slotIndex : Number.POSITIVE_INFINITY
392- const rightPos = Number.isInteger(right.pos) ? right.pos : Number.isInteger(right.slotIndex) ? right.slotIndex : Number.POSITIVE_INFINITY
393- if (leftPos !== rightPos) return leftPos - rightPos
394- 
395- return String(left.id).localeCompare(String(right.id))
396- })
397-})
398- 
399-const normalizedSearchQuery = computed(() => searchQuery.value.trim())
400- 
401-function collectSearchNodesFromRegistry(registry) {
402- if (!(registry instanceof Map) || !registry.size) return []
403- 
404- const roots = [...registry.values()].filter((node) => node?.id && !node.isPlaceholder && Number.isInteger(node.rankId))
405- 
406- const collected = []
407- const seenIds = new Set()
408- 
409- const pushNode = (node, context = {}) => {
410- if (!node?.id || seenIds.has(node.id)) return
411- if (!Number.isInteger(node.rankId)) return
412- 
413- seenIds.add(node.id)
414- collected.push({
415- ...node,
416- searchParentId: context.parentId ?? '',
417- searchDepth: Number.isInteger(context.depth) ? context.depth : 0,
418- searchPath: Array.isArray(context.path) ? context.path : [],
419- searchRootId: context.rootId ?? node.id,
420- })
421- }
422- 
423- const walkSubgraph = (entries, context) => {
424- if (!Array.isArray(entries)) return
425- for (const entry of entries) {
426- if (!entry || typeof entry !== 'object') continue
427- const nestedNodes = normalizeStreamCollection([entry], context.rankId, {
428- stageName: currentGraphStage.value || DEFAULT_MEMVIEW_STAGE,
429- topLevel: false,
430- }).flatMap((stream) => stream.nodes)
431- 
432- for (const nestedNode of nestedNodes) {
433- const nestedContext = {
434- rootId: context.rootId,
435- parentId: context.parentId,
436- depth: context.depth + 1,
437- path: [...context.path, entry.node_id ?? entry.id ?? entry.task_id ?? nestedNode.id],
438- }
439- pushNode(nestedNode, nestedContext)
440- const nestedSubGraph = nestedNode?.rawNode?.task?.task_data?.sub_graph
441- if (Array.isArray(nestedSubGraph) && nestedSubGraph.length) {
442- walkSubgraph(nestedSubGraph.flat(), {
443- rankId: nestedNode.rankId,
444- rootId: context.rootId,
445- parentId: nestedNode.id,
446- depth: nestedContext.depth,
447- path: nestedContext.path,
448- })
449- }
450- }
451- }
452- }
453- 
454- for (const rootNode of roots) {
455- pushNode(rootNode, {
456- rootId: rootNode.id,
457- parentId: '',
458- depth: 0,
459- path: [rootNode.id],
460- })
461- 
462- const subGraph = rootNode?.rawNode?.task?.task_data?.sub_graph
463- if (Array.isArray(subGraph) && subGraph.length) {
464- walkSubgraph(subGraph.flat(), {
465- rankId: rootNode.rankId,
466- rootId: rootNode.id,
467- parentId: rootNode.id,
468- depth: 0,
469- path: [rootNode.id],
470- })
471- }
472- }
473- 
474- return collected
475-}
476- 
477-function isExactSearchMatch(node, field, query) {
478- if (!node || !query) return false
479- const normalizedQuery = String(query).trim().toLowerCase()
480- if (!normalizedQuery) return false
481- 
482- if (field === 'taskType') {
483- const value = String(node.taskType ?? node.rawNode?.task?.task_type ?? node.rawNode?.task_type ?? '').trim().toLowerCase()
484- return value === normalizedQuery
485- }
486- 
487- if (field === 'notifyId') {
488- const value = normalizeSearchFieldValue(
489- node.notifyId ??
490- node.notify_id ??
491- node.taskData?.notifyId ??
492- node.taskData?.notify_id ??
493- node.rawNode?.notifyId ??
494- node.rawNode?.notify_id ??
495- node.rawNode?.task?.notifyId ??
496- node.rawNode?.task?.notify_id,
497- )
498- return value === normalizedQuery
499- }
500- 
501- const values = resolveSearchNodeIds(node)
502- return values.some((value) => String(value).trim().toLowerCase() === normalizedQuery)
503-}
504- 
505-function resolveSearchNodeIds(node) {
506- const ids = new Set()
507- const push = (value) => {
508- if (typeof value === 'string' && value.trim()) {
509- ids.add(value.trim())
510- }
511- }
512- 
513- push(node?.id)
514- push(node?.rawNode?.node_id)
515- push(node?.rawNode?.id)
516- push(node?.rawNode?.task_id)
517- return [...ids]
518-}
519- 
520-function sortSearchNodes(nodes) {
521- return [...nodes].sort((left, right) => {
522- const leftRank = Number.isInteger(left.rankId) ? left.rankId : Number.POSITIVE_INFINITY
523- const rightRank = Number.isInteger(right.rankId) ? right.rankId : Number.POSITIVE_INFINITY
524- if (leftRank !== rightRank) return leftRank - rightRank
525- 
526- const leftQueue = Number.isInteger(left.queueId) ? left.queueId : Number.POSITIVE_INFINITY
527- const rightQueue = Number.isInteger(right.queueId) ? right.queueId : Number.POSITIVE_INFINITY
528- if (leftQueue !== rightQueue) return leftQueue - rightQueue
529- 
530- const leftPos = Number.isInteger(left.pos) ? left.pos : Number.isInteger(left.slotIndex) ? left.slotIndex : Number.POSITIVE_INFINITY
531- const rightPos = Number.isInteger(right.pos) ? right.pos : Number.isInteger(right.slotIndex) ? right.slotIndex : Number.POSITIVE_INFINITY
532- if (leftPos !== rightPos) return leftPos - rightPos
533- 
534- return String(left.id).localeCompare(String(right.id))
535- })
536-}
537- 
538-const searchResults = computed(() => {
539- const query = normalizedSearchQuery.value
540- if (!query) return []
541- 
542- const field = searchField.value
543- const allSearchNodes = collectSearchNodesFromRegistry(nodeRegistry.value)
544- const matched = allSearchNodes.filter((node) => isExactSearchMatch(node, field, query))
545- 
546- return sortSearchNodes(matched)
547- .map((node) => ({
548- node,
549- taskId: node.id,
550- taskType: String(node.taskType ?? '--').replaceAll('_', ' '),
551- rank: Number.isInteger(node.rankId) ? node.rankId : '--',
552- queue: Number.isInteger(node.queueId) ? node.queueId : '--',
553- pos: Number.isInteger(node.pos) ? node.pos : Number.isInteger(node.slotIndex) ? node.slotIndex : '--',
554- location: formatSearchLocation(node),
555- notifyId: resolveSearchFieldDisplayValue(node, 'notifyId'),
556- }))
557-})
558- 
559-const canResolveRelation = computed(() => (nodeId) => Boolean(resolveNodeNavigationTarget(nodeId)))
560-const resolveRelationMeta = computed(() => (nodeId) => {
561- if (typeof nodeId !== 'string' || !nodeId) return null
562- 
563- const resolvedTarget = resolveNodeNavigationTarget(nodeId)
564- const node = resolvedTarget?.node ?? nodeRegistry.value?.get(nodeId) ?? null
565- if (!node) return null
566- 
567- return {
568- taskType: typeof node.taskType === 'string' ? node.taskType.replaceAll('_', ' ') : '',
569- }
570-})
571-const datasetIssueNodeIds = computed(() => {
572- const issues = datasetIssuePayload.value?.issues ?? []
573- const registry = nodeRegistry.value
574- if (!(registry instanceof Map) || !registry.size || !issues.length) return null
575- 
576- const ids = new Set()
577- const nodes = [...new Set([...registry.values()].filter(Boolean))]
578- for (const issue of issues) {
579- for (const node of nodes) {
580- if (issueMatchesNode(issue, node)) {
581- ids.add(node.id)
582- }
583- }
584- }
585- 
586- return ids.size ? ids : null
587-})
588- 
589-const dagIssueNodeIds = computed(() => {
590- const ids = new Set()
591- datasetIssueNodeIds.value?.forEach((nodeId) => ids.add(nodeId))
592- issueHighlightedNodeIds.value?.forEach?.((nodeId) => ids.add(nodeId))
593- return ids.size ? ids : null
594-})
595 341 
596-function handleTreeCheck(checkedKeys) {342+const selectedRankIdSet = computed(() => {
597- setSelectedRankKeys(checkedKeys)343+ const ids = new Set()
598-}344+ for (const key of selectedRankKeys.value) {
599- 345+ const match = String(key).match(/^rank-(\d+)$/)
600-function issueMatchesNode(issue, node) {346+ if (match) {
601- if (!issue || !node?.id) return false347+ ids.add(Number(match[1]))
602- 348+ }
603- const targetIds = issueTargetNodeIds(issue)349+ }
604- const lookupIds = new Set(resolveNodeLookupIds(node))350+ return ids
605- if (targetIds.some((nodeId) => lookupIds.has(nodeId) || node.mappingKey === nodeId)) {351+})
606- return true352+ 
607- }353+const selectedNodeTaskOps = computed(() => {
608- 354+ const node = selectedNode.value
609- const target = issue.dagTarget ?? {}355+ const step = selectedNodeStep.value
610- const detail = issue.rawDetail ?? {}356+ if (!node?.id || !indexes.value || !Number.isInteger(step)) return []
611- const rankId = firstIssueInteger(target.rankId, issue.primaryRankId, detail.rank_id, detail.task_rank, detail.peer_rank)357+ return taskOpsAtStep(indexes.value, step).filter((op) => taskOpMatchesSelectedNode(node, op))
612- const queueId = firstIssueInteger(target.queueId, issue.queueId, detail.queue_id)358+})
613- const slotIndex = firstIssueInteger(target.slotIndex, detail.task_pos, detail.pos)359+ 
614- if (!Number.isInteger(rankId) || !Number.isInteger(queueId) || !Number.isInteger(slotIndex)) return false360+const selectedSearchFieldLabel = computed(
615- 361+ () => SEARCH_FIELD_OPTIONS.find((option) => option.value === searchField.value)?.label ?? searchField.value,
616- const nodeSlot = Number.isInteger(node.pos) ? node.pos : node.slotIndex362+)
617- const nodeQueue = Number.isInteger(node.queueId) ? node.queueId : node.streamId363+ 
618- return node.rankId === rankId && nodeQueue === queueId && nodeSlot === slotIndex364+const selectedRankSearchNodes = computed(() => {
619-}365+ if (!selectedRankIdSet.value.size) return []
620- 366+ 
621-function issueTargetNodeIds(issue) {367+ const registry = nodeRegistry.value
622- const ids = []368+ if (!(registry instanceof Map) || !registry.size) return []
623- const push = (value) => {369+ 
624- if (typeof value === 'string' && value.trim()) ids.push(value.trim())370+ const seen = new Map()
625- }371+ for (const node of registry.values()) {
626- 372+ if (!node?.id || seen.has(node.id)) continue
627- push(issue?.dagTarget?.nodeId)373+ if (Number.isInteger(node.rankId) && !selectedRankIdSet.value.has(node.rankId)) {
628- push(issue?.taskNode?.nodeId)374+ continue
629- push(issue?.rawDetail?.task_id)375+ }
630- issue?.relatedNodeAnchors?.forEach((anchor) => push(anchor?.nodeId))376+ if (!Number.isInteger(node.rankId)) {
631- 377+ continue
632- return [...new Set(ids)]378+ }
633-}379+ seen.set(node.id, node)
634- 380+ }
635-function firstIssueInteger(...values) {381+ 
636- for (const value of values) {382+ return [...seen.values()].sort((left, right) => {
637- if (Number.isInteger(value)) return value383+ const leftRank = Number.isInteger(left.rankId) ? left.rankId : Number.POSITIVE_INFINITY
638- if (typeof value === 'string' && value.trim()) {384+ const rightRank = Number.isInteger(right.rankId) ? right.rankId : Number.POSITIVE_INFINITY
639- const numeric = Number(value)385+ if (leftRank !== rightRank) return leftRank - rightRank
640- if (Number.isInteger(numeric)) return numeric386+ 
641- }387+ const leftQueue = Number.isInteger(left.queueId) ? left.queueId : Number.POSITIVE_INFINITY
642- }388+ const rightQueue = Number.isInteger(right.queueId) ? right.queueId : Number.POSITIVE_INFINITY
643- return null389+ if (leftQueue !== rightQueue) return leftQueue - rightQueue
644-}390+ 
391+ const leftPos = Number.isInteger(left.pos) ? left.pos : Number.isInteger(left.slotIndex) ? left.slotIndex : Number.POSITIVE_INFINITY
392+ const rightPos = Number.isInteger(right.pos) ? right.pos : Number.isInteger(right.slotIndex) ? right.slotIndex : Number.POSITIVE_INFINITY
393+ if (leftPos !== rightPos) return leftPos - rightPos
394+ 
395+ return String(left.id).localeCompare(String(right.id))
396+ })
397+})
398+ 
399+const normalizedSearchQuery = computed(() => searchQuery.value.trim())
400+ 
401+function collectSearchNodesFromRegistry(registry) {
402+ if (!(registry instanceof Map) || !registry.size) return []
403+ 
404+ const roots = [...registry.values()].filter((node) => node?.id && !node.isPlaceholder && Number.isInteger(node.rankId))
405+ 
406+ const collected = []
407+ const seenIds = new Set()
408+ 
409+ const pushNode = (node, context = {}) => {
410+ if (!node?.id || seenIds.has(node.id)) return
411+ if (!Number.isInteger(node.rankId)) return
412+ 
413+ seenIds.add(node.id)
414+ collected.push({
415+ ...node,
416+ searchParentId: context.parentId ?? '',
417+ searchDepth: Number.isInteger(context.depth) ? context.depth : 0,
418+ searchPath: Array.isArray(context.path) ? context.path : [],
419+ searchRootId: context.rootId ?? node.id,
420+ })
421+ }
422+ 
423+ const walkSubgraph = (entries, context) => {
424+ if (!Array.isArray(entries)) return
425+ for (const entry of entries) {
426+ if (!entry || typeof entry !== 'object') continue
427+ const nestedNodes = normalizeStreamCollection([entry], context.rankId, {
428+ stageName: currentGraphStage.value || DEFAULT_MEMVIEW_STAGE,
429+ topLevel: false,
430+ }).flatMap((stream) => stream.nodes)
431+ 
432+ for (const nestedNode of nestedNodes) {
433+ const nestedContext = {
434+ rootId: context.rootId,
435+ parentId: context.parentId,
436+ depth: context.depth + 1,
437+ path: [...context.path, entry.node_id ?? entry.id ?? entry.task_id ?? nestedNode.id],
438+ }
439+ pushNode(nestedNode, nestedContext)
440+ const nestedSubGraph = nestedNode?.rawNode?.task?.task_data?.sub_graph
441+ if (Array.isArray(nestedSubGraph) && nestedSubGraph.length) {
442+ walkSubgraph(nestedSubGraph.flat(), {
443+ rankId: nestedNode.rankId,
444+ rootId: context.rootId,
445+ parentId: nestedNode.id,
446+ depth: nestedContext.depth,
447+ path: nestedContext.path,
448+ })
449+ }
450+ }
451+ }
452+ }
453+ 
454+ for (const rootNode of roots) {
455+ pushNode(rootNode, {
456+ rootId: rootNode.id,
457+ parentId: '',
458+ depth: 0,
459+ path: [rootNode.id],
460+ })
461+ 
462+ const subGraph = rootNode?.rawNode?.task?.task_data?.sub_graph
463+ if (Array.isArray(subGraph) && subGraph.length) {
464+ walkSubgraph(subGraph.flat(), {
465+ rankId: rootNode.rankId,
466+ rootId: rootNode.id,
467+ parentId: rootNode.id,
468+ depth: 0,
469+ path: [rootNode.id],
470+ })
471+ }
472+ }
473+ 
474+ return collected
475+}
476+ 
477+function isExactSearchMatch(node, field, query) {
478+ if (!node || !query) return false
479+ const normalizedQuery = String(query).trim().toLowerCase()
480+ if (!normalizedQuery) return false
481+ 
482+ if (field === 'taskType') {
483+ const value = String(node.taskType ?? node.rawNode?.task?.task_type ?? node.rawNode?.task_type ?? '').trim().toLowerCase()
484+ return value === normalizedQuery
485+ }
486+ 
487+ if (field === 'notifyId') {
488+ const value = normalizeSearchFieldValue(
489+ node.notifyId ??
490+ node.notify_id ??
491+ node.taskData?.notifyId ??
492+ node.taskData?.notify_id ??
493+ node.rawNode?.notifyId ??
494+ node.rawNode?.notify_id ??
495+ node.rawNode?.task?.notifyId ??
496+ node.rawNode?.task?.notify_id,
497+ )
498+ return value === normalizedQuery
499+ }
500+ 
501+ const values = resolveSearchNodeIds(node)
502+ return values.some((value) => String(value).trim().toLowerCase() === normalizedQuery)
503+}
504+ 
505+function resolveSearchNodeIds(node) {
506+ const ids = new Set()
507+ const push = (value) => {
508+ if (typeof value === 'string' && value.trim()) {
509+ ids.add(value.trim())
510+ }
511+ }
512+ 
513+ push(node?.id)
514+ push(node?.rawNode?.node_id)
515+ push(node?.rawNode?.id)
516+ push(node?.rawNode?.task_id)
517+ return [...ids]
518+}
519+ 
520+function sortSearchNodes(nodes) {
521+ return [...nodes].sort((left, right) => {
522+ const leftRank = Number.isInteger(left.rankId) ? left.rankId : Number.POSITIVE_INFINITY
523+ const rightRank = Number.isInteger(right.rankId) ? right.rankId : Number.POSITIVE_INFINITY
524+ if (leftRank !== rightRank) return leftRank - rightRank
525+ 
526+ const leftQueue = Number.isInteger(left.queueId) ? left.queueId : Number.POSITIVE_INFINITY
527+ const rightQueue = Number.isInteger(right.queueId) ? right.queueId : Number.POSITIVE_INFINITY
528+ if (leftQueue !== rightQueue) return leftQueue - rightQueue
529+ 
530+ const leftPos = Number.isInteger(left.pos) ? left.pos : Number.isInteger(left.slotIndex) ? left.slotIndex : Number.POSITIVE_INFINITY
531+ const rightPos = Number.isInteger(right.pos) ? right.pos : Number.isInteger(right.slotIndex) ? right.slotIndex : Number.POSITIVE_INFINITY
532+ if (leftPos !== rightPos) return leftPos - rightPos
533+ 
534+ return String(left.id).localeCompare(String(right.id))
535+ })
536+}
537+ 
538+const searchResults = computed(() => {
539+ const query = normalizedSearchQuery.value
540+ if (!query) return []
541+ 
542+ const field = searchField.value
543+ const allSearchNodes = collectSearchNodesFromRegistry(nodeRegistry.value)
544+ const matched = allSearchNodes.filter((node) => isExactSearchMatch(node, field, query))
545+ 
546+ return sortSearchNodes(matched)
547+ .map((node) => ({
548+ node,
549+ taskId: node.id,
550+ taskType: String(node.taskType ?? '--').replaceAll('_', ' '),
551+ rank: Number.isInteger(node.rankId) ? node.rankId : '--',
552+ queue: Number.isInteger(node.queueId) ? node.queueId : '--',
553+ pos: Number.isInteger(node.pos) ? node.pos : Number.isInteger(node.slotIndex) ? node.slotIndex : '--',
554+ formatLoc: formatSearchLocation(node),
555+ notifyId: resolveSearchFieldDisplayValue(node, 'notifyId'),
556+ }))
557+})
558+ 
559+const canResolveRelation = computed(() => (nodeId) => Boolean(resolveNodeNavigationTarget(nodeId)))
560+const resolveRelationMeta = computed(() => (nodeId) => {
561+ if (typeof nodeId !== 'string' || !nodeId) return null
562+ 
563+ const resolvedTarget = resolveNodeNavigationTarget(nodeId)
564+ const node = resolvedTarget?.node ?? nodeRegistry.value?.get(nodeId) ?? null
565+ if (!node) return null
566+ 
567+ return {
568+ taskType: typeof node.taskType === 'string' ? node.taskType.replaceAll('_', ' ') : '',
569+ }
570+})
571+const datasetIssueNodeIds = computed(() => {
572+ const issues = datasetIssuePayload.value?.issues ?? []
573+ const registry = nodeRegistry.value
574+ if (!(registry instanceof Map) || !registry.size || !issues.length) return null
575+ 
576+ const ids = new Set()
577+ const nodes = [...new Set([...registry.values()].filter(Boolean))]
578+ for (const issue of issues) {
579+ for (const node of nodes) {
580+ if (issueMatchesNode(issue, node)) {
581+ ids.add(node.id)
582+ }
583+ }
584+ }
585+ 
586+ return ids.size ? ids : null
587+})
588+ 
589+const dagIssueNodeIds = computed(() => {
590+ const ids = new Set()
591+ datasetIssueNodeIds.value?.forEach((nodeId) => ids.add(nodeId))
592+ issueHighlightedNodeIds.value?.forEach?.((nodeId) => ids.add(nodeId))
593+ return ids.size ? ids : null
594+})
595+ 
596+function handleTreeCheck(checkedKeys) {
597+ setSelectedRankKeys(checkedKeys)
598+}
599+ 
600+function issueMatchesNode(issue, node) {
601+ if (!issue || !node?.id) return false
602+ 
603+ const targetIds = issueTargetNodeIds(issue)
604+ const lookupIds = new Set(resolveNodeLookupIds(node))
605+ if (targetIds.some((nodeId) => lookupIds.has(nodeId) || node.mappingKey === nodeId)) {
606+ return true
607+ }
608+ 
609+ const target = issue.dagTarget ?? {}
610+ const detail = issue.rawDetail ?? {}
611+ const rankId = firstIssueInteger(target.rankId, issue.primaryRankId, detail.rank_id, detail.task_rank, detail.peer_rank)
612+ const queueId = firstIssueInteger(target.queueId, issue.queueId, detail.queue_id)
613+ const slotIndex = firstIssueInteger(target.slotIndex, detail.task_pos, detail.pos)
614+ if (!Number.isInteger(rankId) || !Number.isInteger(queueId) || !Number.isInteger(slotIndex)) return false
615+ 
616+ const nodeSlot = Number.isInteger(node.pos) ? node.pos : node.slotIndex
617+ const nodeQueue = Number.isInteger(node.queueId) ? node.queueId : node.streamId
618+ return node.rankId === rankId && nodeQueue === queueId && nodeSlot === slotIndex
619+}
620+ 
621+function issueTargetNodeIds(issue) {
622+ const ids = []
623+ const push = (value) => {
624+ if (typeof value === 'string' && value.trim()) ids.push(value.trim())
625+ }
626+ 
627+ push(issue?.dagTarget?.nodeId)
628+ push(issue?.taskNode?.nodeId)
629+ push(issue?.rawDetail?.task_id)
630+ issue?.relatedNodeAnchors?.forEach((anchor) => push(anchor?.nodeId))
631+ 
632+ return [...new Set(ids)]
633+}
634+ 
635+function firstIssueInteger(...values) {
636+ for (const value of values) {
637+ if (Number.isInteger(value)) return value
638+ if (typeof value === 'string' && value.trim()) {
639+ const numeric = Number(value)
640+ if (Number.isInteger(numeric)) return numeric
641+ }
642+ }
643+ return null
644+}
645 645 
646function handleLeftPanelResize(size) {646function handleLeftPanelResize(size) {
647 if (!sidebarCollapsed.value && typeof size === 'number') {647 if (!sidebarCollapsed.value && typeof size === 'number') {
@@ -692,206 +692,206 @@ function expandCenterPane(target) {
692 notifyDagLayoutChange()692 notifyDagLayoutChange()
693}693}
694 694 
695-function handleNodeSelect(node) {695+function handleNodeSelect(node) {
696- focusNode(node, { follow: true, sync: true })696+ focusNode(node, { follow: true, sync: true })
697-}697+}
698- 698+ 
699-async function handleIssueSelect(issue) {699+async function handleIssueSelect(issue) {
700- if (!issue) return700+ if (!issue) return
701- 701+ 
702- selectedIssueId.value = issue.issueId702+ selectedIssueId.value = issue.issueId
703- 703+ 
704- const target = resolveIssueNodeTarget(issue)704+ const target = resolveIssueNodeTarget(issue)
705- const step = resolveIssueNavigationStep(issue)705+ const step = resolveIssueNavigationStep(issue)
706- const rankIds = resolveNavigationRankIds(issue)706+ const rankIds = resolveNavigationRankIds(issue)
707- const stageName = issue?.dagTarget?.stageName ?? issue?.memTarget?.stageName ?? ''707+ const stageName = issue?.dagTarget?.stageName ?? issue?.memTarget?.stageName ?? ''
708- const hasNodeTarget = Boolean(target?.nodeId)708+ const hasNodeTarget = Boolean(target?.nodeId)
709- const hasStepTarget = Number.isInteger(step)709+ const hasStepTarget = Number.isInteger(step)
710- 710+ 
711- if (!hasNodeTarget && !hasStepTarget && !rankIds.length) {711+ if (!hasNodeTarget && !hasStepTarget && !rankIds.length) {
712- return712+ return
713- }713+ }
714- 714+ 
715- if (rankIds.length) {715+ if (rankIds.length) {
716- setSelectedRankKeys(rankIds.map((rankId) => `rank-${rankId}`))716+ setSelectedRankKeys(rankIds.map((rankId) => `rank-${rankId}`))
717- }717+ }
718- 718+ 
719- if (stageName && stageName !== currentGraphStage.value) {719+ if (stageName && stageName !== currentGraphStage.value) {
720- handleStageUpdate(stageName)720+ handleStageUpdate(stageName)
721- }721+ }
722- 722+ 
723- if (hasNodeTarget) {723+ if (hasNodeTarget) {
724- pendingExternalNavigation.value = {724+ pendingExternalNavigation.value = {
725- source: 'analytic',725+ source: 'analytic',
726- targetView: 'dag',726+ targetView: 'dag',
727- stageName: stageName || currentGraphStage.value,727+ stageName: stageName || currentGraphStage.value,
728- lookupId: target.nodeId,728+ lookupId: target.nodeId,
729- nodeId: target.nodeId,729+ nodeId: target.nodeId,
730- rankId: target.rankId,730+ rankId: target.rankId,
731- queueId: target.queueId,731+ queueId: target.queueId,
732- slotIndex: target.slotIndex,732+ slotIndex: target.slotIndex,
733- taskType: target.taskType,733+ taskType: target.taskType,
734- }734+ }
735- 735+ 
736- await nextTick()736+ await nextTick()
737- resolvePendingExternalNavigation()737+ resolvePendingExternalNavigation()
738- return738+ return
739- }739+ }
740- 740+ 
741- if (hasStepTarget) {741+ if (hasStepTarget) {
742- focusStep(step, { smoothScroll: false })742+ focusStep(step, { smoothScroll: false })
743- }743+ }
744-}744+}
745- 745+ 
746-function handleSearchResultSelect(result) {746+function handleSearchResultSelect(result) {
747- if (!result?.node) return747+ if (!result?.node) return
748- handleNavigate({748+ handleNavigate({
749- stageName: result.node.stageName || currentGraphStage.value || '',749+ stageName: result.node.stageName || currentGraphStage.value || '',
750- lookupId: result.node.id,750+ lookupId: result.node.id,
751- containerLookupId:751+ containerLookupId:
752- result.node.searchParentId && result.node.searchParentId !== result.node.id752+ result.node.searchParentId && result.node.searchParentId !== result.node.id
753- ? result.node.searchParentId753+ ? result.node.searchParentId
754- : result.node.searchRootId && result.node.searchRootId !== result.node.id754+ : result.node.searchRootId && result.node.searchRootId !== result.node.id
755- ? result.node.searchRootId755+ ? result.node.searchRootId
756- : '',756+ : '',
757- })757+ })
758-}758+}
759- 759+ 
760-function resolveNavigationRankIds(issue) {760+function resolveNavigationRankIds(issue) {
761- if (issue?.relatedRankIds?.length) {761+ if (issue?.relatedRankIds?.length) {
762- return issue.relatedRankIds762+ return issue.relatedRankIds
763- }763+ }
764- 764+ 
765- return selectedRankKeys.value765+ return selectedRankKeys.value
766 .map((key) => {766 .map((key) => {
767 const match = String(key).match(/^rank-(\d+)$/)767 const match = String(key).match(/^rank-(\d+)$/)
768 return match ? Number(match[1]) : null768 return match ? Number(match[1]) : null
769 })769 })
770 .filter((rankId) => Number.isInteger(rankId))770 .filter((rankId) => Number.isInteger(rankId))
771-}771+}
772- 
773-function resolveIssueNavigationStep(issue) {
774- const primaryStep = issue?.primaryRelatedStep
775- if (Number.isInteger(primaryStep?.snapshotStep)) return primaryStep.snapshotStep
776- 
777- const taskNode = issue?.taskNode
778- if (Number.isInteger(taskNode?.globalStep)) return taskNode.globalStep
779- if (Number.isInteger(taskNode?.localStep)) return taskNode.localStep
780- 
781- const stage = typeof issue?.stage === 'string' ? issue.stage : ''
782- const stageMatch = stage.match(/^step_(\d+)_/)
783- if (stageMatch) return Number(stageMatch[1])
784- 
785- return null
786-}
787- 
788-function resolveIssueNodeTarget(issue) {
789- const relatedNode = issue?.primaryRelatedNode ?? issue?.taskNode
790- if (relatedNode?.nodeId || (Number.isInteger(relatedNode?.rankId) && Number.isInteger(relatedNode?.queueId))) {
791- return {
792- nodeId: relatedNode?.nodeId ?? '',
793- rankId: Number.isInteger(relatedNode?.rankId) ? relatedNode.rankId : issue?.primaryRankId ?? null,
794- queueId: Number.isInteger(relatedNode?.queueId) ? relatedNode.queueId : issue?.queueId ?? null,
795- slotIndex: Number.isInteger(relatedNode?.pos) ? relatedNode.pos : null,
796- taskType: relatedNode?.taskType ?? issue?.taskType ?? '',
797- }
798- }
799- 
800- if (!issue?.dagTarget) {
801- return {
802- nodeId: '',
803- rankId: null,
804- queueId: null,
805- slotIndex: null,
806- taskType: '',
807- }
808- }
809- 
810- return {
811- nodeId: issue.dagTarget.nodeId ?? '',
812- rankId:
813- Number.isInteger(issue?.dagTarget?.queueId) || Number.isInteger(issue?.dagTarget?.slotIndex) || issue?.dagTarget?.nodeId
814- ? issue.dagTarget.rankId ?? issue?.primaryRankId ?? null
815- : null,
816- queueId: issue.dagTarget.queueId ?? issue?.queueId ?? null,
817- slotIndex: issue?.dagTarget?.slotIndex ?? null,
818- taskType: issue?.dagTarget?.taskType ?? issue?.taskType ?? '',
819- }
820-}
821- 
822-function resolveSearchFieldValue(node, field) {
823- if (!node) return ''
824- const rawNode = node.rawNode ?? {}
825- const taskData = node.taskData ?? rawNode?.task?.task_data ?? rawNode?.task_data ?? {}
826- 
827- switch (field) {
828- case 'taskType':
829- return String(node.taskType ?? rawNode?.task?.task_type ?? rawNode?.task_type ?? '').toLowerCase()
830- case 'notifyId':
831- return normalizeSearchFieldValue(
832- node.notifyId ??
833- node.notify_id ??
834- taskData.notifyId ??
835- taskData.notify_id ??
836- rawNode.notifyId ??
837- rawNode.notify_id ??
838- rawNode?.task?.notifyId ??
839- rawNode?.task?.notify_id,
840- )
841- case 'taskId':
842- default:
843- return String(node.id ?? rawNode?.node_id ?? rawNode?.id ?? '').toLowerCase()
844- }
845-}
846- 
847-function resolveSearchFieldDisplayValue(node, field) {
848- if (!node) return '--'
849- const rawNode = node.rawNode ?? {}
850- const taskData = node.taskData ?? rawNode?.task?.task_data ?? rawNode?.task_data ?? {}
851- 
852- switch (field) {
853- case 'taskType':
854- return String(node.taskType ?? rawNode?.task?.task_type ?? rawNode?.task_type ?? '--').replaceAll('_', ' ')
855- case 'notifyId':
856- return formatSearchFieldDisplayValue(
857- node.notifyId ??
858- node.notify_id ??
859- taskData.notifyId ??
860- taskData.notify_id ??
861- rawNode.notifyId ??
862- rawNode.notify_id ??
863- rawNode?.task?.notifyId ??
864- rawNode?.task?.notify_id,
865- )
866- case 'taskId':
867- default:
868- return formatSearchFieldDisplayValue(node.id ?? rawNode?.node_id ?? rawNode?.id)
869- }
870-}
871- 
872-function formatSearchLocation(node) {
873- const rank = Number.isInteger(node?.rankId) ? `rank ${node.rankId}` : 'rank --'
874- const queue = Number.isInteger(node?.queueId) ? `queue ${node.queueId}` : 'queue --'
875- const posValue = Number.isInteger(node?.pos)
876- ? node.pos
877- : Number.isInteger(node?.slotIndex)
878- ? node.slotIndex
879- : null
880- const pos = Number.isInteger(posValue) ? `pos ${posValue}` : 'pos --'
881- return `${rank} / ${queue} / ${pos}`
882-}
883- 
884-function normalizeSearchFieldValue(value) {
885- if (value === null || value === undefined || value === '') return ''
886- return String(value).toLowerCase()
887-}
888- 
889-function formatSearchFieldDisplayValue(value) {
890- if (value === null || value === undefined || value === '') return '--'
891- return String(value)
892-}
893 772 
894-function handleStageUpdate(nextStage) {773+function resolveIssueNavigationStep(issue) {
774+ const primaryStep = issue?.primaryRelatedStep
775+ if (Number.isInteger(primaryStep?.snapshotStep)) return primaryStep.snapshotStep
776+ 
777+ const taskNode = issue?.taskNode
778+ if (Number.isInteger(taskNode?.globalStep)) return taskNode.globalStep
779+ if (Number.isInteger(taskNode?.localStep)) return taskNode.localStep
780+ 
781+ const stage = typeof issue?.stage === 'string' ? issue.stage : ''
782+ const stageMatch = stage.match(/^step_(\d+)_/)
783+ if (stageMatch) return Number(stageMatch[1])
784+ 
785+ return null
786+}
787+ 
788+function resolveIssueNodeTarget(issue) {
789+ const relatedNode = issue?.primaryRelatedNode ?? issue?.taskNode
790+ if (relatedNode?.nodeId || (Number.isInteger(relatedNode?.rankId) && Number.isInteger(relatedNode?.queueId))) {
791+ return {
792+ nodeId: relatedNode?.nodeId ?? '',
793+ rankId: Number.isInteger(relatedNode?.rankId) ? relatedNode.rankId : issue?.primaryRankId ?? null,
794+ queueId: Number.isInteger(relatedNode?.queueId) ? relatedNode.queueId : issue?.queueId ?? null,
795+ slotIndex: Number.isInteger(relatedNode?.pos) ? relatedNode.pos : null,
796+ taskType: relatedNode?.taskType ?? issue?.taskType ?? '',
797+ }
798+ }
799+ 
800+ if (!issue?.dagTarget) {
801+ return {
802+ nodeId: '',
803+ rankId: null,
804+ queueId: null,
805+ slotIndex: null,
806+ taskType: '',
807+ }
808+ }
809+ 
810+ return {
811+ nodeId: issue.dagTarget.nodeId ?? '',
812+ rankId:
813+ Number.isInteger(issue?.dagTarget?.queueId) || Number.isInteger(issue?.dagTarget?.slotIndex) || issue?.dagTarget?.nodeId
814+ ? issue.dagTarget.rankId ?? issue?.primaryRankId ?? null
815+ : null,
816+ queueId: issue.dagTarget.queueId ?? issue?.queueId ?? null,
817+ slotIndex: issue?.dagTarget?.slotIndex ?? null,
818+ taskType: issue?.dagTarget?.taskType ?? issue?.taskType ?? '',
819+ }
820+}
821+ 
822+function resolveSearchFieldValue(node, field) {
823+ if (!node) return ''
824+ const rawNode = node.rawNode ?? {}
825+ const taskData = node.taskData ?? rawNode?.task?.task_data ?? rawNode?.task_data ?? {}
826+ 
827+ switch (field) {
828+ case 'taskType':
829+ return String(node.taskType ?? rawNode?.task?.task_type ?? rawNode?.task_type ?? '').toLowerCase()
830+ case 'notifyId':
831+ return normalizeSearchFieldValue(
832+ node.notifyId ??
833+ node.notify_id ??
834+ taskData.notifyId ??
835+ taskData.notify_id ??
836+ rawNode.notifyId ??
837+ rawNode.notify_id ??
838+ rawNode?.task?.notifyId ??
839+ rawNode?.task?.notify_id,
840+ )
841+ case 'taskId':
842+ default:
843+ return String(node.id ?? rawNode?.node_id ?? rawNode?.id ?? '').toLowerCase()
844+ }
845+}
846+ 
847+function resolveSearchFieldDisplayValue(node, field) {
848+ if (!node) return '--'
849+ const rawNode = node.rawNode ?? {}
850+ const taskData = node.taskData ?? rawNode?.task?.task_data ?? rawNode?.task_data ?? {}
851+ 
852+ switch (field) {
853+ case 'taskType':
854+ return String(node.taskType ?? rawNode?.task?.task_type ?? rawNode?.task_type ?? '--').replaceAll('_', ' ')
855+ case 'notifyId':
856+ return formatSearchFieldDisplayValue(
857+ node.notifyId ??
858+ node.notify_id ??
859+ taskData.notifyId ??
860+ taskData.notify_id ??
861+ rawNode.notifyId ??
862+ rawNode.notify_id ??
863+ rawNode?.task?.notifyId ??
864+ rawNode?.task?.notify_id,
865+ )
866+ case 'taskId':
867+ default:
868+ return formatSearchFieldDisplayValue(node.id ?? rawNode?.node_id ?? rawNode?.id)
869+ }
870+}
871+ 
872+function formatSearchLocation(node) {
873+ const rank = Number.isInteger(node?.rankId) ? `rank ${node.rankId}` : 'rank --'
874+ const queue = Number.isInteger(node?.queueId) ? `queue ${node.queueId}` : 'queue --'
875+ const posValue = Number.isInteger(node?.pos)
876+ ? node.pos
877+ : Number.isInteger(node?.slotIndex)
878+ ? node.slotIndex
879+ : null
880+ const pos = Number.isInteger(posValue) ? `pos ${posValue}` : 'pos --'
881+ return `${rank} / ${queue} / ${pos}`
882+}
883+ 
884+function normalizeSearchFieldValue(value) {
885+ if (value === null || value === undefined || value === '') return ''
886+ return String(value).toLowerCase()
887+}
888+ 
889+function formatSearchFieldDisplayValue(value) {
890+ if (value === null || value === undefined || value === '') return '--'
891+ return String(value)
892+}
893+ 
894+function handleStageUpdate(nextStage) {
895 if (typeof nextStage !== 'string' || !nextStage || nextStage === currentGraphStage.value) return895 if (typeof nextStage !== 'string' || !nextStage || nextStage === currentGraphStage.value) return
896 896 
897 const mappedTarget = selectedNode.value?.nodeMappings?.find((item) => item?.stageName === nextStage) ?? null897 const mappedTarget = selectedNode.value?.nodeMappings?.find((item) => item?.stageName === nextStage) ?? null
@@ -948,42 +948,42 @@ function handleNavigate(target) {
948 return948 return
949 }949 }
950 950 
951- if (resolvedTarget.containerNode) {951+ if (resolvedTarget.containerNode) {
952- enterSubgraph(resolvedTarget.containerNode)952+ enterSubgraph(resolvedTarget.containerNode)
953- } else if (subgraphNode.value) {953+ } else if (subgraphNode.value) {
954- exitSubgraph()954+ exitSubgraph()
955- }955+ }
956 956 
957 registerNodes([...resolvedTarget.path, resolvedTarget.node])957 registerNodes([...resolvedTarget.path, resolvedTarget.node])
958 focusNode(resolvedTarget.node, { follow: true, sync: true })958 focusNode(resolvedTarget.node, { follow: true, sync: true })
959-}959+}
960- 960+ 
961-function handleFocusStep(step) {961+function handleFocusStep(step) {
962- focusStep(step, { smoothScroll: true })962+ focusStep(step, { smoothScroll: true })
963-}963+}
964- 964+ 
965-function handleStepUpdate(step) {965+function handleStepUpdate(step) {
966- if (!Number.isFinite(step)) return966+ if (!Number.isFinite(step)) return
967- focusStep(step, { smoothScroll: false })967+ focusStep(step, { smoothScroll: false })
968-}968+}
969- 969+ 
970-function handleOverviewStepUpdate(step) {970+function handleOverviewStepUpdate(step) {
971- if (!Number.isFinite(step)) return971+ if (!Number.isFinite(step)) return
972- selectedNode.value = null972+ selectedNode.value = null
973- focusStep(step, { smoothScroll: false })973+ focusStep(step, { smoothScroll: false })
974-}974+}
975- 975+ 
976-async function handleBufferSelect(selection) {976+async function handleBufferSelect(selection) {
977- const step = Number(selection?.step)977+ const step = Number(selection?.step)
978 const rankId = Number(selection?.rankId)978 const rankId = Number(selection?.rankId)
979 const bufferId = Number(selection?.bufferId)979 const bufferId = Number(selection?.bufferId)
980- if (!Number.isInteger(step) || !Number.isInteger(rankId) || !Number.isInteger(bufferId)) return980+ if (!Number.isInteger(step) || !Number.isInteger(rankId) || !Number.isInteger(bufferId)) return
981- 981+ 
982- selectedNode.value = null982+ selectedNode.value = null
983- focusStep(step, { smoothScroll: false })983+ focusStep(step, { smoothScroll: false })
984- await nextTick()984+ await nextTick()
985- inspectorRef.value?.selectLayout?.(rankId, bufferId)985+ inspectorRef.value?.selectLayout?.(rankId, bufferId)
986-}986+}
987 987 
988function handleTotalStepsUpdate(total) {988function handleTotalStepsUpdate(total) {
989 setTotalSteps(total)989 setTotalSteps(total)
@@ -1390,19 +1390,19 @@ function findNodeInDescendants(rootNode, targetId, visited, path = []) {
1390 return null1390 return null
1391}1391}
1392 1392 
1393-function iterateSubgraphEntries(streamCollection, fallbackRankId) {1393+function iterateSubgraphEntries(streamCollection, fallbackRankId) {
1394- return normalizeStreamCollection(streamCollection, fallbackRankId, {1394+ return normalizeStreamCollection(streamCollection, fallbackRankId, {
1395- stageName: DEFAULT_MEMVIEW_STAGE,1395+ stageName: DEFAULT_MEMVIEW_STAGE,
1396- topLevel: false,1396+ topLevel: false,
1397- }).flatMap((stream) =>1397+ }).flatMap((stream) =>
1398- stream.nodes.map((node) => ({1398+ stream.nodes.map((node) => ({
1399- rawNode: node.rawNode,1399+ rawNode: node.rawNode,
1400- node,1400+ node,
1401- })),1401+ })),
1402- )1402+ )
1403-}1403+}
1404- 1404+ 
1405-watch(1405+watch(
1406 () => [selectedDatasetName.value, selectedRankKeys.value.join(',')],1406 () => [selectedDatasetName.value, selectedRankKeys.value.join(',')],
1407 () => {1407 () => {
1408 resetTimeline()1408 resetTimeline()
@@ -1434,17 +1434,17 @@ watch(
1434 },1434 },
1435)1435)
1436 1436 
1437-watch(1437+watch(
1438- () => [playbackVisibleSteps.value.length, playbackVisibleIndex.value],1438+ () => [playbackVisibleSteps.value.length, playbackVisibleIndex.value],
1439- ([stepCount, currentIndex]) => {1439+ ([stepCount, currentIndex]) => {
1440- if (!playbackPlaying.value) return1440+ if (!playbackPlaying.value) return
1441- if (!stepCount || currentIndex >= stepCount - 1) {1441+ if (!stepCount || currentIndex >= stepCount - 1) {
1442- stopPlayback()1442+ stopPlayback()
1443- }1443+ }
1444- },1444+ },
1445-)1445+)
1446- 1446+ 
1447-onMounted(() => {1447+onMounted(() => {
1448 centerTopSize.value = resolveDefaultCenterTopSize()1448 centerTopSize.value = resolveDefaultCenterTopSize()
1449 ensureDatasetsLoaded()1449 ensureDatasetsLoaded()
1450 updateCenterAvailableHeight()1450 updateCenterAvailableHeight()
@@ -1477,190 +1477,190 @@ onBeforeUnmount(() => {
1477 :resizable="!sidebarCollapsed"1477 :resizable="!sidebarCollapsed"
1478 @update:size="handleLeftPanelResize"1478 @update:size="handleLeftPanelResize"
1479 >1479 >
1480- <aside class="dashboard-sidebar-shell" :class="{ 'is-collapsed': sidebarCollapsed }">1480+ <aside class="dashboard-sidebar-shell" :class="{ 'is-collapsed': sidebarCollapsed }">
1481- <DashboardSidebar1481+ <DashboardSidebar
1482- :selected-rank-count="selectedRankCount"1482+ :selected-rank-count="selectedRankCount"
1483- :dataset-detail="datasetDetail"1483+ :dataset-detail="datasetDetail"
1484- :selected-dataset="selectedDataset"1484+ :selected-dataset="selectedDataset"
1485- :tree-data="treeData"1485+ :tree-data="treeData"
1486- :checked-keys="selectedRankKeys"1486+ :checked-keys="selectedRankKeys"
1487- :empty-text="loadingList ? 'Loading...' : EMPTY_TEXT"1487+ :empty-text="loadingList ? 'Loading...' : EMPTY_TEXT"
1488- @tree-check="handleTreeCheck"1488+ @tree-check="handleTreeCheck"
1489- >1489+ >
1490- <template #after-rank>1490+ <template #after-rank>
1491- <el-collapse1491+ <el-collapse
1492- v-model="issuePanelSections"1492+ v-model="issuePanelSections"
1493- class="dashboard-sidebar-collapse memview-issue-collapse"1493+ class="dashboard-sidebar-collapse memview-issue-collapse"
1494- >1494+ >
1495- <el-collapse-item name="issues" class="dashboard-subpanel analytic-list-panel">1495+ <el-collapse-item name="issues" class="dashboard-subpanel analytic-list-panel">
1496- <template #title>1496+ <template #title>
1497- <div class="dashboard-panel-toggle">1497+ <div class="dashboard-panel-toggle">
1498- <span class="dashboard-panel-toggle__title">1498+ <span class="dashboard-panel-toggle__title">
1499- <el-icon><WarningFilled /></el-icon>1499+ <el-icon><WarningFilled /></el-icon>
1500- <span>报错列表</span>1500+ <span>报错列表</span>
1501- </span>1501+ </span>
1502- <span class="dashboard-panel-toggle__meta">1502+ <span class="dashboard-panel-toggle__meta">
1503- <el-tag size="small" type="danger">{{ issueCount }} 条</el-tag>1503+ <el-tag size="small" type="danger">{{ issueCount }} 条</el-tag>
1504- </span>1504+ </span>
1505- </div>1505+ </div>
1506- </template>1506+ </template>
1507- 1507+ 
1508- <div v-if="loadingIssues" class="dashboard-empty-wrap analytic-list-empty">1508+ <div v-if="loadingIssues" class="dashboard-empty-wrap analytic-list-empty">
1509- <el-empty description="正在读取 issues.msgpack..." />1509+ <el-empty description="正在读取 issues.msgpack..." />
1510- </div>1510+ </div>
1511- 1511+ 
1512- <div v-else-if="issues.length" class="analytic-issue-list">1512+ <div v-else-if="issues.length" class="analytic-issue-list">
1513- <button1513+ <button
1514- v-for="issue in issues"1514+ v-for="issue in issues"
1515- :key="issue.issueId"1515+ :key="issue.issueId"
1516- type="button"1516+ type="button"
1517- class="analytic-issue-row"1517+ class="analytic-issue-row"
1518- :class="{ 'is-active': selectedIssue?.issueId === issue.issueId }"1518+ :class="{ 'is-active': selectedIssue?.issueId === issue.issueId }"
1519- @click="handleIssueSelect(issue)"1519+ @click="handleIssueSelect(issue)"
1520- >1520+ >
1521- <span class="analytic-issue-row__icon" :class="`is-${issue.severityType}`">1521+ <span class="analytic-issue-row__icon" :class="`is-${issue.severityType}`">
1522- <el-icon><WarningFilled /></el-icon>1522+ <el-icon><WarningFilled /></el-icon>
1523- </span>1523+ </span>
1524- <div class="analytic-issue-row__content">1524+ <div class="analytic-issue-row__content">
1525- <div class="analytic-issue-row__head">1525+ <div class="analytic-issue-row__head">
1526- <strong>{{ issue.title }}</strong>1526+ <strong>{{ issue.title }}</strong>
1527- <div class="analytic-issue-row__head-tags">1527+ <div class="analytic-issue-row__head-tags">
1528- <el-tag size="small" :type="issue.severityType">{{ issue.severityLabel }}</el-tag>1528+ <el-tag size="small" :type="issue.severityType">{{ issue.severityLabel }}</el-tag>
1529- </div>1529+ </div>
1530- </div>1530+ </div>
1531- <div class="analytic-issue-row__body">1531+ <div class="analytic-issue-row__body">
1532- <div v-if="issue.listFieldRows.primaryRow.length" class="analytic-issue-row__field-stack">1532+ <div v-if="issue.listFieldRows.primaryRow.length" class="analytic-issue-row__field-stack">
1533- <div1533+ <div
1534- v-for="field in issue.listFieldRows.primaryRow"1534+ v-for="field in issue.listFieldRows.primaryRow"
1535- :key="`primary-${issue.issueId}-${field.label}`"1535+ :key="`primary-${issue.issueId}-${field.label}`"
1536- class="analytic-issue-row__field"1536+ class="analytic-issue-row__field"
1537- >1537+ >
1538- <span>{{ field.label }}</span>1538+ <span>{{ field.label }}</span>
1539- <strong>{{ field.value }}</strong>1539+ <strong>{{ field.value }}</strong>
1540- </div>1540+ </div>
1541- </div>1541+ </div>
1542- <div1542+ <div
1543- v-if="issue.listFieldRows.secondaryRow.length"1543+ v-if="issue.listFieldRows.secondaryRow.length"
1544- class="analytic-issue-row__field-stack is-secondary"1544+ class="analytic-issue-row__field-stack is-secondary"
1545- >1545+ >
1546- <div1546+ <div
1547- v-for="field in issue.listFieldRows.secondaryRow"1547+ v-for="field in issue.listFieldRows.secondaryRow"
1548- :key="`secondary-${issue.issueId}-${field.label}`"1548+ :key="`secondary-${issue.issueId}-${field.label}`"
1549- class="analytic-issue-row__field"1549+ class="analytic-issue-row__field"
1550- >1550+ >
1551- <span>{{ field.label }}</span>1551+ <span>{{ field.label }}</span>
1552- <strong>{{ field.value }}</strong>1552+ <strong>{{ field.value }}</strong>
1553- </div>1553+ </div>
1554- </div>1554+ </div>
1555- </div>1555+ </div>
1556- <span class="analytic-issue-row__code">{{ issue.code }}</span>1556+ <span class="analytic-issue-row__code">{{ issue.code }}</span>
1557- </div>1557+ </div>
1558- </button>1558+ </button>
1559- </div>1559+ </div>
1560- 1560+ 
1561- <div v-else class="dashboard-empty-wrap analytic-list-empty">1561+ <div v-else class="dashboard-empty-wrap analytic-list-empty">
1562- <el-empty description="当前数据集没有 issue 记录。" />1562+ <el-empty description="当前数据集没有 issue 记录。" />
1563- </div>1563+ </div>
1564- </el-collapse-item>1564+ </el-collapse-item>
1565- </el-collapse>1565+ </el-collapse>
1566- 1566+ 
1567- <el-collapse1567+ <el-collapse
1568- v-model="searchPanelSections"1568+ v-model="searchPanelSections"
1569- class="dashboard-sidebar-collapse memview-search-collapse"1569+ class="dashboard-sidebar-collapse memview-search-collapse"
1570- >1570+ >
1571- <el-collapse-item name="search" class="dashboard-subpanel memview-search-panel">1571+ <el-collapse-item name="search" class="dashboard-subpanel memview-search-panel">
1572- <template #title>1572+ <template #title>
1573- <div class="dashboard-panel-toggle">1573+ <div class="dashboard-panel-toggle">
1574- <span class="dashboard-panel-toggle__title">1574+ <span class="dashboard-panel-toggle__title">
1575- <el-icon><Search /></el-icon>1575+ <el-icon><Search /></el-icon>
1576- <span>搜索</span>1576+ <span>搜索</span>
1577- </span>1577+ </span>
1578- <span class="dashboard-panel-toggle__meta">1578+ <span class="dashboard-panel-toggle__meta">
1579- <el-tag size="small" type="info">{{ selectedSearchFieldLabel }}</el-tag>1579+ <el-tag size="small" type="info">{{ selectedSearchFieldLabel }}</el-tag>
1580- <el-tag size="small" type="success">1580+ <el-tag size="small" type="success">
1581- {{ normalizedSearchQuery ? `${searchResults.length} results` : `${selectedRankCount} ranks` }}1581+ {{ normalizedSearchQuery ? `${searchResults.length} results` : `${selectedRankCount} ranks` }}
1582- </el-tag>1582+ </el-tag>
1583- </span>1583+ </span>
1584- </div>1584+ </div>
1585- </template>1585+ </template>
1586- 1586+ 
1587- <section class="memview-sidebar-search">1587+ <section class="memview-sidebar-search">
1588- <div class="memview-sidebar-search__controls">1588+ <div class="memview-sidebar-search__controls">
1589- <el-select1589+ <el-select
1590- v-model="searchField"1590+ v-model="searchField"
1591- class="memview-sidebar-search__field-select"1591+ class="memview-sidebar-search__field-select"
1592- aria-label="Search field"1592+ aria-label="Search field"
1593- >1593+ >
1594- <el-option1594+ <el-option
1595- v-for="option in SEARCH_FIELD_OPTIONS"1595+ v-for="option in SEARCH_FIELD_OPTIONS"
1596- :key="option.value"1596+ :key="option.value"
1597- :label="option.label"1597+ :label="option.label"
1598- :value="option.value"1598+ :value="option.value"
1599- />1599+ />
1600- </el-select>1600+ </el-select>
1601- 1601+ 
1602- <el-input1602+ <el-input
1603- v-model="searchQuery"1603+ v-model="searchQuery"
1604- class="memview-sidebar-search__input"1604+ class="memview-sidebar-search__input"
1605- clearable1605+ clearable
1606- placeholder="Enter keyword"1606+ placeholder="Enter keyword"
1607- aria-label="Search keyword"1607+ aria-label="Search keyword"
1608- />1608+ />
1609- </div>1609+ </div>
1610- 1610+ 
1611- <div class="memview-sidebar-search__meta">1611+ <div class="memview-sidebar-search__meta">
1612- <span>Selected ranks: {{ selectedRankCount }}</span>1612+ <span>Selected ranks: {{ selectedRankCount }}</span>
1613- <span>Results: {{ searchResults.length }}</span>1613+ <span>Results: {{ searchResults.length }}</span>
1614- </div>1614+ </div>
1615- 1615+ 
1616- <div1616+ <div
1617- v-if="normalizedSearchQuery && searchResults.length"1617+ v-if="normalizedSearchQuery && searchResults.length"
1618- class="memview-sidebar-search__results"1618+ class="memview-sidebar-search__results"
1619- >1619+ >
1620- <button1620+ <button
1621- v-for="result in searchResults"1621+ v-for="result in searchResults"
1622- :key="result.node.id"1622+ :key="result.node.id"
1623- type="button"1623+ type="button"
1624- class="memview-sidebar-search-card"1624+ class="memview-sidebar-search-card"
1625- :class="{ 'is-active': selectedNode?.id === result.node.id }"1625+ :class="{ 'is-active': selectedNode?.id === result.node.id }"
1626- @click="handleSearchResultSelect(result)"1626+ @click="handleSearchResultSelect(result)"
1627- >1627+ >
1628- <div class="memview-sidebar-search-card__head">1628+ <div class="memview-sidebar-search-card__head">
1629- <strong>{{ result.taskId }}</strong>1629+ <strong>{{ result.taskId }}</strong>
1630- <el-tag size="small" type="info">{{ result.taskType }}</el-tag>1630+ <el-tag size="small" type="info">{{ result.taskType }}</el-tag>
1631- </div>1631+ </div>
1632- 1632+ 
1633- <div class="memview-sidebar-search-card__grid">1633+ <div class="memview-sidebar-search-card__grid">
1634- <div class="memview-sidebar-search-card__field memview-sidebar-search-card__field--wide">1634+ <div class="memview-sidebar-search-card__field memview-sidebar-search-card__field--wide">
1635- <span>rank / queue / pos</span>1635+ <span>rank / queue / pos</span>
1636- <strong>{{ result.location }}</strong>1636+ <strong>{{ result.formatLoc }}</strong>
1637- </div>1637+ </div>
1638- <div1638+ <div
1639- v-if="searchField === 'notifyId'"1639+ v-if="searchField === 'notifyId'"
1640- class="memview-sidebar-search-card__field memview-sidebar-search-card__field--wide"1640+ class="memview-sidebar-search-card__field memview-sidebar-search-card__field--wide"
1641- >1641+ >
1642- <span>notifyId</span>1642+ <span>notifyId</span>
1643- <strong>{{ result.notifyId }}</strong>1643+ <strong>{{ result.notifyId }}</strong>
1644- </div>1644+ </div>
1645- </div>1645+ </div>
1646- </button>1646+ </button>
1647- </div>1647+ </div>
1648- 1648+ 
1649- <div v-else-if="normalizedSearchQuery" class="memview-sidebar-search__empty">1649+ <div v-else-if="normalizedSearchQuery" class="memview-sidebar-search__empty">
1650- <el-empty description="No matching tasks" :image-size="72" />1650+ <el-empty description="No matching tasks" :image-size="72" />
1651- </div>1651+ </div>
1652- 1652+ 
1653- <div v-else class="memview-sidebar-search__hint">1653+ <div v-else class="memview-sidebar-search__hint">
1654- Choose taskId, taskType, or notifyId and enter a keyword to search within the selected ranks.1654+ Choose taskId, taskType, or notifyId and enter a keyword to search within the selected ranks.
1655- </div>1655+ </div>
1656- </section>1656+ </section>
1657- </el-collapse-item>1657+ </el-collapse-item>
1658- </el-collapse>1658+ </el-collapse>
1659- </template>1659+ </template>
1660- </DashboardSidebar>1660+ </DashboardSidebar>
1661- 1661+ 
1662- <button1662+ <button
1663- v-if="!sidebarCollapsed"1663+ v-if="!sidebarCollapsed"
1664 type="button"1664 type="button"
1665 class="dashboard-sidebar-toggle dashboard-sidebar-toggle--collapse"1665 class="dashboard-sidebar-toggle dashboard-sidebar-toggle--collapse"
1666 aria-label="Collapse sidebar"1666 aria-label="Collapse sidebar"
@@ -1699,14 +1699,14 @@ onBeforeUnmount(() => {
1699 >1699 >
1700 MemView 内存视图1700 MemView 内存视图
1701 </button>1701 </button>
1702- <MemViewOverviewPanel1702+ <MemViewOverviewPanel
1703- v-else1703+ v-else
1704- :dataset-name="selectedDatasetName"1704+ :dataset-name="selectedDatasetName"
1705- :enabled="hasMemoryData"1705+ :enabled="hasMemoryData"
1706- :selected-rank-keys="selectedRankKeys"1706+ :selected-rank-keys="selectedRankKeys"
1707- :current-step="clampedCurrentStep"1707+ :current-step="clampedCurrentStep"
1708- :display-step-items="displayStepItems"1708+ :display-step-items="displayStepItems"
1709- :empty-text="overviewEmptyText"1709+ :empty-text="overviewEmptyText"
1710 @update:current-step="handleOverviewStepUpdate"1710 @update:current-step="handleOverviewStepUpdate"
1711 @update:total-steps="handleTotalStepsUpdate"1711 @update:total-steps="handleTotalStepsUpdate"
1712 @buffer-select="handleBufferSelect"1712 @buffer-select="handleBufferSelect"
@@ -1722,23 +1722,23 @@ onBeforeUnmount(() => {
1722 >1722 >
1723 DAGView 任务视图1723 DAGView 任务视图
1724 </button>1724 </button>
1725- <MemViewDagPanel1725+ <MemViewDagPanel
1726- v-else1726+ v-else
1727- :dataset-name="selectedDatasetName"1727+ :dataset-name="selectedDatasetName"
1728- :selected-rank-keys="selectedRankKeys"1728+ :selected-rank-keys="selectedRankKeys"
1729- :selected-node-id="selectedNode?.id ?? ''"1729+ :selected-node-id="selectedNode?.id ?? ''"
1730- :issue-node-ids="dagIssueNodeIds"1730+ :issue-node-ids="dagIssueNodeIds"
1731- :subgraph-node="subgraphNode"1731+ :subgraph-node="subgraphNode"
1732- :current-step="clampedCurrentStep"1732+ :current-step="clampedCurrentStep"
1733- :total-steps="totalSteps"1733+ :total-steps="totalSteps"
1734- :display-step-items="displayStepItems"1734+ :display-step-items="displayStepItems"
1735- :empty-text="dagEmptyText"1735+ :empty-text="dagEmptyText"
1736- @node-select="handleNodeSelect"1736+ @node-select="handleNodeSelect"
1737- @update:current-step="handleStepUpdate"1737+ @update:current-step="handleStepUpdate"
1738- @update:total-steps="handleTotalStepsUpdate"1738+ @update:total-steps="handleTotalStepsUpdate"
1739- @nodes-loaded="handleNodesLoaded"1739+ @nodes-loaded="handleNodesLoaded"
1740- @graph-node-ids-change="handleGraphNodeIdsChange"1740+ @graph-node-ids-change="handleGraphNodeIdsChange"
1741- />1741+ />
1742 </el-splitter-panel>1742 </el-splitter-panel>
1743 </el-splitter>1743 </el-splitter>
1744 </div>1744 </div>
@@ -1798,14 +1798,14 @@ onBeforeUnmount(() => {
1798 :node-task-ops="selectedNodeTaskOps"1798 :node-task-ops="selectedNodeTaskOps"
1799 :step-buffer-ops="activeStepBufferOps"1799 :step-buffer-ops="activeStepBufferOps"
1800 :step-task-ops="activeStepTaskOps"1800 :step-task-ops="activeStepTaskOps"
1801- :step-layout-buffers="activeStepLayoutBuffers"1801+ :step-layout-buffers="activeStepLayoutBuffers"
1802- :selected-rank-keys="selectedRankKeys"1802+ :selected-rank-keys="selectedRankKeys"
1803- :can-resolve-relation="canResolveRelation"1803+ :can-resolve-relation="canResolveRelation"
1804- :resolve-relation-meta="resolveRelationMeta"1804+ :resolve-relation-meta="resolveRelationMeta"
1805- :empty-text="hasData ? 'Select a node to inspect.' : EMPTY_TEXT"1805+ :empty-text="hasData ? 'Select a node to inspect.' : EMPTY_TEXT"
1806- @enter-subgraph="handleEnterSubgraph"1806+ @enter-subgraph="handleEnterSubgraph"
1807- @exit-subgraph="handleExitSubgraph"1807+ @exit-subgraph="handleExitSubgraph"
1808- @navigate="handleNavigate"1808+ @navigate="handleNavigate"
1809 @focus-step="handleFocusStep"1809 @focus-step="handleFocusStep"
1810 />1810 />
1811 1811 
Mtest/hccl_vm/src/plugin/runner/CMakeLists.txt+10-3
@@ -28,6 +28,7 @@ file(GLOB_RECURSE VR_LIB_SOURCES
28 # 添加插件库28 # 添加插件库
29add_executable(runner ${VR_LIB_SOURCES})29add_executable(runner ${VR_LIB_SOURCES})
30 30 
31+ 
31target_compile_options(runner PRIVATE32target_compile_options(runner PRIVATE
32 -g33 -g
33 -O034 -O0
@@ -56,17 +57,23 @@ target_include_directories(runner PRIVATE
56)57)
57 58 
58target_link_libraries(runner PRIVATE59target_link_libraries(runner PRIVATE
60+ -Wl,--whole-archive
59 aiv_executor61 aiv_executor
60 store62 store
63+ loader
61 runnerdb64 runnerdb
65+ modeldb
62 ccu_executor66 ccu_executor
63 log67 log
68+ -Wl,--no-whole-archive
64 rt69 rt
65 JSON::JSON70 JSON::JSON
66- loader71+
72+ yaml-cpp::yaml-cpp
73+ sqlite3
67)74)
68install(TARGETS runner75install(TARGETS runner
69- DESTINATION "plugin/validate/runner"76+ DESTINATION "plugin/runner"
70 # 文件的权限 (755)77 # 文件的权限 (755)
71 PERMISSIONS 78 PERMISSIONS
72 OWNER_READ OWNER_WRITE OWNER_EXECUTE 79 OWNER_READ OWNER_WRITE OWNER_EXECUTE
@@ -75,7 +82,7 @@ install(TARGETS runner
75 )82 )
76 83 
77install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../manifest.json"84install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../manifest.json"
78- DESTINATION "plugin/validate/runner"85+ DESTINATION "plugin/runner"
79 # 文件的权限 (755)86 # 文件的权限 (755)
80 PERMISSIONS87 PERMISSIONS
81 OWNER_READ OWNER_WRITE OWNER_EXECUTE88 OWNER_READ OWNER_WRITE OWNER_EXECUTE
Mtest/hccl_vm/src/plugin/runner/aiv_executor/CMakeLists.txt+0-1
@@ -46,7 +46,6 @@ target_include_directories(aiv_executor
46 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/runtime46 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/runtime
47 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/profiling47 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/profiling
48 48 
49- ${THRID_PARTY_DIR}
50 ${THRID_PARTY_DIR}/nlohmann_json49 ${THRID_PARTY_DIR}/nlohmann_json
51 ${THRID_PARTY_DIR}/dbg_macro # for debug50 ${THRID_PARTY_DIR}/dbg_macro # for debug
52)51)
Mtest/hccl_vm/src/plugin/runner/aiv_executor/aiv_resource_manager.cc+3-3
@@ -23,9 +23,9 @@ namespace {
23constexpr uint8_t INPUT_BUFFER_TYPE = 0;23constexpr uint8_t INPUT_BUFFER_TYPE = 0;
24constexpr uint8_t OUTPUT_BUFFER_TYPE = 1;24constexpr uint8_t OUTPUT_BUFFER_TYPE = 1;
25constexpr uint8_t CCL_BUFFER_TYPE = 2;25constexpr uint8_t CCL_BUFFER_TYPE = 2;
26-constexpr uint64_t AIV_COMM_INFO_SIZE = 33ULL * 1024ULL * 1024ULL;26+constexpr uint64_t AIV_COMM_INFO_SIZE = 65ULL * 1024ULL * 1024ULL;
27-constexpr uint64_t AIV_FLAG_ADDR_OFFSET = 40ULL * 1024ULL;27+constexpr uint64_t AIV_FLAG1_OFFSET = 1ULL * 1024ULL * 1024ULL;
28-constexpr uint64_t AIV_FLAG_BUFFER_SIZE = AIV_COMM_INFO_SIZE - AIV_FLAG_ADDR_OFFSET;28+constexpr uint64_t AIV_FLAG_BUFFER_SIZE = AIV_COMM_INFO_SIZE - AIV_FLAG1_OFFSET;
29 29 
30const char *GetBufferTypeName(uint8_t bufferType)30const char *GetBufferTypeName(uint8_t bufferType)
31{31{
Mtest/hccl_vm/src/plugin/runner/ccu_executor/CMakeLists.txt+3-11
@@ -8,8 +8,9 @@
8 8 
9file(GLOB_RECURSE HCCL_SOURCES "*.cc")9file(GLOB_RECURSE HCCL_SOURCES "*.cc")
10 10 
11-# 添加共享11+# 添加静态
12-add_library(ccu_executor SHARED ${HCCL_SOURCES})12+add_library(ccu_executor STATIC ${HCCL_SOURCES})
13+ 
13target_compile_options(ccu_executor PRIVATE14target_compile_options(ccu_executor PRIVATE
14 -fPIC15 -fPIC
15 -g16 -g
@@ -41,12 +42,3 @@ target_link_libraries(ccu_executor PRIVATE
41 log42 log
42 rt43 rt
43)44)
44- 
45-install(TARGETS ccu_executor
46- DESTINATION "plugin/validate/runner"
47- # 文件的权限 (755)
48- PERMISSIONS
49- OWNER_READ OWNER_WRITE OWNER_EXECUTE
50- GROUP_READ GROUP_EXECUTE
51- WORLD_READ WORLD_EXECUTE
52-)
Mtest/hccl_vm/src/proxy/CMakeLists.txt+9-3
@@ -11,6 +11,8 @@ cmake_minimum_required(VERSION 3.10)
11message(STATUS "TOP_DIR: ${TOP_DIR}")11message(STATUS "TOP_DIR: ${TOP_DIR}")
12message(STATUS "ASCEND_CANN_PACKAGE_PATH: ${ASCEND_CANN_PACKAGE_PATH}")12message(STATUS "ASCEND_CANN_PACKAGE_PATH: ${ASCEND_CANN_PACKAGE_PATH}")
13 13 
14+find_package(SQLite3 REQUIRED)
15+ 
14add_library(hccl_proxy_level2 SHARED "")16add_library(hccl_proxy_level2 SHARED "")
15 17 
16target_include_directories(hccl_proxy_level2 PRIVATE18target_include_directories(hccl_proxy_level2 PRIVATE
@@ -121,18 +123,22 @@ target_compile_options(hccl_proxy_level2 PRIVATE
121)123)
122 124 
123target_link_libraries(hccl_proxy_level2 PRIVATE125target_link_libraries(hccl_proxy_level2 PRIVATE
124- dl126+ -Wl,--whole-archive
125- rt
126 log127 log
127 runnerdb128 runnerdb
129+ modeldb
128 store130 store
129 cmd131 cmd
132+ -Wl,--no-whole-archive
133+ dl
134+ rt
135+ SQLite::SQLite3
130 JSON::JSON136 JSON::JSON
131 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/lib64/libc_sec.a137 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/lib64/libc_sec.a
132)138)
133 139 
134install(TARGETS hccl_proxy_level2140install(TARGETS hccl_proxy_level2
135- DESTINATION "."141+ DESTINATION "lib/x86_64"
136 # 文件的权限 (755)142 # 文件的权限 (755)
137 PERMISSIONS143 PERMISSIONS
138 OWNER_READ OWNER_WRITE OWNER_EXECUTE144 OWNER_READ OWNER_WRITE OWNER_EXECUTE
Dtest/hccl_vm/src/proxy/README.md+0-17
@@ -1,17 +0,0 @@
1-# 编译 hccl_proxy
2- 
3-1. 安装CANN包
4- 
5- ```bash
6- # install-path参数写自己期望的安装路径
7- ./Ascend-cann-toolkit_8.5.0_linux-x86_64.run --full --install-path=/home/l30069909/Ascend
8- ```
9- 
10-2. 在海思极速空间代码根目录`work_code`下拉HCCL_Checker_L2代码
11-3.`hccl_vm/hccl_proxy/CMakeLists.txt` 中的 `ASCEND_CANN_PACKAGE_PATH` 参数修改为自己本机CANN包的安装路径
12-4. 进入hccl_vm目录 `cd .../work_code/HCCL_Checker_L2/hccl_vm`, 执行以下命令
13- 
14- ```bash
15- cmake -S . -B build
16- cmake --build build --target hccl_proxy
17- ```
Mtest/hccl_vm/src/proxy/aclrt_context_stub.cc+4-4
@@ -34,7 +34,7 @@ aclError aclrtCreateContext(aclrtContext *context, int32_t deviceId)
34 return d.logic_id == (uint32_t)deviceId;34 return d.logic_id == (uint32_t)deviceId;
35 });35 });
36 if (!ret.second) {36 if (!ret.second) {
37- HCCL_VM_ERROR("[{}] cannot find device: {:d}", __func__, deviceId);37+ HCCL_VM_ERROR("cannot find device: {:d}", deviceId);
38 return HcclResult::HCCL_E_NOT_FOUND;38 return HcclResult::HCCL_E_NOT_FOUND;
39 }39 }
40 // 找不到context,则默认新增一个context40 // 找不到context,则默认新增一个context
@@ -61,7 +61,7 @@ aclError aclrtDestroyContext(aclrtContext context)
61 return stm.ctx_id == ctxId && stm.is_primary_default == 1;61 return stm.ctx_id == ctxId && stm.is_primary_default == 1;
62 });62 });
63 if (!ret.second) {63 if (!ret.second) {
64- HCCL_VM_ERROR("[{}] cannot find stream by ctx: {:d}", __func__, ctxId);64+ HCCL_VM_ERROR("cannot find stream by ctx: {:d}", ctxId);
65 return HcclResult::HCCL_E_NOT_FOUND;65 return HcclResult::HCCL_E_NOT_FOUND;
66 }66 }
67 67 
@@ -98,7 +98,7 @@ aclError aclrtCtxGetCurrentDefaultStream(aclrtStream *stream)
98 return stm.ctx_id == curCtxId && stm.is_primary_default == 1;98 return stm.ctx_id == curCtxId && stm.is_primary_default == 1;
99 });99 });
100 if (!stm.second) {100 if (!stm.second) {
101- HCCL_VM_ERROR("[{}] cannot find stream by ctx: {:d}", __func__, curCtxId);101+ HCCL_VM_ERROR("cannot find stream by ctx: {:d}", curCtxId);
102 return HcclResult::HCCL_E_NOT_FOUND;102 return HcclResult::HCCL_E_NOT_FOUND;
103 }103 }
104 104 
@@ -117,7 +117,7 @@ aclError aclrtGetPrimaryCtxState(int32_t deviceId, uint32_t *flags, int32_t *act
117 auto currCtx = RunnerDB::GetById<sim::Context>(runner.current_ctx_id);117 auto currCtx = RunnerDB::GetById<sim::Context>(runner.current_ctx_id);
118 if (!currCtx.has_value()) {118 if (!currCtx.has_value()) {
119 // not find119 // not find
120- HCCL_VM_ERROR("[{}] can not get currContext: {:d}", __func__, runner.current_ctx_id);120+ HCCL_VM_ERROR("can not get currContext: {:d}", runner.current_ctx_id);
121 return ACL_ERROR_INVALID_PARAM;121 return ACL_ERROR_INVALID_PARAM;
122 }122 }
123 123 
Mtest/hccl_vm/src/proxy/aclrt_device_stub.cc+28-32
@@ -67,21 +67,35 @@ aclError aclrtSetDevice(int32_t deviceId)
67 try {67 try {
68 HCCL_VM_DEBUG("[aclrtSetDevice] start set device {:d}", deviceId);68 HCCL_VM_DEBUG("[aclrtSetDevice] start set device {:d}", deviceId);
69 uint32_t rankId;69 uint32_t rankId;
70- if (!sim::GetRankIdByMPI(rankId)) {70+ uint64_t serverId = 0;
71- HCCL_VM_ERROR("[aclrtSetDevice] cannot get rank id by MPI.");71+ if (!sim::GetRankIdByMPI(rankId, serverId)) {
72+ HCCL_VM_ERROR("[aclrtSetDevice] cannot get rank id by MPI, serverId is {:d}", serverId);
72 return ACL_ERROR_INVALID_PARAM;73 return ACL_ERROR_INVALID_PARAM;
73 }74 }
74 75 
75 sim::Device device{};76 sim::Device device{};
76- auto devRet = sim::GetDeviceByRankId(rankId, device);77+ if (serverId == 0) {
77- if (devRet != ACL_SUCCESS) {78+ auto devRet = sim::GetDeviceByRankId(rankId, device);
78- HCCL_VM_ERROR("[aclrtSetDevice] cannot find device by rank id {:d}", rankId);79+ if (devRet != ACL_SUCCESS) {
79- return devRet;80+ HCCL_VM_ERROR("[aclrtSetDevice] cannot find device by rank id {:d}", rankId);
81+ return devRet;
82+ }
83+ serverId = device.server_id;
84+ HCCL_VM_DEBUG("[aclrtSetDevice] device logic id: {:d}, rank id: {:d}, device key: {:d}", device.logic_id, rankId, device.id);
85+ } else {
86+ auto ret = RunnerDB::GetOneByPred<sim::Device>([serverId, deviceId](const sim::Device &d) {
87+ return d.server_id == serverId && d.logic_id == (uint32_t)deviceId;
88+ });
89+ if (!ret.second) {
90+ HCCL_VM_ERROR("[aclrtSetDevice] cannot find device by logic id {:d} in server id {:d}", deviceId, serverId);
91+ return ACL_ERROR_INVALID_PARAM;
92+ }
93+ device = ret.first;
94+ HCCL_VM_DEBUG("[aclrtSetDevice] device logic id: {:d}, server id: {:d}, device key: {:d}", device.logic_id, serverId, device.id);
80 }95 }
81- HCCL_VM_DEBUG("[aclrtSetDevice] device logic id: {:d}, rank id: {:d}, device key: {:d}", device.logic_id, rankId, device.id);
82 96 
83 sim::Runner runner{};97 sim::Runner runner{};
84- if (!sim::GetCurrRunnerTls(device.server_id, runner)) {98+ if (!sim::GetCurrRunnerTls(serverId, runner)) {
85 return ACL_ERROR_INVALID_PARAM;99 return ACL_ERROR_INVALID_PARAM;
86 }100 }
87 auto curRunnerId = runner.id;101 auto curRunnerId = runner.id;
@@ -327,30 +341,12 @@ aclError aclrtQueryDeviceStatus(int32_t deviceId, aclrtDeviceStatus *deviceStatu
327const char *aclrtGetSocName()341const char *aclrtGetSocName()
328{342{
329 try {343 try {
330- sim::Runner runner{};344+ // GetSocName接口根据获取server内任意一个device的soc_version
331- if (!sim::GetCurrRunnerTls(0, runner)) {345+ auto devRes = RunnerDB::GetOneByPred<sim::Device>([](const sim::Device& d) {
332- return "invalid param";346+ return d.server_id == 1;
333- }
334- auto hostId = runner.host_id;
335- if (hostId == 0) {
336- HCCL_VM_ERROR("[aclrtGetSocName] wrong host id: {:d}", hostId);
337- return "invalid param";
338- }
339- 
340- auto host = RunnerDB::GetOneByPred<sim::Host>([hostId](const sim::Host& h) {
341- return h.id == hostId;
342- });
343- if (!host.second) {
344- HCCL_VM_ERROR("[aclrtResetDevice] can not find host by key {:d}", hostId);
345- return "";
346- }
347- 
348- auto serverId = host.first.server_id;
349- auto devRes = RunnerDB::GetOneByPred<sim::Device>([serverId](const sim::Device& d) {
350- return d.server_id == serverId;
351 });347 });
352 if (!devRes.second) {348 if (!devRes.second) {
353- HCCL_VM_ERROR("[aclrtResetDevice] can not find device by server id {:d}", serverId);349+ HCCL_VM_ERROR("[aclrtGetSocName] can not find device by server id 1");
354 return "";350 return "";
355 }351 }
356 352 
@@ -706,7 +702,7 @@ aclError aclrtDevicePeerAccessStatus(int32_t deviceId, int32_t peerDeviceId, int
706aclError aclInit(const char *configPath)702aclError aclInit(const char *configPath)
707{703{
708 (void) configPath;704 (void) configPath;
709- HCCL_VM_DEBUG("[{0}] Success", __func__);705+ HCCL_VM_DEBUG("aclInit Success");
710 return ACL_SUCCESS;706 return ACL_SUCCESS;
711}707}
712 708 
@@ -715,7 +711,7 @@ aclError aclFinalize()
715 if (g_devicePid != 0) {711 if (g_devicePid != 0) {
716 kill(g_devicePid, SIGKILL);712 kill(g_devicePid, SIGKILL);
717 }713 }
718- HCCL_VM_DEBUG("[{0}] Success", __func__);714+ HCCL_VM_DEBUG("aclFinalize Success");
719 return ACL_SUCCESS;715 return ACL_SUCCESS;
720}716}
721 717 
Mtest/hccl_vm/src/proxy/aclrt_event_stub.cc+9-9
@@ -34,7 +34,7 @@ aclError aclrtCreateEventWithFlag(aclrtEvent *event, uint32_t flag)
34 auto currCtx = RunnerDB::GetById<sim::Context>(runner.current_ctx_id);34 auto currCtx = RunnerDB::GetById<sim::Context>(runner.current_ctx_id);
35 if (!currCtx.has_value()) {35 if (!currCtx.has_value()) {
36 // not find36 // not find
37- HCCL_VM_ERROR("[{}] can not get CurrContext:{:d}", __func__, runner.current_ctx_id);37+ HCCL_VM_ERROR("can not get CurrContext:{:d}", runner.current_ctx_id);
38 return ACL_ERROR_INVALID_PARAM;38 return ACL_ERROR_INVALID_PARAM;
39 }39 }
40 40
@@ -51,7 +51,7 @@ aclError aclrtCreateEventWithFlag(aclrtEvent *event, uint32_t flag)
51 auto res = RunnerDB::Add<sim::Event>(tmp);51 auto res = RunnerDB::Add<sim::Event>(tmp);
52 52 
53 *event = (aclrtEvent)res;53 *event = (aclrtEvent)res;
54- HCCL_VM_INFO("[{}] event: {:d}", __func__, res);54+ HCCL_VM_INFO("event: {:d}", res);
55 return ACL_SUCCESS; 55 return ACL_SUCCESS;
56}56}
57 57 
@@ -68,7 +68,7 @@ aclError aclrtCreateEvent(aclrtEvent *event)
68aclError aclrtDestroyEvent(aclrtEvent event)68aclError aclrtDestroyEvent(aclrtEvent event)
69{69{
70 uint64_t eventId = (uint32_t)(uintptr_t)event;70 uint64_t eventId = (uint32_t)(uintptr_t)event;
71- HCCL_VM_INFO("[{}] event: {:d}", __func__, eventId);71+ HCCL_VM_INFO("event: {:d}", eventId);
72 RunnerDB::Delete<sim::Event>(eventId);72 RunnerDB::Delete<sim::Event>(eventId);
73 return ACL_SUCCESS;73 return ACL_SUCCESS;
74}74}
@@ -81,14 +81,14 @@ aclError aclrtRecordEvent(aclrtEvent event, aclrtStream stream)
81 auto currEvent = RunnerDB::GetById<sim::Event>(eventIdx);81 auto currEvent = RunnerDB::GetById<sim::Event>(eventIdx);
82 if (!currEvent.has_value()) {82 if (!currEvent.has_value()) {
83 // not find83 // not find
84- HCCL_VM_ERROR("[{}] can not get Event: {:d}", __func__, eventIdx);84+ HCCL_VM_ERROR("can not get Event: {:d}", eventIdx);
85 return ACL_ERROR_INVALID_PARAM;85 return ACL_ERROR_INVALID_PARAM;
86 }86 }
87 87 
88 auto currStm = RunnerDB::GetById<sim::Stream>(streamIdx);88 auto currStm = RunnerDB::GetById<sim::Stream>(streamIdx);
89 if (!currStm.has_value()) {89 if (!currStm.has_value()) {
90 // not find90 // not find
91- HCCL_VM_ERROR("[{}] can not get stream:{:d}", __func__, streamIdx);91+ HCCL_VM_ERROR("can not get stream:{:d}", streamIdx);
92 return ACL_ERROR_INVALID_PARAM;92 return ACL_ERROR_INVALID_PARAM;
93 }93 }
94 94 
@@ -104,7 +104,7 @@ aclError aclrtResetEvent(aclrtEvent event, aclrtStream stream)
104 uint64_t eventIdx = (uint32_t)(uintptr_t)event;104 uint64_t eventIdx = (uint32_t)(uintptr_t)event;
105 auto currEvent = RunnerDB::GetById<sim::Event>(eventIdx);105 auto currEvent = RunnerDB::GetById<sim::Event>(eventIdx);
106 if (!currEvent.has_value()) {106 if (!currEvent.has_value()) {
107- HCCL_VM_ERROR("[{}] can not get Event:{:d}", __func__, eventIdx);107+ HCCL_VM_ERROR("can not get Event:{:d}", eventIdx);
108 return ACL_ERROR_INVALID_PARAM;108 return ACL_ERROR_INVALID_PARAM;
109 }109 }
110 RunnerDB::Update<sim::Event>(eventIdx, [](sim::Event &evt) { evt.status = ACL_EVENT_RECORDED_STATUS_COMPLETE;});110 RunnerDB::Update<sim::Event>(eventIdx, [](sim::Event &evt) { evt.status = ACL_EVENT_RECORDED_STATUS_COMPLETE;});
@@ -116,7 +116,7 @@ aclError aclrtQueryEventStatus(aclrtEvent event, aclrtEventRecordedStatus *statu
116 uint64_t eventId = (uint32_t)(uintptr_t)event;116 uint64_t eventId = (uint32_t)(uintptr_t)event;
117 auto res = RunnerDB::GetById<sim::Event>(eventId);117 auto res = RunnerDB::GetById<sim::Event>(eventId);
118 if (!res.has_value()) {118 if (!res.has_value()) {
119- HCCL_VM_ERROR("[{}] can not get event:{:d}", __func__, eventId);119+ HCCL_VM_ERROR("can not get event:{:d}", eventId);
120 return ACL_ERROR_INVALID_PARAM;120 return ACL_ERROR_INVALID_PARAM;
121 }121 }
122 122 
@@ -191,7 +191,7 @@ aclError aclrtGetEventAvailNum(uint32_t *eventCount)
191 auto currCtx = RunnerDB::GetById<sim::Context>(runner.current_ctx_id);191 auto currCtx = RunnerDB::GetById<sim::Context>(runner.current_ctx_id);
192 if (!currCtx.has_value()) {192 if (!currCtx.has_value()) {
193 // not find193 // not find
194- HCCL_VM_ERROR("[{}] can not get CurrContext:{:d}", __func__, runner.current_ctx_id);194+ HCCL_VM_ERROR("can not get CurrContext:{:d}", runner.current_ctx_id);
195 return ACL_ERROR_INVALID_PARAM;195 return ACL_ERROR_INVALID_PARAM;
196 }196 }
197 197
@@ -199,7 +199,7 @@ aclError aclrtGetEventAvailNum(uint32_t *eventCount)
199 199 
200 auto device = RunnerDB::GetById<sim::Device>(devId);200 auto device = RunnerDB::GetById<sim::Device>(devId);
201 if (!device.has_value()) {201 if (!device.has_value()) {
202- HCCL_VM_ERROR("[{}] can not get device:{:d}", __func__, devId);202+ HCCL_VM_ERROR("can not get device:{:d}", devId);
203 return ACL_ERROR_INVALID_PARAM;203 return ACL_ERROR_INVALID_PARAM;
204 }204 }
205 205 
Mtest/hccl_vm/src/proxy/aclrt_exception_stub.cc+40-40
@@ -28,42 +28,56 @@ extern "C" {
28aclError aclrtSetExceptionInfoCallback(aclrtExceptionInfoCallback callback)28aclError aclrtSetExceptionInfoCallback(aclrtExceptionInfoCallback callback)
29{29{
30 (void) callback;30 (void) callback;
31- HCCL_VM_TRACE("[{}] not supported", __func__);31+ HCCL_VM_TRACE("not supported");
32- return ACL_SUCCESS;
33-}
34- 
35-uint32_t aclrtGetErrorCodeFromExceptionInfo(const aclrtExceptionInfo *info)
36-{
37- (void) info;
38- HCCL_VM_TRACE("[{}] not supported", __func__);
39- return ACL_SUCCESS;
40-}
41- 
42-aclError aclrtPeekAtLastError(aclrtLastErrLevel level)
43-{
44- (void) level;
45- HCCL_VM_TRACE("[{}] not supported", __func__);
46 return ACL_SUCCESS;32 return ACL_SUCCESS;
47}33}
48 34 
49uint32_t aclrtGetTaskIdFromExceptionInfo(const aclrtExceptionInfo *info)35uint32_t aclrtGetTaskIdFromExceptionInfo(const aclrtExceptionInfo *info)
50{36{
51 (void) info;37 (void) info;
52- HCCL_VM_TRACE("[{}] not supported", __func__);38+ HCCL_VM_TRACE("not supported");
53 return ACL_SUCCESS;39 return ACL_SUCCESS;
54}40}
55 41 
56-aclError aclrtGetLastError(aclrtLastErrLevel level)42+uint32_t aclrtGetStreamIdFromExceptionInfo(const aclrtExceptionInfo *info)
57{43{
58- (void) level;44+ (void) info;
59- HCCL_VM_TRACE("[{}] not supported", __func__);45+ HCCL_VM_TRACE("not supported");
60 return ACL_SUCCESS;46 return ACL_SUCCESS;
61}47}
62 48 
63uint32_t aclrtGetThreadIdFromExceptionInfo(const aclrtExceptionInfo *info)49uint32_t aclrtGetThreadIdFromExceptionInfo(const aclrtExceptionInfo *info)
64{50{
65 (void) info;51 (void) info;
66- HCCL_VM_TRACE("[{}] not supported", __func__);52+ HCCL_VM_TRACE("not supported");
53+ return ACL_SUCCESS;
54+}
55+ 
56+uint32_t aclrtGetDeviceIdFromExceptionInfo(const aclrtExceptionInfo *info)
57+{
58+ (void) info;
59+ HCCL_VM_TRACE("not supported");
60+ return ACL_SUCCESS;
61+}
62+ 
63+uint32_t aclrtGetErrorCodeFromExceptionInfo(const aclrtExceptionInfo *info)
64+{
65+ (void) info;
66+ HCCL_VM_TRACE("not supported");
67+ return ACL_SUCCESS;
68+}
69+ 
70+aclError aclrtPeekAtLastError(aclrtLastErrLevel level)
71+{
72+ (void) level;
73+ HCCL_VM_TRACE("not supported");
74+ return ACL_SUCCESS;
75+}
76+ 
77+aclError aclrtGetLastError(aclrtLastErrLevel level)
78+{
79+ (void) level;
80+ HCCL_VM_TRACE("not supported");
67 return ACL_SUCCESS;81 return ACL_SUCCESS;
68}82}
69 83 
@@ -73,14 +87,7 @@ aclError aclrtGetMemUceInfo(int32_t deviceId, aclrtMemUceInfo *memUceInfoArray,
73 (void) memUceInfoArray;87 (void) memUceInfoArray;
74 (void) arraySize;88 (void) arraySize;
75 (void) retSize;89 (void) retSize;
76- HCCL_VM_TRACE("[{}] not supported", __func__);90+ HCCL_VM_TRACE("not supported");
77- return ACL_SUCCESS;
78-}
79- 
80-uint32_t aclrtGetDeviceIdFromExceptionInfo(const aclrtExceptionInfo *info)
81-{
82- (void) info;
83- HCCL_VM_TRACE("[{}] not supported", __func__);
84 return ACL_SUCCESS;91 return ACL_SUCCESS;
85}92}
86 93 
@@ -89,14 +96,7 @@ aclError aclrtMemUceRepair(int32_t deviceId, aclrtMemUceInfo *memUceInfoArray, s
89 (void) deviceId;96 (void) deviceId;
90 (void) memUceInfoArray;97 (void) memUceInfoArray;
91 (void) arraySize;98 (void) arraySize;
92- HCCL_VM_TRACE("[{}] not supported", __func__);99+ HCCL_VM_TRACE("not supported");
93- return ACL_SUCCESS;
94-}
95- 
96-uint32_t aclrtGetStreamIdFromExceptionInfo(const aclrtExceptionInfo *info)
97-{
98- (void) info;
99- HCCL_VM_TRACE("[{}] not supported", __func__);
100 return ACL_SUCCESS;100 return ACL_SUCCESS;
101}101}
102 102 
@@ -104,14 +104,14 @@ aclError aclrtDeviceTaskAbort(int32_t deviceId, uint32_t timeout)
104{104{
105 (void) deviceId;105 (void) deviceId;
106 (void) timeout;106 (void) timeout;
107- HCCL_VM_TRACE("[{}] not supported", __func__);107+ HCCL_VM_TRACE("not supported");
108 return ACL_SUCCESS;108 return ACL_SUCCESS;
109}109}
110 110 
111aclError aclRecoverAllHcclTasks(int32_t deviceId)111aclError aclRecoverAllHcclTasks(int32_t deviceId)
112{112{
113 (void) deviceId;113 (void) deviceId;
114- HCCL_VM_TRACE("[{}] not supported", __func__);114+ HCCL_VM_TRACE("not supported");
115 return ACL_SUCCESS;115 return ACL_SUCCESS;
116}116}
117 117 
@@ -119,7 +119,7 @@ aclError aclrtGetErrorVerbose(int32_t deviceId, aclrtErrorInfo *errorInfo)
119{119{
120 (void) deviceId;120 (void) deviceId;
121 (void) errorInfo;121 (void) errorInfo;
122- HCCL_VM_TRACE("[{}] not supported", __func__);122+ HCCL_VM_TRACE("not supported");
123 return ACL_SUCCESS;123 return ACL_SUCCESS;
124}124}
125 125 
@@ -127,7 +127,7 @@ aclError aclrtRepairError(int32_t deviceId, const aclrtErrorInfo *errorInfo)
127{127{
128 (void) deviceId;128 (void) deviceId;
129 (void) errorInfo;129 (void) errorInfo;
130- HCCL_VM_TRACE("[{}] not supported", __func__);130+ HCCL_VM_TRACE("not supported");
131 return ACL_SUCCESS;131 return ACL_SUCCESS;
132}132}
133#ifdef __cplusplus133#ifdef __cplusplus
Mtest/hccl_vm/src/proxy/aclrt_exec_control.cc+13-13
@@ -31,7 +31,7 @@ aclError aclrtLaunchCallback(aclrtCallback fn, void *userData, aclrtCallbackBloc
31 (void) userData;31 (void) userData;
32 (void) blockType;32 (void) blockType;
33 (void) stream;33 (void) stream;
34- HCCL_VM_TRACE("[{}] not supported", __func__);34+ HCCL_VM_TRACE("not supported");
35 return ACL_SUCCESS;35 return ACL_SUCCESS;
36}36}
37 37 
@@ -39,14 +39,14 @@ aclError aclrtSubscribeReport(uint64_t threadId, aclrtStream stream)
39{39{
40 (void) threadId;40 (void) threadId;
41 (void) stream;41 (void) stream;
42- HCCL_VM_TRACE("[{}] not supported", __func__);42+ HCCL_VM_TRACE("not supported");
43 return ACL_SUCCESS;43 return ACL_SUCCESS;
44}44}
45 45 
46aclError aclrtProcessReport(int32_t timeout)46aclError aclrtProcessReport(int32_t timeout)
47{47{
48 (void) timeout;48 (void) timeout;
49- HCCL_VM_TRACE("[{}] not supported", __func__);49+ HCCL_VM_TRACE("not supported");
50 return ACL_SUCCESS;50 return ACL_SUCCESS;
51}51}
52 52 
@@ -54,7 +54,7 @@ aclError aclrtUnSubscribeReport(uint64_t threadId, aclrtStream stream)
54{54{
55 (void) threadId;55 (void) threadId;
56 (void) stream;56 (void) stream;
57- HCCL_VM_TRACE("[{}] not supported", __func__);57+ HCCL_VM_TRACE("not supported");
58 return ACL_SUCCESS;58 return ACL_SUCCESS;
59}59}
60 60 
@@ -62,14 +62,14 @@ aclError aclrtSubscribeHostFunc(uint64_t hostFuncThreadId, aclrtStream exeStream
62{62{
63 (void) hostFuncThreadId;63 (void) hostFuncThreadId;
64 (void) exeStream;64 (void) exeStream;
65- HCCL_VM_TRACE("[{}] not supported", __func__);65+ HCCL_VM_TRACE("not supported");
66 return ACL_SUCCESS;66 return ACL_SUCCESS;
67}67}
68 68 
69aclError aclrtProcessHostFunc(int32_t timeout)69aclError aclrtProcessHostFunc(int32_t timeout)
70{70{
71 (void) timeout;71 (void) timeout;
72- HCCL_VM_TRACE("[{}] not supported", __func__);72+ HCCL_VM_TRACE("not supported");
73 return ACL_SUCCESS;73 return ACL_SUCCESS;
74}74}
75 75 
@@ -77,21 +77,21 @@ aclError aclrtUnSubscribeHostFunc(uint64_t hostFuncThreadId, aclrtStream exeStre
77{77{
78 (void) hostFuncThreadId;78 (void) hostFuncThreadId;
79 (void) exeStream;79 (void) exeStream;
80- HCCL_VM_TRACE("[{}] not supported", __func__);80+ HCCL_VM_TRACE("not supported");
81 return ACL_SUCCESS;81 return ACL_SUCCESS;
82}82}
83 83 
84aclError aclrtGetOpTimeoutInterval(uint64_t *interval)84aclError aclrtGetOpTimeoutInterval(uint64_t *interval)
85{85{
86 (void) interval;86 (void) interval;
87- HCCL_VM_TRACE("[{}] not supported", __func__);87+ HCCL_VM_TRACE("not supported");
88 return ACL_SUCCESS;88 return ACL_SUCCESS;
89}89}
90 90 
91aclError aclrtSetOpExecuteTimeOut(uint32_t timeout)91aclError aclrtSetOpExecuteTimeOut(uint32_t timeout)
92{92{
93 (void) timeout;93 (void) timeout;
94- HCCL_VM_TRACE("[{}] not supported", __func__);94+ HCCL_VM_TRACE("not supported");
95 return ACL_SUCCESS;95 return ACL_SUCCESS;
96}96}
97 97 
@@ -99,21 +99,21 @@ aclError aclrtSetOpExecuteTimeOutV2(uint64_t timeout, uint64_t *actualTimeout)
99{99{
100 (void) timeout;100 (void) timeout;
101 (void) actualTimeout;101 (void) actualTimeout;
102- HCCL_VM_TRACE("[{}] not supported", __func__);102+ HCCL_VM_TRACE("not supported");
103 return ACL_SUCCESS;103 return ACL_SUCCESS;
104}104}
105 105 
106aclError aclrtSetOpExecuteTimeOutWithMs(uint32_t timeout)106aclError aclrtSetOpExecuteTimeOutWithMs(uint32_t timeout)
107{107{
108 (void) timeout;108 (void) timeout;
109- HCCL_VM_TRACE("[{}] not supported", __func__);109+ HCCL_VM_TRACE("not supported");
110 return ACL_SUCCESS;110 return ACL_SUCCESS;
111}111}
112 112 
113aclError aclrtGetThreadLastTaskId(uint32_t *taskId)113aclError aclrtGetThreadLastTaskId(uint32_t *taskId)
114{114{
115 (void) taskId;115 (void) taskId;
116- HCCL_VM_TRACE("[{}] not supported", __func__);116+ HCCL_VM_TRACE("not supported");
117 return ACL_SUCCESS;117 return ACL_SUCCESS;
118}118}
119 119 
@@ -122,7 +122,7 @@ aclError aclrtLaunchHostFunc(aclrtStream stream, aclrtHostFunc fn, void *args)
122 (void) stream;122 (void) stream;
123 (void) fn;123 (void) fn;
124 (void) args;124 (void) args;
125- HCCL_VM_TRACE("[{}] not supported", __func__);125+ HCCL_VM_TRACE("not supported");
126 return ACL_SUCCESS;126 return ACL_SUCCESS;
127}127}
128#ifdef __cplusplus128#ifdef __cplusplus
Mtest/hccl_vm/src/proxy/aclrt_kernel_stub.cc+40-6
@@ -7,7 +7,10 @@
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.8 * See LICENSE in the root of the software repository for the full text of the License.
9 */9 */
10- 10+#include <errno.h>
11+#include <signal.h>
12+#include <sys/types.h>
13+#include <sys/wait.h>
11#include <atomic>14#include <atomic>
12#include <atomic>15#include <atomic>
13#include <chrono>16#include <chrono>
@@ -36,6 +39,31 @@ struct ArgsBuffer {
36 39 
37pid_t g_devicePid = 0;40pid_t g_devicePid = 0;
38 41 
42+static bool CheckDeviceProcStatus()
43+{
44+ if (g_devicePid == 0) {
45+ return false;
46+ }
47+ int status = 0;
48+ pid_t result = waitpid(g_devicePid, &status, WNOHANG);
49+ if (result == 0) {
50+ return true;
51+ }
52+ if (result == g_devicePid) {
53+ if (WIFSIGNALED(status)) {
54+ HCCL_VM_ERROR("device process[{}] killed by signal {}", g_devicePid, WTERMSIG(status));
55+ } else {
56+ HCCL_VM_ERROR("device process[{}] exited with status {}", g_devicePid, WEXITSTATUS(status));
57+ }
58+ } else {
59+ HCCL_VM_ERROR("waitpid failed for pid {}, errno: {} ({})", g_devicePid, errno, strerror(errno));
60+ }
61+ FlushLog();
62+ exit(EXIT_FAILURE);
63+ g_devicePid = 0;
64+ return false;
65+}
66+ 
39namespace sim {67namespace sim {
40constexpr size_t MAX_ARGS_BUFF_SIZE = 64 * 1024U;68constexpr size_t MAX_ARGS_BUFF_SIZE = 64 * 1024U;
41 69 
@@ -396,18 +424,23 @@ void ForkAndStartAicpuProcess(int32_t rankId, uint8_t* devState)
396 exit(EXIT_FAILURE);424 exit(EXIT_FAILURE);
397 }425 }
398 426 
427+ const char* installDir = getenv("HCCL_VM_INSTALL_DIR");
428+ std::string args = std::to_string(rankId);
429+ std::string devicePath = installDir ? std::string(installDir) + "/bin/device" : "./bin/device";
430+ std::string libPath = installDir ? std::string(installDir) + "/lib/aarch64" : "./lib/aarch64";
431+ std::string preloadPath = libPath + "/libhccl-device-proxy.so";
399 if (pid == 0) {432 if (pid == 0) {
400- std::string args = std::to_string(rankId);433+ g_logger = nullptr;
401- HCCL_VM_INFO("rankId[{}] aicpu process start.", rankId);
402 setenv("QEMU_LD_PREFIX", "/usr/aarch64-linux-gnu", 1);434 setenv("QEMU_LD_PREFIX", "/usr/aarch64-linux-gnu", 1);
403- setenv("LD_PRELOAD", "./device/lib/libhccl-device-proxy.so", 1);435+ setenv("LD_PRELOAD", preloadPath.c_str(), 1);
404- setenv("LD_LIBRARY_PATH", "./device/lib64:./device/lib", 1);436+ setenv("LD_LIBRARY_PATH", libPath.c_str(), 1);
405- execlp("qemu-aarch64-static", "qemu-aarch64-static", "./device/bin/device", args.c_str(), nullptr);437+ execlp("qemu-aarch64-static", "qemu-aarch64-static", devicePath.c_str(), args.c_str(), nullptr);
406 HCCL_VM_ERROR("[ForkAndStartAicpuProcess] execlp aicpu process failed.");438 HCCL_VM_ERROR("[ForkAndStartAicpuProcess] execlp aicpu process failed.");
407 exit(EXIT_FAILURE);439 exit(EXIT_FAILURE);
408 } else {440 } else {
409 g_devicePid = pid; // 记录device进程id用于host结束时杀掉device进程441 g_devicePid = pid; // 记录device进程id用于host结束时杀掉device进程
410 while (*devState == DEVICE_RUN) {442 while (*devState == DEVICE_RUN) {
443+ CheckDeviceProcStatus();
411 sleep(1);444 sleep(1);
412 }445 }
413 }446 }
@@ -456,6 +489,7 @@ void LaunchAICPUKernelFunc(std::string kernelName, aclrtArgsHandle argsHandle)
456 std::atomic_thread_fence(std::memory_order_release); // 内存屏障强制内存操作的顺序性489 std::atomic_thread_fence(std::memory_order_release); // 内存屏障强制内存操作的顺序性
457 aicpuData->task[rankId].devState = DEVICE_RUN;490 aicpuData->task[rankId].devState = DEVICE_RUN;
458 while (aicpuData->task[rankId].devState == DEVICE_RUN) {491 while (aicpuData->task[rankId].devState == DEVICE_RUN) {
492+ CheckDeviceProcStatus();
459 sleep(1);493 sleep(1);
460 }494 }
461}495}
Mtest/hccl_vm/src/proxy/aclrt_memory_stub.cc+141-15
@@ -20,14 +20,38 @@
20#include "store_sim_store_pub.h"20#include "store_sim_store_pub.h"
21#include "sim_log.h"21#include "sim_log.h"
22#include "rt_external_mem.h"22#include "rt_external_mem.h"
23+#include "store_sim_comm_pool_policy.h"
23#include "store_sim_device_memory_manager.h"24#include "store_sim_device_memory_manager.h"
25+#include "store_sim_memory_manager.h"
26+#include "store_sim_run_mode.h"
24#include "sim_models.h"27#include "sim_models.h"
25#include "db_sim_runner_ops.h"28#include "db_sim_runner_ops.h"
26 29 
30+// rank 进程加载本库时先于 main 预热仅校验模式缓存,使后续引流判定拿到确定值。
31+// 只读 DB、不打日志(此刻日志组件可能尚未初始化)。
27#ifdef __cplusplus32#ifdef __cplusplus
28extern "C" {33extern "C" {
29#endif // __cplusplus34#endif // __cplusplus
30 35 
36+// 主机侧大块复用区句柄(懒加载缓存,与设备侧共用同一块 HcclCommPool)
37+static std::atomic<void*> g_commPoolBase{nullptr};
38+ 
39+// 记下复用区在本进程的地址,首个大块申请时由 CAS 保证只有一个线程写入。
40+// 首次记录时多 acquire 一次复用区且进程内不释放,使复用区在进程退出前保持映射、地址不变,
41+// 后续用地址判断一块内存是否在复用区时地址不会失效。
42+static void CacheCommPoolBase(void* base)
43+{
44+ if (base == nullptr) {
45+ return;
46+ }
47+ void* expected = nullptr;
48+ // 写入用 release、IsInCommPool 读取用 acquire 配对,保证其它线程读到的地址是有效的。
49+ if (g_commPoolBase.compare_exchange_strong(expected, base,
50+ std::memory_order_release, std::memory_order_relaxed)) {
51+ sim::MemoryManager::GetInstance().AcquireMemByName(sim::CommPoolPolicy::kPoolName);
52+ }
53+}
54+ 
31std::string GenDevMemName(int deviceId)55std::string GenDevMemName(int deviceId)
32{56{
33 std::ostringstream oss;57 std::ostringstream oss;
@@ -101,10 +125,42 @@ void* GetRealPtrByAddr(const void* virPtr)
101 return hostPtr;125 return hostPtr;
102}126}
103 127 
128+// 判断一个真实内存地址是否落在复用区内,主机侧和设备侧共用。
129+// 设备侧传进来的是设备指针对应的真实地址(用 GetHostPtrByDevPtr 取得)。
130+// 地址在 [基址, 基址+kPoolSize) 范围内,说明这块内存是引流到复用区的大块。
131+static bool IsInCommPool(const void* ptr)
132+{
133+ void* base = g_commPoolBase.load(std::memory_order_acquire);
134+ return base != nullptr &&
135+ ptr >= base &&
136+ ptr < static_cast<char *>(base) + sim::CommPoolPolicy::kPoolSize;
137+}
138+ 
104aclError aclrtMallocHost(void **hostPtr, size_t size)139aclError aclrtMallocHost(void **hostPtr, size_t size)
105{140{
106- *hostPtr = malloc(size);141+ bool checkOnlyMode = sim::IsCheckOnlyMode();
107- HCCL_VM_INFO("[MEM] malloc host addr:{:p}, size:{:d}", *hostPtr, size);142+ // 超过复用区上界直接报错,不回退真实分配。
143+ if (sim::CommPoolPolicy::ExceedsCeiling(size, checkOnlyMode)) {
144+ HCCL_VM_ERROR("[MEM] malloc host size:{:d} exceeds pool ceiling, reject", size);
145+ return ACL_ERROR_INTERNAL_ERROR;
146+ }
147+ // 仅校验模式下大块引流到共享复用区 HcclCommPool
148+ if (sim::CommPoolPolicy::ShouldRedirect(size, checkOnlyMode)) {
149+ void* base = g_commPoolBase.load(std::memory_order_acquire);
150+ if (base == nullptr) {
151+ base = sim::MemoryManager::GetInstance().AcquireMemByName(sim::CommPoolPolicy::kPoolName);
152+ if (base == nullptr) {
153+ HCCL_VM_ERROR("[MEM] malloc host acquire HcclCommPool failed, size:{:d}", size);
154+ return ACL_ERROR_INTERNAL_ERROR;
155+ }
156+ CacheCommPoolBase(base);
157+ }
158+ *hostPtr = base;
159+ HCCL_VM_INFO("[MEM] malloc host (SHM) addr:{:p}, size:{:d}", *hostPtr, size);
160+ } else {
161+ *hostPtr = malloc(size);
162+ HCCL_VM_INFO("[MEM] malloc host addr:{:p}, size:{:d}", *hostPtr, size);
163+ }
108 return ACL_SUCCESS;164 return ACL_SUCCESS;
109}165}
110 166 
@@ -117,6 +173,11 @@ aclError aclrtMallocHostWithCfg(void **ptr, uint64_t size, aclrtMallocConfig *cf
117aclError aclrtFreeHost(void *hostPtr)173aclError aclrtFreeHost(void *hostPtr)
118{174{
119 HCCL_VM_INFO("[MEM] free host addr:{:p}", hostPtr);175 HCCL_VM_INFO("[MEM] free host addr:{:p}", hostPtr);
176+ // 池内地址:空操作(复用区生命周期由主进程管理,不在此 free)
177+ if (IsInCommPool(hostPtr)) {
178+ HCCL_VM_INFO("[MEM] free host (SHM noop) addr:{:p}", hostPtr);
179+ return ACL_SUCCESS;
180+ }
120 free(hostPtr);181 free(hostPtr);
121 return ACL_SUCCESS;182 return ACL_SUCCESS;
122}183}
@@ -156,6 +217,10 @@ aclError aclrtMalloc(void **devPtr, size_t size, aclrtMemMallocPolicy policy)
156 HCCL_VM_ERROR("can not alloc phy mem deviceId:{:d}, size:{:d}", deviceId, size);217 HCCL_VM_ERROR("can not alloc phy mem deviceId:{:d}, size:{:d}", deviceId, size);
157 return ACL_ERROR_INTERNAL_ERROR;218 return ACL_ERROR_INTERNAL_ERROR;
158 }219 }
220+ // 大块引流到复用区时缓存池基址,供 IsInCommPool 判定(与主机侧共用同一基址)。
221+ if (sim::CommPoolPolicy::ShouldRedirect(size, sim::IsCheckOnlyMode())) {
222+ CacheCommPoolBase(hostPtr);
223+ }
159 224 
160 // 记录物理内存信息到数据库225 // 记录物理内存信息到数据库
161 sim::PhyMemBlock phyMem{};226 sim::PhyMemBlock phyMem{};
@@ -247,12 +312,20 @@ aclError aclrtFree(void* devPtr)
247 312 
248 auto devPhyId = dev->physical_id;313 auto devPhyId = dev->physical_id;
249 314 
315+ // 先取这块内存的真实地址,判断它是否在复用区(必须在 Unmap 之前取)。
316+ // 第二次释放时映射已删、取不到地址,就按非复用区处理,天然幂等。
317+ void* backing = sim::DeviceMemoryManager::GetInstance().GetHostPtrByDevPtr(devPtr);
318+ 
250 // 去缓存设备地址到host地址319 // 去缓存设备地址到host地址
251 sim::DeviceMemoryManager::GetInstance().UnmapDevPtrHostPtr(devPtr);320 sim::DeviceMemoryManager::GetInstance().UnmapDevPtrHostPtr(devPtr);
252 321 
253 // 删除内存322 // 删除内存
254 sim::DeviceMemoryManager::GetInstance().FreeVirMem(devPhyId, devPtr);323 sim::DeviceMemoryManager::GetInstance().FreeVirMem(devPhyId, devPtr);
255- sim::DeviceMemoryManager::GetInstance().FreePhyMem(phyMemRes->name, deviceId);324+ if (IsInCommPool(backing)) {
325+ sim::MemoryManager::GetInstance().ReleaseMemByName(sim::CommPoolPolicy::kPoolName);
326+ } else {
327+ sim::DeviceMemoryManager::GetInstance().FreePhyMem(phyMemRes->name, deviceId);
328+ }
256 329 
257 // 更新数据库记录,标记为已释放330 // 更新数据库记录,标记为已释放
258 RunnerDB::Update<sim::PhyMemBlock>(phyMemId, [](sim::PhyMemBlock &memBlock) { memBlock.is_freed = 1; });331 RunnerDB::Update<sim::PhyMemBlock>(phyMemId, [](sim::PhyMemBlock &memBlock) { memBlock.is_freed = 1; });
@@ -278,8 +351,18 @@ aclError aclrtMemset(void *devPtr, size_t maxCount, int32_t value, size_t count)
278 return ACL_ERROR_INTERNAL_ERROR;351 return ACL_ERROR_INTERNAL_ERROR;
279 }352 }
280 353 
281- memset(hostPtr + offset, value, count);354+ // 目的设备块按 size 判是否在复用区,与申请一致,命中则跳过实际写入并按池释放。
282- sim::DeviceMemoryManager::GetInstance().ReleasePhyMem(phyMem.name, phyMem.device_id);355+ bool pooled = sim::CommPoolPolicy::ShouldRedirect(phyMem.size, sim::IsCheckOnlyMode());
356+ if (pooled) {
357+ HCCL_VM_INFO("[MEM] memset skip (pool) ptr:{:p}, size:{:d}, count:{:d}", devPtr, phyMem.size, count);
358+ } else {
359+ memset(hostPtr + offset, value, count);
360+ }
361+ if (pooled) {
362+ sim::MemoryManager::GetInstance().ReleaseMemByName(sim::CommPoolPolicy::kPoolName);
363+ } else {
364+ sim::DeviceMemoryManager::GetInstance().ReleasePhyMem(phyMem.name, phyMem.device_id);
365+ }
283 HCCL_VM_INFO("[MEM] dev mem memset ptr:{:p}, memName:{}, count: {:d}", devPtr, phyMem.name, count);366 HCCL_VM_INFO("[MEM] dev mem memset ptr:{:p}, memName:{}, count: {:d}", devPtr, phyMem.name, count);
284 return ACL_SUCCESS;367 return ACL_SUCCESS;
285}368}
@@ -316,7 +399,12 @@ aclError aclrtMemcpy(void *dst, size_t destMax, const void *src, size_t count, a
316 HCCL_VM_WARN("[aclrtMemcpy D2D] only support self D2D memcpy");399 HCCL_VM_WARN("[aclrtMemcpy D2D] only support self D2D memcpy");
317 return ACL_ERROR_RT_FEATURE_NOT_SUPPORT;400 return ACL_ERROR_RT_FEATURE_NOT_SUPPORT;
318 }401 }
319- memcpy(dstAddr, srcAddr, count);402+ // 目的地址在复用区(dst 是引流过来的大块):跳过实际拷贝。
403+ if (IsInCommPool(dstAddr)) {
404+ HCCL_VM_INFO("[MEM] memcpy D2D skip (dst big->pool) count:{:d}", count);
405+ } else {
406+ memcpy(dstAddr, srcAddr, count);
407+ }
320 return ACL_SUCCESS;408 return ACL_SUCCESS;
321 } else {409 } else {
322 HCCL_VM_INFO("src:{:p} to dst:{:p}, size:{:d} type: {:d} not support", src, dst, count, (int)kind);410 HCCL_VM_INFO("src:{:p} to dst:{:p}, size:{:d} type: {:d} not support", src, dst, count, (int)kind);
@@ -337,13 +425,28 @@ aclError aclrtMemcpy(void *dst, size_t destMax, const void *src, size_t count, a
337 return ACL_ERROR_INTERNAL_ERROR;425 return ACL_ERROR_INTERNAL_ERROR;
338 }426 }
339 427 
428+ // 目的设备块按 size 判是否在复用区,与申请一致,命中则跳过实际拷贝并按池释放。
429+ // D2H 的目的是主机缓冲,仍按地址判断主机缓冲是否在复用区。
430+ bool devPooled = sim::CommPoolPolicy::ShouldRedirect(phyMem.size, sim::IsCheckOnlyMode());
340 if (kind == ACL_MEMCPY_HOST_TO_DEVICE) {431 if (kind == ACL_MEMCPY_HOST_TO_DEVICE) {
341- memcpy(hostDevPtr + offset, hostPtr, count);432+ if (devPooled) {
433+ HCCL_VM_INFO("[MEM] memcpy H2D skip (dst big->pool) count:{:d}", count);
434+ } else {
435+ memcpy(hostDevPtr + offset, hostPtr, count);
436+ }
342 } else if (kind == ACL_MEMCPY_DEVICE_TO_HOST) {437 } else if (kind == ACL_MEMCPY_DEVICE_TO_HOST) {
343- memcpy(hostPtr, hostDevPtr + offset, count);438+ if (IsInCommPool(hostPtr)) {
439+ HCCL_VM_INFO("[MEM] memcpy D2H skip (dst host pool) count:{:d}", count);
440+ } else {
441+ memcpy(hostPtr, hostDevPtr + offset, count);
442+ }
344 }443 }
345 444 
346- sim::DeviceMemoryManager::GetInstance().ReleasePhyMem(phyMem.name, phyMem.device_id);445+ if (devPooled) {
446+ sim::MemoryManager::GetInstance().ReleaseMemByName(sim::CommPoolPolicy::kPoolName);
447+ } else {
448+ sim::DeviceMemoryManager::GetInstance().ReleasePhyMem(phyMem.name, phyMem.device_id);
449+ }
347 450 
348 return ACL_SUCCESS;451 return ACL_SUCCESS;
349}452}
@@ -411,13 +514,28 @@ aclError aclrtMemcpyAsync(void *dst, size_t destMax, const void *src, size_t cou
411 return ACL_ERROR_INTERNAL_ERROR;514 return ACL_ERROR_INTERNAL_ERROR;
412 }515 }
413 516 
517+ // 目的设备块按 size 判是否在复用区,与申请一致,命中则跳过实际拷贝并按池释放。
518+ // D2H 的目的是主机缓冲,仍按地址判断主机缓冲是否在复用区。
519+ bool devPooled = sim::CommPoolPolicy::ShouldRedirect(phyMem.size, sim::IsCheckOnlyMode());
414 if (kind == ACL_MEMCPY_HOST_TO_DEVICE) {520 if (kind == ACL_MEMCPY_HOST_TO_DEVICE) {
415- memcpy(hostDevPtr + offset, hostPtr, count);521+ if (devPooled) {
522+ HCCL_VM_INFO("[MEM] memcpy H2D skip (dst big->pool) count:{:d}", count);
523+ } else {
524+ memcpy(hostDevPtr + offset, hostPtr, count);
525+ }
416 } else if (kind == ACL_MEMCPY_DEVICE_TO_HOST) {526 } else if (kind == ACL_MEMCPY_DEVICE_TO_HOST) {
417- memcpy(hostPtr, hostDevPtr + offset, count);527+ if (IsInCommPool(hostPtr)) {
528+ HCCL_VM_INFO("[MEM] memcpy D2H skip (dst host pool) count:{:d}", count);
529+ } else {
530+ memcpy(hostPtr, hostDevPtr + offset, count);
531+ }
418 }532 }
419 533 
420- sim::DeviceMemoryManager::GetInstance().ReleasePhyMem(phyMem.name, phyMem.device_id);534+ if (devPooled) {
535+ sim::MemoryManager::GetInstance().ReleaseMemByName(sim::CommPoolPolicy::kPoolName);
536+ } else {
537+ sim::DeviceMemoryManager::GetInstance().ReleasePhyMem(phyMem.name, phyMem.device_id);
538+ }
421 539 
422 return ACL_SUCCESS;540 return ACL_SUCCESS;
423}541}
@@ -529,6 +647,10 @@ aclError aclrtMallocPhysical(aclrtDrvMemHandle *handle, size_t size, const aclrt
529 HCCL_VM_ERROR("can not alloc phy mem deviceId:{:d}, size:{:d}", deviceId, size);647 HCCL_VM_ERROR("can not alloc phy mem deviceId:{:d}, size:{:d}", deviceId, size);
530 return ACL_ERROR_INTERNAL_ERROR;648 return ACL_ERROR_INTERNAL_ERROR;
531 }649 }
650+ // 大块物理内存同样引流到复用区,缓存池基址,使其被 map 后的 memcpy/memset 能按地址判定。
651+ if (sim::CommPoolPolicy::ShouldRedirect(size, sim::IsCheckOnlyMode())) {
652+ CacheCommPoolBase(hostPtr);
653+ }
532 654 
533 // 记录物理内存信息到数据库655 // 记录物理内存信息到数据库
534 sim::PhyMemBlock phyMem{};656 sim::PhyMemBlock phyMem{};
@@ -552,8 +674,12 @@ aclError aclrtFreePhysical(aclrtDrvMemHandle handle)
552 return ACL_ERROR_INTERNAL_ERROR;674 return ACL_ERROR_INTERNAL_ERROR;
553 };675 };
554 676 
555- // 删除内存677+ // 物理内存无 devPtr→host 映射,按 size 判定是否走了复用区(与申请同一判据)
556- sim::DeviceMemoryManager::GetInstance().FreePhyMem(phyMemRes->name, phyMemRes->device_id);678+ if (sim::CommPoolPolicy::ShouldRedirect(phyMemRes->size, sim::IsCheckOnlyMode())) {
679+ sim::MemoryManager::GetInstance().ReleaseMemByName(sim::CommPoolPolicy::kPoolName);
680+ } else {
681+ sim::DeviceMemoryManager::GetInstance().FreePhyMem(phyMemRes->name, phyMemRes->device_id);
682+ }
557 // 删除数据库记录683 // 删除数据库记录
558 RunnerDB::Delete<sim::PhyMemBlock>(phyMemId);684 RunnerDB::Delete<sim::PhyMemBlock>(phyMemId);
559 HCCL_VM_INFO("free phy mem, id:{:d}", phyMemId);685 HCCL_VM_INFO("free phy mem, id:{:d}", phyMemId);
@@ -1083,7 +1209,7 @@ aclError aclrtReduceAsync(void *dst, const void *src, uint64_t count, aclrtReduc
1083 HCCL_VM_DEBUG("[aclstub][aclrtReduceAsync] Get reduce task, streamId={:d}", streamId);1209 HCCL_VM_DEBUG("[aclstub][aclrtReduceAsync] Get reduce task, streamId={:d}", streamId);
1084 auto ret = InsertTaskToCollection(&taskMetaData, &index);1210 auto ret = InsertTaskToCollection(&taskMetaData, &index);
1085 if (ret != HcclSim::HcclVmResult::HCCL_SIM_SUCCESS) {1211 if (ret != HcclSim::HcclVmResult::HCCL_SIM_SUCCESS) {
1086- HCCL_VM_ERROR("[{}] InsertTaskToCollection fail", __func__);1212+ HCCL_VM_ERROR("[aclstub] InsertTaskToCollection fail");
1087 return ACL_ERROR_INTERNAL_ERROR;1213 return ACL_ERROR_INTERNAL_ERROR;
1088 }1214 }
1089 1215 
Mtest/hccl_vm/src/proxy/aclrt_new_stub.cc+3-3
@@ -37,16 +37,16 @@ HcclResult hrtGetDeviceType(DevType &devType)
37 auto device = RunnerDB::GetById<sim::Device>(devKey);37 auto device = RunnerDB::GetById<sim::Device>(devKey);
38 if (!device.has_value()) {38 if (!device.has_value()) {
39 // not find39 // not find
40- HCCL_VM_ERROR("[{}] can not get current device type: {:d}", __func__, devKey);40+ HCCL_VM_ERROR("[aclstub] can not get current device type: {:d}", devKey);
41 return HCCL_E_NOT_FOUND;41 return HCCL_E_NOT_FOUND;
42 }42 }
43 if (strcmp(device->soc_version, "Ascend950") == 0) {43 if (strcmp(device->soc_version, "Ascend950") == 0) {
44 devType = DevType::DEV_TYPE_950;44 devType = DevType::DEV_TYPE_950;
45 } else {45 } else {
46- HCCL_VM_ERROR("[{}] not support device soc version: {:s}", __func__, device->soc_version);46+ HCCL_VM_ERROR("[aclstub] not support device soc version: {:s}", device->soc_version);
47 return HCCL_E_NOT_SUPPORT;47 return HCCL_E_NOT_SUPPORT;
48 }48 }
49- HCCL_VM_TRACE("[{}] Get current device type: {}", __func__, static_cast<int>(devType));49+ HCCL_VM_TRACE("[aclstub] Get current device type: {}", static_cast<int>(devType));
50 return HCCL_SUCCESS;50 return HCCL_SUCCESS;
51}51}
52 52 
Mtest/hccl_vm/src/proxy/aclrt_notify_stub.cc+2-2
@@ -119,7 +119,7 @@ aclError aclrtRecordNotify(aclrtNotify notify, aclrtStream stream)
119 HCCL_VM_DEBUG("[aclstub][aclrtRecordNotify] Get notify task, id={:d}, streamId={:d}", notifyId, streamId);119 HCCL_VM_DEBUG("[aclstub][aclrtRecordNotify] Get notify task, id={:d}, streamId={:d}", notifyId, streamId);
120 auto ret = InsertTaskToCollection(&taskMetaData, &index);120 auto ret = InsertTaskToCollection(&taskMetaData, &index);
121 if (ret != HcclSim::HcclVmResult::HCCL_SIM_SUCCESS) {121 if (ret != HcclSim::HcclVmResult::HCCL_SIM_SUCCESS) {
122- HCCL_VM_ERROR("[{}] InsertTaskToCollection fail", __func__);122+ HCCL_VM_ERROR("[aclstub] InsertTaskToCollection fail");
123 return ACL_ERROR_INTERNAL_ERROR;123 return ACL_ERROR_INTERNAL_ERROR;
124 }124 }
125 125 
@@ -156,7 +156,7 @@ aclError aclrtWaitAndResetNotify(aclrtNotify notify, aclrtStream stream, uint32_
156 HCCL_VM_DEBUG("[aclstub][aclrtWaitAndResetNotify] Get notify task, id={:d}, streamId={:d}", notifyId, streamId);156 HCCL_VM_DEBUG("[aclstub][aclrtWaitAndResetNotify] Get notify task, id={:d}, streamId={:d}", notifyId, streamId);
157 auto ret = InsertTaskToCollection(&taskMetaData, &index);157 auto ret = InsertTaskToCollection(&taskMetaData, &index);
158 if (ret != HcclSim::HcclVmResult::HCCL_SIM_SUCCESS) {158 if (ret != HcclSim::HcclVmResult::HCCL_SIM_SUCCESS) {
159- HCCL_VM_ERROR("[{}] InsertTaskToCollection fail", __func__);159+ HCCL_VM_ERROR("[aclstub] InsertTaskToCollection fail");
160 return ACL_ERROR_INTERNAL_ERROR;160 return ACL_ERROR_INTERNAL_ERROR;
161 }161 }
162 162 
Mtest/hccl_vm/src/proxy/aclrt_runtime_config.cc+19-10
@@ -31,7 +31,7 @@ aclError aclrtSetSysParamOpt(aclSysParamOpt opt, int64_t value)
31{31{
32 (void) opt;32 (void) opt;
33 (void) value;33 (void) value;
34- HCCL_VM_WARN("[{}] not support", __func__);34+ HCCL_VM_WARN("[aclstub] not support");
35 return ACL_SUCCESS;35 return ACL_SUCCESS;
36}36}
37 37 
@@ -39,7 +39,7 @@ aclError aclrtGetSysParamOpt(aclSysParamOpt opt, int64_t *value)
39{39{
40 (void) opt;40 (void) opt;
41 (void) value;41 (void) value;
42- HCCL_VM_WARN("[{}] not support", __func__);42+ HCCL_VM_WARN("[aclstub] not support");
43 return ACL_SUCCESS;43 return ACL_SUCCESS;
44}44}
45 45 
@@ -48,7 +48,7 @@ aclError aclrtGetDeviceResLimit(int32_t deviceId, aclrtDevResLimitType type, uin
48 (void) deviceId;48 (void) deviceId;
49 (void) type;49 (void) type;
50 (void) value;50 (void) value;
51- HCCL_VM_WARN("[{}] not support", __func__);51+ HCCL_VM_WARN("[aclstub] not support");
52 return ACL_SUCCESS;52 return ACL_SUCCESS;
53}53}
54 54 
@@ -57,14 +57,14 @@ aclError aclrtSetDeviceResLimit(int32_t deviceId, aclrtDevResLimitType type, uin
57 (void) deviceId;57 (void) deviceId;
58 (void) type;58 (void) type;
59 (void) value;59 (void) value;
60- HCCL_VM_WARN("[{}] not support", __func__);60+ HCCL_VM_WARN("[aclstub] not support");
61 return ACL_SUCCESS;61 return ACL_SUCCESS;
62}62}
63 63 
64aclError aclrtResetDeviceResLimit(int32_t deviceId)64aclError aclrtResetDeviceResLimit(int32_t deviceId)
65{65{
66 (void) deviceId;66 (void) deviceId;
67- HCCL_VM_WARN("[{}] not support", __func__);67+ HCCL_VM_WARN("[aclstub] not support");
68 return ACL_SUCCESS;68 return ACL_SUCCESS;
69}69}
70 70 
@@ -73,7 +73,7 @@ aclError aclrtGetStreamResLimit(aclrtStream stream, aclrtDevResLimitType type, u
73 (void) stream;73 (void) stream;
74 (void) type;74 (void) type;
75 (void) value;75 (void) value;
76- HCCL_VM_WARN("[{}] not support", __func__);76+ HCCL_VM_WARN("[aclstub] not support");
77 return ACL_SUCCESS;77 return ACL_SUCCESS;
78}78}
79 79 
@@ -82,28 +82,28 @@ aclError aclrtSetStreamResLimit(aclrtStream stream, aclrtDevResLimitType type, u
82 (void) stream;82 (void) stream;
83 (void) type;83 (void) type;
84 (void) value;84 (void) value;
85- HCCL_VM_WARN("[{}] not support", __func__);85+ HCCL_VM_WARN("[aclstub] not support");
86 return ACL_SUCCESS;86 return ACL_SUCCESS;
87}87}
88 88 
89aclError aclrtResetStreamResLimit(aclrtStream stream)89aclError aclrtResetStreamResLimit(aclrtStream stream)
90{90{
91 (void) stream;91 (void) stream;
92- HCCL_VM_WARN("[{}] not support", __func__);92+ HCCL_VM_WARN("[aclstub] not support");
93 return ACL_SUCCESS;93 return ACL_SUCCESS;
94}94}
95 95 
96aclError aclrtUseStreamResInCurrentThread(aclrtStream stream)96aclError aclrtUseStreamResInCurrentThread(aclrtStream stream)
97{97{
98 (void) stream;98 (void) stream;
99- HCCL_VM_WARN("[{}] not support", __func__);99+ HCCL_VM_WARN("[aclstub] not support");
100 return ACL_SUCCESS;100 return ACL_SUCCESS;
101}101}
102 102 
103aclError aclrtUnuseStreamResInCurrentThread(aclrtStream stream)103aclError aclrtUnuseStreamResInCurrentThread(aclrtStream stream)
104{104{
105 (void) stream;105 (void) stream;
106- HCCL_VM_WARN("[{}] not support", __func__);106+ HCCL_VM_WARN("[aclstub] not support");
107 return ACL_SUCCESS;107 return ACL_SUCCESS;
108}108}
109 109 
@@ -114,6 +114,15 @@ aclError aclrtGetResInCurrentThread(aclrtDevResLimitType type, uint32_t *value)
114 return ACL_SUCCESS;114 return ACL_SUCCESS;
115}115}
116 116 
117+aclError aclrtGetOpTimeOutInterval(uint64_t *interval)
118+{
119+ if (interval == nullptr) {
120+ return ACL_ERROR_INVALID_PARAM;
121+ }
122+ *interval = 5ULL * 1000ULL * 1000ULL; // 模拟赋值
123+ return ACL_SUCCESS;
124+}
125+ 
117#ifdef __cplusplus126#ifdef __cplusplus
118}127}
119#endif // __cplusplus128#endif // __cplusplus
Mtest/hccl_vm/src/proxy/aclrt_stream_stub.cc+3-0
@@ -34,6 +34,8 @@ extern "C" {
34 34 
35aclError aclrtCreateStreamWithConfig(aclrtStream *stream, uint32_t priority, uint32_t flag)35aclError aclrtCreateStreamWithConfig(aclrtStream *stream, uint32_t priority, uint32_t flag)
36{36{
37+ (void) flag;
38+ (void) priority;
37 sim::Runner runner;39 sim::Runner runner;
38 if (!sim::GetCurrRunnerTls(0, runner)) {40 if (!sim::GetCurrRunnerTls(0, runner)) {
39 return ACL_ERROR_INVALID_PARAM;41 return ACL_ERROR_INVALID_PARAM;
@@ -200,6 +202,7 @@ aclError aclrtStreamQuery(aclrtStream stream, aclrtStreamStatus *status)
200 202 
201aclError aclrtGetStreamAvailableNum(uint32_t *streamCount)203aclError aclrtGetStreamAvailableNum(uint32_t *streamCount)
202{204{
205+ (void) streamCount;
203 sim::Runner runner;206 sim::Runner runner;
204 if (!sim::GetCurrRunnerTls(0, runner)) {207 if (!sim::GetCurrRunnerTls(0, runner)) {
205 return ACL_ERROR_INVALID_PARAM;208 return ACL_ERROR_INVALID_PARAM;
Mtest/hccl_vm/src/proxy/adapter_rts_stub.cc+5-5
@@ -44,14 +44,14 @@ extern "C" {
44int RaIsFirstUsed(int insId)44int RaIsFirstUsed(int insId)
45{45{
46 (void) insId;46 (void) insId;
47- HCCL_VM_ERROR("[HCCP] [{}] stub", __func__);47+ HCCL_VM_ERROR("[HCCP] stub");
48 return 0;48 return 0;
49}49}
50 50 
51int RaIsLastUsed(int insId)51int RaIsLastUsed(int insId)
52{52{
53 (void) insId;53 (void) insId;
54- HCCL_VM_ERROR("[HCCP] [{}] stub", __func__);54+ HCCL_VM_ERROR("[HCCP] stub");
55 return 0;55 return 0;
56}56}
57 57 
@@ -59,7 +59,7 @@ int ibv_get_cq_event_stub(struct ibv_comp_channel *channel, struct ibv_cq **cq,
59{59{
60 (void) cq;60 (void) cq;
61 (void) cq_context;61 (void) cq_context;
62- HCCL_VM_TRACE("[{}] Stub", __func__);62+ HCCL_VM_TRACE("[HCCP] Stub");
63 if (!channel) {63 if (!channel) {
64 return -1;64 return -1;
65 }65 }
@@ -70,7 +70,7 @@ void ibv_ack_cq_events_stub(struct ibv_cq *cq, unsigned int nevents)
70{70{
71 (void) cq;71 (void) cq;
72 (void) nevents;72 (void) nevents;
73- HCCL_VM_TRACE("[{}] Stub", __func__);73+ HCCL_VM_TRACE("[HCCP] Stub");
74 return;74 return;
75}75}
76 76 
@@ -80,7 +80,7 @@ void ibv_query_qp_stub(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mas
80 (void) attr;80 (void) attr;
81 (void) attr_mask;81 (void) attr_mask;
82 (void) init_attr;82 (void) init_attr;
83- HCCL_VM_TRACE("[{}] Stub", __func__);83+ HCCL_VM_TRACE("[HCCP] Stub");
84 return;84 return;
85}85}
86 86 
Mtest/hccl_vm/src/proxy/aiv_kernel/CMakeLists.txt+1-2
@@ -46,8 +46,7 @@ add_library(hccl_aiv_kernel SHARED
46target_link_libraries(hccl_aiv_kernel PRIVATE log)46target_link_libraries(hccl_aiv_kernel PRIVATE log)
47 47 
48install(TARGETS hccl_aiv_kernel48install(TARGETS hccl_aiv_kernel
49- DESTINATION "."49+ DESTINATION "lib/x86_64"
50- # 文件的权限 (755)
51 PERMISSIONS50 PERMISSIONS
52 OWNER_READ OWNER_WRITE OWNER_EXECUTE51 OWNER_READ OWNER_WRITE OWNER_EXECUTE
53 GROUP_READ GROUP_EXECUTE52 GROUP_READ GROUP_EXECUTE
Mtest/hccl_vm/src/proxy/aiv_kernel/hccl_op_stub/aiv_communication_base_v2.h+87-183
@@ -1,5 +1,5 @@
1/**1/**
2- * Copyright (c) 2026 Huawei Technologies Co., Ltd.2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.5 * Please refer to the License for details. You may not use this file except in compliance with the License.
@@ -11,7 +11,6 @@
11#ifndef AIV_COMMUNICATION_BASE_V2_H11#ifndef AIV_COMMUNICATION_BASE_V2_H
12#define AIV_COMMUNICATION_BASE_V2_H12#define AIV_COMMUNICATION_BASE_V2_H
13 13 
14-#include <cstdint>
15#include "kernel_operator.h"14#include "kernel_operator.h"
16#include "sync_interface.h"15#include "sync_interface.h"
17 16 
@@ -22,26 +21,22 @@ static const struct FunLevelKType kernel_name##_kernel_type_section __attribute_
22((used, section (".ascend.meta." #kernel_name))) \21((used, section (".ascend.meta." #kernel_name))) \
23= {{F_TYPE_KTYPE, sizeof(unsigned int), K_TYPE_AIV}}22= {{F_TYPE_KTYPE, sizeof(unsigned int), K_TYPE_AIV}}
24 23 
25-constexpr uint32_t MAX_RANK_SIZE = 128; // server内最大卡数24+constexpr uint32_t MAX_RANK_SIZE = 512; // server内最大卡数
25+constexpr uint32_t MAX_RANK_SIZE_V = 64;
26constexpr uint64_t BUFFER_OUT_ADDR_OFFSET = 16 * 1024;26constexpr uint64_t BUFFER_OUT_ADDR_OFFSET = 16 * 1024;
27-constexpr uint64_t TOPO_ADDR_OFFSET = 32 * 1024;
28-constexpr uint64_t FLAG_ADDR_OFFSET = 40 * 1024;
29-constexpr uint64_t TOPO_LEN = 128;
30constexpr uint64_t TOPO_LEN_Y_OFFSET = 8;27constexpr uint64_t TOPO_LEN_Y_OFFSET = 8;
31constexpr uint64_t TOPO_LEN_Z_OFFSET = 16;28constexpr uint64_t TOPO_LEN_Z_OFFSET = 16;
32-constexpr uint64_t IPC_SYNC_OFFSET = 500 * 1024;
33-constexpr uint64_t BARRIER_OFFSET = 900 * 1024;
34-constexpr uint64_t SYNC_CORE_OFFSET = 950 * 1024;
35constexpr uint64_t LOCAL_FLAG_BUF_LEN = 2560;29constexpr uint64_t LOCAL_FLAG_BUF_LEN = 2560;
36constexpr uint64_t AIV_TAG_MOVE_RIGHT_BITS = 16;30constexpr uint64_t AIV_TAG_MOVE_RIGHT_BITS = 16;
37constexpr uint64_t LOW_16_BITS = 0xFFFF;31constexpr uint64_t LOW_16_BITS = 0xFFFF;
38constexpr uint64_t DATA_LIMIT = 512 * 1024;32constexpr uint64_t DATA_LIMIT = 512 * 1024;
33+constexpr uint32_t PING_PONG = 2;
39 34 
40struct ExtraArgs {35struct ExtraArgs {
41- uint64_t sendCounts[MAX_RANK_SIZE] = {};36+ uint64_t sendCounts[MAX_RANK_SIZE_V] = {};
42- uint64_t sendDispls[MAX_RANK_SIZE] = {};37+ uint64_t sendDispls[MAX_RANK_SIZE_V] = {};
43- uint64_t recvCounts[MAX_RANK_SIZE] = {};38+ uint64_t recvCounts[MAX_RANK_SIZE_V] = {};
44- uint64_t recvDispls[MAX_RANK_SIZE] = {};39+ uint64_t recvDispls[MAX_RANK_SIZE_V] = {};
45};40};
46 41 
47using AivSuperKernelArgs = struct AivSuperKernelArgsDef {42using AivSuperKernelArgs = struct AivSuperKernelArgsDef {
@@ -83,7 +78,7 @@ GM_ADDR buffIn, \
83uint64_t input, uint64_t output, uint32_t rank, uint32_t sendRecvRemoteRank, uint32_t rankSize, uint64_t xRankSize, uint64_t yRankSize, uint64_t zRankSize, uint64_t len, \78uint64_t input, uint64_t output, uint32_t rank, uint32_t sendRecvRemoteRank, uint32_t rankSize, uint64_t xRankSize, uint64_t yRankSize, uint64_t zRankSize, uint64_t len, \
84uint32_t dataType, uint32_t reduceOp, uint32_t root, uint32_t sliceId, \79uint32_t dataType, uint32_t reduceOp, uint32_t root, uint32_t sliceId, \
85uint64_t inputSliceStride, uint64_t outputSliceStride, uint64_t repeatNum, uint64_t inputRepeatStride, uint64_t outputRepeatStride, \80uint64_t inputSliceStride, uint64_t outputSliceStride, uint64_t repeatNum, uint64_t inputRepeatStride, uint64_t outputRepeatStride, \
86-bool isOpBase, \81+uint32_t numBlocks, bool isOpBase, \
87GM_ADDR headCountMem, \82GM_ADDR headCountMem, \
88GM_ADDR tailCountMem, GM_ADDR addOneMem, uint32_t counterMemSize, bool isEnableCounter83GM_ADDR tailCountMem, GM_ADDR addOneMem, uint32_t counterMemSize, bool isEnableCounter
89 84 
@@ -94,7 +89,7 @@ KERNEL_ARGS_DEF, ExtraArgs extraArgs
94buffIn, \89buffIn, \
95input, output, rank, sendRecvRemoteRank, rankSize, xRankSize, yRankSize, zRankSize, len, dataType, reduceOp, root, sliceId, \90input, output, rank, sendRecvRemoteRank, rankSize, xRankSize, yRankSize, zRankSize, len, dataType, reduceOp, root, sliceId, \
96inputSliceStride, outputSliceStride, repeatNum, inputRepeatStride, outputRepeatStride, \91inputSliceStride, outputSliceStride, repeatNum, inputRepeatStride, outputRepeatStride, \
97-isOpBase, \92+numBlocks, isOpBase, \
98headCountMem, tailCountMem, addOneMem, counterMemSize, isEnableCounter93headCountMem, tailCountMem, addOneMem, counterMemSize, isEnableCounter
99 94 
100#define EXTERN_KERNEL_ARGS_CALL \95#define EXTERN_KERNEL_ARGS_CALL \
@@ -104,7 +99,7 @@ KERNEL_ARGS_CALL, extraArgs
104buffIn, input, output,\99buffIn, input, output,\
105rank, sendRecvRemoteRank, rankSize, xRankSize, yRankSize, zRankSize, len, dataType, reduceOp, root, \100rank, sendRecvRemoteRank, rankSize, xRankSize, yRankSize, zRankSize, len, dataType, reduceOp, root, \
106inputSliceStride, outputSliceStride, repeatNum, inputRepeatStride, outputRepeatStride, \101inputSliceStride, outputSliceStride, repeatNum, inputRepeatStride, outputRepeatStride, \
107-headCountMem, tailCountMem, addOneMem, counterMemSize, isEnableCounter102+headCountMem, tailCountMem, addOneMem, counterMemSize, isEnableCounter, numBlocks
108 103 
109#define SUPERKERNEL_LITE_ARGS_DEF \104#define SUPERKERNEL_LITE_ARGS_DEF \
110uint64_t args_offset105uint64_t args_offset
@@ -124,27 +119,7 @@ hiddenInput, input, output
124#define SUPERKERNEL_CLASS_INIT \119#define SUPERKERNEL_CLASS_INIT \
125hiddenInput, input, output120hiddenInput, input, output
126 121 
127-#define SUPERKERNEL_LITE_ARGS_DEF \122+constexpr uint64_t AIV_FLAG_BUFFER_SIZE = 3 * 1024 * 1024; // aiv算子的flag区域大小
128-uint64_t args_offset
129- 
130-#define SUPERKERNEL_LITE_ARGS_EXTRACT \
131- GM_ADDR *param_base = (GM_ADDR *)get_para_base();\
132- GM_ADDR hiddenInput = param_base[args_offset++];\
133- GM_ADDR input = param_base[args_offset++];\
134- GM_ADDR output = param_base[args_offset++]
135- 
136-#define SUPERKERNEL_ARGS_DEF \
137-GM_ADDR hiddenInput, GM_ADDR input, GM_ADDR output
138- 
139-#define SUPERKERNEL_ARGS_CALL \
140-hiddenInput, input, output
141- 
142-#define SUPERKERNEL_CLASS_INIT \
143-hiddenInput, input, output
144- 
145-constexpr uint64_t AIV_COMM_INFO_SIZE = 33 * 1024 * 1024; // aiv算子的通信信息区大小
146-constexpr uint64_t AIV_FLAG_BUFFER_SIZE = AIV_COMM_INFO_SIZE - FLAG_ADDR_OFFSET; // aiv算子的flag区域大小
147-constexpr uint64_t CLEAR_BUFFER_OFFSET = 1024 * 1024; // 用于清空的aiv buffer的偏移
148constexpr uint64_t SYNC_BUFFER_OFFSET = 2 * 1024 * 1024; // 用于sync的aiv buffer的偏移123constexpr uint64_t SYNC_BUFFER_OFFSET = 2 * 1024 * 1024; // 用于sync的aiv buffer的偏移
149constexpr uint64_t BUFFER_AREA = 1024 * 1024; // aiv算子的单独功能flag区域大小124constexpr uint64_t BUFFER_AREA = 1024 * 1024; // aiv算子的单独功能flag区域大小
150 125 
@@ -175,15 +150,21 @@ constexpr uint64_t CHUNK_SIZE = 2048;
175 150 
176constexpr int32_t TAG_INIT_VALUE = 1;151constexpr int32_t TAG_INIT_VALUE = 1;
177constexpr int32_t TAG_RESET_COUNT = 1000;152constexpr int32_t TAG_RESET_COUNT = 1000;
178-constexpr uint32_t AIV_FLAG_CLEAR_OFFSET = 16 * 1024 * 1024;153+constexpr uint32_t AIV_FLAG_CLEAR_OFFSET = 512 * 1024;
179-constexpr uint32_t AIV_FLAG_EMPTY_OFFSET = 17 * 1024 * 1024;154+// 相对于GM_OUT,前同步、尾同步使用的同步标记区的偏移,也是普通标记区的大小
155+constexpr uint32_t FLAG1_OFFSET = 1 * 1024 * 1024;
156+constexpr uint32_t FLAG2_OFFSET = 5 * 1024 * 1024;
157+constexpr uint32_t BASE_FLAG_OFFSET = 9 * 1024 * 1024;
158+constexpr uint32_t AIV_FLAG_EMPTY_OFFSET = 10 * 1024 * 1024;
159+constexpr uint32_t GM_OUT_PING_OFFSET = 18 * 1024 * 1024;
160+constexpr uint32_t GM_OUT_PONG_OFFSET = 34 * 1024 * 1024;
180 161 
181/**162/**
182- * GM_OUT BarrierBase(大小n*FLAG_SIZE) Tag(大小4) Clear163+ * ccl buffers GM_OUT Tag(大小4) flag1 flag2 BarrierBase Clear data1 data2
183- * 0 | 40K(FLAG_ADDR_OFFSET) | 16M(AIV_FLAG_CLEAR_OFFSET)-n*FLAG_SIZE | 16M(AIV_FLAG_CLEAR_OFFSET) | 17M(AIV_FLAG_EMPTY_OFFSET)164+ * 0 | 16K | 512K | 1M | 5M | 9M | 10M | 18M | 34M
165+ * BUFFER_OUT_ADDR_OFFSET | AIV_FLAG_CLEAR_OFFSET | FLAG1_OFFSET| FLAG2_OFFSET | BASE_FLAG_OFFSET | AIV_FLAG_EMPTY_OFFSET |GM_OUT_PING_OFFSET | GM_OUT_PONG_OFFSET
184 */166 */
185-// 相对于GM_OUT,前同步、尾同步使用的同步标记区的偏移,也是普通标记区的大小167+ 
186-constexpr uint32_t BASE_FLAG_OFFSET = (AIV_FLAG_CLEAR_OFFSET - FLAG_ADDR_OFFSET) - MAX_RANK_SIZE * FLAG_SIZE;
187 168 
188class AivCommBase {169class AivCommBase {
189public:170public:
@@ -195,8 +176,8 @@ public:
195 uint32_t dataType, uint32_t reduceOp, uint32_t root,176 uint32_t dataType, uint32_t reduceOp, uint32_t root,
196 uint64_t inputSliceStride, uint64_t outputSliceStride, uint64_t repeatNum, uint64_t inputRepeatStride, uint64_t outputRepeatStride,177 uint64_t inputSliceStride, uint64_t outputSliceStride, uint64_t repeatNum, uint64_t inputRepeatStride, uint64_t outputRepeatStride,
197 GM_ADDR headCountMem,178 GM_ADDR headCountMem,
198- GM_ADDR tailCountMem, GM_ADDR addOneMem, uint32_t counterMemSize, bool isEnableCounter,179+ GM_ADDR tailCountMem, GM_ADDR addOneMem, uint32_t counterMemSize, bool isEnableCounter, uint32_t numBlocks,
199- bool useDoubleBuffer)180+ bool useDoubleBuffer, bool pingpong = false)
200 {181 {
201 rank_ = rank;182 rank_ = rank;
202 sendRecvRemoteRank_ = sendRecvRemoteRank;183 sendRecvRemoteRank_ = sendRecvRemoteRank;
@@ -211,7 +192,7 @@ public:
211 output_ = output;192 output_ = output;
212 dataType_ = dataType;193 dataType_ = dataType;
213 useDoubleBuffer_ = useDoubleBuffer;194 useDoubleBuffer_ = useDoubleBuffer;
214- numBlocks_ = block_num;195+ numBlocks_ = numBlocks;
215 196 
216 inputSliceStride_ = inputSliceStride;197 inputSliceStride_ = inputSliceStride;
217 outputSliceStride_ = outputSliceStride;198 outputSliceStride_ = outputSliceStride;
@@ -219,8 +200,6 @@ public:
219 inputRepeatStride_ = inputRepeatStride;200 inputRepeatStride_ = inputRepeatStride;
220 outputRepeatStride_ = outputRepeatStride;201 outputRepeatStride_ = outputRepeatStride;
221 202 
222- InitBuffArray(buffIn);
223- 
224 localOffset = (rankSize_ * NUM_BLOCKS_FOUR_PER_RANK_A3 * FLAG_BUF_NUM) * FLAG_SIZE;203 localOffset = (rankSize_ * NUM_BLOCKS_FOUR_PER_RANK_A3 * FLAG_BUF_NUM) * FLAG_SIZE;
225 multiOffset = MAX_NUM_BLOCKS * DOUBLE * FLAG_SIZE+ localOffset;204 multiOffset = MAX_NUM_BLOCKS * DOUBLE * FLAG_SIZE+ localOffset;
226 pingpongOffset = multiOffset + DOUBLE * DOUBLE * NUM_BLOCKS_FOUR_PER_RANK_A3 * ATOMIC_FLAG_SIZE * DOUBLE;205 pingpongOffset = multiOffset + DOUBLE * DOUBLE * NUM_BLOCKS_FOUR_PER_RANK_A3 * ATOMIC_FLAG_SIZE * DOUBLE;
@@ -241,24 +220,33 @@ public:
241 pipe.InitBuffer(outQueueZ, 1, chunkSize);220 pipe.InitBuffer(outQueueZ, 1, chunkSize);
242 221 
243 GetTag(buffIn);222 GetTag(buffIn);
223+ InitBuffArray(buffIn, pingpong);
244 }224 }
245 225 
246- __aicore__ inline void Init(GM_ADDR hiddenInput, GM_ADDR input, GM_ADDR output)226+ __aicore__ inline void Init(GM_ADDR hiddenInput, GM_ADDR input, GM_ADDR output, bool pingpong = false)
247 {227 {
248 // SuperKernel 当前不支持228 // SuperKernel 当前不支持
249 HCCL_VM_ERROR("Not support yet!");229 HCCL_VM_ERROR("Not support yet!");
250 }230 }
251 231 
252- __aicore__ inline void InitBuffArray(GM_ADDR buffIn)232+ __aicore__ inline void InitBuffArray(GM_ADDR buffIn, bool pingpong = false)
253 {233 {
254 GlobalTensor<uint64_t> ipcBufferGlobal;234 GlobalTensor<uint64_t> ipcBufferGlobal;
255 ipcBufferGlobal.SetGlobalBuffer((__gm__ uint64_t*)(buffIn));235 ipcBufferGlobal.SetGlobalBuffer((__gm__ uint64_t*)(buffIn));
256- for(int i=0; i<rankSize_;i++){236+ if(!pingpong){
257- GM_IN[i] = (GM_ADDR)ipcBufferGlobal.GetValue(i);237+ for(int i=0; i<rankSize_;i++){
258- GM_OUT[i] = (GM_ADDR)ipcBufferGlobal.GetValue(BUFFER_OUT_ADDR_OFFSET / sizeof(uint64_t) + i) + FLAG_ADDR_OFFSET;238+ GM_IN[i] = (GM_ADDR)ipcBufferGlobal.GetValue(i);
259- }239+ GM_OUT[i] = (GM_ADDR)ipcBufferGlobal.GetValue(BUFFER_OUT_ADDR_OFFSET / sizeof(uint64_t) + i) + FLAG1_OFFSET;
260- for(int i=0; i< TOPO_LEN ;i++){240+ gmOutOffset = FLAG1_OFFSET;
261- TOPO_[i] = (uint64_t)ipcBufferGlobal.GetValue(TOPO_ADDR_OFFSET / sizeof(uint64_t) + i);241+ }
242+ } else {
243+ for(int i=0; i<rankSize_;i++){
244+ GM_IN[i] = tag_ % 2 == 0 ? (GM_ADDR)ipcBufferGlobal.GetValue(BUFFER_OUT_ADDR_OFFSET / sizeof(uint64_t) + i) + GM_OUT_PING_OFFSET :
245+ (GM_ADDR)ipcBufferGlobal.GetValue(BUFFER_OUT_ADDR_OFFSET / sizeof(uint64_t) + i) + GM_OUT_PONG_OFFSET;
246+ GM_OUT[i] = tag_ % 2 == 0 ? (GM_ADDR)ipcBufferGlobal.GetValue(BUFFER_OUT_ADDR_OFFSET / sizeof(uint64_t) + i) + FLAG1_OFFSET:
247+ (GM_ADDR)ipcBufferGlobal.GetValue(BUFFER_OUT_ADDR_OFFSET / sizeof(uint64_t) + i) + FLAG2_OFFSET;
248+ gmOutOffset = tag_ % 2 == 0 ? FLAG1_OFFSET : FLAG2_OFFSET;
249+ }
262 }250 }
263 pipe_barrier(PIPE_ALL);251 pipe_barrier(PIPE_ALL);
264 }252 }
@@ -290,22 +278,34 @@ public:
290 template<typename T>278 template<typename T>
291 __aicore__ inline void Reduce64(__gm__ T *outputGM, __gm__ T *inputGM, uint64_t count, uint32_t reduceOp);279 __aicore__ inline void Reduce64(__gm__ T *outputGM, __gm__ T *inputGM, uint64_t count, uint32_t reduceOp);
292 280 
281+ __aicore__ inline void BarrierForFirstOPInner(uint32_t barrierStage)
282+ {
283+ uint32_t perCoreRankNum = rankSize_ / numBlocks_;
284+ uint32_t remainRankNum = rankSize_ % numBlocks_;
285+ uint32_t curCoreRankNum = blockIdx_ < remainRankNum ? perCoreRankNum + 1 : perCoreRankNum;
286+ uint32_t startRank = blockIdx_ < remainRankNum
287+ ? (perCoreRankNum + 1) * blockIdx_
288+ : perCoreRankNum * blockIdx_ + remainRankNum;
289+ for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
290+ uint64_t flag_offset = BASE_FLAG_OFFSET - gmOutOffset + rank * FLAG_SIZE + barrierStage * rankSize_ * FLAG_SIZE;
291+ Record(rank_, flag_offset / FLAG_SIZE, DOUBLE);
292+ }
293+ PipeBarrier<PIPE_ALL>();
294+ uint64_t flag_offset = BASE_FLAG_OFFSET - gmOutOffset + rank_ * FLAG_SIZE + barrierStage * rankSize_ * FLAG_SIZE;
295+ for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
296+ WaitFlag(rank, flag_offset / FLAG_SIZE, DOUBLE);
297+ Record(rank_, flag_offset / FLAG_SIZE, 0);
298+ }
299+ }
300+ 
293 __aicore__ inline void BarrierAll();301 __aicore__ inline void BarrierAll();
294 302 
295- __aicore__ inline void SubBarrierAllForAlltoAllV(uint32_t (&sendRecvRank)[MAX_RANK_SIZE], uint64_t loop);
296- 
297- __aicore__ inline void PreBarrierAllForAlltoAllV(uint32_t tag, uint64_t loopTimes);
298- 
299- __aicore__ inline void PostBarrierAllForAlltoAllV(uint32_t tag);
300- 
301 __aicore__ inline void SendRecvBarrierAll(uint32_t myRank, uint32_t remoteRank);303 __aicore__ inline void SendRecvBarrierAll(uint32_t myRank, uint32_t remoteRank);
302 304 
303 __aicore__ inline bool IsFirstOP(int32_t sliceId);305 __aicore__ inline bool IsFirstOP(int32_t sliceId);
304 306 
305 __aicore__ inline void ClearGM();307 __aicore__ inline void ClearGM();
306 308 
307- __aicore__ inline void BarrierForFirstOP();
308- 
309 __aicore__ inline void SendRecvBarrierForFirstOP(uint32_t myRank, uint32_t remoteRank);309 __aicore__ inline void SendRecvBarrierForFirstOP(uint32_t myRank, uint32_t remoteRank);
310 310 
311 __aicore__ inline void WaitFlag(uint32_t targetRank, uint64_t flag_offset, int32_t curTag);311 __aicore__ inline void WaitFlag(uint32_t targetRank, uint64_t flag_offset, int32_t curTag);
@@ -316,13 +316,20 @@ public:
316 316 
317 __aicore__ inline void ClearFlag();317 __aicore__ inline void ClearFlag();
318 318 
319- __aicore__ inline void BlockSync();
320- 
321 __aicore__ inline void ClearSyncBuf();319 __aicore__ inline void ClearSyncBuf();
322 320 
321+ __aicore__ inline void BarrierForFirstOP()
322+ {
323+ BarrierForFirstOPInner(0);
324+ SyncAll<true>();
325+ ClearGM();
326+ SyncAll<true>();
327+ BarrierForFirstOPInner(1);
328+ SyncAll<true>();
329+ }
330+ 
323 GM_ADDR GM_IN[MAX_RANK_SIZE];331 GM_ADDR GM_IN[MAX_RANK_SIZE];
324 GM_ADDR GM_OUT[MAX_RANK_SIZE];332 GM_ADDR GM_OUT[MAX_RANK_SIZE];
325- uint64_t TOPO_[TOPO_LEN];
326 uint32_t rank_;333 uint32_t rank_;
327 uint32_t sendRecvRemoteRank_;334 uint32_t sendRecvRemoteRank_;
328 uint32_t root_;335 uint32_t root_;
@@ -337,11 +344,13 @@ public:
337 uint64_t input_;344 uint64_t input_;
338 uint64_t output_;345 uint64_t output_;
339 uint64_t cclBufferSize_;346 uint64_t cclBufferSize_;
347+ uint64_t gmOutOffset;
340 348 
341 uint64_t len_;349 uint64_t len_;
342 uint32_t tag_;350 uint32_t tag_;
343 uint32_t curTag_{0};351 uint32_t curTag_{0};
344 int32_t numBlocks_;352 int32_t numBlocks_;
353+ uint32_t blockIdx_ = GetBlockIdx(); // 在构造函数中初始化,以免漏初始化
345 354 
346 uint64_t inputSliceStride_;355 uint64_t inputSliceStride_;
347 uint64_t outputSliceStride_;356 uint64_t outputSliceStride_;
@@ -373,6 +382,7 @@ public:
373 uint32_t seperateOffset;382 uint32_t seperateOffset;
374};383};
375 384 
385+ 
376__aicore__ inline void AivCommBase::Record(uint32_t targetRank, uint64_t flag_offset, int32_t curTag)386__aicore__ inline void AivCommBase::Record(uint32_t targetRank, uint64_t flag_offset, int32_t curTag)
377{387{
378 send_flag(targetRank, flag_offset, curTag);388 send_flag(targetRank, flag_offset, curTag);
@@ -398,12 +408,6 @@ __aicore__ inline void AivCommBase::ClearFlag()
398 HCCL_VM_ERROR("Not support yet!");408 HCCL_VM_ERROR("Not support yet!");
399}409}
400 410 
401-__aicore__ inline void AivCommBase::BlockSync()
402-{
403- // SuperKernel 当前不支持
404- HCCL_VM_ERROR("Not support yet!");
405-}
406- 
407__aicore__ inline void AivCommBase::WaitFlag(uint32_t targetRank, uint64_t flag_offset, int32_t curTag)411__aicore__ inline void AivCommBase::WaitFlag(uint32_t targetRank, uint64_t flag_offset, int32_t curTag)
408{412{
409 recv_flag(targetRank, flag_offset, curTag);413 recv_flag(targetRank, flag_offset, curTag);
@@ -415,40 +419,14 @@ __aicore__ inline bool AivCommBase::IsFirstOP(int32_t sliceId)
415 return sliceId == 1 && tag_ == 1;419 return sliceId == 1 && tag_ == 1;
416}420}
417 421 
418-__aicore__ inline void AivCommBase::ClearGM() // todo422+__aicore__ inline void AivCommBase::ClearGM()
419{423{
420- uint32_t emptyOffset = AIV_FLAG_EMPTY_OFFSET - FLAG_ADDR_OFFSET;424+ uint32_t emptyOffset = AIV_FLAG_EMPTY_OFFSET - gmOutOffset;
421- uint32_t blockCount = BASE_FLAG_OFFSET / numBlocks_;425+ uint32_t blockCount = (BASE_FLAG_OFFSET - FLAG1_OFFSET) / numBlocks_;
422- uint32_t blockOffset = blockCount * block_idx;426+ uint32_t blockOffset = blockCount * blockIdx_;
423 CpGM2GM(GM_OUT[rank_] + blockOffset, GM_OUT[rank_] + blockOffset + emptyOffset, blockCount);427 CpGM2GM(GM_OUT[rank_] + blockOffset, GM_OUT[rank_] + blockOffset + emptyOffset, blockCount);
424}428}
425 429 
426-__aicore__ inline void AivCommBase::BarrierForFirstOP()
427-{
428- // 清零标记区
429- ClearGM();
430- SyncAll<true>();
431- 
432- // 每个核分配多个rank
433- uint32_t perCoreRankNum = rankSize_ / numBlocks_;
434- uint32_t remainRankNum = rankSize_ % numBlocks_;
435- uint32_t curCoreRankNum = block_idx < remainRankNum ? perCoreRankNum + 1 : perCoreRankNum;
436- uint32_t startRank = block_idx < remainRankNum
437- ? (perCoreRankNum + 1) * block_idx
438- : perCoreRankNum * block_idx + remainRankNum;
439- for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
440- uint64_t flag_offset = BASE_FLAG_OFFSET + rank * FLAG_SIZE;
441- Record(rank_, flag_offset / FLAG_SIZE, DOUBLE);
442- }
443- PipeBarrier<PIPE_ALL>();
444- uint64_t flag_offset = BASE_FLAG_OFFSET + rank_ * FLAG_SIZE;
445- for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
446- WaitFlag(rank, flag_offset / FLAG_SIZE, DOUBLE);
447- }
448- 
449- SyncAll<true>();
450-}
451- 
452// 为sendRecv单独设计430// 为sendRecv单独设计
453__aicore__ inline void AivCommBase::SendRecvBarrierForFirstOP(uint32_t myRank, uint32_t remoteRank)431__aicore__ inline void AivCommBase::SendRecvBarrierForFirstOP(uint32_t myRank, uint32_t remoteRank)
454{432{
@@ -463,96 +441,22 @@ __aicore__ inline void AivCommBase::BarrierAll()
463 // 每个核分配多个rank441 // 每个核分配多个rank
464 uint32_t perCoreRankNum = rankSize_ / numBlocks_;442 uint32_t perCoreRankNum = rankSize_ / numBlocks_;
465 uint32_t remainRankNum = rankSize_ % numBlocks_;443 uint32_t remainRankNum = rankSize_ % numBlocks_;
466- uint32_t curCoreRankNum = block_idx < remainRankNum ? perCoreRankNum + 1 : perCoreRankNum;444+ uint32_t curCoreRankNum = blockIdx_ < remainRankNum ? perCoreRankNum + 1 : perCoreRankNum;
467- uint32_t startRank = block_idx < remainRankNum445+ uint32_t startRank = blockIdx_ < remainRankNum
468- ? (perCoreRankNum + 1) * block_idx446+ ? (perCoreRankNum + 1) * blockIdx_
469- : perCoreRankNum * block_idx + remainRankNum;447+ : perCoreRankNum * blockIdx_ + remainRankNum;
470- uint64_t flag_offset = BASE_FLAG_OFFSET + rank_ * FLAG_SIZE;448+ uint64_t flag_offset = BASE_FLAG_OFFSET - gmOutOffset + rank_ * FLAG_SIZE;
471 for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {449 for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
472 Record(rank, flag_offset / FLAG_SIZE, 1);450 Record(rank, flag_offset / FLAG_SIZE, 1);
473 }451 }
474 PipeBarrier<PIPE_ALL>();452 PipeBarrier<PIPE_ALL>();
475 for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {453 for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
476- uint64_t flag_offset = BASE_FLAG_OFFSET + rank * FLAG_SIZE;454+ uint64_t flag_offset = BASE_FLAG_OFFSET - gmOutOffset + rank * FLAG_SIZE;
477 WaitFlag(rank_, flag_offset / FLAG_SIZE, 1);455 WaitFlag(rank_, flag_offset / FLAG_SIZE, 1);
478 Record(rank_, flag_offset / FLAG_SIZE, 0);456 Record(rank_, flag_offset / FLAG_SIZE, 0);
479 }457 }
480}458}
481 459 
482-__aicore__ inline void AivCommBase::SubBarrierAllForAlltoAllV(uint32_t (&sendRecvRank)[MAX_RANK_SIZE], uint64_t loop)
483-{
484- SyncAll<true>();
485- // 每个核分配多个rank
486- uint32_t perCoreRankNum = rankSize_ / numBlocks_;
487- uint32_t remainRankNum = rankSize_ % numBlocks_;
488- uint32_t curCoreRankNum = block_idx < remainRankNum ? perCoreRankNum + 1 : perCoreRankNum;
489- uint32_t startRank = block_idx < remainRankNum
490- ? (perCoreRankNum + 1) * block_idx
491- : perCoreRankNum * block_idx + remainRankNum;
492- uint64_t flag_offset = BASE_FLAG_OFFSET + rank_ * FLAG_SIZE;
493- for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
494- // loop为0的时候,给对端初始化置为0
495- if (loop == 0 && sendRecvRank[rank] == 0) {
496- Record(rank, flag_offset / FLAG_SIZE, 0);
497- }
498- if (sendRecvRank[rank] == 1) {
499- if (loop > 0) {
500- WaitFlag(rank, flag_offset / FLAG_SIZE, 0); // 防止后续record 0 之前重入
501- }
502- Record(rank, flag_offset / FLAG_SIZE, 1);
503- }
504- }
505- PipeBarrier<PIPE_ALL>();
506- for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
507- if (sendRecvRank[rank] == 1) {
508- uint64_t flag_offset = BASE_FLAG_OFFSET + rank * FLAG_SIZE;
509- WaitFlag(rank_, flag_offset / FLAG_SIZE, 1);
510- Record(rank_, flag_offset / FLAG_SIZE, 0);
511- }
512- }
513- SyncAll<true>();
514-}
515- 
516-__aicore__ inline void AivCommBase::PreBarrierAllForAlltoAllV(uint32_t tag, uint64_t loopTimes)
517-{
518- SyncAll<true>();
519- // 每个核分配多个rank
520- uint32_t perCoreRankNum = rankSize_ / numBlocks_;
521- uint32_t remainRankNum = rankSize_ % numBlocks_;
522- uint32_t curCoreRankNum = block_idx < remainRankNum ? perCoreRankNum + 1 : perCoreRankNum;
523- uint32_t startRank = block_idx < remainRankNum
524- ? (perCoreRankNum + 1) * block_idx
525- : perCoreRankNum * block_idx + remainRankNum;
526- uint64_t flag_offset = BASE_FLAG_OFFSET + rank_ * FLAG_SIZE;
527- for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
528- if (loopTimes > 0) {
529- // loopTimes>0的时候,SubBarrierAllForAlltoAllV会给全部rank置0,这里肯定可以wait到
530- WaitFlag(rank, flag_offset / FLAG_SIZE, 0); // 防止重入
531- }
532- Record(rank, flag_offset / FLAG_SIZE, tag);
533- }
534- PipeBarrier<PIPE_ALL>();
535-}
536- 
537-__aicore__ inline void AivCommBase::PostBarrierAllForAlltoAllV(uint32_t tag)
538-{
539- SyncAll<true>();
540- 
541- // 每个核分配多个rank
542- uint32_t perCoreRankNum = rankSize_ / numBlocks_;
543- uint32_t remainRankNum = rankSize_ % numBlocks_;
544- uint32_t curCoreRankNum = block_idx < remainRankNum ? perCoreRankNum + 1 : perCoreRankNum;
545- uint32_t startRank = block_idx < remainRankNum
546- ? (perCoreRankNum + 1) * block_idx
547- : perCoreRankNum * block_idx + remainRankNum;
548- 
549- for (uint32_t rank = startRank; rank < startRank + curCoreRankNum; rank++) {
550- uint64_t flag_offset = BASE_FLAG_OFFSET + rank * FLAG_SIZE;
551- WaitFlag(rank_, flag_offset / FLAG_SIZE, tag);
552- Record(rank_, flag_offset / FLAG_SIZE, 0);
553- }
554-}
555- 
556// 为sendRecv单独设计460// 为sendRecv单独设计
557__aicore__ inline void AivCommBase::SendRecvBarrierAll(uint32_t myRank, uint32_t remoteRank)461__aicore__ inline void AivCommBase::SendRecvBarrierAll(uint32_t myRank, uint32_t remoteRank)
558{462{
Mtest/hccl_vm/src/proxy/aiv_kernel/hccl_op_stub/aiv_model_init.cc+2-2
@@ -17,7 +17,7 @@
17#include "sim_log.h"17#include "sim_log.h"
18 18 
19constexpr uint64_t BUFFER_OUT_ADDR_OFFSET = 16 * 1024;19constexpr uint64_t BUFFER_OUT_ADDR_OFFSET = 16 * 1024;
20-constexpr uint64_t FLAG_ADDR_OFFSET = 40 * 1024;20+constexpr uint64_t FLAG1_OFFSET = 1 * 1024 * 1024;
21 21 
22void aiv_env_init(uint32_t rankId,22void aiv_env_init(uint32_t rankId,
23 size_t blockNum,23 size_t blockNum,
@@ -85,7 +85,7 @@ void aiv_env_init(uint32_t rankId,
85 static_cast<const uint8_t *>(buffIn) + BUFFER_OUT_ADDR_OFFSET);85 static_cast<const uint8_t *>(buffIn) + BUFFER_OUT_ADDR_OFFSET);
86 for (uint32_t i = 0; i < rankSize; ++i) {86 for (uint32_t i = 0; i < rankSize; ++i) {
87 const uint64_t cclBuffer = cclBufferTable[i];87 const uint64_t cclBuffer = cclBufferTable[i];
88- const uint64_t flagBuffer = flagBufferTable[i] + FLAG_ADDR_OFFSET;88+ const uint64_t flagBuffer = flagBufferTable[i] + FLAG1_OFFSET;
89 const std::string cclMemDesc = AivSim::Mem{cclBuffer, cclBufferSize}.Describe();89 const std::string cclMemDesc = AivSim::Mem{cclBuffer, cclBufferSize}.Describe();
90 const std::string flagMemDesc = AivSim::Mem{flagBuffer, flagBufferSize}.Describe();90 const std::string flagMemDesc = AivSim::Mem{flagBuffer, flagBufferSize}.Describe();
91 HCCL_VM_DEBUG(91 HCCL_VM_DEBUG(
Mtest/hccl_vm/src/proxy/aprofiling_stub.cc+7-7
@@ -25,7 +25,7 @@ uint64_t MsprofStr2Id(const char *hashInfo, size_t length)
25{25{
26 (void) hashInfo;26 (void) hashInfo;
27 (void) length;27 (void) length;
28- HCCL_VM_INFO("[APROF] [{}] stub", __func__);28+ HCCL_VM_INFO("[APROF] stub");
29 return 1;29 return 1;
30}30}
31 31 
@@ -34,13 +34,13 @@ int32_t MsprofRegTypeInfo(uint16_t level, uint32_t typeId, const char *typeName)
34 (void) level;34 (void) level;
35 (void) typeId;35 (void) typeId;
36 (void) typeName;36 (void) typeName;
37- HCCL_VM_INFO("[APROF] [{}] stub", __func__);37+ HCCL_VM_INFO("[APROF] stub");
38 return 0;38 return 0;
39}39}
40 40 
41uint64_t MsprofSysCycleTime()41uint64_t MsprofSysCycleTime()
42{42{
43- HCCL_VM_INFO("[APROF] [{}] stub", __func__);43+ HCCL_VM_INFO("[APROF] stub");
44 return 1;44 return 1;
45}45}
46 46 
@@ -48,7 +48,7 @@ int32_t MsprofRegisterCallback(uint32_t moduleId, ProfCommandHandle handle)
48{48{
49 (void) moduleId;49 (void) moduleId;
50 (void) handle;50 (void) handle;
51- HCCL_VM_INFO("[APROF] [{}] stub", __func__);51+ HCCL_VM_INFO("[APROF] stub");
52 return 0;52 return 0;
53}53}
54 54 
@@ -56,7 +56,7 @@ int32_t MsprofReportApi(uint32_t agingFlag, const MsprofApi *api)
56{56{
57 (void) agingFlag;57 (void) agingFlag;
58 (void) api;58 (void) api;
59- HCCL_VM_INFO("[APROF] [{}] stub", __func__);59+ HCCL_VM_INFO("[APROF] stub");
60 return 0;60 return 0;
61}61}
62 62 
@@ -65,7 +65,7 @@ int32_t MsprofReportAdditionalInfo(uint32_t agingFlag, const VOID_PTR data, uint
65 (void) agingFlag;65 (void) agingFlag;
66 (void) data;66 (void) data;
67 (void) length;67 (void) length;
68- HCCL_VM_INFO("[APROF] [{}] stub", __func__);68+ HCCL_VM_INFO("[APROF] stub");
69 return 0;69 return 0;
70}70}
71 71 
@@ -74,7 +74,7 @@ int32_t MsprofReportCompactInfo(uint32_t agingFlag, const VOID_PTR data, uint32_
74 (void) agingFlag;74 (void) agingFlag;
75 (void) data;75 (void) data;
76 (void) length;76 (void) length;
77- HCCL_VM_INFO("[APROF] [{}] stub", __func__);77+ HCCL_VM_INFO("[APROF] stub");
78 return 0;78 return 0;
79}79}
80 80 
Mtest/hccl_vm/src/proxy/ascend_hal_stub.cc+1-0
@@ -121,6 +121,7 @@ drvError_t drvGetPlatformInfo(uint32_t *info)
121 121 
122drvError_t drvGetDevNum(uint32_t *num_dev)122drvError_t drvGetDevNum(uint32_t *num_dev)
123{123{
124+ (void) num_dev;
124 sim::Runner runner;125 sim::Runner runner;
125 if (!sim::GetCurrRunnerTls(0, runner)) {126 if (!sim::GetCurrRunnerTls(0, runner)) {
126 return DRV_ERROR_NO_DEVICE;127 return DRV_ERROR_NO_DEVICE;
Mtest/hccl_vm/src/proxy/hccl_op_stub.cc+64-33
@@ -31,7 +31,10 @@
31#include "hccl_rank_graph.h"31#include "hccl_rank_graph.h"
32#include "store_sim_store_pub.h"32#include "store_sim_store_pub.h"
33#include "sim_log.h"33#include "sim_log.h"
34+#include "store_sim_comm_pool_policy.h"
34#include "store_sim_device_memory_manager.h"35#include "store_sim_device_memory_manager.h"
36+#include "store_sim_memory_manager.h"
37+#include "store_sim_run_mode.h"
35#include "db_sim_op_db_ops.h"38#include "db_sim_op_db_ops.h"
36#include "db_sim_runner_ops.h"39#include "db_sim_runner_ops.h"
37 40
@@ -628,16 +631,16 @@ constexpr int32_t AIV_STUB_TOPO_LEN = 128;
628constexpr uint64_t AIV_STUB_GM_IN_TABLE_OFFSET = 0;631constexpr uint64_t AIV_STUB_GM_IN_TABLE_OFFSET = 0;
629constexpr uint64_t AIV_STUB_GM_OUT_TABLE_OFFSET = 16 * 1024;632constexpr uint64_t AIV_STUB_GM_OUT_TABLE_OFFSET = 16 * 1024;
630constexpr uint64_t AIV_STUB_TOPO_OFFSET = 32 * 1024;633constexpr uint64_t AIV_STUB_TOPO_OFFSET = 32 * 1024;
631-constexpr uint64_t AIV_STUB_FLAG_OFFSET = 40 * 1024;634+constexpr uint64_t AIV_STUB_FLAG1_OFFSET = 1 * 1024 * 1024;
632-constexpr uint64_t AIV_STUB_TAG_CLEAR_OFFSET = 16 * 1024 * 1024;635+constexpr uint64_t AIV_STUB_FLAG2_OFFSET = 5 * 1024 * 1024;
633-constexpr uint64_t AIV_STUB_FLAG_EMPTY_OFFSET = 17 * 1024 * 1024;636+constexpr uint64_t AIV_STUB_TAG_CLEAR_OFFSET = 512 * 1024;
634-constexpr uint64_t AIV_STUB_COMM_INFO_SIZE = 33 * 1024 * 1024;637+constexpr uint64_t AIV_STUB_BASE_FLAG_OFFSET = 9 * 1024 * 1024;
638+constexpr uint64_t AIV_STUB_FLAG_EMPTY_OFFSET = 10 * 1024 * 1024;
639+constexpr uint64_t AIV_STUB_GM_OUT_PING_OFFSET = 18 * 1024 * 1024;
640+constexpr uint64_t AIV_STUB_GM_OUT_PONG_OFFSET = 34 * 1024 * 1024;
641+constexpr uint64_t AIV_STUB_COMM_INFO_SIZE = 65 * 1024 * 1024;
635constexpr uint64_t AIV_STUB_UB_ALIGN_SIZE = 32;642constexpr uint64_t AIV_STUB_UB_ALIGN_SIZE = 32;
636constexpr uint64_t AIV_STUB_FLAG_SLOT_SIZE = 128;643constexpr uint64_t AIV_STUB_FLAG_SLOT_SIZE = 128;
637-constexpr uint64_t AIV_STUB_BASE_FLAG_OFFSET =
638- (AIV_STUB_TAG_CLEAR_OFFSET - AIV_STUB_FLAG_OFFSET) -
639- AIV_STUB_MAX_RANK_SIZE * AIV_STUB_FLAG_SLOT_SIZE;
640-constexpr uint32_t AIV_STUB_DEVICE_MAX_NUM_BLOCKS = 48;
641constexpr uint32_t AIV_STUB_FLAG_SLOT_PRINT_NUM = 16;644constexpr uint32_t AIV_STUB_FLAG_SLOT_PRINT_NUM = 16;
642constexpr uint32_t AIV_STUB_TAG_PRINT_NUM = 16;645constexpr uint32_t AIV_STUB_TAG_PRINT_NUM = 16;
643 646 
@@ -718,6 +721,7 @@ struct AivKernelArgs {
718 uint64_t repeatNum = 0;721 uint64_t repeatNum = 0;
719 uint64_t inputRepeatStride = 0;722 uint64_t inputRepeatStride = 0;
720 uint64_t outputRepeatStride = 0;723 uint64_t outputRepeatStride = 0;
724+ uint32_t numBlocks = 0;
721 bool isOpBase = false;725 bool isOpBase = false;
722 const void *headCountMem = nullptr;726 const void *headCountMem = nullptr;
723 const void *tailCountMem = nullptr;727 const void *tailCountMem = nullptr;
@@ -746,6 +750,7 @@ struct AivExtraKernelArgs {
746 uint64_t repeatNum = 0;750 uint64_t repeatNum = 0;
747 uint64_t inputRepeatStride = 0;751 uint64_t inputRepeatStride = 0;
748 uint64_t outputRepeatStride = 0;752 uint64_t outputRepeatStride = 0;
753+ uint32_t numBlocks = 0;
749 bool isOpBase = false;754 bool isOpBase = false;
750 const void *headCountMem = nullptr;755 const void *headCountMem = nullptr;
751 const void *tailCountMem = nullptr;756 const void *tailCountMem = nullptr;
@@ -775,6 +780,7 @@ struct AivHostLaunchArgs {
775 uint64_t repeatNum = 0;780 uint64_t repeatNum = 0;
776 uint64_t inputRepeatStride = 0;781 uint64_t inputRepeatStride = 0;
777 uint64_t outputRepeatStride = 0;782 uint64_t outputRepeatStride = 0;
783+ uint32_t numBlocks = 0;
778 bool isOpBase = false;784 bool isOpBase = false;
779 const void *headCountMem = nullptr;785 const void *headCountMem = nullptr;
780 const void *tailCountMem = nullptr;786 const void *tailCountMem = nullptr;
@@ -810,6 +816,7 @@ using AivOpKernelFunc = void (*)(
810 uint64_t repeatNum,816 uint64_t repeatNum,
811 uint64_t inputRepeatStride,817 uint64_t inputRepeatStride,
812 uint64_t outputRepeatStride,818 uint64_t outputRepeatStride,
819+ uint32_t numBlocks,
813 bool isOpBase,820 bool isOpBase,
814 uint8_t *headCountMem,821 uint8_t *headCountMem,
815 uint8_t *tailCountMem,822 uint8_t *tailCountMem,
@@ -836,6 +843,7 @@ using AivExtraOpKernelFunc = void (*)(
836 uint64_t repeatNum,843 uint64_t repeatNum,
837 uint64_t inputRepeatStride,844 uint64_t inputRepeatStride,
838 uint64_t outputRepeatStride,845 uint64_t outputRepeatStride,
846+ uint32_t numBlocks,
839 bool isOpBase,847 bool isOpBase,
840 uint8_t *headCountMem,848 uint8_t *headCountMem,
841 uint8_t *tailCountMem,849 uint8_t *tailCountMem,
@@ -924,7 +932,6 @@ static ResolvedHostPtrHandle ResolveHostPtr(const void *devPtr)
924 handle.phyMem = phyMem;932 handle.phyMem = phyMem;
925 handle.needRelease = true;933 handle.needRelease = true;
926 }934 }
927- 
928 handle.hostPtr = hostBasePtr + offset;935 handle.hostPtr = hostBasePtr + offset;
929 return handle;936 return handle;
930}937}
@@ -935,7 +942,12 @@ static void ReleaseHostPtr(ResolvedHostPtrHandle &handle)
935 return;942 return;
936 }943 }
937 944 
938- sim::DeviceMemoryManager::GetInstance().ReleasePhyMem(handle.phyMem.name, handle.phyMem.device_id);945+ // needRelease 仅在走 AcquirePhyMem 时置位;按 size 判定是否走了复用区(与申请同一判据)
946+ if (sim::CommPoolPolicy::ShouldRedirect(handle.phyMem.size, sim::IsCheckOnlyMode())) {
947+ sim::MemoryManager::GetInstance().ReleaseMemByName(sim::CommPoolPolicy::kPoolName);
948+ } else {
949+ sim::DeviceMemoryManager::GetInstance().ReleasePhyMem(handle.phyMem.name, handle.phyMem.device_id);
950+ }
939 handle.hostPtr = nullptr;951 handle.hostPtr = nullptr;
940 handle.phyMem = {};952 handle.phyMem = {};
941 handle.needRelease = false;953 handle.needRelease = false;
@@ -1020,19 +1032,31 @@ static void DumpBuffersInParsedDeviceView(const void *buffersInDev, uint32_t ran
1020 const auto *gmInTable = reinterpret_cast<const uint64_t *>(buffersInHost + AIV_STUB_GM_IN_TABLE_OFFSET);1032 const auto *gmInTable = reinterpret_cast<const uint64_t *>(buffersInHost + AIV_STUB_GM_IN_TABLE_OFFSET);
1021 const auto *gmOutTable = reinterpret_cast<const uint64_t *>(buffersInHost + AIV_STUB_GM_OUT_TABLE_OFFSET);1033 const auto *gmOutTable = reinterpret_cast<const uint64_t *>(buffersInHost + AIV_STUB_GM_OUT_TABLE_OFFSET);
1022 const auto *topoTable = reinterpret_cast<const uint64_t *>(buffersInHost + AIV_STUB_TOPO_OFFSET);1034 const auto *topoTable = reinterpret_cast<const uint64_t *>(buffersInHost + AIV_STUB_TOPO_OFFSET);
1023- const auto *flagBase = buffersInHost + AIV_STUB_FLAG_OFFSET;1035+ const auto *flag1Base = buffersInHost + AIV_STUB_FLAG1_OFFSET;
1024 const auto *tagTable = reinterpret_cast<const int32_t *>(buffersInHost + AIV_STUB_TAG_CLEAR_OFFSET);1036 const auto *tagTable = reinterpret_cast<const int32_t *>(buffersInHost + AIV_STUB_TAG_CLEAR_OFFSET);
1025 const auto *emptyClearTable = reinterpret_cast<const int32_t *>(buffersInHost + AIV_STUB_FLAG_EMPTY_OFFSET);1037 const auto *emptyClearTable = reinterpret_cast<const int32_t *>(buffersInHost + AIV_STUB_FLAG_EMPTY_OFFSET);
1038+ const uint64_t nonPingpongBaseFlagOffset = AIV_STUB_BASE_FLAG_OFFSET - AIV_STUB_FLAG1_OFFSET;
1026 1039 
1027 oss << " [buffersIn-parse] device-side parsed results from buffersIn (not a raw buffersIn pointer dump):\n";1040 oss << " [buffersIn-parse] device-side parsed results from buffersIn (not a raw buffersIn pointer dump):\n";
1028 oss << " AIV comm layout: commInfoSize=0x" << std::hex1041 oss << " AIV comm layout: commInfoSize=0x" << std::hex
1029 << static_cast<unsigned long long>(AIV_STUB_COMM_INFO_SIZE)1042 << static_cast<unsigned long long>(AIV_STUB_COMM_INFO_SIZE)
1030 << ", GM_OUT_TABLE=0x" << static_cast<unsigned long long>(AIV_STUB_GM_OUT_TABLE_OFFSET)1043 << ", GM_OUT_TABLE=0x" << static_cast<unsigned long long>(AIV_STUB_GM_OUT_TABLE_OFFSET)
1031 << ", TOPO=0x" << static_cast<unsigned long long>(AIV_STUB_TOPO_OFFSET)1044 << ", TOPO=0x" << static_cast<unsigned long long>(AIV_STUB_TOPO_OFFSET)
1032- << ", FLAG=0x" << static_cast<unsigned long long>(AIV_STUB_FLAG_OFFSET)
1033 << ", TAG/CLEAR=0x" << static_cast<unsigned long long>(AIV_STUB_TAG_CLEAR_OFFSET)1045 << ", TAG/CLEAR=0x" << static_cast<unsigned long long>(AIV_STUB_TAG_CLEAR_OFFSET)
1046+ << ", FLAG1=0x" << static_cast<unsigned long long>(AIV_STUB_FLAG1_OFFSET)
1047+ << ", FLAG2=0x" << static_cast<unsigned long long>(AIV_STUB_FLAG2_OFFSET)
1048+ << ", BASE_FLAG=0x" << static_cast<unsigned long long>(AIV_STUB_BASE_FLAG_OFFSET)
1034 << ", EMPTY_CLEAR=0x" << static_cast<unsigned long long>(AIV_STUB_FLAG_EMPTY_OFFSET)1049 << ", EMPTY_CLEAR=0x" << static_cast<unsigned long long>(AIV_STUB_FLAG_EMPTY_OFFSET)
1050+ << ", PING=0x" << static_cast<unsigned long long>(AIV_STUB_GM_OUT_PING_OFFSET)
1051+ << ", PONG=0x" << static_cast<unsigned long long>(AIV_STUB_GM_OUT_PONG_OFFSET)
1035 << std::dec << '\n';1052 << std::dec << '\n';
1053+ oss << " Compatibility note: not support pingpong yet; AllGather pingpong data"
1054+ << " slots are dumped for layout visibility only.\n";
1055+ oss << " Legacy host DFX flag offset remains 0x" << std::hex
1056+ << static_cast<unsigned long long>(40 * 1024)
1057+ << ", but device-side non-pingpong GM_OUT uses FLAG1_OFFSET(0x"
1058+ << static_cast<unsigned long long>(AIV_STUB_FLAG1_OFFSET) << ").\n"
1059+ << std::dec;
1036 oss << " GM_IN parsed entries count=" << parsedRankSize1060 oss << " GM_IN parsed entries count=" << parsedRankSize
1037 << " from +0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_GM_IN_TABLE_OFFSET) << std::dec1061 << " from +0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_GM_IN_TABLE_OFFSET) << std::dec
1038 << '\n';1062 << '\n';
@@ -1046,11 +1070,11 @@ static void DumpBuffersInParsedDeviceView(const void *buffersInDev, uint32_t ran
1046 1070 
1047 oss << " GM_OUT parsed entries count=" << parsedRankSize1071 oss << " GM_OUT parsed entries count=" << parsedRankSize
1048 << " from (+0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_GM_OUT_TABLE_OFFSET)1072 << " from (+0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_GM_OUT_TABLE_OFFSET)
1049- << " table) + FLAG_OFFSET(0x" << static_cast<unsigned long long>(AIV_STUB_FLAG_OFFSET) << ')'1073+ << " table) + FLAG1_OFFSET(0x" << static_cast<unsigned long long>(AIV_STUB_FLAG1_OFFSET) << ')'
1050 << std::dec << '\n';1074 << std::dec << '\n';
1051 for (uint32_t i = 0; i < parsedRankSize; ++i) {1075 for (uint32_t i = 0; i < parsedRankSize; ++i) {
1052 const uint64_t commInfoDev = gmOutTable[i];1076 const uint64_t commInfoDev = gmOutTable[i];
1053- const uint64_t flagDev = (commInfoDev == 0) ? 0 : (commInfoDev + AIV_STUB_FLAG_OFFSET);1077+ const uint64_t flagDev = (commInfoDev == 0) ? 0 : (commInfoDev + AIV_STUB_FLAG1_OFFSET);
1054 oss << " GM_OUT[" << i << "] dev=0x" << std::hex << static_cast<unsigned long long>(flagDev)1078 oss << " GM_OUT[" << i << "] dev=0x" << std::hex << static_cast<unsigned long long>(flagDev)
1055 << " (src commInfoDev=0x" << std::hex << static_cast<unsigned long long>(commInfoDev)1079 << " (src commInfoDev=0x" << std::hex << static_cast<unsigned long long>(commInfoDev)
1056 << std::dec << ")\n";1080 << std::dec << ")\n";
@@ -1067,29 +1091,29 @@ static void DumpBuffersInParsedDeviceView(const void *buffersInDev, uint32_t ran
1067 1091 
1068 const uint32_t barrierSlotPrintNum =1092 const uint32_t barrierSlotPrintNum =
1069 (rankSize < AIV_STUB_MAX_RANK_SIZE) ? rankSize : AIV_STUB_MAX_RANK_SIZE;1093 (rankSize < AIV_STUB_MAX_RANK_SIZE) ? rankSize : AIV_STUB_MAX_RANK_SIZE;
1070- const uint32_t deviceNumBlocks =
1071- (numBlocks < AIV_STUB_DEVICE_MAX_NUM_BLOCKS) ? numBlocks : AIV_STUB_DEVICE_MAX_NUM_BLOCKS;
1072- 
1073 oss << " [buffersIn-parse] follow-up work areas derived from the same base:\n";1094 oss << " [buffersIn-parse] follow-up work areas derived from the same base:\n";
1074- oss << " FLAG work area @ +0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_FLAG_OFFSET)1095+ oss << " FLAG1 non-pingpong GM_OUT @ +0x" << std::hex
1075- << ", bytes=[0x0, 0x"1096+ << static_cast<unsigned long long>(AIV_STUB_FLAG1_OFFSET)
1076- << static_cast<unsigned long long>(AIV_STUB_TAG_CLEAR_OFFSET - AIV_STUB_FLAG_OFFSET) << ')'
1077 << ", flagSlotSize=" << std::dec << AIV_STUB_FLAG_SLOT_SIZE1097 << ", flagSlotSize=" << std::dec << AIV_STUB_FLAG_SLOT_SIZE
1078- << ", baseFlagOffset=0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_BASE_FLAG_OFFSET)1098+ << ", baseFlagOffset relative to GM_OUT=0x" << std::hex
1099+ << static_cast<unsigned long long>(nonPingpongBaseFlagOffset)
1100+ << ", absoluteBaseFlag=0x" << static_cast<unsigned long long>(AIV_STUB_BASE_FLAG_OFFSET)
1079 << std::dec << '\n';1101 << std::dec << '\n';
1080- DumpFlagSlots(oss, flagBase, 0, AIV_STUB_FLAG_SLOT_PRINT_NUM, "operator slots[0..15]");1102+ oss << " FLAG2 pingpong alt @ +0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_FLAG2_OFFSET)
1081- DumpFlagSlots(oss, flagBase, AIV_STUB_BASE_FLAG_OFFSET, barrierSlotPrintNum, "BASE_FLAG_OFFSET barrier slots");1103+ << ", PING data @ +0x" << static_cast<unsigned long long>(AIV_STUB_GM_OUT_PING_OFFSET)
1104+ << ", PONG data @ +0x" << static_cast<unsigned long long>(AIV_STUB_GM_OUT_PONG_OFFSET)
1105+ << std::dec << '\n';
1106+ oss << " FLAG1 bytes=[0x0, 0x"
1107+ << std::hex << static_cast<unsigned long long>(AIV_STUB_FLAG_EMPTY_OFFSET - AIV_STUB_FLAG1_OFFSET) << ')'
1108+ << ", flagSlotSize=" << std::dec << AIV_STUB_FLAG_SLOT_SIZE
1109+ << std::dec << '\n';
1110+ DumpFlagSlots(oss, flag1Base, 0, AIV_STUB_FLAG_SLOT_PRINT_NUM, "FLAG1 operator slots[0..15]");
1111+ DumpFlagSlots(oss, flag1Base, nonPingpongBaseFlagOffset, barrierSlotPrintNum,
1112+ "BASE_FLAG_OFFSET - FLAG1_OFFSET barrier slots");
1082 1113 
1083 oss << " TAG/CLEAR ints[0.." << (AIV_STUB_TAG_PRINT_NUM - 1)1114 oss << " TAG/CLEAR ints[0.." << (AIV_STUB_TAG_PRINT_NUM - 1)
1084 << "] @ +0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_TAG_CLEAR_OFFSET) << std::dec << '\n';1115 << "] @ +0x" << std::hex << static_cast<unsigned long long>(AIV_STUB_TAG_CLEAR_OFFSET) << std::dec << '\n';
1085- if (deviceNumBlocks == 0) {1116+ oss << " non-pingpong model currently keeps tag_ fixed at 1; real HCCL reads/writes this slot.\n";
1086- oss << " numBlocks is 0, skip GetTag block mapping summary.\n";
1087- } else {
1088- oss << " GetTag uses deviceMaxBlocks=" << AIV_STUB_DEVICE_MAX_NUM_BLOCKS
1089- << ", numBlocks=" << deviceNumBlocks
1090- << ", baseSetBlockNum=" << (AIV_STUB_DEVICE_MAX_NUM_BLOCKS / deviceNumBlocks)
1091- << ", remainder=" << (AIV_STUB_DEVICE_MAX_NUM_BLOCKS % deviceNumBlocks) << '\n';
1092- }
1093 for (uint32_t i = 0; i < AIV_STUB_TAG_PRINT_NUM; ++i) {1117 for (uint32_t i = 0; i < AIV_STUB_TAG_PRINT_NUM; ++i) {
1094 oss << " TAG_CLEAR[" << i << "] = " << tagTable[i] << '\n';1118 oss << " TAG_CLEAR[" << i << "] = " << tagTable[i] << '\n';
1095 }1119 }
@@ -1201,6 +1225,7 @@ static void CopyCommonKernelArgsToHostArgs(const AivKernelArgs &src, AivHostLaun
1201 dst.repeatNum = src.repeatNum;1225 dst.repeatNum = src.repeatNum;
1202 dst.inputRepeatStride = src.inputRepeatStride;1226 dst.inputRepeatStride = src.inputRepeatStride;
1203 dst.outputRepeatStride = src.outputRepeatStride;1227 dst.outputRepeatStride = src.outputRepeatStride;
1228+ dst.numBlocks = src.numBlocks;
1204 dst.isOpBase = src.isOpBase;1229 dst.isOpBase = src.isOpBase;
1205 dst.headCountMem = src.headCountMem;1230 dst.headCountMem = src.headCountMem;
1206 dst.tailCountMem = src.tailCountMem;1231 dst.tailCountMem = src.tailCountMem;
@@ -1230,6 +1255,7 @@ static void CopyCommonKernelArgsToHostArgs(const AivExtraKernelArgs &src, AivHos
1230 dst.repeatNum = src.repeatNum;1255 dst.repeatNum = src.repeatNum;
1231 dst.inputRepeatStride = src.inputRepeatStride;1256 dst.inputRepeatStride = src.inputRepeatStride;
1232 dst.outputRepeatStride = src.outputRepeatStride;1257 dst.outputRepeatStride = src.outputRepeatStride;
1258+ dst.numBlocks = src.numBlocks;
1233 dst.isOpBase = src.isOpBase;1259 dst.isOpBase = src.isOpBase;
1234 dst.headCountMem = src.headCountMem;1260 dst.headCountMem = src.headCountMem;
1235 dst.tailCountMem = src.tailCountMem;1261 dst.tailCountMem = src.tailCountMem;
@@ -1312,6 +1338,7 @@ static void DumpHostLaunchArgs(const AivHostLaunchArgs *hostArgs, size_t argsSiz
1312 oss << " hostArgs.repeatNum = " << static_cast<unsigned long long>(hostArgs->repeatNum) << '\n';1338 oss << " hostArgs.repeatNum = " << static_cast<unsigned long long>(hostArgs->repeatNum) << '\n';
1313 oss << " hostArgs.inputRepeatStride = " << static_cast<unsigned long long>(hostArgs->inputRepeatStride) << '\n';1339 oss << " hostArgs.inputRepeatStride = " << static_cast<unsigned long long>(hostArgs->inputRepeatStride) << '\n';
1314 oss << " hostArgs.outputRepeatStride = " << static_cast<unsigned long long>(hostArgs->outputRepeatStride) << '\n';1340 oss << " hostArgs.outputRepeatStride = " << static_cast<unsigned long long>(hostArgs->outputRepeatStride) << '\n';
1341+ oss << " hostArgs.numBlocks = " << hostArgs->numBlocks << '\n';
1315 oss << " hostArgs.isOpBase = " << static_cast<int>(hostArgs->isOpBase) << '\n';1342 oss << " hostArgs.isOpBase = " << static_cast<int>(hostArgs->isOpBase) << '\n';
1316 oss << " hostArgs.headCountMem = " << hostArgs->headCountMem << '\n';1343 oss << " hostArgs.headCountMem = " << hostArgs->headCountMem << '\n';
1317 oss << " hostArgs.tailCountMem = " << hostArgs->tailCountMem << '\n';1344 oss << " hostArgs.tailCountMem = " << hostArgs->tailCountMem << '\n';
@@ -1494,7 +1521,7 @@ static std::string GetAivLibraryPath(const std::string &soName, const std::strin
1494 1521 
1495 std::error_code ec;1522 std::error_code ec;
1496 const fs::path installDir = fs::path(installDirEnv);1523 const fs::path installDir = fs::path(installDirEnv);
1497- const fs::path soPath = installDir / soName;1524+ const fs::path soPath = installDir / "lib" / "x86_64" / soName;
1498 if (!fs::exists(soPath, ec)) {1525 if (!fs::exists(soPath, ec)) {
1499 if (ec) {1526 if (ec) {
1500 HCCL_VM_ERROR("[virtual-aiv] failed to stat aiv library path, kernel={}, HCCL_VM_INSTALL_DIR={}, so={}, err={}",1527 HCCL_VM_ERROR("[virtual-aiv] failed to stat aiv library path, kernel={}, HCCL_VM_INSTALL_DIR={}, so={}, err={}",
@@ -1769,7 +1796,7 @@ static void ResolveVirtualAivBufferSizes(const std::string &kernelName,
1769 }1796 }
1770 1797 
1771 cclBufferSize = 0;1798 cclBufferSize = 0;
1772- flagBufferSize = AIV_STUB_COMM_INFO_SIZE - AIV_STUB_FLAG_OFFSET;1799+ flagBufferSize = AIV_STUB_COMM_INFO_SIZE - AIV_STUB_FLAG1_OFFSET;
1773 1800 
1774 if (resolvedArgs.args.buffersIn == nullptr) {1801 if (resolvedArgs.args.buffersIn == nullptr) {
1775 return;1802 return;
@@ -1888,6 +1915,8 @@ static void DumpVirtualKernelFuncArgs(
1888 oss << " kernelFunc.outputRepeatStride = "1915 oss << " kernelFunc.outputRepeatStride = "
1889 << static_cast<unsigned long long>(resolvedArgs.args.outputRepeatStride)1916 << static_cast<unsigned long long>(resolvedArgs.args.outputRepeatStride)
1890 << " <- aclrtLaunchKernelWithHostArgs(hostArgs->outputRepeatStride)\n";1917 << " <- aclrtLaunchKernelWithHostArgs(hostArgs->outputRepeatStride)\n";
1918+ oss << " kernelFunc.numBlocks = " << resolvedArgs.args.numBlocks
1919+ << " <- aclrtLaunchKernelWithHostArgs(hostArgs->numBlocks)\n";
1891 oss << " kernelFunc.isOpBase = " << (resolvedArgs.args.isOpBase ? "true" : "false")1920 oss << " kernelFunc.isOpBase = " << (resolvedArgs.args.isOpBase ? "true" : "false")
1892 << " <- aclrtLaunchKernelWithHostArgs(hostArgs->isOpBase)\n";1921 << " <- aclrtLaunchKernelWithHostArgs(hostArgs->isOpBase)\n";
1893 oss << " kernelFunc.headCountMem = " << resolvedArgs.args.headCountMem1922 oss << " kernelFunc.headCountMem = " << resolvedArgs.args.headCountMem
@@ -2150,6 +2179,7 @@ static aclError VirtualExecuteAivKernel(
2150 resolvedArgs.args.repeatNum,2179 resolvedArgs.args.repeatNum,
2151 resolvedArgs.args.inputRepeatStride,2180 resolvedArgs.args.inputRepeatStride,
2152 resolvedArgs.args.outputRepeatStride,2181 resolvedArgs.args.outputRepeatStride,
2182+ resolvedArgs.args.numBlocks,
2153 resolvedArgs.args.isOpBase,2183 resolvedArgs.args.isOpBase,
2154 const_cast<uint8_t *>(static_cast<const uint8_t *>(resolvedArgs.args.headCountMem)),2184 const_cast<uint8_t *>(static_cast<const uint8_t *>(resolvedArgs.args.headCountMem)),
2155 const_cast<uint8_t *>(static_cast<const uint8_t *>(resolvedArgs.args.tailCountMem)),2185 const_cast<uint8_t *>(static_cast<const uint8_t *>(resolvedArgs.args.tailCountMem)),
@@ -2179,6 +2209,7 @@ static aclError VirtualExecuteAivKernel(
2179 resolvedArgs.args.repeatNum,2209 resolvedArgs.args.repeatNum,
2180 resolvedArgs.args.inputRepeatStride,2210 resolvedArgs.args.inputRepeatStride,
2181 resolvedArgs.args.outputRepeatStride,2211 resolvedArgs.args.outputRepeatStride,
2212+ resolvedArgs.args.numBlocks,
2182 resolvedArgs.args.isOpBase,2213 resolvedArgs.args.isOpBase,
2183 const_cast<uint8_t *>(static_cast<const uint8_t *>(resolvedArgs.args.headCountMem)),2214 const_cast<uint8_t *>(static_cast<const uint8_t *>(resolvedArgs.args.headCountMem)),
2184 const_cast<uint8_t *>(static_cast<const uint8_t *>(resolvedArgs.args.tailCountMem)),2215 const_cast<uint8_t *>(static_cast<const uint8_t *>(resolvedArgs.args.tailCountMem)),
Mtest/hccl_vm/src/proxy/hccp_ccu_stub.cc+9-3
@@ -24,6 +24,8 @@
24#include "dtype_common.h"24#include "dtype_common.h"
25#include "store_sim_store_pub.h"25#include "store_sim_store_pub.h"
26#include "sim_log.h"26#include "sim_log.h"
27+#include "sim_common_api.h"
28+#include "sim_yaml_config.h"
27#include "hccp_common.h"29#include "hccp_common.h"
28#include "hccp_ctx.h"30#include "hccp_ctx.h"
29#include "sim_ip_address.h"31#include "sim_ip_address.h"
@@ -119,7 +121,9 @@ void DumpCcuSqeToFile(uint32_t startId, uint32_t instrCnt, uint32_t argSize, uin
119namespace fs = std::filesystem;121namespace fs = std::filesystem;
120 122 
121bool write_or_overwrite_in_cwd(const std::string& filename, const std::string &data) {123bool write_or_overwrite_in_cwd(const std::string& filename, const std::string &data) {
122- fs::path target = fs::current_path() / filename;124+ fs::path target = fs::path(filename).is_absolute()
125+ ? fs::path(filename)
126+ : fs::current_path() / filename;
123 127 
124 std::error_code ec;128 std::error_code ec;
125 bool exists = fs::exists(target, ec);129 bool exists = fs::exists(target, ec);
@@ -199,6 +203,7 @@ int LoadMicrocodeInstructionStub(uint32_t devId, uint8_t dieId, const channel_in
199 return -1;203 return -1;
200 }204 }
201 205 
206+ fs::create_directories(fs::path(InstallPath::ResolveToInstallRoot("data")));
202 std::ostringstream fileName;207 std::ostringstream fileName;
203 fileName << "mc_instr_info_rank_" << rankId<<"_die_"<<static_cast<uint32_t>(dieId)<<".txt";208 fileName << "mc_instr_info_rank_" << rankId<<"_die_"<<static_cast<uint32_t>(dieId)<<".txt";
204 std::ostringstream mcData;209 std::ostringstream mcData;
@@ -212,7 +217,7 @@ int LoadMicrocodeInstructionStub(uint32_t devId, uint8_t dieId, const channel_in
212 return HCCL_E_NOT_SUPPORT;217 return HCCL_E_NOT_SUPPORT;
213 }218 }
214 219 
215- auto status = write_or_overwrite_in_cwd(fileName.str(), mcData.str());220+ auto status = write_or_overwrite_in_cwd(InstallPath::ResolveToInstallRoot("data/" + fileName.str()), mcData.str());
216 221 
217 sim::CcuInstrTab instr{};222 sim::CcuInstrTab instr{};
218 instr.id = 0;223 instr.id = 0;
@@ -523,6 +528,7 @@ int rtCCULaunch(rtCcuTaskInfo_t *taskInfo, rtStream_t const stream)
523 528 
524 memcpy(&taskMetaData.taskData.ccu, taskInfo, sizeof(rtCcuTaskInfo_t));529 memcpy(&taskMetaData.taskData.ccu, taskInfo, sizeof(rtCcuTaskInfo_t));
525 530 
531+ fs::create_directories(fs::path(InstallPath::ResolveToInstallRoot("data")));
526 std::ostringstream fileName;532 std::ostringstream fileName;
527 fileName << "sqe_info_rank_" << curRank << "_die_" << static_cast<uint32_t>(taskInfo->dieId) << "_mission_"533 fileName << "sqe_info_rank_" << curRank << "_die_" << static_cast<uint32_t>(taskInfo->dieId) << "_mission_"
528 << static_cast<uint32_t>(taskInfo->missionId) << "_startId_" << taskInfo->instStartId << ".txt";534 << static_cast<uint32_t>(taskInfo->missionId) << "_startId_" << taskInfo->instStartId << ".txt";
@@ -531,7 +537,7 @@ int rtCCULaunch(rtCcuTaskInfo_t *taskInfo, rtStream_t const stream)
531 for (uint32_t idx = 0; idx < taskInfo->argSize; idx++) {537 for (uint32_t idx = 0; idx < taskInfo->argSize; idx++) {
532 sqeData << "[SQE Arg][" << idx << "]: " << taskInfo->args[idx] << "\n";538 sqeData << "[SQE Arg][" << idx << "]: " << taskInfo->args[idx] << "\n";
533 }539 }
534- auto status = write_or_overwrite_in_cwd(fileName.str(), sqeData.str());540+ auto status = write_or_overwrite_in_cwd(InstallPath::ResolveToInstallRoot("data/" + fileName.str()), sqeData.str());
535 541 
536 uint32_t index{0};542 uint32_t index{0};
537 auto ret = InsertTaskToCollection(&taskMetaData, &index);543 auto ret = InsertTaskToCollection(&taskMetaData, &index);
Mtest/hccl_vm/src/proxy/hccp_ra_socket_stub.cc+11-3
@@ -252,7 +252,7 @@ int RaSocketBatchConnect(struct SocketConnectInfoT conn[], unsigned int num)
252 HCCL_VM_INFO("[RASOCKET] dev:{} sock:{} connect dev:{} ip addr:{} tag:{}", localSock->device_id, localFd, remoteDevId, ipAddr, conn[i].tag);252 HCCL_VM_INFO("[RASOCKET] dev:{} sock:{} connect dev:{} ip addr:{} tag:{}", localSock->device_id, localFd, remoteDevId, ipAddr, conn[i].tag);
253 uint32_t count = 0;253 uint32_t count = 0;
254 while (true) {254 while (true) {
255- if (count++ >= 120) {255+ if (count++ >= 600) { // 超时60s
256 HCCL_VM_ERROR("[RASOCKET] can not get break dev:{} sock:{} connect dev:{} ip addr:{} tag:{}", localSock->device_id, localFd, remoteDevId, ipAddr, conn[i].tag);256 HCCL_VM_ERROR("[RASOCKET] can not get break dev:{} sock:{} connect dev:{} ip addr:{} tag:{}", localSock->device_id, localFd, remoteDevId, ipAddr, conn[i].tag);
257 break;257 break;
258 }258 }
@@ -261,7 +261,7 @@ int RaSocketBatchConnect(struct SocketConnectInfoT conn[], unsigned int num)
261 return socket.device_id == remoteDevId && socket.endpoint_id == remoteEndPointId;261 return socket.device_id == remoteDevId && socket.endpoint_id == remoteEndPointId;
262 });262 });
263 if (!remoteSockRes.second) {263 if (!remoteSockRes.second) {
264- if (count % 10 == 0) {264+ if (count % 100 == 0) {
265 HCCL_VM_WARN("[RASOCKET] can not find remote dev:{}, endpoint:{}", remoteDevId, remoteEndPointId);265 HCCL_VM_WARN("[RASOCKET] can not find remote dev:{}, endpoint:{}", remoteDevId, remoteEndPointId);
266 }266 }
267 267 
@@ -344,12 +344,16 @@ int RaGetSockets(unsigned int role, struct SocketInfoT conn[], unsigned int num,
344 344 
345 uint32_t count = 0;345 uint32_t count = 0;
346 bool found = false;346 bool found = false;
347- while (count++ < 60) {347+ while (count++ < 180) {
348 auto remoteSockRes = RunnerDB::GetOneByPred<sim::RaSocket>(348 auto remoteSockRes = RunnerDB::GetOneByPred<sim::RaSocket>(
349 [remoteDevId, remoteEndpointId](const sim::RaSocket &socket) {349 [remoteDevId, remoteEndpointId](const sim::RaSocket &socket) {
350 return socket.device_id == remoteDevId && socket.endpoint_id == remoteEndpointId;350 return socket.device_id == remoteDevId && socket.endpoint_id == remoteEndpointId;
351 });351 });
352 if (!remoteSockRes.second) {352 if (!remoteSockRes.second) {
353+ if (count % 60 == 0) {
354+ HCCL_VM_WARN("[RASOCKET] waiting for remote socket dev:{}, endpoint:{}, attempt:{}/180",
355+ remoteDevId, remoteEndpointId, count);
356+ }
353 std::this_thread::sleep_for(std::chrono::milliseconds(500));357 std::this_thread::sleep_for(std::chrono::milliseconds(500));
354 continue;358 continue;
355 }359 }
@@ -364,6 +368,10 @@ int RaGetSockets(unsigned int role, struct SocketInfoT conn[], unsigned int num,
364 });368 });
365 369 
366 if (!pairRes.second) {370 if (!pairRes.second) {
371+ if (count % 60 == 0) {
372+ HCCL_VM_WARN("[RASOCKET] waiting for socket pair local:{:d} peer:{:d} tag_hash:{:d}, attempt:{}/180",
373+ loadFd, peerFd, tagHash, count);
374+ }
367 std::this_thread::sleep_for(std::chrono::milliseconds(500));375 std::this_thread::sleep_for(std::chrono::milliseconds(500));
368 continue;376 continue;
369 }377 }
Mtest/hccl_vm/src/proxy/log_stub.cc+4-8
@@ -35,14 +35,10 @@ int32_t CheckLogLevel(int32_t moduleId, int32_t logLevel)
35void DlogPrintStub(int level, const char *msgBuffer)35void DlogPrintStub(int level, const char *msgBuffer)
36{36{
37 std::call_once(log_initialized_flag, []() {37 std::call_once(log_initialized_flag, []() {
38- LogConfig config;38+ if (g_logger != nullptr) {
39- config.fileBaseName = "hccl_proxy";39+ return;
40- // auto* proxyConfig = SHMManager::GetProxyConfig();40+ }
41- // if (proxyConfig != nullptr) {41+ LogConfig config = LoadLogConfig("proxy");
42- // config.consoleLevel = proxyConfig->consoleLogLevel;
43- // config.fileLevel = proxyConfig->fileLogLevel;
44- // }
45- config.filePath = "logs/proxy";
46 InitLogger(config);42 InitLogger(config);
47 });43 });
48 44 
Mtest/hccl_vm/src/runnerdb/CMakeLists.txt+7-35
@@ -8,7 +8,7 @@
8 8 
9add_subdirectory(modeldb)9add_subdirectory(modeldb)
10 10 
11-add_library(runnerdb SHARED)11+add_library(runnerdb STATIC)
12 12 
13target_sources(runnerdb PRIVATE13target_sources(runnerdb PRIVATE
14 ${CMAKE_CURRENT_SOURCE_DIR}/tracedb/db_sim_runner_db.cc14 ${CMAKE_CURRENT_SOURCE_DIR}/tracedb/db_sim_runner_db.cc
@@ -25,10 +25,12 @@ target_include_directories(runnerdb PRIVATE
25 ${ASCEND_CANN_PACKAGE_PATH}/include/hccl25 ${ASCEND_CANN_PACKAGE_PATH}/include/hccl
26)26)
27target_include_directories(runnerdb PRIVATE27target_include_directories(runnerdb PRIVATE
28- ${PROJECT_SOURCE_DIR}/include28+ ${PROJECT_SOURCE_DIR}/include
29- ${PROJECT_SOURCE_DIR}/include/runnerdb29+ ${PROJECT_SOURCE_DIR}/include/runnerdb
30)30)
31 31 
32+target_link_libraries(runnerdb PUBLIC log modeldb)
33+ 
32target_include_directories(runnerdb PRIVATE34target_include_directories(runnerdb PRIVATE
33 # hccl_proxy依赖的Checker L2其他模块的头文件35 # hccl_proxy依赖的Checker L2其他模块的头文件
34 ${HCOMM_VM_ROOT_PATH}/include36 ${HCOMM_VM_ROOT_PATH}/include
@@ -43,6 +45,7 @@ target_include_directories(runnerdb PRIVATE
43)45)
44 46 
45target_compile_options(runnerdb PRIVATE47target_compile_options(runnerdb PRIVATE
48+ -fPIC
46 -Werror49 -Werror
47 -fno-common50 -fno-common
48 -fno-strict-aliasing51 -fno-strict-aliasing
@@ -54,36 +57,5 @@ target_compile_options(runnerdb PRIVATE
54 57 
55find_package(SQLite3 REQUIRED)58find_package(SQLite3 REQUIRED)
56 59 
57-target_link_libraries(runnerdb PRIVATE60+target_link_libraries(runnerdb PUBLIC SQLite::SQLite3)
58- log
59- modeldb
60- SQLite::SQLite3
61- sqlite3
62-)
63 61 
64-install(TARGETS runnerdb
65- DESTINATION "."
66- # 文件的权限 (755)
67- PERMISSIONS
68- OWNER_READ OWNER_WRITE OWNER_EXECUTE
69- GROUP_READ GROUP_EXECUTE
70- WORLD_READ WORLD_EXECUTE
71-)
72- 
73-install(TARGETS runnerdb
74- DESTINATION "plugin/validate/runner"
75- # 文件的权限 (755)
76- PERMISSIONS
77- OWNER_READ OWNER_WRITE OWNER_EXECUTE
78- GROUP_READ GROUP_EXECUTE
79- WORLD_READ WORLD_EXECUTE
80-)
81- 
82-install(TARGETS runnerdb
83- DESTINATION "plugin/validate/checker"
84- # 文件的权限 (755)
85- PERMISSIONS
86- OWNER_READ OWNER_WRITE OWNER_EXECUTE
87- GROUP_READ GROUP_EXECUTE
88- WORLD_READ WORLD_EXECUTE
89-)
Mtest/hccl_vm/src/runnerdb/modeldb/CMakeLists.txt+6-14
@@ -15,12 +15,14 @@ target_sources(modeldb PRIVATE
15)15)
16 16 
17target_include_directories(modeldb PRIVATE17target_include_directories(modeldb PRIVATE
18- ${PROJECT_SOURCE_DIR}/include18+ ${PROJECT_SOURCE_DIR}/include
19- ${PROJECT_SOURCE_DIR}/include/runnerdb19+ ${PROJECT_SOURCE_DIR}/include/runnerdb
20- ${PROJECT_SOURCE_DIR}/include
21)20)
22 21 
22+target_link_libraries(modeldb PRIVATE log)
23+ 
23target_compile_options(modeldb PRIVATE24target_compile_options(modeldb PRIVATE
25+ -fPIC
24 -Werror26 -Werror
25 -fno-common27 -fno-common
26 -fno-strict-aliasing28 -fno-strict-aliasing
@@ -32,15 +34,5 @@ target_compile_options(modeldb PRIVATE
32 34 
33find_package(SQLite3 REQUIRED)35find_package(SQLite3 REQUIRED)
34 36 
35-target_link_libraries(modeldb PRIVATE37+target_link_libraries(modeldb PUBLIC SQLite::SQLite3)
36- log
37- SQLite::SQLite3
38-)
39 38 
40-install(TARGETS modeldb
41- DESTINATION "."
42- PERMISSIONS
43- OWNER_READ OWNER_WRITE OWNER_EXECUTE
44- GROUP_READ GROUP_EXECUTE
45- WORLD_READ WORLD_EXECUTE
46-)
Mtest/hccl_vm/src/runnerdb/modeldb/db_hccl_op_db_ops.cc+4-1
@@ -11,12 +11,15 @@
11#include "db_hccl_op_db_ops.h"11#include "db_hccl_op_db_ops.h"
12 12 
13#include <cstdint>13#include <cstdint>
14+#include <filesystem>
14#include <unistd.h>15#include <unistd.h>
15 16 
16#include "sim_common_macro.h"17#include "sim_common_macro.h"
17#include "db_hccl_db_factory.h"18#include "db_hccl_db_factory.h"
18#include "db_hccl_db_sqlite.h"19#include "db_hccl_db_sqlite.h"
19#include "sim_log.h"20#include "sim_log.h"
21+#include "sim_common_api.h"
22+#include "sim_yaml_config.h"
20 23 
21namespace HcclSim {24namespace HcclSim {
22namespace DB {25namespace DB {
@@ -29,7 +32,7 @@ OpDbOps::OpDbOps()
29{32{
30 sim::DBConfig config;33 sim::DBConfig config;
31 config.type = sim::DbType::SQLITE3;34 config.type = sim::DbType::SQLITE3;
32- config.dbPath = "./hccl_vm_data.db";35+ config.dbPath = InstallPath::ResolveToInstallRoot("data/hccl_vm_data.db");
33 36 
34 auto db = HcclSim::DB::HcclDBFactory::Instance().CreateDB(config.type);37 auto db = HcclSim::DB::HcclDBFactory::Instance().CreateDB(config.type);
35 if (!db || db->Connect(config) != HcclSim::HCCL_SIM_SUCCESS) {38 if (!db || db->Connect(config) != HcclSim::HCCL_SIM_SUCCESS) {
Mtest/hccl_vm/src/runnerdb/tracedb/db_sim_runner_common.cc+38-2
@@ -9,6 +9,7 @@
9 */9 */
10 10 
11#include <cstdint>11#include <cstdint>
12+#include <set>
12#include <iostream>13#include <iostream>
13#include <runnerdb/db_sim_runner_common.h>14#include <runnerdb/db_sim_runner_common.h>
14 15 
@@ -350,12 +351,47 @@ uint32_t GetCubeCoreCount(uint64_t deviceId)
350 return vectorCoreCount;351 return vectorCoreCount;
351}352}
352 353 
353-bool GetRankIdByMPI(uint32_t &rankId)354+std::set<uint64_t> GetUsedServerNum()
355+{
356+ try {
357+ auto allRanks = RunnerDB::GetByPred<sim::Rank>([](const sim::Rank& d) {
358+ return d.state == 1;
359+ });
360+ std::set<uint64_t> usedServerIds;
361+ for (auto &rank : allRanks) {
362+ auto deviceKey = rank.device_id;
363+ auto ret = RunnerDB::GetOneByPred<sim::Device>([deviceKey](const sim::Device &d) {
364+ return d.id == deviceKey;
365+ });
366+ if (!ret.second) {
367+ continue;
368+ }
369+ auto serverId = ret.first.server_id;
370+ usedServerIds.insert(serverId);
371+ }
372+ return usedServerIds;
373+ } catch (const std::exception &e) {
374+ HCCL_VM_ERROR("[GetUsedServerNum] exception: {}", e.what());
375+ return {};
376+ }
377+}
378+ 
379+bool GetRankIdByMPI(uint32_t &rankId, uint64_t &serverId)
354{380{
355 const char *ompiRankStr = std::getenv("OMPI_COMM_WORLD_RANK");381 const char *ompiRankStr = std::getenv("OMPI_COMM_WORLD_RANK");
356 const char *mpichRankStr = std::getenv("PMI_RANK");382 const char *mpichRankStr = std::getenv("PMI_RANK");
357 if (ompiRankStr == nullptr && mpichRankStr == nullptr) {383 if (ompiRankStr == nullptr && mpichRankStr == nullptr) {
358- HCCL_VM_ERROR("[HVM] env OMPI_COMM_WORLD_RANK or PMI_RANK are not found.");384+ // 单server用例,默认serverId为1
385+ auto usedServerIds = GetUsedServerNum();
386+ if (usedServerIds.size() == 1) {
387+ if (*usedServerIds.begin() != 1) {
388+ HCCL_VM_ERROR("[HVM] env OMPI_COMM_WORLD_RANK or PMI_RANK are not found, serverId is not 1.");
389+ return false;
390+ }
391+ serverId = 1;
392+ return true;
393+ }
394+ HCCL_VM_ERROR("[HVM] env OMPI_COMM_WORLD_RANK or PMI_RANK are not found, usedServerIds size is not 1.");
359 return false;395 return false;
360 } else if (ompiRankStr != nullptr) {396 } else if (ompiRankStr != nullptr) {
361 rankId = static_cast<uint32_t>(atoi(ompiRankStr));397 rankId = static_cast<uint32_t>(atoi(ompiRankStr));
Mtest/hccl_vm/src/runnerdb/tracedb/db_sim_runner_ops.cc+1-1
@@ -74,7 +74,7 @@ bool GetCurrRunnerTls(uint64_t serverKey, Runner &runner)
74{74{
75 if (g_runner.id == 0 && serverKey == 0)75 if (g_runner.id == 0 && serverKey == 0)
76 {76 {
77- HCCL_VM_ERROR("[{}] can not get runner by server key: {:d}", __func__, serverKey);77+ HCCL_VM_ERROR("can not get runner by server key: {:d}", serverKey);
78 return false;78 return false;
79 }79 }
80 if (g_runner.id == 0) {80 if (g_runner.id == 0) {
Mtest/hccl_vm/src/store/CMakeLists.txt+3-26
@@ -8,8 +8,8 @@
8 8 
9file(GLOB_RECURSE HCCL_SOURCES "*.cc")9file(GLOB_RECURSE HCCL_SOURCES "*.cc")
10 10 
11-# 添加共享11+# 添加静态
12-add_library(store SHARED ${HCCL_SOURCES})12+add_library(store STATIC ${HCCL_SOURCES})
13target_compile_options(store PRIVATE13target_compile_options(store PRIVATE
14 -fPIC14 -fPIC
15 -g15 -g
@@ -46,27 +46,4 @@ target_include_directories(store PRIVATE
46 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/runtime/runtime/rts46 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/runtime/runtime/rts
47)47)
48 48 
49-target_link_libraries(store PRIVATE49+target_link_libraries(store PUBLIC log loader)
50- log
51- loader
52- runnerdb
53- rt
54-)
55- 
56-install(TARGETS store
57- DESTINATION "."
58- # 文件的权限 (755)
59- PERMISSIONS
60- OWNER_READ OWNER_WRITE OWNER_EXECUTE
61- GROUP_READ GROUP_EXECUTE
62- WORLD_READ WORLD_EXECUTE
63-)
64- 
65-install(TARGETS store
66- DESTINATION "plugin/validate/runner"
67- # 文件的权限 (755)
68- PERMISSIONS
69- OWNER_READ OWNER_WRITE OWNER_EXECUTE
70- GROUP_READ GROUP_EXECUTE
71- WORLD_READ WORLD_EXECUTE
72-)
Mtest/hccl_vm/src/store/store_dump_shm_data.cc+48-15
@@ -20,6 +20,8 @@
20#include "store_binary_data_operator.h"20#include "store_binary_data_operator.h"
21#include "sim_common_defs.h"21#include "sim_common_defs.h"
22#include "sim_log.h"22#include "sim_log.h"
23+#include "sim_common_api.h"
24+#include "sim_yaml_config.h"
23#include "sim_ip_address.h"25#include "sim_ip_address.h"
24#include "sim_loader.h"26#include "sim_loader.h"
25#include "sim_models.h"27#include "sim_models.h"
@@ -197,9 +199,41 @@ HcclVmResult CreateChannelInfo(HcclVmSynData &hvmSynData)
197 chData.dstDieId = static_cast<uint8_t>(ch.dstDieId);199 chData.dstDieId = static_cast<uint8_t>(ch.dstDieId);
198 chData.srcRank = ch.srcRankId;200 chData.srcRank = ch.srcRankId;
199 chData.dstRank = ch.dstRankId;201 chData.dstRank = ch.dstRankId;
202+ 
200 std::memcpy(chData.leid, ch.leid, sizeof(chData.leid));203 std::memcpy(chData.leid, ch.leid, sizeof(chData.leid));
201 std::memcpy(chData.reid, ch.reid, sizeof(chData.reid));204 std::memcpy(chData.reid, ch.reid, sizeof(chData.reid));
202- chData.protocol = ch.protocol;205+ 
206+ uint8_t* leidPtr = chData.leid;
207+ auto lEpRet = RunnerDB::GetOneByPred<sim::EndPoint>([leidPtr](const sim::EndPoint &ep) {
208+ return memcmp(ep.eid, leidPtr, sizeof(ep.eid)) == 0;
209+ });
210+ if (!lEpRet.second) {
211+ std::cout<<"cannot find EndPoint by ip addr:"<<leidPtr<< std::endl;
212+ return HcclVmResult::HCCL_SIM_E_NOT_FOUND;
213+ }
214+ 
215+ uint8_t* reidPtr = chData.reid;
216+ auto rEpRet = RunnerDB::GetOneByPred<sim::EndPoint>([reidPtr](const sim::EndPoint &ep) {
217+ return memcmp(ep.eid, reidPtr, sizeof(ep.eid)) == 0;
218+ });
219+ if (!rEpRet.second) {
220+ std::cout<<"cannot find EndPoint by ip addr:"<<reidPtr<< std::endl;
221+ return HcclVmResult::HCCL_SIM_E_NOT_FOUND;
222+ }
223+ 
224+ auto localEpId = lEpRet.first.id;
225+ auto remoteEpId = rEpRet.first.id;
226+ 
227+ auto pairOpt = RunnerDB::GetOneByPred<sim::EndPointPair>([localEpId, remoteEpId](const sim::EndPointPair &pair) {
228+ return ((pair.local_enpoint_id == localEpId) && (pair.remote_enpoint_id == remoteEpId));
229+ });
230+ 
231+ if (!pairOpt.second) {
232+ std::cout<<"cannot find EndPointPair by local:"<<localEpId<<" remote:"<<remoteEpId<< std::endl;
233+ return HcclVmResult::HCCL_SIM_E_NOT_FOUND;
234+ }
235+ 
236+ chData.protocol = pairOpt.first.tp_type;
203 chData.jettyNum = ch.jettyNum;237 chData.jettyNum = ch.jettyNum;
204 // ChannelData.jettyId[32] 仅容纳前 32 个,与 CcuChannelTab.jettyId[64] 差异238 // ChannelData.jettyId[32] 仅容纳前 32 个,与 CcuChannelTab.jettyId[64] 差异
205 uint32_t copyNum = std::min(static_cast<uint32_t>(chData.jettyNum),239 uint32_t copyNum = std::min(static_cast<uint32_t>(chData.jettyNum),
@@ -208,7 +242,7 @@ HcclVmResult CreateChannelInfo(HcclVmSynData &hvmSynData)
208 242 
209 std::cout << "[CreateChannelInfo] channelId=" << chData.channelId243 std::cout << "[CreateChannelInfo] channelId=" << chData.channelId
210 << ", srcRank=" << chData.srcRank << ", dstRank=" << chData.dstRank244 << ", srcRank=" << chData.srcRank << ", dstRank=" << chData.dstRank
211- << ", jettyNum=" << chData.jettyNum << std::endl;245+ << ", jettyNum=" << chData.jettyNum << "protocol:"<< chData.protocol << std::endl;
212 246 
213 hvmSynData.channel_info.data.push_back(chData);247 hvmSynData.channel_info.data.push_back(chData);
214 }248 }
@@ -259,7 +293,10 @@ HcclVmResult CreateJettyInfo(HcclVmSynData &hvmSynData)
259 }293 }
260 294 
261 uint64_t raCtxHandle = raCtx.first.id;295 uint64_t raCtxHandle = raCtx.first.id;
262- auto raJettys = RunnerDB::GetByPred<sim::RaJetty>([raCtxHandle](const sim::RaJetty& jetty) { return jetty.ctx_handle == raCtxHandle; });296+ auto raJettys = RunnerDB::GetByPred<sim::RaJetty>([raCtxHandle](const sim::RaJetty& jetty) { return jetty.ctx_handle == raCtxHandle && jetty.mode == 3; });
297+ if (raJettys.empty()) {
298+ continue;
299+ }
263 channelData.jettyNum = raJettys.size();300 channelData.jettyNum = raJettys.size();
264 for (int i = 0; i < channelData.jettyNum; i++) {301 for (int i = 0; i < channelData.jettyNum; i++) {
265 channelData.jettyId[i] = raJettys[i].jetty_id;302 channelData.jettyId[i] = raJettys[i].jetty_id;
@@ -383,9 +420,8 @@ HcclVmResult DumpHcclVmSynthesisData(const std::string &dataId)
383 char fileName[256];420 char fileName[256];
384 snprintf(fileName, sizeof(fileName), HCCLVM_SYN_DATA_FILE.c_str(), dataId.c_str());421 snprintf(fileName, sizeof(fileName), HCCLVM_SYN_DATA_FILE.c_str(), dataId.c_str());
385 422 
386- // 假设 FindRootPath() 已经实现并返回插件根目录423+ fs::create_directories(fs::path(InstallPath::ResolveToInstallRoot("data")));
387- std::string rootPath = GetBinLocation();424+ std::string fullPath = InstallPath::ResolveToInstallRoot("data" + std::string(fileName));
388- std::string fullPath = rootPath + DATA_FILE_PATH + fileName;
389 425 
390 // 构造hccl vm synthesis数据426 // 构造hccl vm synthesis数据
391 HcclVmSynData hvmSynData;427 HcclVmSynData hvmSynData;
@@ -471,13 +507,11 @@ HcclVmResult DumpHcclVmInstrData(const std::string &dataId)
471 char fileName[256];507 char fileName[256];
472 snprintf(fileName, sizeof(fileName), HCCLVM_INSTR_DATA_FILE.c_str(), dataId.c_str());508 snprintf(fileName, sizeof(fileName), HCCLVM_INSTR_DATA_FILE.c_str(), dataId.c_str());
473 509
474- // 假设 FindRootPath() 已经实现并返回插件根目录510+ fs::create_directories(fs::path(InstallPath::ResolveToInstallRoot("data")));
475- std::string rootPath = GetBinLocation();511+ std::string fullPath = InstallPath::ResolveToInstallRoot("data" + std::string(fileName));
476- std::string fullPath = rootPath + DATA_FILE_PATH + fileName;
477- 
478 FILE *fp = fopen(fullPath.c_str(), "wb");512 FILE *fp = fopen(fullPath.c_str(), "wb");
479 if (!fp) {513 if (!fp) {
480- std::cout << "[ERROR][DumpHcclVmInstrData] Open file failed: "<<rootPath << std::endl;514+ std::cout << "[ERROR][DumpHcclVmInstrData] Open file failed: "<<fullPath << std::endl;
481 return HcclVmResult::HCCL_SIM_E_INTERNAL;515 return HcclVmResult::HCCL_SIM_E_INTERNAL;
482 }516 }
483 517 
@@ -563,13 +597,12 @@ HcclVmResult DumpHcclVmTask(const std::string &dataId)
563 char fileName[256];597 char fileName[256];
564 snprintf(fileName, sizeof(fileName), HCCLVM_TASK_DATA_FILE.c_str(), dataId.c_str());598 snprintf(fileName, sizeof(fileName), HCCLVM_TASK_DATA_FILE.c_str(), dataId.c_str());
565 599
566- // 假设 FindRootPath() 已经实现并返回插件根目录600+ fs::create_directories(fs::path(InstallPath::ResolveToInstallRoot("data")));
567- std::string rootPath = GetBinLocation();601+ std::string fullPath = InstallPath::ResolveToInstallRoot("data" + std::string(fileName));
568- std::string fullPath = rootPath + DATA_FILE_PATH + fileName;
569 602 
570 FILE *fp = fopen(fullPath.c_str(), "wb");603 FILE *fp = fopen(fullPath.c_str(), "wb");
571 if (!fp) {604 if (!fp) {
572- std::cout << "[ERROR][DumpHcclVmTask] Open file failed: "<<rootPath << std::endl;605+ std::cout << "[ERROR][DumpHcclVmTask] Open file failed: "<<fullPath << std::endl;
573 return HcclVmResult::HCCL_SIM_E_INTERNAL;606 return HcclVmResult::HCCL_SIM_E_INTERNAL;
574 }607 }
575 608 
Mtest/hccl_vm/src/store/store_sim_device_memory_manager.cc+42-9
@@ -19,7 +19,9 @@
19 19 
20#include "sim_log.h"20#include "sim_log.h"
21#include "store_sim_memory_manager.h"21#include "store_sim_memory_manager.h"
22+#include "store_sim_comm_pool_policy.h"
22#include "sim_models.h"23#include "sim_models.h"
24+#include "store_sim_run_mode.h"
23 25 
24namespace sim26namespace sim
25{27{
@@ -71,31 +73,62 @@ namespace sim
71 HCCL_VM_INFO("dev:{:d} free vir mem:{:p}", devPhyId, virAddr);73 HCCL_VM_INFO("dev:{:d} free vir mem:{:p}", devPhyId, virAddr);
72 }74 }
73 75 
74- // 分配物理内存76+ // 分配物理内存:大块(仅校验模式开)引流到复用区,否则按名独立分配
75 void* DeviceMemoryManager::AllocPhyMem(const char* name, uint64_t deviceId, size_t size)77 void* DeviceMemoryManager::AllocPhyMem(const char* name, uint64_t deviceId, size_t size)
76 {78 {
77- HCCL_VM_INFO("dev:{:d} alloc phy mem:{}", deviceId, name);79+ if (name == nullptr) {
80+ return nullptr;
81+ }
82+ HCCL_VM_INFO("dev:{:d} alloc phy mem:{}, size:{}", deviceId, name, size);
83+ bool checkOnlyMode = IsCheckOnlyMode();
84+ // 超过复用区上界直接报错,不回退真实分配(避免巨块吃满 /dev/shm)。
85+ if (CommPoolPolicy::ExceedsCeiling(size, checkOnlyMode)) {
86+ HCCL_VM_ERROR("dev:{:d} alloc phy mem:{} size:{} exceeds pool ceiling:{}, reject",
87+ deviceId, name, size, CommPoolPolicy::kPoolSize);
88+ return nullptr;
89+ }
90+ if (CommPoolPolicy::ShouldRedirect(size, checkOnlyMode)) {
91+ return MemoryManager::GetInstance().AcquireMemByName(CommPoolPolicy::kPoolName);
92+ }
78 return MemoryManager::GetInstance().AllocMemByName(name, size);93 return MemoryManager::GetInstance().AllocMemByName(name, size);
79 }94 }
80 95 
81- // 释放物理内存96+ // 释放物理内存(非复用区):是否在复用区由调用方按 size 判断后分流,这里只做真正的释放。
82 void DeviceMemoryManager::FreePhyMem(const char* name, uint64_t deviceId)97 void DeviceMemoryManager::FreePhyMem(const char* name, uint64_t deviceId)
83 {98 {
99+ if (name == nullptr) {
100+ return;
101+ }
84 HCCL_VM_INFO("dev:{:d} free phy mem:{}", deviceId, name);102 HCCL_VM_INFO("dev:{:d} free phy mem:{}", deviceId, name);
85 MemoryManager::GetInstance().FreeMemByName(name);103 MemoryManager::GetInstance().FreeMemByName(name);
86 }104 }
87- 105+ 
88- // 获取物理内存106+ // 获取物理内存:大块(仅校验模式开)引流到复用区,否则按名独立分配
89 void* DeviceMemoryManager::AcquirePhyMem(const char* name, uint64_t deviceId, size_t size)107 void* DeviceMemoryManager::AcquirePhyMem(const char* name, uint64_t deviceId, size_t size)
90 {108 {
91- (void) size;109+ if (name == nullptr) {
92- HCCL_VM_INFO("dev:{:d} acquire phy mem:{}", deviceId, name);110+ return nullptr;
93- return MemoryManager::GetInstance().AcquireMemByName(name); 111+ }
112+ HCCL_VM_INFO("dev:{:d} acquire phy mem:{}, size:{}", deviceId, name, size);
113+ bool checkOnlyMode = IsCheckOnlyMode();
114+ // 超过复用区上界直接报错,不回退真实分配。
115+ if (CommPoolPolicy::ExceedsCeiling(size, checkOnlyMode)) {
116+ HCCL_VM_ERROR("dev:{:d} acquire phy mem:{} size:{} exceeds pool ceiling:{}, reject",
117+ deviceId, name, size, CommPoolPolicy::kPoolSize);
118+ return nullptr;
119+ }
120+ if (CommPoolPolicy::ShouldRedirect(size, checkOnlyMode)) {
121+ return MemoryManager::GetInstance().AcquireMemByName(CommPoolPolicy::kPoolName);
122+ }
123+ return MemoryManager::GetInstance().AcquireMemByName(name);
94 }124 }
95 125 
96- // 释放物理内存126+ // 释放物理内存(非复用区):是否在复用区由调用方按 size 判断后分流,这里只做真正的释放。
97 int DeviceMemoryManager::ReleasePhyMem(const char* name, uint64_t deviceId)127 int DeviceMemoryManager::ReleasePhyMem(const char* name, uint64_t deviceId)
98 {128 {
129+ if (name == nullptr) {
130+ return 0;
131+ }
99 HCCL_VM_INFO("dev:{:d} release phy mem:{}", deviceId, name);132 HCCL_VM_INFO("dev:{:d} release phy mem:{}", deviceId, name);
100 MemoryManager::GetInstance().ReleaseMemByName(name);133 MemoryManager::GetInstance().ReleaseMemByName(name);
101 return 0;134 return 0;
Atest/hccl_vm/src/store/store_sim_run_mode.cc+30-0
@@ -0,0 +1,30 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "store_sim_run_mode.h"
12+ 
13+#include "db_sim_runner_db.h"
14+#include "sim_models.h"
15+ 
16+namespace sim {
17+bool ProbeCheckOnlyMode()
18+{
19+ auto cfg = RunnerDB::GetOneByPred<RunModeConfig>(
20+ [](const RunModeConfig&) { return true; });
21+ return cfg.second && cfg.first.mode != 0;
22+}
23+ 
24+bool IsCheckOnlyMode()
25+{
26+ // 函数局部静态变量初始化线程安全,进程内只读一次并缓存。
27+ static bool cached = ProbeCheckOnlyMode();
28+ return cached;
29+}
30+} // namespace sim
Mtest/hccl_vm/src/topo/CMakeLists.txt+3-15
@@ -6,7 +6,7 @@
6# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.6# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
7# See LICENSE in the root of the software repository for the full text of the License.7# See LICENSE in the root of the software repository for the full text of the License.
8 8 
9-add_library(topo SHARED "")9+add_library(topo STATIC "")
10 10 
11target_sources(topo PRIVATE11target_sources(topo PRIVATE
12 topo_cluster_ir.cc12 topo_cluster_ir.cc
@@ -16,7 +16,6 @@ target_sources(topo PRIVATE
16)16)
17 17 
18target_include_directories(topo PRIVATE18target_include_directories(topo PRIVATE
19- ${THRID_PARTY_DIR}
20 ${THRID_PARTY_DIR}/nlohmann_json19 ${THRID_PARTY_DIR}/nlohmann_json
21 ${HCOMM_VM_ROOT_PATH}/include20 ${HCOMM_VM_ROOT_PATH}/include
22 ${HCOMM_VM_ROOT_PATH}/include21 ${HCOMM_VM_ROOT_PATH}/include
@@ -30,6 +29,7 @@ target_include_directories(topo PRIVATE
30)29)
31 30 
32target_compile_options(topo PRIVATE31target_compile_options(topo PRIVATE
32+ -fPIC
33 -Werror33 -Werror
34 -fno-common34 -fno-common
35 -fno-strict-aliasing35 -fno-strict-aliasing
@@ -39,16 +39,4 @@ target_compile_options(topo PRIVATE
39 -D_GLIBCXX_USE_CXX11_ABI=039 -D_GLIBCXX_USE_CXX11_ABI=0
40)40)
41 41 
42-target_link_libraries(topo PRIVATE42+target_link_libraries(topo PRIVATE log)
43- runnerdb
44- log
45-)
46- 
47-install(TARGETS topo
48- DESTINATION "."
49- # 文件的权限 (755)
50- PERMISSIONS
51- OWNER_READ OWNER_WRITE OWNER_EXECUTE
52- GROUP_READ GROUP_EXECUTE
53- WORLD_READ WORLD_EXECUTE
54-)
Mtest/hccl_vm/src/topo/topo_ascend_cluster_parser.cc+20-6
@@ -18,10 +18,16 @@
18#include <sstream>18#include <sstream>
19#include <string>19#include <string>
20#include <unordered_map>20#include <unordered_map>
21+#include <filesystem>
22+#include <system_error>
23+ 
24+namespace fs = std::filesystem;
21 25 
22#include "topo_cluster_dumper.h"26#include "topo_cluster_dumper.h"
23#include "sim_common_macro.h"27#include "sim_common_macro.h"
24#include "sim_log.h"28#include "sim_log.h"
29+#include "sim_common_api.h"
30+#include "sim_yaml_config.h"
25#include "sim_ip_address.h"31#include "sim_ip_address.h"
26#include "db_sim_runner_ops.h"32#include "db_sim_runner_ops.h"
27 33 
@@ -107,7 +113,8 @@ HcclVmResult AscendClusterTopoParser::InitClusterTopo(const std::string &cluster
107 HCCL_VM_DEBUG("Total device count: {}", network_.GetTotalDeviceCount());113 HCCL_VM_DEBUG("Total device count: {}", network_.GetTotalDeviceCount());
108 HCCL_VM_DEBUG("Total link count: {}", network_.GetTotalLinkCount());114 HCCL_VM_DEBUG("Total link count: {}", network_.GetTotalLinkCount());
109 115 
110- ClusterTopoDumper::DumpToFile(network_, outputFile_);116+ fs::create_directories(fs::path(InstallPath::ResolveToInstallRoot("data")));
117+ ClusterTopoDumper::DumpToFile(network_, InstallPath::ResolveToInstallRoot("data/" + outputFile_));
111 118 
112 // 3. 初始化IR数据,保存至DB层119 // 3. 初始化IR数据,保存至DB层
113 if (InitClusterStaticTopoData() != HcclVmResult::HCCL_SIM_SUCCESS) {120 if (InitClusterStaticTopoData() != HcclVmResult::HCCL_SIM_SUCCESS) {
@@ -307,10 +314,8 @@ HcclVmResult AscendClusterTopoParser::BuildLevelList(const Server &server, int s
307 rankAddrList.push_back(addrEntry);314 rankAddrList.push_back(addrEntry);
308 }315 }
309 316 
310- if (!rankAddrList.empty()) {317+ levelEntry["rank_addr_list"] = rankAddrList;
311- levelEntry["rank_addr_list"] = rankAddrList;318+ levelList.push_back(levelEntry);
312- levelList.push_back(levelEntry);
313- }
314 }319 }
315 320 
316 // Process PEER2NET from rootinfo portGroups321 // Process PEER2NET from rootinfo portGroups
@@ -411,7 +416,16 @@ HcclVmResult AscendClusterTopoParser::CreateRankTableFile(const TopoMeta &topoMe
411 }416 }
412 }417 }
413 418 
414- std::string outputPath = "ranktable.json";419+ std::error_code ec;
420+ fs::path exePath = fs::read_symlink("/proc/self/exe", ec);
421+ fs::path binDir = ec ? fs::current_path() : exePath.parent_path();
422+ if (binDir.filename() == "bin") {
423+ binDir = binDir.parent_path();
424+ }
425+ fs::path dataDir = binDir / "data";
426+ std::error_code ec2;
427+ fs::create_directories(dataDir, ec2);
428+ std::string outputPath = (dataDir / "ranktable.json").string();
415 std::ofstream ofs(outputPath);429 std::ofstream ofs(outputPath);
416 if (!ofs.is_open()) {430 if (!ofs.is_open()) {
417 HCCL_VM_ERROR("[{}] failed to open ranktable.json for writing", __func__);431 HCCL_VM_ERROR("[{}] failed to open ranktable.json for writing", __func__);
Mtest/hccl_vm/src/utils/CMakeLists.txt+13-49
@@ -6,21 +6,27 @@
6# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.6# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
7# See LICENSE in the root of the software repository for the full text of the License.7# See LICENSE in the root of the software repository for the full text of the License.
8 8 
9-# --- log library (from sim_log.cc) ---9+# --- log library (sim_log.cc + sim_yaml_config.cc + sim_common.cpp) ---
10-add_library(log SHARED sim_log.cc)10+add_library(log STATIC sim_log.cc sim_yaml_config.cc sim_common_api.cc)
11 11 
12target_include_directories(log PUBLIC12target_include_directories(log PUBLIC
13 ${CMAKE_SOURCE_DIR}/include13 ${CMAKE_SOURCE_DIR}/include
14 ${CMAKE_SOURCE_DIR}/third_party14 ${CMAKE_SOURCE_DIR}/third_party
15+ ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
16+ ${CMAKE_SOURCE_DIR}/third_party/yaml-cpp/include
15)17)
16 18 
17target_include_directories(log PRIVATE19target_include_directories(log PRIVATE
18 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc20 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc
19 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/runtime21 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/runtime
20 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/profiling22 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/profiling
23+ ${YAMLCPP_INCLUDE_DIR}
21)24)
22 25 
26+target_compile_definitions(log PRIVATE HAVE_YAML_CPP)
27+ 
23target_compile_options(log PRIVATE28target_compile_options(log PRIVATE
29+ -fPIC
24 -std=c++1430 -std=c++14
25 -Werror31 -Werror
26 -fno-common32 -fno-common
@@ -31,32 +37,14 @@ target_compile_options(log PRIVATE
31 -D_GLIBCXX_USE_CXX11_ABI=037 -D_GLIBCXX_USE_CXX11_ABI=0
32)38)
33 39 
34-install(TARGETS log40+target_link_libraries(log PRIVATE yaml-cpp::yaml-cpp)
35- DESTINATION "."
36- PERMISSIONS
37- OWNER_READ OWNER_WRITE OWNER_EXECUTE
38- GROUP_READ GROUP_EXECUTE
39- WORLD_READ WORLD_EXECUTE
40-)
41 41 
42-install(TARGETS log42+if(TARGET third_party_yaml_cpp)
43- DESTINATION "plugin/validate/runner"43+ add_dependencies(log third_party_yaml_cpp)
44- PERMISSIONS44+endif()
45- OWNER_READ OWNER_WRITE OWNER_EXECUTE
46- GROUP_READ GROUP_EXECUTE
47- WORLD_READ WORLD_EXECUTE
48-)
49- 
50-install(TARGETS log
51- DESTINATION "plugin/validate/checker"
52- PERMISSIONS
53- OWNER_READ OWNER_WRITE OWNER_EXECUTE
54- GROUP_READ GROUP_EXECUTE
55- WORLD_READ WORLD_EXECUTE
56-)
57 45 
58# --- loader library (from sim_loader.cc, migrated from src/loader) ---46# --- loader library (from sim_loader.cc, migrated from src/loader) ---
59-add_library(loader SHARED sim_loader.cc)47+add_library(loader STATIC sim_loader.cc)
60 48 
61target_compile_options(loader PRIVATE49target_compile_options(loader PRIVATE
62 -fPIC50 -fPIC
@@ -95,27 +83,3 @@ target_link_libraries(loader PRIVATE
95 runnerdb83 runnerdb
96 rt84 rt
97)85)
98- 
99-install(TARGETS loader
100- DESTINATION "."
101- PERMISSIONS
102- OWNER_READ OWNER_WRITE OWNER_EXECUTE
103- GROUP_READ GROUP_EXECUTE
104- WORLD_READ WORLD_EXECUTE
105-)
106- 
107-install(TARGETS loader
108- DESTINATION "plugin/validate/checker"
109- PERMISSIONS
110- OWNER_READ OWNER_WRITE OWNER_EXECUTE
111- GROUP_READ GROUP_EXECUTE
112- WORLD_READ WORLD_EXECUTE
113-)
114- 
115-install(TARGETS loader
116- DESTINATION "plugin/validate/runner"
117- PERMISSIONS
118- OWNER_READ OWNER_WRITE OWNER_EXECUTE
119- GROUP_READ GROUP_EXECUTE
120- WORLD_READ WORLD_EXECUTE
121-)
Atest/hccl_vm/src/utils/sim_common_api.cc+76-0
@@ -0,0 +1,76 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "sim_common_api.h"
12+ 
13+#include <cstdlib>
14+#include <fstream>
15+#include <string>
16+#include <unistd.h>
17+#include <climits>
18+ 
19+static std::string GetExePath()
20+{
21+ char buf[PATH_MAX] = {0};
22+ ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
23+ if (len <= 0) return "";
24+ buf[len] = '\0';
25+ return std::string(buf);
26+}
27+ 
28+static std::string GetExeDir()
29+{
30+ std::string full = GetExePath();
31+ if (full.empty()) {
32+ return ".";
33+ }
34+ size_t pos = full.find_last_of('/');
35+ return (pos == std::string::npos) ? "." : full.substr(0, pos);
36+}
37+ 
38+static std::string ComputeInstallRoot()
39+{
40+ const char* env = std::getenv("HCCL_VM_INSTALL_ROOT");
41+ if (env && *env) {
42+ return std::string(env);
43+ }
44+ std::string exe = GetExePath();
45+ if (exe.empty()) {
46+ return ".";
47+ }
48+ 
49+ std::string cur = exe;
50+ for (int i = 0; i < 8; ++i) {
51+ size_t pos = cur.find_last_of('/');
52+ if (pos == std::string::npos || pos == 0) {
53+ break;
54+ }
55+ cur = cur.substr(0, pos);
56+ if (std::ifstream(cur + "/config/log_config.yaml").good()) {
57+ return cur;
58+ }
59+ }
60+ return GetExeDir();
61+}
62+ 
63+const std::string& InstallPath::GetHcclVmInstallAbsPath()
64+{
65+ static const std::string root = ComputeInstallRoot();
66+ return root;
67+}
68+ 
69+std::string InstallPath::ResolveToInstallRoot(const std::string& relPath)
70+{
71+ if (relPath.empty() || relPath[0] == '/' ||
72+ (relPath.size() >= 2 && relPath[0] == '.' && relPath[1] == '/')) {
73+ return relPath;
74+ }
75+ return GetHcclVmInstallAbsPath() + "/" + relPath;
76+}
Mtest/hccl_vm/src/utils/sim_loader.cc+5-2
@@ -15,6 +15,8 @@
15 15 
16#include "sim_common_defs.h"16#include "sim_common_defs.h"
17#include "sim_log.h"17#include "sim_log.h"
18+#include "sim_common_api.h"
19+#include "sim_yaml_config.h"
18#include "db_sim_op_db_ops.h"20#include "db_sim_op_db_ops.h"
19 21 
20namespace loader {22namespace loader {
@@ -35,8 +37,9 @@ HcclResult Loader::LoadOpTaskFile(const std::string dbPath)
35 targetPath = dbPath;37 targetPath = dbPath;
36 HCCL_VM_INFO("[Loader] Loading from specific backup path: {}", targetPath);38 HCCL_VM_INFO("[Loader] Loading from specific backup path: {}", targetPath);
37 } else {39 } else {
38- targetPath = "./../../../hccl_vm_data.db";40+ targetPath = InstallPath::ResolveToInstallRoot("data/hccl_vm_data.db");
39- HCCL_VM_INFO("[Loader] Loading using default configuration path: {}", targetPath);41+ std::string absPath = std::filesystem::absolute(targetPath).string();
42+ HCCL_VM_INFO("[Loader] Loading using default configuration path: {}", absPath);
40 }43 }
41 44 
42 config.dbPath = targetPath;45 config.dbPath = targetPath;
Mtest/hccl_vm/src/utils/sim_log.cc+83-3
@@ -11,8 +11,15 @@
11#include "sim_log.h"11#include "sim_log.h"
12 12 
13#include <cstdint>13#include <cstdint>
14+#include <cstdlib>
14#include <fstream>15#include <fstream>
16+#include <map>
17+#include <string>
18+#include <unistd.h>
19+#include <errno.h>
15 20 
21+#include "sim_common_api.h"
22+#include "sim_yaml_config.h"
16#include "spdlog/details/os-inl.h"23#include "spdlog/details/os-inl.h"
17#include "spdlog/sinks/rotating_file_sink.h"24#include "spdlog/sinks/rotating_file_sink.h"
18#include "spdlog/sinks/stdout_color_sinks.h"25#include "spdlog/sinks/stdout_color_sinks.h"
@@ -47,13 +54,11 @@ std::shared_ptr<spdlog::sinks::rotating_file_sink_mt> InitFileSink(const LogConf
47 << config.fileSuffix;54 << config.fileSuffix;
48 const std::string newPath = oss.str();55 const std::string newPath = oss.str();
49 if (spdlog::details::os::rename(filePath, newPath) != 0) {56 if (spdlog::details::os::rename(filePath, newPath) != 0) {
50- std::cout << "[ERROR] Fail to rename rotating log file: " << newPath << std::endl;57+ std::cout << "[ERROR] Fail to rename rotating log file: " << filePath << " -> " << newPath << " current pid " << getpid() << " errno: " << errno << std::endl;
51 return;58 return;
52 }59 }
53- 
54 };60 };
55 61 
56- // log file name
57 std::ostringstream logFileName;62 std::ostringstream logFileName;
58 logFileName << config.filePath << "/" << config.fileBaseName << "_" << std::to_string(getpid()) << config.fileSuffix;63 logFileName << config.filePath << "/" << config.fileBaseName << "_" << std::to_string(getpid()) << config.fileSuffix;
59 64 
@@ -64,6 +69,81 @@ std::shared_ptr<spdlog::sinks::rotating_file_sink_mt> InitFileSink(const LogConf
64 return sink;69 return sink;
65}70}
66 71 
72+static std::string GetLogYamlConfigPath()
73+{
74+ const char* env = std::getenv("HCCL_VM_LOG_CONFIG_PATH");
75+ if (env && *env) {
76+ return env;
77+ }
78+ return InstallPath::ResolveToInstallRoot("config/log_config.yaml");
79+}
80+ 
81+LogConfig LoadLogConfig(const std::string& process_name)
82+{
83+ LogConfig cfg;
84+ 
85+ static const std::map<std::string, std::string> yaml_node_map = {
86+ {"device_aarch64", "proxy"}
87+ };
88+ std::string node_name = process_name;
89+ auto it = yaml_node_map.find(process_name);
90+ if (it != yaml_node_map.end()) {
91+ node_name = it->second;
92+ }
93+ 
94+ std::map<std::string, std::string> fields;
95+ if (LoadYamlStringMap(GetLogYamlConfigPath(), node_name, fields)) {
96+ auto it_console = fields.find("console_level");
97+ auto it_file = fields.find("file_level");
98+ auto it_max_size = fields.find("max_file_size");
99+ auto it_max_num = fields.find("max_files");
100+ auto it_path = fields.find("file_path");
101+ auto it_suffix = fields.find("file_suffix");
102+ auto it_compress = fields.find("enable_compress");
103+ 
104+ if (it_console != fields.end()) {
105+ cfg.consoleLevel = std::stoi(it_console->second);
106+ }
107+ if (it_file != fields.end()) {
108+ cfg.fileLevel = std::stoi(it_file->second);
109+ }
110+ if (it_max_size != fields.end()) {
111+ cfg.maxFileSize = static_cast<size_t>(std::stoull(it_max_size->second));
112+ }
113+ if (it_max_num != fields.end()) {
114+ cfg.maxFiles = static_cast<size_t>(std::stoull(it_max_num->second));
115+ }
116+ if (it_path != fields.end()) {
117+ cfg.filePath = InstallPath::ResolveToInstallRoot(it_path->second);
118+ }
119+ if (it_suffix != fields.end()) {
120+ cfg.fileSuffix = it_suffix->second;
121+ }
122+ if (it_compress != fields.end()) {
123+ cfg.enableCompress = (it_compress->second == "true" || it_compress->second == "1");
124+ }
125+ } else {
126+ if (process_name == "proxy") {
127+ cfg.filePath = InstallPath::ResolveToInstallRoot("logs/proxy");
128+ } else if (process_name == "device_aarch64") {
129+ cfg.filePath = InstallPath::ResolveToInstallRoot("logs/proxy");
130+ } else if (process_name == "runner") {
131+ cfg.filePath = InstallPath::ResolveToInstallRoot("logs/runner");
132+ } else if (process_name == "checker") {
133+ cfg.filePath = InstallPath::ResolveToInstallRoot("logs/checker");
134+ } else if (process_name == "hccl_vm") {
135+ cfg.filePath = InstallPath::ResolveToInstallRoot("logs");
136+ }
137+ }
138+ 
139+ if (process_name == "device_aarch64") {
140+ cfg.fileBaseName = "proxy_" + std::to_string(getppid());
141+ } else {
142+ cfg.fileBaseName = process_name;
143+ }
144+ return cfg;
145+}
146+ 
67void InitLogger(const LogConfig& config)147void InitLogger(const LogConfig& config)
68{148{
69 try {149 try {
Atest/hccl_vm/src/utils/sim_yaml_config.cc+50-0
@@ -0,0 +1,50 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "sim_yaml_config.h"
12+ 
13+#include <fstream>
14+#include <string>
15+ 
16+#if !defined(NO_YAML_CONFIG) && defined(HAVE_YAML_CPP)
17+#include "yaml-cpp/yaml.h"
18+#endif
19+ 
20+#include "sim_log.h"
21+ 
22+bool LoadYamlStringMap(const std::string& yamlPath,
23+ const std::string& nodeName,
24+ std::map<std::string, std::string>& out)
25+{
26+#if !defined(NO_YAML_CONFIG) && defined(HAVE_YAML_CPP)
27+ try {
28+ if (!std::ifstream(yamlPath).good()) {
29+ return false;
30+ }
31+ YAML::Node root = YAML::LoadFile(yamlPath);
32+ if (!root[nodeName]) {
33+ return false;
34+ }
35+ const YAML::Node& node = root[nodeName];
36+ for (auto it = node.begin(); it != node.end(); ++it) {
37+ out[it->first.as<std::string>()] = it->second.as<std::string>();
38+ }
39+ return true;
40+ } catch (const std::exception& e) {
41+ HCCL_VM_ERROR("[SimYamlConfig] Failed to parse YAML file {}: {}", yamlPath, e.what());
42+ return false;
43+ }
44+#else
45+ (void)yamlPath;
46+ (void)nodeName;
47+ (void)out;
48+ return false;
49+#endif
50+}
Mtest/hccl_vm/test/cmd/CMakeLists.txt+3-0
@@ -69,6 +69,7 @@ target_link_libraries(test_cmd_base_utils PRIVATE
69 cmd69 cmd
70 store70 store
71 runnerdb71 runnerdb
72+ topo
72 log73 log
73 yaml-cpp74 yaml-cpp
74 z75 z
@@ -116,6 +117,7 @@ target_link_libraries(test_cmd_table_utils PRIVATE
116 cmd117 cmd
117 store118 store
118 runnerdb119 runnerdb
120+ topo
119 log121 log
120 yaml-cpp122 yaml-cpp
121 z123 z
@@ -155,6 +157,7 @@ target_link_libraries(test_cmd_cluster_model_utils PRIVATE
155 cmd157 cmd
156 store158 store
157 runnerdb159 runnerdb
160+ topo
158 log161 log
159 yaml-cpp162 yaml-cpp
160 z163 z
Mtest/hccl_vm/test/cmd/cmd_base_utils_test.cc+20-2
@@ -11,6 +11,7 @@
11#include <cstdint>11#include <cstdint>
12#include <cstdlib>12#include <cstdlib>
13#include <cstring>13#include <cstring>
14+#include <sys/mman.h> // shm_unlink
14#include <filesystem>15#include <filesystem>
15#include <fstream>16#include <fstream>
16#include <gtest/gtest.h>17#include <gtest/gtest.h>
@@ -23,6 +24,7 @@
23#include "cmd_base_utils_internal.h"24#include "cmd_base_utils_internal.h"
24#include "cmd_cluster_model_utils.h"25#include "cmd_cluster_model_utils.h"
25#include "store_sim_memory_manager.h"26#include "store_sim_memory_manager.h"
27+#include "store_sim_comm_pool_policy.h"
26 28 
27using namespace HcclSim;29using namespace HcclSim;
28 30 
@@ -1461,32 +1463,48 @@ class InitHvmEnvTest : public testing::Test {
1461protected:1463protected:
1462 void SetUp() override {1464 void SetUp() override {
1463 sim::MemoryManager::GetInstance().FreeMemByName("HcclAicpuData");1465 sim::MemoryManager::GetInstance().FreeMemByName("HcclAicpuData");
1466+ shm_unlink("HcclAicpuData");
1467+ // 逐用例清掉 HcclCommPool 残留。
1468+ sim::MemoryManager::GetInstance().FreeMemByName(sim::CommPoolPolicy::kPoolName);
1469+ shm_unlink(sim::CommPoolPolicy::kPoolName);
1464 unsetenv("HCCL_OP_EXPANSION_MODE");1470 unsetenv("HCCL_OP_EXPANSION_MODE");
1465 unsetenv("HCCL_VM_INSTALL_DIR");1471 unsetenv("HCCL_VM_INSTALL_DIR");
1466 }1472 }
1467 void TearDown() override {1473 void TearDown() override {
1468 sim::MemoryManager::GetInstance().FreeMemByName("HcclAicpuData");1474 sim::MemoryManager::GetInstance().FreeMemByName("HcclAicpuData");
1475+ sim::MemoryManager::GetInstance().FreeMemByName(sim::CommPoolPolicy::kPoolName);
1469 unsetenv("HCCL_OP_EXPANSION_MODE");1476 unsetenv("HCCL_OP_EXPANSION_MODE");
1470 unsetenv("HCCL_VM_INSTALL_DIR");1477 unsetenv("HCCL_VM_INSTALL_DIR");
1471 }1478 }
1472};1479};
1473 1480 
1474TEST_F(InitHvmEnvTest, InitializesSharedMemoryWithoutAivValidation) {1481TEST_F(InitHvmEnvTest, InitializesSharedMemoryWithoutAivValidation) {
1475- HcclVmResult ret = InitHvmEnv("/nonexistent/path/for/ut", 2);1482+ HcclVmResult ret = InitHvmEnv("/nonexistent/path/for/ut", 2, false);
1476 EXPECT_EQ(ret, HCCL_SIM_HOST_SUCCESS_CMD);1483 EXPECT_EQ(ret, HCCL_SIM_HOST_SUCCESS_CMD);
1477 1484 
1478 void *shm = sim::MemoryManager::GetInstance().AcquireMemByName("HcclAicpuData");1485 void *shm = sim::MemoryManager::GetInstance().AcquireMemByName("HcclAicpuData");
1479 ASSERT_NE(shm, nullptr);1486 ASSERT_NE(shm, nullptr);
1480 sim::MemoryManager::GetInstance().ReleaseMemByName("HcclAicpuData");1487 sim::MemoryManager::GetInstance().ReleaseMemByName("HcclAicpuData");
1488+ 
1489+ // clean 模式不建复用区 HcclCommPool。
1490+ EXPECT_EQ(sim::MemoryManager::GetInstance().AcquireMemByName(sim::CommPoolPolicy::kPoolName), nullptr);
1481}1491}
1482 1492 
1483TEST_F(InitHvmEnvTest, FailsInAivModeWithoutInstallDir) {1493TEST_F(InitHvmEnvTest, FailsInAivModeWithoutInstallDir) {
1484 setenv("HCCL_OP_EXPANSION_MODE", "AIV", 1);1494 setenv("HCCL_OP_EXPANSION_MODE", "AIV", 1);
1485 1495 
1486- HcclVmResult ret = InitHvmEnv("/nonexistent/path/for/ut", 2);1496+ HcclVmResult ret = InitHvmEnv("/nonexistent/path/for/ut", 2, false);
1487 EXPECT_EQ(ret, HCCL_SIM_HOST_ERROR_CMD);1497 EXPECT_EQ(ret, HCCL_SIM_HOST_ERROR_CMD);
1488}1498}
1489 1499 
1500+TEST_F(InitHvmEnvTest, FailsWhenCommPoolNameAlreadyExists) {
1501+ // 仅校验模式下池名被预先占用时,InitHvmEnv 建池失败、返回错误,且不创建 HcclAicpuData。
1502+ ASSERT_NE(sim::MemoryManager::GetInstance().AllocMemByName(
1503+ sim::CommPoolPolicy::kPoolName, 4096), nullptr);
1504+ EXPECT_EQ(InitHvmEnv("/nonexistent/path/for/ut", 2, true), HCCL_SIM_HOST_ERROR_CMD);
1505+ EXPECT_EQ(sim::MemoryManager::GetInstance().AcquireMemByName("HcclAicpuData"), nullptr);
1506+}
1507+ 
1490// ==================== IsAivExpansionModeEnabled Tests ====================1508// ==================== IsAivExpansionModeEnabled Tests ====================
1491 1509 
1492class IsAivExpansionModeEnabledTest : public testing::Test {1510class IsAivExpansionModeEnabledTest : public testing::Test {
Mtest/hccl_vm/test/cmd/subcmd/CMakeLists.txt+1-1
@@ -20,7 +20,7 @@ set(SUBCMD_OBJS
20set(SUBCMD_LIBS20set(SUBCMD_LIBS
21 gtest_main gtest21 gtest_main gtest
22 ${SUBCMD_OBJS}22 ${SUBCMD_OBJS}
23- common cmd store runnerdb log yaml-cpp sqlite3 dl rt23+ common cmd store runnerdb topo log yaml-cpp sqlite3 dl rt
24 z24 z
25)25)
26 26 
Mtest/hccl_vm/test/device_arm/CMakeLists.txt+1-1
@@ -21,7 +21,7 @@ set(COMMON_INCLUDE_DIRS
21 ${CMAKE_SOURCE_DIR}/src/plugin/checker/header/external21 ${CMAKE_SOURCE_DIR}/src/plugin/checker/header/external
22 ${CMAKE_SOURCE_DIR}/third_party/CLI1122 ${CMAKE_SOURCE_DIR}/third_party/CLI11
23 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include23 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include
24- ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json24+ ${CMAKE_SOURCE_DIR}/third_party
25 $ENV{HCCL_CODE_HOME}/src/ops/op_common/inc25 $ENV{HCCL_CODE_HOME}/src/ops/op_common/inc
26 $ENV{HCCL_CODE_HOME}/src/common26 $ENV{HCCL_CODE_HOME}/src/common
27 $ENV{HCCL_CODE_HOME}/src/common/hcomm_dlsym27 $ENV{HCCL_CODE_HOME}/src/common/hcomm_dlsym
Mtest/hccl_vm/test/device_vir/CMakeLists.txt+1-1
@@ -17,7 +17,7 @@ target_include_directories(test_device_vir PRIVATE
17 ${CMAKE_SOURCE_DIR}/include/runnerdb17 ${CMAKE_SOURCE_DIR}/include/runnerdb
18 ${CMAKE_SOURCE_DIR}/third_party/CLI1118 ${CMAKE_SOURCE_DIR}/third_party/CLI11
19 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include19 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include
20- ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json20+ ${CMAKE_SOURCE_DIR}/third_party
21)21)
22 22 
23target_link_libraries(test_device_vir PRIVATE23target_link_libraries(test_device_vir PRIVATE
Mtest/hccl_vm/test/hccl_plugin_manager/CMakeLists.txt+1-1
@@ -30,7 +30,7 @@ include_directories(
30 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/asc/hccl/internal/hcomm/pkg_inc/hcomm/ccu30 ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc/asc/hccl/internal/hcomm/pkg_inc/hcomm/ccu
31 ${HOME}31 ${HOME}
32 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include32 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include
33- ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json33+ ${CMAKE_SOURCE_DIR}/third_party
34)34)
35 35 
36link_directories(36link_directories(
Mtest/hccl_vm/test/plugin/CMakeLists.txt+1-1
@@ -28,7 +28,7 @@ include_directories(
28 ${CMAKE_SOURCE_DIR}/include/runnerdb28 ${CMAKE_SOURCE_DIR}/include/runnerdb
29 ${CMAKE_SOURCE_DIR}/third_party/CLI1129 ${CMAKE_SOURCE_DIR}/third_party/CLI11
30 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include30 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include
31- ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json31+ ${CMAKE_SOURCE_DIR}/third_party
32 ${CMAKE_SOURCE_DIR}/third_party32 ${CMAKE_SOURCE_DIR}/third_party
33 ${CMAKE_SOURCE_DIR}/third_party/spdlog33 ${CMAKE_SOURCE_DIR}/third_party/spdlog
34 ${CMAKE_SOURCE_DIR}/third_party/spdlog/include34 ${CMAKE_SOURCE_DIR}/third_party/spdlog/include
Mtest/hccl_vm/test/plugin/checker/CMakeLists.txt+9-0
@@ -108,6 +108,9 @@ target_include_directories(test_checker BEFORE PRIVATE
108 ${CHECKER_HDR}/internal108 ${CHECKER_HDR}/internal
109 ${CHECKER_HDR}/external109 ${CHECKER_HDR}/external
110 ${CHECKER_HDR}/external/nlohmann110 ${CHECKER_HDR}/external/nlohmann
111+ ${THRID_PARTY_DIR}/nlohmann_json
112+ ${THRID_PARTY_DIR}
113+ ${THRID_PARTY_DIR}/yaml-cpp/include
111 ${CMAKE_SOURCE_DIR}/src/plugin/runner/ccu_executor114 ${CMAKE_SOURCE_DIR}/src/plugin/runner/ccu_executor
112 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/include115 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/include
113 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/pkg_inc116 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/pkg_inc
@@ -178,6 +181,9 @@ target_include_directories(test_storage_manager_checker BEFORE PRIVATE
178 ${CHECKER_HDR}/internal181 ${CHECKER_HDR}/internal
179 ${CHECKER_HDR}/external182 ${CHECKER_HDR}/external
180 ${CHECKER_HDR}/external/nlohmann183 ${CHECKER_HDR}/external/nlohmann
184+ ${THRID_PARTY_DIR}/nlohmann_json
185+ ${THRID_PARTY_DIR}
186+ ${THRID_PARTY_DIR}/yaml-cpp/include
181 ${CMAKE_SOURCE_DIR}/src/plugin/runner/ccu_executor187 ${CMAKE_SOURCE_DIR}/src/plugin/runner/ccu_executor
182 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/include188 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/include
183 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/pkg_inc189 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/pkg_inc
@@ -230,6 +236,9 @@ target_include_directories(test_task_utils BEFORE PRIVATE
230 ${CHECKER_HDR}/internal236 ${CHECKER_HDR}/internal
231 ${CHECKER_HDR}/external237 ${CHECKER_HDR}/external
232 ${CHECKER_HDR}/external/nlohmann238 ${CHECKER_HDR}/external/nlohmann
239+ ${THRID_PARTY_DIR}/nlohmann_json
240+ ${THRID_PARTY_DIR}
241+ ${THRID_PARTY_DIR}/yaml-cpp/include
233 ${CMAKE_SOURCE_DIR}/src/plugin/runner/ccu_executor242 ${CMAKE_SOURCE_DIR}/src/plugin/runner/ccu_executor
234 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/pkg_inc243 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/pkg_inc
235 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/pkg_inc/hccl244 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/pkg_inc/hccl
Mtest/hccl_vm/test/plugin/checker/st/aiv/aiv_st_test.cc+204-0
@@ -39,13 +39,20 @@ using Json = nlohmann::json;
39namespace V3 = HcclSim::TaskGraphGeneratorV3;39namespace V3 = HcclSim::TaskGraphGeneratorV3;
40 40 
41constexpr uint32_t TASK_MEM_COPY = 0;41constexpr uint32_t TASK_MEM_COPY = 0;
42+constexpr uint32_t TASK_REDUCE = 1;
43+constexpr uint32_t TASK_SET_FLAG = 2;
44+constexpr uint32_t TASK_WAIT_FLAG = 3;
42constexpr uint32_t TASK_PIPE_BARRIER = 4;45constexpr uint32_t TASK_PIPE_BARRIER = 4;
43constexpr uint32_t TASK_SYNC_ALL = 5;46constexpr uint32_t TASK_SYNC_ALL = 5;
44constexpr uint32_t TASK_SEND_FLAG = 6;47constexpr uint32_t TASK_SEND_FLAG = 6;
45constexpr uint32_t TASK_RECV_FLAG = 7;48constexpr uint32_t TASK_RECV_FLAG = 7;
49+constexpr uint32_t BUFFER_OUTPUT = 1;
46constexpr uint32_t BUFFER_INPUT = 0;50constexpr uint32_t BUFFER_INPUT = 0;
47constexpr uint32_t BUFFER_CCL = 2;51constexpr uint32_t BUFFER_CCL = 2;
48constexpr uint32_t BUFFER_UB = 3;52constexpr uint32_t BUFFER_UB = 3;
53+constexpr uint32_t BUFFER_FLAG = 4;
54+constexpr uint32_t PIPE_MTE2 = 1;
55+constexpr uint32_t PIPE_MTE3 = 2;
49constexpr uint32_t PIPE_ALL = 3;56constexpr uint32_t PIPE_ALL = 3;
50constexpr uint64_t DEFAULT_BUFFER_SIZE = 1ULL << 20;57constexpr uint64_t DEFAULT_BUFFER_SIZE = 1ULL << 20;
51 58 
@@ -160,6 +167,29 @@ Json MemCopy(uint32_t taskId, uint32_t rank, uint32_t block, uint32_t pipe, uint
160 {"dst", Slice(dstType, dstOffset, size)}});167 {"dst", Slice(dstType, dstOffset, size)}});
161}168}
162 169 
170+Json Reduce(uint32_t taskId, uint32_t rank, uint32_t block, uint32_t pipe, uint32_t srcRank, uint32_t dstRank,
171+ uint32_t srcType, uint64_t srcOffset, uint32_t dstType, uint64_t dstOffset, uint64_t size = 64,
172+ uint32_t dataType = 0, uint32_t reduceOp = 0)
173+{
174+ return RuntimeTask(TASK_REDUCE, "Reduce", taskId, rank, block, pipe,
175+ Json{{"srcRank", srcRank}, {"dstRank", dstRank}, {"src", Slice(srcType, srcOffset, size)},
176+ {"dst", Slice(dstType, dstOffset, size)}, {"dataType", dataType}, {"reduceOp", reduceOp}});
177+}
178+ 
179+Json SetFlag(uint32_t taskId, uint32_t rank, uint32_t block, uint32_t pipe, uint32_t srcPipe, uint32_t dstPipe,
180+ int32_t eventId)
181+{
182+ return RuntimeTask(TASK_SET_FLAG, "SetFlag", taskId, rank, block, pipe,
183+ Json{{"srcPipe", srcPipe}, {"dstPipe", dstPipe}, {"eventId", eventId}});
184+}
185+ 
186+Json WaitFlag(uint32_t taskId, uint32_t rank, uint32_t block, uint32_t pipe, uint32_t srcPipe, uint32_t dstPipe,
187+ int32_t eventId)
188+{
189+ return RuntimeTask(TASK_WAIT_FLAG, "WaitFlag", taskId, rank, block, pipe,
190+ Json{{"srcPipe", srcPipe}, {"dstPipe", dstPipe}, {"eventId", eventId}});
191+}
192+ 
163Json NormalTask(uint32_t taskId, uint32_t rank, uint32_t block, uint32_t pipe)193Json NormalTask(uint32_t taskId, uint32_t rank, uint32_t block, uint32_t pipe)
164{194{
165 const uint64_t base = 0x20000ULL + static_cast<uint64_t>(rank) * 0x4000ULL +195 const uint64_t base = 0x20000ULL + static_cast<uint64_t>(rank) * 0x4000ULL +
@@ -374,6 +404,58 @@ Json SendRecvSnapshot(uint32_t rank, uint32_t rankSize, uint64_t launch)
374 {NormalTask(12, rank, 0, 2)})});404 {NormalTask(12, rank, 0, 2)})});
375}405}
376 406 
407+Json CpGm2GMSnapshot(uint32_t rank, uint32_t rankSize, uint64_t launch, uint32_t iterationCount, bool reduceOut,
408+ bool flagMem = false, bool externalGap = false)
409+{
410+ std::vector<Json> mte2;
411+ std::vector<Json> mte3;
412+ const uint64_t sliceSize = 64;
413+ const uint32_t extSrcType = flagMem ? BUFFER_FLAG : BUFFER_INPUT;
414+ const uint32_t extDstType = flagMem ? BUFFER_FLAG : BUFFER_OUTPUT;
415+ for (uint32_t index = 0; index < iterationCount; ++index) {
416+ const uint32_t base = 100 + index * 6;
417+ const uint64_t offset = static_cast<uint64_t>(index) * (sliceSize + (externalGap ? sliceSize : 0));
418+ mte2.push_back(MemCopy(base + 0, rank, 0, PIPE_MTE2, rank, rank, extSrcType, offset, BUFFER_UB, 0,
419+ sliceSize));
420+ mte2.push_back(SetFlag(base + 1, rank, 0, PIPE_MTE2, PIPE_MTE2, PIPE_MTE3, 0));
421+ mte2.push_back(WaitFlag(base + 5, rank, 0, PIPE_MTE2, PIPE_MTE3, PIPE_MTE2, 1));
422+ 
423+ mte3.push_back(WaitFlag(base + 2, rank, 0, PIPE_MTE3, PIPE_MTE2, PIPE_MTE3, 0));
424+ if (reduceOut) {
425+ mte3.push_back(Reduce(base + 3, rank, 0, PIPE_MTE3, rank, rank, BUFFER_UB, 0, extDstType, offset,
426+ sliceSize));
427+ } else {
428+ mte3.push_back(MemCopy(base + 3, rank, 0, PIPE_MTE3, rank, rank, BUFFER_UB, 0, extDstType, offset,
429+ sliceSize));
430+ }
431+ mte3.push_back(SetFlag(base + 4, rank, 0, PIPE_MTE3, PIPE_MTE3, PIPE_MTE2, 1));
432+ }
433+ return Snapshot(rank, rankSize, launch, {Block(0, {}, std::move(mte2), std::move(mte3))});
434+}
435+ 
436+Json CpGm2GMPipeBoundarySnapshot(uint32_t rank, uint32_t rankSize, uint64_t launch)
437+{
438+ std::vector<Json> mte2{NormalTask(90, rank, 0, PIPE_MTE2)};
439+ std::vector<Json> mte3{NormalTask(91, rank, 0, PIPE_MTE3)};
440+ const uint64_t sliceSize = 64;
441+ for (uint32_t index = 0; index < 2; ++index) {
442+ const uint32_t base = 100 + index * 6;
443+ const uint64_t offset = static_cast<uint64_t>(index) * sliceSize;
444+ mte2.push_back(MemCopy(base + 0, rank, 0, PIPE_MTE2, rank, rank, BUFFER_INPUT, offset, BUFFER_UB, 0,
445+ sliceSize));
446+ mte2.push_back(SetFlag(base + 1, rank, 0, PIPE_MTE2, PIPE_MTE2, PIPE_MTE3, 0));
447+ mte2.push_back(WaitFlag(base + 5, rank, 0, PIPE_MTE2, PIPE_MTE3, PIPE_MTE2, 1));
448+ 
449+ mte3.push_back(WaitFlag(base + 2, rank, 0, PIPE_MTE3, PIPE_MTE2, PIPE_MTE3, 0));
450+ mte3.push_back(MemCopy(base + 3, rank, 0, PIPE_MTE3, rank, rank, BUFFER_UB, 0, BUFFER_OUTPUT, offset,
451+ sliceSize));
452+ mte3.push_back(SetFlag(base + 4, rank, 0, PIPE_MTE3, PIPE_MTE3, PIPE_MTE2, 1));
453+ }
454+ mte2.push_back(NormalTask(1000, rank, 0, PIPE_MTE2));
455+ mte3.push_back(NormalTask(1001, rank, 0, PIPE_MTE3));
456+ return Snapshot(rank, rankSize, launch, {Block(0, {}, std::move(mte2), std::move(mte3))});
457+}
458+ 
377struct CaseConfig {459struct CaseConfig {
378 std::string name;460 std::string name;
379 uint32_t rankSize{2};461 uint32_t rankSize{2};
@@ -497,6 +579,49 @@ size_t CountDirectEdges(const V3::TaskGraphGeneratorV3 &graph, V3::TaskType pare
497 return count;579 return count;
498}580}
499 581 
582+std::vector<const V3::TaskNode *> CollectNodesByType(const V3::TaskGraphGeneratorV3 &graph, V3::TaskType type)
583+{
584+ std::vector<const V3::TaskNode *> result;
585+ for (const auto &node : graph.GetNodes()) {
586+ if (node != nullptr && node->GetType() == type) {
587+ result.push_back(node.get());
588+ }
589+ }
590+ return result;
591+}
592+ 
593+const V3::TaskNode *FindNodeByTaskId(const V3::TaskGraphGeneratorV3 &graph, uint32_t rank, uint32_t pipe,
594+ uint32_t taskId)
595+{
596+ for (const auto &node : graph.GetNodes()) {
597+ if (node == nullptr) {
598+ continue;
599+ }
600+ const V3::TaskPosition &position = node->GetPosition();
601+ if (position.rankId == rank && position.launchIdx == 0 && position.blockId == 0 && position.pipe == pipe &&
602+ position.taskId == taskId) {
603+ return node.get();
604+ }
605+ }
606+ return nullptr;
607+}
608+ 
609+size_t CountLinkedNodesByTypeAndPipe(const std::vector<V3::TaskNode *> &nodes, V3::TaskType type, uint32_t pipe,
610+ uint32_t minTaskId)
611+{
612+ size_t count = 0;
613+ for (const V3::TaskNode *node : nodes) {
614+ if (node == nullptr) {
615+ continue;
616+ }
617+ const V3::TaskPosition &position = node->GetPosition();
618+ if (node->GetType() == type && position.pipe == pipe && position.taskId > minTaskId) {
619+ ++count;
620+ }
621+ }
622+ return count;
623+}
624+ 
500class AivStTest : public testing::Test {625class AivStTest : public testing::Test {
501protected:626protected:
502 void SetUp() override627 void SetUp() override
@@ -739,3 +864,82 @@ TEST_F(AivStTest, AIV_ST_4P_009_CclCrossRankParallelWriteNoConflict)
739 ASSERT_EQ(result.genRet, HCCL_SUCCESS);864 ASSERT_EQ(result.genRet, HCCL_SUCCESS);
740 EXPECT_EQ(result.memRet, HCCL_SUCCESS);865 EXPECT_EQ(result.memRet, HCCL_SUCCESS);
741}866}
867+ 
868+TEST_F(AivStTest, AIV_ST_2P_014_CpGm2GMMemCopyLoopMergePositive)
869+{
870+ RunResult result = RunAivCase({"AIV_ST_2P_014", 2, {0},
871+ [](uint32_t rank, uint64_t launch) { return CpGm2GMSnapshot(rank, 2, launch, 3, false); }}, true);
872+ ASSERT_EQ(result.genRet, HCCL_SUCCESS);
873+ EXPECT_EQ(result.memRet, HCCL_SUCCESS);
874+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmLoopMergeCount, 2U);
875+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmMergedIterationCount, 6U);
876+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmMergedOriginalNodeCount, 36U);
877+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmGeneratedNodeCount, 12U);
878+ EXPECT_LT(result.graph->GetAivExpandStats().dagNodeCountAfterCpGmMerge,
879+ result.graph->GetAivExpandStats().dagNodeCountBeforeCpGmMerge);
880+ EXPECT_EQ(CollectNodesByType(*result.graph, V3::TaskType::BATCH_TRANS_MEM).size(), 4U);
881+}
882+ 
883+TEST_F(AivStTest, AIV_ST_2P_015_CpGm2GMReduceLoopMergePositive)
884+{
885+ RunResult result = RunAivCase({"AIV_ST_2P_015", 2, {0},
886+ [](uint32_t rank, uint64_t launch) { return CpGm2GMSnapshot(rank, 2, launch, 2, true); }}, true);
887+ ASSERT_EQ(result.genRet, HCCL_SUCCESS);
888+ EXPECT_EQ(result.memRet, HCCL_SUCCESS);
889+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmLoopMergeCount, 2U);
890+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmMergedIterationCount, 4U);
891+ EXPECT_EQ(CollectNodesByType(*result.graph, V3::TaskType::BATCH_TRANS_MEM).size(), 2U);
892+ EXPECT_EQ(CollectNodesByType(*result.graph, V3::TaskType::BATCH_REDUCE).size(), 2U);
893+}
894+ 
895+TEST_F(AivStTest, AIV_ST_2P_016_CpGm2GMSingleIterationNoMerge)
896+{
897+ RunResult result = RunAivCase({"AIV_ST_2P_016", 2, {0},
898+ [](uint32_t rank, uint64_t launch) { return CpGm2GMSnapshot(rank, 2, launch, 1, false); }});
899+ ASSERT_EQ(result.genRet, HCCL_SUCCESS);
900+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmLoopMergeCount, 0U);
901+ EXPECT_EQ(CollectNodesByType(*result.graph, V3::TaskType::BATCH_TRANS_MEM).size(), 0U);
902+}
903+ 
904+TEST_F(AivStTest, AIV_ST_2P_017_CpGm2GMFlagMemLoopMergePositive)
905+{
906+ RunResult result = RunAivCase({"AIV_ST_2P_017", 2, {0},
907+ [](uint32_t rank, uint64_t launch) { return CpGm2GMSnapshot(rank, 2, launch, 3, false, true); }});
908+ ASSERT_EQ(result.genRet, HCCL_SUCCESS);
909+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmLoopMergeCount, 2U);
910+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmMergedIterationCount, 6U);
911+ EXPECT_EQ(CollectNodesByType(*result.graph, V3::TaskType::BATCH_TRANS_MEM).size(), 4U);
912+}
913+ 
914+TEST_F(AivStTest, AIV_ST_2P_018_CpGm2GMExternalGapNoMerge)
915+{
916+ RunResult result = RunAivCase({"AIV_ST_2P_018", 2, {0},
917+ [](uint32_t rank, uint64_t launch) { return CpGm2GMSnapshot(rank, 2, launch, 2, false, false, true); }});
918+ ASSERT_EQ(result.genRet, HCCL_SUCCESS);
919+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmLoopMergeCount, 0U);
920+ EXPECT_EQ(CollectNodesByType(*result.graph, V3::TaskType::BATCH_TRANS_MEM).size(), 0U);
921+}
922+ 
923+TEST_F(AivStTest, AIV_ST_2P_019_CpGm2GMMte3BoundaryKeepPipe)
924+{
925+ RunResult result = RunAivCase({"AIV_ST_2P_019", 2, {0},
926+ [](uint32_t rank, uint64_t launch) { return CpGm2GMPipeBoundarySnapshot(rank, 2, launch); }});
927+ ASSERT_EQ(result.genRet, HCCL_SUCCESS);
928+ EXPECT_EQ(result.graph->GetAivExpandStats().cpGmLoopMergeCount, 2U);
929+ 
930+ constexpr uint32_t minSyntheticTaskId = 1001;
931+ for (uint32_t rank = 0; rank < 2; ++rank) {
932+ const V3::TaskNode *mte3Before = FindNodeByTaskId(*result.graph, rank, PIPE_MTE3, 91);
933+ const V3::TaskNode *mte3After = FindNodeByTaskId(*result.graph, rank, PIPE_MTE3, 1001);
934+ ASSERT_NE(mte3Before, nullptr);
935+ ASSERT_NE(mte3After, nullptr);
936+ EXPECT_EQ(CountLinkedNodesByTypeAndPipe(mte3Before->GetChildren(), V3::TaskType::AIV_WAIT_FLAG, PIPE_MTE3,
937+ minSyntheticTaskId), 1U);
938+ EXPECT_EQ(CountLinkedNodesByTypeAndPipe(mte3Before->GetChildren(), V3::TaskType::BATCH_TRANS_MEM, PIPE_MTE2,
939+ minSyntheticTaskId), 0U);
940+ EXPECT_EQ(CountLinkedNodesByTypeAndPipe(mte3After->GetParents(), V3::TaskType::AIV_SET_FLAG, PIPE_MTE3,
941+ minSyntheticTaskId), 1U);
942+ EXPECT_EQ(CountLinkedNodesByTypeAndPipe(mte3After->GetParents(), V3::TaskType::AIV_WAIT_FLAG, PIPE_MTE2,
943+ minSyntheticTaskId), 0U);
944+ }
945+}
Mtest/hccl_vm/test/plugin/runner/CMakeLists.txt+9-0
@@ -131,6 +131,10 @@ target_sources(test_aiv_task_json PRIVATE
131set_target_properties(test_aiv_task_json PROPERTIES131set_target_properties(test_aiv_task_json PROPERTIES
132 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"132 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"
133)133)
134+target_include_directories(test_aiv_task_json PRIVATE
135+ ${THRID_PARTY_DIR}/nlohmann_json
136+ ${THRID_PARTY_DIR}
137+)
134target_link_libraries(test_aiv_task_json log pthread)138target_link_libraries(test_aiv_task_json log pthread)
135 139 
136add_executable(test_aiv_task_snapshot_loader aiv_task_snapshot_loader_test.cc ../main.cc)140add_executable(test_aiv_task_snapshot_loader aiv_task_snapshot_loader_test.cc ../main.cc)
@@ -143,6 +147,11 @@ target_sources(test_aiv_task_snapshot_loader PRIVATE
143set_target_properties(test_aiv_task_snapshot_loader PROPERTIES147set_target_properties(test_aiv_task_snapshot_loader PROPERTIES
144 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"148 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"
145)149)
150+target_include_directories(test_aiv_task_snapshot_loader PRIVATE
151+ ${THRID_PARTY_DIR}/nlohmann_json
152+ ${THRID_PARTY_DIR}
153+ ${CMAKE_SOURCE_DIR}/src/plugin/runner/aiv_executor
154+)
146target_link_libraries(test_aiv_task_snapshot_loader log pthread)155target_link_libraries(test_aiv_task_snapshot_loader log pthread)
147 156 
148add_executable(test_aiv_resource_manager aiv_resource_manager_test.cc ../main.cc)157add_executable(test_aiv_resource_manager aiv_resource_manager_test.cc ../main.cc)
Mtest/hccl_vm/test/plugin/runner/ccu_executor/CMakeLists.txt+1-0
@@ -53,6 +53,7 @@ include_directories(
53 ${CMAKE_SOURCE_DIR}/include/store53 ${CMAKE_SOURCE_DIR}/include/store
54 ${CMAKE_SOURCE_DIR}/include/runnerdb54 ${CMAKE_SOURCE_DIR}/include/runnerdb
55 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include55 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include
56+ ${CMAKE_SOURCE_DIR}/third_party/yaml-cpp/include
56 ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json57 ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
57)58)
58 59 
Mtest/hccl_vm/test/proxy/CMakeLists.txt+12-0
@@ -147,6 +147,18 @@ set_target_properties(test_aclrt_stub PROPERTIES
147)147)
148target_link_libraries(test_aclrt_stub gtest_main)148target_link_libraries(test_aclrt_stub gtest_main)
149 149 
150+# test_aclrt_memory_stub —— 主机侧大块引流/上界/FreeHost 行为单测
151+# InsertTaskToCollection 由 link_libraries 的 store 库提供,无需额外编 device_arm 源。
152+add_executable(test_aclrt_memory_stub aclrt_memory_stub_test.cc)
153+target_sources(test_aclrt_memory_stub PRIVATE
154+ ${CMAKE_SOURCE_DIR}/src/proxy/aclrt_memory_stub.cc
155+ ${CMAKE_SOURCE_DIR}/src/proxy/hccl_proxy_common.cc
156+)
157+set_target_properties(test_aclrt_memory_stub PROPERTIES
158+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"
159+)
160+target_link_libraries(test_aclrt_memory_stub gtest_main)
161+ 
150add_executable(test_hccl_comm_stub hccl_comm_stub_test.cc)162add_executable(test_hccl_comm_stub hccl_comm_stub_test.cc)
151target_sources(test_hccl_comm_stub PRIVATE163target_sources(test_hccl_comm_stub PRIVATE
152 ${CMAKE_SOURCE_DIR}/src/proxy/hccl_comm_stub.cc164 ${CMAKE_SOURCE_DIR}/src/proxy/hccl_comm_stub.cc
Atest/hccl_vm/test/proxy/aclrt_memory_stub_test.cc+147-0
@@ -0,0 +1,147 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+// 主机侧单测:覆盖大块引流、上界报错、aclrtFreeHost 身份判定。
12+// MallocHost 走真实 MemoryManager 和 HcclCommPool 共享内存,不用 mock。
13+// memcpy/memset 的设备侧短路判定由 CommPoolPolicy::ShouldRedirect 的纯函数用例覆盖边界。
14+// 本文件聚焦不依赖 RunnerDB/Runner 上下文的主机侧入口。
15+ 
16+#include <cstdint>
17+#include <cstring>
18+#include <sys/mman.h> // shm_unlink
19+#include <gtest/gtest.h>
20+ 
21+#include "acl/acl_base.h"
22+#include "acl/acl_rt.h"
23+#include "store_sim_comm_pool_policy.h"
24+#include "store_sim_device_memory_manager.h"
25+#include "store_sim_memory_manager.h"
26+#include "store_sim_run_mode.h"
27+#include "db_sim_runner_db.h"
28+#include "sim_models.h"
29+ 
30+extern "C" {
31+ aclError aclrtMallocHost(void **hostPtr, size_t size);
32+ aclError aclrtFreeHost(void *hostPtr);
33+ aclError aclrtMallocHostWithCfg(void **ptr, uint64_t size, aclrtMallocConfig *cfg);
34+}
35+ 
36+// aclrt_memory_stub.cc 编进本二进制,其 __attribute__((constructor)) PrimeCheckOnlyMode 在
37+// 加载期(先于 main)缓存仅校验模式。须在该构造器之前把 mode=1 写进 DB,否则缓存成 false。
38+// 带优先级的构造器先于无优先级的 PrimeCheckOnlyMode 运行,借此抢先写入。
39+__attribute__((constructor(101))) static void SeedCheckOnlyMode()
40+{
41+ RunnerDB::DeleteAll<sim::RunModeConfig>();
42+ sim::RunModeConfig cfg{};
43+ cfg.mode = 1;
44+ RunnerDB::Add<sim::RunModeConfig>(cfg);
45+}
46+ 
47+// 复用区在整个套件期间保持存活,套件开头建池一次,结束再回收。
48+class AclrtMemStubTest : public testing::Test {
49+protected:
50+ static void SetUpTestSuite() {
51+ // 清掉上次异常退出残留的 /dev/shm/HcclCommPool(ShmCreate 用 O_EXCL,残留会建池失败)。
52+ shm_unlink(sim::CommPoolPolicy::kPoolName);
53+ ASSERT_NE(sim::MemoryManager::GetInstance().AllocMemByName(
54+ sim::CommPoolPolicy::kPoolName, sim::CommPoolPolicy::kPoolSize), nullptr);
55+ }
56+ static void TearDownTestSuite() {
57+ // 关闭并 unlink,保证 /dev/shm 不泄漏,独立重跑不撞 O_EXCL。
58+ sim::MemoryManager::GetInstance().FreeMemByName(sim::CommPoolPolicy::kPoolName);
59+ // 清掉本套件写进共享 DB 的仅校验模式行,避免给其它测试二进制留下 check-only(mode=1)。
60+ RunnerDB::DeleteAll<sim::RunModeConfig>();
61+ }
62+};
63+ 
64+// 主机大块两次申请归同一池首址,且与设备侧大块同址,主机与设备共用 HcclCommPool。
65+TEST_F(AclrtMemStubTest, MallocHost_BigBlock_TwiceSameAddr_SharesDevicePool) {
66+ EXPECT_TRUE(sim::IsCheckOnlyMode()); // 加载期构造器已写入仅校验模式,首次缓存须为 true。
67+ const size_t big = sim::CommPoolPolicy::kBigBlockThreshold; // 200MB
68+ void* h1 = nullptr;
69+ void* h2 = nullptr;
70+ ASSERT_EQ(aclrtMallocHost(&h1, big), ACL_SUCCESS);
71+ ASSERT_EQ(aclrtMallocHost(&h2, big), ACL_SUCCESS);
72+ ASSERT_NE(h1, nullptr);
73+ EXPECT_EQ(h1, h2); // 两次大块同址
74+ 
75+ // 设备侧大块也归同一池基址
76+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
77+ void* d = mgr.AllocPhyMem("host_share_probe", 0, big);
78+ EXPECT_EQ(h1, d); // 主机与设备共用同一池
79+ mgr.FreePhyMem("host_share_probe", 0);
80+ 
81+ EXPECT_EQ(aclrtFreeHost(h1), ACL_SUCCESS);
82+ EXPECT_EQ(aclrtFreeHost(h2), ACL_SUCCESS);
83+}
84+ 
85+// 主机小块走真实 malloc,独立于池,内容正确。
86+TEST_F(AclrtMemStubTest, MallocHost_SmallBlock_RealAlloc_NotPool) {
87+ void* big = nullptr;
88+ ASSERT_EQ(aclrtMallocHost(&big, sim::CommPoolPolicy::kBigBlockThreshold), ACL_SUCCESS);
89+ 
90+ void* s1 = nullptr;
91+ void* s2 = nullptr;
92+ ASSERT_EQ(aclrtMallocHost(&s1, 4096), ACL_SUCCESS);
93+ ASSERT_EQ(aclrtMallocHost(&s2, 4096), ACL_SUCCESS);
94+ ASSERT_NE(s1, nullptr);
95+ ASSERT_NE(s2, nullptr);
96+ EXPECT_NE(s1, s2); // 两个小块各自独立分配
97+ EXPECT_NE(s1, big); // 小块不进池
98+ 
99+ // 小块内容正确,互不覆盖
100+ memset(s1, 0xAA, 4096);
101+ memset(s2, 0xBB, 4096);
102+ EXPECT_EQ(static_cast<unsigned char*>(s1)[0], 0xAAu);
103+ EXPECT_EQ(static_cast<unsigned char*>(s2)[0], 0xBBu);
104+ 
105+ EXPECT_EQ(aclrtFreeHost(s1), ACL_SUCCESS);
106+ EXPECT_EQ(aclrtFreeHost(s2), ACL_SUCCESS);
107+ EXPECT_EQ(aclrtFreeHost(big), ACL_SUCCESS);
108+}
109+ 
110+// 主机大块 >4GB 报错,不引流也不真实分配。
111+TEST_F(AclrtMemStubTest, MallocHost_ExceedCeiling_Reject) {
112+ void* p = reinterpret_cast<void*>(0xDEADBEEF); // 哨兵:失败时不应被改写
113+ aclError ret = aclrtMallocHost(&p, sim::CommPoolPolicy::kPoolSize + 1);
114+ EXPECT_NE(ret, ACL_SUCCESS);
115+ EXPECT_EQ(p, reinterpret_cast<void*>(0xDEADBEEF)); // 报错路径未写出指针
116+}
117+ 
118+// aclrtFreeHost 身份判定:池内地址 noop,池外真实地址正常 free。
119+TEST_F(AclrtMemStubTest, FreeHost_PoolAddrNoop_RealAddrFree) {
120+ const size_t big = sim::CommPoolPolicy::kBigBlockThreshold;
121+ void* poolPtr = nullptr;
122+ ASSERT_EQ(aclrtMallocHost(&poolPtr, big), ACL_SUCCESS);
123+ ASSERT_NE(poolPtr, nullptr);
124+ 
125+ // 池内地址 free 为 noop:返回成功且池仍可写读
126+ EXPECT_EQ(aclrtFreeHost(poolPtr), ACL_SUCCESS);
127+ const char* sentinel = "pool-alive-after-noop-free";
128+ memcpy(poolPtr, sentinel, strlen(sentinel) + 1);
129+ EXPECT_STREQ(static_cast<char*>(poolPtr), sentinel);
130+ 
131+ // 池外真实地址:正常 free,不抛异常
132+ void* real = nullptr;
133+ ASSERT_EQ(aclrtMallocHost(&real, 4096), ACL_SUCCESS);
134+ EXPECT_EQ(aclrtFreeHost(real), ACL_SUCCESS);
135+}
136+ 
137+// MallocHostWithCfg 委托 MallocHost:大块同样引流到池。
138+TEST_F(AclrtMemStubTest, MallocHostWithCfg_BigBlock_DelegatesToPool) {
139+ void* viaPlain = nullptr;
140+ void* viaCfg = nullptr;
141+ const size_t big = sim::CommPoolPolicy::kBigBlockThreshold;
142+ ASSERT_EQ(aclrtMallocHost(&viaPlain, big), ACL_SUCCESS);
143+ ASSERT_EQ(aclrtMallocHostWithCfg(&viaCfg, big, nullptr), ACL_SUCCESS);
144+ EXPECT_EQ(viaPlain, viaCfg); // 两条入口归同一池
145+ aclrtFreeHost(viaPlain);
146+ aclrtFreeHost(viaCfg);
147+}
Mtest/hccl_vm/test/run_ut.sh+4-14文件内容审核中,请稍后刷新重试
Mtest/hccl_vm/test/run_ut_README.md+5-5
@@ -1,8 +1,8 @@
1-# CheckerL2 UT 测试执行脚本1+# HCCL_VM UT 测试执行脚本
2 2 
3## 概述3## 概述
4 4 
5-`run_ut.sh` 是 CheckerL2 项目的单元测试执行脚本,用于自动化编译和执行测试用例。5+`run_ut.sh` 是 HCCL_VM 项目的单元测试执行脚本,用于自动化编译和执行测试用例。
6 6 
7## 三步流程7## 三步流程
8 8 
@@ -18,7 +18,7 @@
18## 用法18## 用法
19 19 
20```bash20```bash
21-cd /home/q30033976/checker_0506/CheckerL2/test21+cd {HCCL_VM路径}/test
22 22 
23# 基本用法23# 基本用法
24./run_ut.sh # 全量编译+执行所有测试24./run_ut.sh # 全量编译+执行所有测试
@@ -139,7 +139,7 @@ cd /home/q30033976/checker_0506/CheckerL2/test
139每次执行都会在 `ut_logs/<时间戳>/` 下生成日志文件:139每次执行都会在 `ut_logs/<时间戳>/` 下生成日志文件:
140 140 
141```text141```text
142-/home/q30033976/checker_0506/CheckerL2/ut_logs/20260425_142048/142+{HCCL_VM路径}/ut_logs/20260425_142048/
143├── build.log # 编译详细日志 (cmake + make 输出)143├── build.log # 编译详细日志 (cmake + make 输出)
144├── run.log # 执行详细日志 (每个测试的完整输出)144├── run.log # 执行详细日志 (每个测试的完整输出)
145└── summary.log # 汇总日志 (每个测试的执行结果)145└── summary.log # 汇总日志 (每个测试的执行结果)
@@ -256,7 +256,7 @@ source /home/myuser/workspace/Ascend/cann/set_env.sh
256A: 目前脚本设计为编译+执行一体化,如需只编译,请直接使用 cmake 和 make 命令:256A: 目前脚本设计为编译+执行一体化,如需只编译,请直接使用 cmake 和 make 命令:
257 257 
258```bash258```bash
259-cd /home/q30033976/checker_0506/CheckerL2/build259+cd {HCCL_VM路径}/build
260cmake .. && make -j8 test_checker260cmake .. && make -j8 test_checker
261```261```
262 262 
Mtest/hccl_vm/test/runnerdb/CMakeLists.txt+1-1
@@ -18,7 +18,7 @@ include_directories(
18 ${CMAKE_SOURCE_DIR}/include18 ${CMAKE_SOURCE_DIR}/include
19 ${CMAKE_SOURCE_DIR}/include/store19 ${CMAKE_SOURCE_DIR}/include/store
20 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include20 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include
21- ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json21+ ${CMAKE_SOURCE_DIR}/third_party
22 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/include22 ${ASCEND_CANN_PACKAGE_PATH}/x86_64-linux/include
23)23)
24 24 
Mtest/hccl_vm/test/runnerdb/sim_sqlite_db_test.cc+33-0
@@ -511,3 +511,36 @@ TEST_F(SimSqliteDbTableTest, AddCcuResource_InsertsRecord)
511 ASSERT_TRUE(found.has_value());511 ASSERT_TRUE(found.has_value());
512 EXPECT_EQ(found->ccu_id, 1);512 EXPECT_EQ(found->ccu_id, 1);
513}513}
514+ 
515+TEST_F(SimSqliteDbTest, RunModeConfig_WriteThenRead_RoundTrip) {
516+ // RunModeConfig 表惰性注册,ClearAll 不一定覆盖到它,写入前先显式清空,避免跨用例残留。
517+ SimRunnerSqliteDB::Instance().DeleteAll<sim::RunModeConfig>();
518+ sim::RunModeConfig cfg{};
519+ cfg.mode = 1;
520+ SimRunnerSqliteDB::Instance().Add<sim::RunModeConfig>(cfg);
521+ 
522+ auto got = SimRunnerSqliteDB::Instance().Query<sim::RunModeConfig>(
523+ [](const sim::RunModeConfig&) { return true; });
524+ EXPECT_TRUE(got.second);
525+ EXPECT_EQ(got.first.mode, 1);
526+}
527+ 
528+TEST_F(SimSqliteDbTest, RunModeConfig_DeleteAllThenWrite_LatestSingleRowWins) {
529+ // RunModeConfig 表惰性注册,ClearAll 不一定覆盖到它,进表前先显式清空,避免跨用例残留。
530+ SimRunnerSqliteDB::Instance().DeleteAll<sim::RunModeConfig>();
531+ // 先写仅校验模式行,DeleteAll 清空后再写 clean 行:单行覆盖语义,读到的是清空后的最新值。
532+ sim::RunModeConfig checkOnly{};
533+ checkOnly.mode = 1;
534+ SimRunnerSqliteDB::Instance().Add<sim::RunModeConfig>(checkOnly);
535+ 
536+ SimRunnerSqliteDB::Instance().DeleteAll<sim::RunModeConfig>();
537+ 
538+ sim::RunModeConfig clean{};
539+ clean.mode = 0;
540+ SimRunnerSqliteDB::Instance().Add<sim::RunModeConfig>(clean);
541+ 
542+ auto all = SimRunnerSqliteDB::Instance().QueryList<sim::RunModeConfig>(
543+ [](const sim::RunModeConfig&) { return true; });
544+ ASSERT_EQ(all.size(), 1);
545+ EXPECT_EQ(all[0].mode, 0);
546+}
Mtest/hccl_vm/test/store/CMakeLists.txt+14-2
@@ -32,7 +32,7 @@ include_directories(
32 ${CMAKE_SOURCE_DIR}/include32 ${CMAKE_SOURCE_DIR}/include
33 ${CMAKE_SOURCE_DIR}/include/runnerdb33 ${CMAKE_SOURCE_DIR}/include/runnerdb
34 ${CMAKE_SOURCE_DIR}/include34 ${CMAKE_SOURCE_DIR}/include
35- ${CMAKE_SOURCE_DIR}/third_party35+ ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
36 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include36 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include
37 ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json37 ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
38 ${CMAKE_SOURCE_DIR}/src38 ${CMAKE_SOURCE_DIR}/src
@@ -142,7 +142,7 @@ include_directories(
142 ${CMAKE_SOURCE_DIR}/include142 ${CMAKE_SOURCE_DIR}/include
143 ${CMAKE_SOURCE_DIR}/include/runnerdb143 ${CMAKE_SOURCE_DIR}/include/runnerdb
144 ${CMAKE_SOURCE_DIR}/include144 ${CMAKE_SOURCE_DIR}/include
145- ${CMAKE_SOURCE_DIR}/third_party145+ ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
146 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include146 ${CMAKE_BINARY_DIR}/_deps/yaml-cpp-src/include
147 ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json147 ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
148)148)
@@ -169,3 +169,15 @@ target_link_libraries(test_sim_shm_ops_static PRIVATE pthread rt log)
169set_target_properties(test_sim_shm_ops_static PROPERTIES169set_target_properties(test_sim_shm_ops_static PROPERTIES
170 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"170 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"
171)171)
172+ 
173+add_executable(test_sim_run_mode sim_run_mode_test.cc)
174+target_link_libraries(test_sim_run_mode PRIVATE pthread rt store runnerdb SQLite::SQLite3 log)
175+set_target_properties(test_sim_run_mode PROPERTIES
176+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"
177+)
178+ 
179+add_executable(test_sim_check_only_off sim_check_only_off_test.cc)
180+target_link_libraries(test_sim_check_only_off PRIVATE pthread rt store runnerdb SQLite::SQLite3 log)
181+set_target_properties(test_sim_check_only_off PROPERTIES
182+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"
183+)
Atest/hccl_vm/test/store/sim_check_only_off_test.cc+47-0
@@ -0,0 +1,47 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+// clean 模式设备侧集成:IsCheckOnlyMode() 每进程只 latch 一次,本二进制全程不 seed check-only(mode=1),
12+// 借此验证 clean 模式下大块走真实独立分配(与仅校验模式同址复用对照)。
13+ 
14+#include <gtest/gtest.h>
15+ 
16+#include <sys/mman.h> // shm_unlink
17+ 
18+#include "store_sim_comm_pool_policy.h"
19+#include "store_sim_device_memory_manager.h"
20+#include "store_sim_memory_manager.h"
21+#include "store_sim_run_mode.h"
22+#include "db_sim_runner_db.h"
23+#include "sim_models.h"
24+ 
25+class CheckOnlyOffTest : public testing::Test {
26+protected:
27+ void SetUp() override {
28+ // 清进程内/磁盘上残留的 HcclCommPool。
29+ sim::MemoryManager::GetInstance().FreeMemByName(sim::CommPoolPolicy::kPoolName);
30+ shm_unlink(sim::CommPoolPolicy::kPoolName);
31+ // 清空 RunModeConfig,保证 ProbeCheckOnlyMode 为 false。本文件不写任何 check-only(mode=1) 行。
32+ RunnerDB::DeleteAll<sim::RunModeConfig>();
33+ }
34+};
35+ 
36+TEST_F(CheckOnlyOffTest, NormalMode_BigBlocks_RealIndependentAlloc) {
37+ EXPECT_FALSE(sim::IsCheckOnlyMode()); // 全程未 seed,首次 latch 须为 false。
38+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
39+ const size_t big = 256ULL * 1024 * 1024;
40+ void* a = mgr.AllocPhyMem("clean_big_a", 0, big);
41+ void* b = mgr.AllocPhyMem("clean_big_b", 0, big);
42+ ASSERT_NE(a, nullptr);
43+ ASSERT_NE(b, nullptr);
44+ EXPECT_NE(a, b); // clean 模式各自真实独立分配(仅校验模式下两者同为池首址)。
45+ mgr.FreePhyMem("clean_big_a", 0);
46+ mgr.FreePhyMem("clean_big_b", 0);
47+}
Mtest/hccl_vm/test/store/sim_device_memory_manager_test.cc+259-0
@@ -10,11 +10,87 @@
10 10 
11#include <gtest/gtest.h>11#include <gtest/gtest.h>
12 12 
13+#include <atomic>
14+#include <cstdio>
15+#include <cstring>
16+#include <sys/mman.h> // shm_unlink
17+#include <thread>
18+#include <vector>
19+ 
20+#include "store_sim_comm_pool_policy.h"
13#include "store_sim_device_memory_manager.h"21#include "store_sim_device_memory_manager.h"
14#include "store_sim_memory_manager.h"22#include "store_sim_memory_manager.h"
23+#include "store_sim_run_mode.h"
24+#include "db_sim_runner_db.h"
25+#include "sim_models.h"
26+ 
27+// 阈值和上界取自 CommPoolPolicy,下面纯判定用例用这两个短别名。
28+static constexpr size_t kThr = sim::CommPoolPolicy::kBigBlockThreshold; // 200MB
29+static constexpr size_t kPool = sim::CommPoolPolicy::kPoolSize; // 4GB
30+ 
31+// 仅校验模式关:任何 size 都不引流。
32+TEST(VmemDecisionTest, CheckOnlyOff_AtThreshold_NoPool) {
33+ EXPECT_FALSE(sim::CommPoolPolicy::ShouldRedirect(kThr, false, kThr, kPool));
34+}
35+TEST(VmemDecisionTest, CheckOnlyOff_AtPoolCeiling_NoPool) {
36+ EXPECT_FALSE(sim::CommPoolPolicy::ShouldRedirect(kPool, false, kThr, kPool));
37+}
38+// 仅校验模式开:边界判定。
39+TEST(VmemDecisionTest, ZeroSize_NoPool) {
40+ EXPECT_FALSE(sim::CommPoolPolicy::ShouldRedirect(0, true, kThr, kPool));
41+}
42+TEST(VmemDecisionTest, BelowThreshold_NoPool) {
43+ EXPECT_FALSE(sim::CommPoolPolicy::ShouldRedirect(kThr - 1, true, kThr, kPool));
44+}
45+TEST(VmemDecisionTest, AtThreshold_Pool) {
46+ EXPECT_TRUE(sim::CommPoolPolicy::ShouldRedirect(kThr, true, kThr, kPool));
47+}
48+TEST(VmemDecisionTest, AboveThreshold_Pool) {
49+ EXPECT_TRUE(sim::CommPoolPolicy::ShouldRedirect(kThr + 1, true, kThr, kPool));
50+}
51+TEST(VmemDecisionTest, JustBelowPoolCeiling_Pool) {
52+ EXPECT_TRUE(sim::CommPoolPolicy::ShouldRedirect(kPool - 1, true, kThr, kPool));
53+}
54+TEST(VmemDecisionTest, AtPoolCeiling_Pool) {
55+ EXPECT_TRUE(sim::CommPoolPolicy::ShouldRedirect(kPool, true, kThr, kPool));
56+}
57+TEST(VmemDecisionTest, AbovePoolCeiling_NoPool) {
58+ EXPECT_FALSE(sim::CommPoolPolicy::ShouldRedirect(kPool + 1, true, kThr, kPool));
59+}
60+ 
61+// 上界纯判定:仅校验模式开 >4GB 拦截报错,==4GB 和小块不拦,仅校验模式关不拦。
62+TEST(VmemCeilingTest, WithinCeiling_NotExceed) {
63+ EXPECT_FALSE(sim::CommPoolPolicy::ExceedsCeiling(kPool, true, kPool));
64+ EXPECT_FALSE(sim::CommPoolPolicy::ExceedsCeiling(kThr, true, kPool));
65+}
66+TEST(VmemCeilingTest, AbovePoolCeiling_Exceed) {
67+ EXPECT_TRUE(sim::CommPoolPolicy::ExceedsCeiling(kPool + 1, true, kPool));
68+}
69+TEST(VmemCeilingTest, CheckOnlyOff_NeverExceed) {
70+ EXPECT_FALSE(sim::CommPoolPolicy::ExceedsCeiling(kPool + 1, false, kPool));
71+}
72+ 
73+// 建/拆复用区 HcclCommPool。
74+static void* CreateCommPool() {
75+ return sim::MemoryManager::GetInstance().AllocMemByName(
76+ sim::CommPoolPolicy::kPoolName, sim::CommPoolPolicy::kPoolSize);
77+}
78+static void DestroyCommPool() {
79+ sim::MemoryManager::GetInstance().FreeMemByName(sim::CommPoolPolicy::kPoolName);
80+}
15 81 
16class DeviceMemoryManagerTest : public testing::Test {82class DeviceMemoryManagerTest : public testing::Test {
17protected:83protected:
84+ void SetUp() override {
85+ // 清进程内和磁盘上残留的 HcclCommPool,保证乱序和重跑自洽。
86+ sim::MemoryManager::GetInstance().FreeMemByName(sim::CommPoolPolicy::kPoolName);
87+ shm_unlink(sim::CommPoolPolicy::kPoolName);
88+ // 写入仅校验模式,让 IsCheckOnlyMode() 缓存为 true,大块引流集成用例才会命中复用区。
89+ RunnerDB::DeleteAll<sim::RunModeConfig>();
90+ sim::RunModeConfig cfg{};
91+ cfg.mode = 1;
92+ RunnerDB::Add<sim::RunModeConfig>(cfg);
93+ }
18 void TearDown() override {94 void TearDown() override {
19 sim::MemoryManager::GetInstance().FreeMemByName("dev_test_phy");95 sim::MemoryManager::GetInstance().FreeMemByName("dev_test_phy");
20 }96 }
@@ -124,3 +200,186 @@ TEST_F(DeviceMemoryManagerTest, GetHostPtrByDevPtr_NotExist_ReturnsNull) {
124 void* result = sim::DeviceMemoryManager::GetInstance().GetHostPtrByDevPtr(devPtr);200 void* result = sim::DeviceMemoryManager::GetInstance().GetHostPtrByDevPtr(devPtr);
125 EXPECT_EQ(result, nullptr);201 EXPECT_EQ(result, nullptr);
126}202}
203+ 
204+// SetUp 已写入仅校验模式,集成路径走仅校验模式开分支。仅校验模式关时不引流由 VmemDecisionTest 覆盖。
205+// 池基址用大块 AllocPhyMem 的返回值获取,命中复用即返回池首址,不依赖内部 getter。
206+ 
207+TEST_F(DeviceMemoryManagerTest, AllocPhyMem_BigBlocks_ShareSamePool) {
208+ EXPECT_TRUE(sim::IsCheckOnlyMode()); // fixture 已写入仅校验模式,首次缓存须为 true。
209+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
210+ ASSERT_NE(CreateCommPool(), nullptr);
211+ const size_t big = sim::CommPoolPolicy::kBigBlockThreshold; // 200MB
212+ void* a = mgr.AllocPhyMem("big_a", 0, big);
213+ void* b = mgr.AllocPhyMem("big_b", 0, big);
214+ void* small = mgr.AllocPhyMem("small_c", 0, 1024);
215+ EXPECT_NE(a, nullptr);
216+ EXPECT_EQ(a, b); // 两个大块归同一复用区
217+ EXPECT_NE(a, small); // 小块走独立真实分配
218+ mgr.FreePhyMem("big_a", 0);
219+ mgr.FreePhyMem("big_b", 0);
220+ mgr.FreePhyMem("small_c", 0);
221+ DestroyCommPool();
222+}
223+ 
224+TEST_F(DeviceMemoryManagerTest, AllocPhyMem_ExceedCeiling_Reject) {
225+ // 仅校验模式下单块 >4GB 报错,不回退真实分配。
226+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
227+ const size_t tooBig = sim::CommPoolPolicy::kPoolSize + 1; // >4GB
228+ EXPECT_EQ(mgr.AllocPhyMem("too_big_alloc", 0, tooBig), nullptr);
229+ EXPECT_EQ(mgr.AcquirePhyMem("too_big_acq", 0, tooBig), nullptr);
230+ // 报错路径不留记账,后续 free/release 当未记录处理,不抛异常。
231+ EXPECT_NO_THROW(mgr.FreePhyMem("too_big_alloc", 0));
232+ EXPECT_EQ(mgr.ReleasePhyMem("too_big_acq", 0), 0);
233+}
234+ 
235+TEST_F(DeviceMemoryManagerTest, FreePhyMem_BigBlock_PoolReleaseMovedToCaller) {
236+ // 大块引流到复用区,FreePhyMem 走非池释放,对从未单独注册的大块名是空操作。
237+ // 复用区的释放由 aclrt 调用方完成,不在 FreePhyMem 处理。
238+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
239+ void* base = CreateCommPool();
240+ ASSERT_NE(base, nullptr);
241+ void* a = mgr.AllocPhyMem("big_free", 0, sim::CommPoolPolicy::kBigBlockThreshold);
242+ EXPECT_EQ(a, base); // 大块拿到的就是复用区基址
243+ EXPECT_NO_THROW(mgr.FreePhyMem("big_free", 0)); // 非池释放,不触碰复用区
244+ // 释放由调用方完成,这里手动配平 AllocPhyMem 内部的那次 acquire
245+ sim::MemoryManager::GetInstance().ReleaseMemByName(sim::CommPoolPolicy::kPoolName);
246+ DestroyCommPool();
247+}
248+ 
249+TEST_F(DeviceMemoryManagerTest, AllocPhyMem_SmallBlock_ContentCorrect) {
250+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
251+ ASSERT_NE(CreateCommPool(), nullptr);
252+ char* p = static_cast<char*>(mgr.AllocPhyMem("small_int", 0, 4096));
253+ ASSERT_NE(p, nullptr);
254+ const char* msg = "checker-vmem-small-correct";
255+ memcpy(p, msg, strlen(msg) + 1);
256+ EXPECT_STREQ(p, msg); // 小块内容正确
257+ mgr.FreePhyMem("small_int", 0); // 小块走原逻辑,内部已 FreeMemByName 释放
258+ DestroyCommPool();
259+}
260+ 
261+TEST_F(DeviceMemoryManagerTest, AllocPhyMem_RepeatedBig_NoGrowth) {
262+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
263+ ASSERT_NE(CreateCommPool(), nullptr);
264+ void* first = mgr.AllocPhyMem("loop_0", 0, sim::CommPoolPolicy::kBigBlockThreshold);
265+ mgr.FreePhyMem("loop_0", 0);
266+ for (int i = 1; i < 100; ++i) {
267+ char n[32];
268+ snprintf(n, sizeof(n), "loop_%d", i);
269+ void* p = mgr.AllocPhyMem(n, 0, sim::CommPoolPolicy::kBigBlockThreshold);
270+ EXPECT_EQ(p, first); // 反复申请恒归同一池、占用不增长
271+ mgr.FreePhyMem(n, 0);
272+ }
273+ DestroyCommPool();
274+}
275+ 
276+TEST_F(DeviceMemoryManagerTest, AllocPhyMem_ConcurrentBig_ThreadSafe) {
277+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
278+ ASSERT_NE(CreateCommPool(), nullptr);
279+ void* base = mgr.AllocPhyMem("cc_base", 0, sim::CommPoolPolicy::kBigBlockThreshold);
280+ mgr.FreePhyMem("cc_base", 0);
281+ std::atomic<int> mismatch{0};
282+ std::vector<std::thread> ts;
283+ for (int t = 0; t < 8; ++t) {
284+ ts.emplace_back([&, t] {
285+ for (int i = 0; i < 50; ++i) {
286+ char n[40];
287+ snprintf(n, sizeof(n), "cc_%d_%d", t, i);
288+ void* p = mgr.AllocPhyMem(n, 0, sim::CommPoolPolicy::kBigBlockThreshold);
289+ if (p != base) {
290+ mismatch++;
291+ }
292+ mgr.FreePhyMem(n, 0);
293+ }
294+ });
295+ }
296+ for (auto& x : ts) {
297+ x.join();
298+ }
299+ EXPECT_EQ(mismatch.load(), 0); // 并发下均归同一池、无竞态
300+ DestroyCommPool();
301+}
302+ 
303+TEST_F(DeviceMemoryManagerTest, PoolCeiling_FullSpanAddressable_ContentCorrect) {
304+ // 复用区 4GB。验证首址和紧贴 4GB 上界的末字节都能写读、内容正确,整段 4GB 在规格内可寻址。
305+ // mmap 惰性提交,只触碰的页才落 /dev/shm,只占几页,不会真占 4GB。
306+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
307+ ASSERT_NE(CreateCommPool(), nullptr);
308+ char* base = static_cast<char*>(
309+ mgr.AllocPhyMem("ceiling_probe", 0, sim::CommPoolPolicy::kBigBlockThreshold));
310+ ASSERT_NE(base, nullptr);
311+ const size_t pool = sim::CommPoolPolicy::kPoolSize; // 规格 4GB
312+ 
313+ // 首址写读
314+ const char* sHead = "vmem-pool-head";
315+ memcpy(base, sHead, strlen(sHead) + 1);
316+ EXPECT_STREQ(base, sHead);
317+ 
318+ // 紧贴 4GB 上界的最后 64 字节写读,验证整段 4GB 在规格内可寻址
319+ const char* sTail = "vmem-4G-ceiling";
320+ char* last = base + pool - 64;
321+ memcpy(last, sTail, strlen(sTail) + 1);
322+ EXPECT_STREQ(last, sTail);
323+ 
324+ mgr.FreePhyMem("ceiling_probe", 0);
325+ DestroyCommPool();
326+}
327+ 
328+TEST_F(DeviceMemoryManagerTest, AllocPhyMem_BigBlockOverwrite_NoContentGuarantee) {
329+ // 两个不同名大块(两个 rank)都引流到同一池区,后写者覆盖先写者,内容不保证正确。
330+ // 覆盖是共享后备存储的性质,与进程边界无关,每个 rank 都 Acquire 同一 HcclCommPool。
331+ // 与小块各自独立分配对照。
332+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
333+ ASSERT_NE(CreateCommPool(), nullptr);
334+ const size_t big = sim::CommPoolPolicy::kBigBlockThreshold; // 200MB
335+ const size_t probe = 4096; // 只触碰首页,避免真占 200MB
336+ 
337+ char* a = static_cast<char*>(mgr.AllocPhyMem("rankA_big", 0, big));
338+ char* b = static_cast<char*>(mgr.AllocPhyMem("rankB_big", 0, big));
339+ ASSERT_NE(a, nullptr);
340+ ASSERT_EQ(a, b); // 两个大块别名同一池区
341+ 
342+ memset(a, 0xAA, probe); // rankA 先写
343+ ASSERT_EQ(static_cast<unsigned char>(a[0]), 0xAAu);
344+ memset(b, 0xBB, probe); // rankB 后写
345+ // 经 A 读回变成 0xBB,rankA 的数据被 rankB 覆盖
346+ EXPECT_EQ(static_cast<unsigned char>(a[0]), 0xBBu);
347+ EXPECT_EQ(static_cast<unsigned char>(a[probe - 1]), 0xBBu);
348+ mgr.FreePhyMem("rankA_big", 0);
349+ mgr.FreePhyMem("rankB_big", 0);
350+ 
351+ // 对照:两个小块各自独立分配,互不覆盖
352+ char* sa = static_cast<char*>(mgr.AllocPhyMem("rankA_small", 0, 4096));
353+ char* sb = static_cast<char*>(mgr.AllocPhyMem("rankB_small", 0, 4096));
354+ ASSERT_NE(sa, nullptr);
355+ ASSERT_NE(sb, nullptr);
356+ EXPECT_NE(sa, sb); // 小块异址
357+ memset(sa, 0xAA, 64);
358+ memset(sb, 0xBB, 64);
359+ EXPECT_EQ(static_cast<unsigned char>(sa[0]), 0xAAu); // 小块不被覆盖
360+ EXPECT_EQ(static_cast<unsigned char>(sb[0]), 0xBBu);
361+ mgr.FreePhyMem("rankA_small", 0);
362+ mgr.FreePhyMem("rankB_small", 0);
363+ DestroyCommPool();
364+}
365+ 
366+TEST_F(DeviceMemoryManagerTest, AcquirePhyMem_BigBlockExternalPool_HitsPool) {
367+ // 池由主进程建好,本进程未建池,大块 Acquire 按 size 引流命中同一池。
368+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
369+ void* poolByMain = CreateCommPool();
370+ ASSERT_NE(poolByMain, nullptr);
371+ char* big = static_cast<char*>(
372+ mgr.AcquirePhyMem("proxy_big", 0, sim::CommPoolPolicy::kBigBlockThreshold));
373+ ASSERT_NE(big, nullptr);
374+ EXPECT_EQ(static_cast<void*>(big), poolByMain); // 未建池也命中同一池首址
375+ mgr.ReleasePhyMem("proxy_big", 0);
376+ DestroyCommPool(); // 强制关闭并 unlink
377+}
378+ 
379+TEST_F(DeviceMemoryManagerTest, FreeReleasePhyMem_UnrecordedAndNull_Tolerated) {
380+ auto& mgr = sim::DeviceMemoryManager::GetInstance();
381+ EXPECT_NO_THROW(mgr.FreePhyMem("never_alloced", 0));
382+ EXPECT_EQ(mgr.ReleasePhyMem("never_acquired", 0), 0);
383+ EXPECT_EQ(mgr.AllocPhyMem(nullptr, 0, sim::CommPoolPolicy::kBigBlockThreshold), nullptr);
384+ EXPECT_EQ(mgr.AcquirePhyMem(nullptr, 0, sim::CommPoolPolicy::kBigBlockThreshold), nullptr);
385+}
Atest/hccl_vm/test/store/sim_run_mode_test.cc+36-0
@@ -0,0 +1,36 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include <gtest/gtest.h>
12+#include "store_sim_run_mode.h"
13+#include "db_sim_runner_db.h"
14+#include "sim_models.h"
15+ 
16+// 表为空时 ProbeCheckOnlyMode 返回 false。
17+TEST(RunModeTest, ProbeCheckOnlyMode_EmptyTable_False) {
18+ RunnerDB::DeleteAll<sim::RunModeConfig>();
19+ EXPECT_FALSE(sim::ProbeCheckOnlyMode());
20+}
21+ 
22+// 写入 mode=1 后 ProbeCheckOnlyMode 返回 true。
23+TEST(RunModeTest, ProbeCheckOnlyMode_CheckOnlyRow_True) {
24+ RunnerDB::DeleteAll<sim::RunModeConfig>();
25+ sim::RunModeConfig cfg{};
26+ cfg.mode = 1;
27+ RunnerDB::Add<sim::RunModeConfig>(cfg);
28+ EXPECT_TRUE(sim::ProbeCheckOnlyMode());
29+}
30+ 
31+// IsCheckOnlyMode 进程内 latch:第二次调用与第一次一致。
32+TEST(RunModeTest, IsCheckOnlyMode_Latches) {
33+ bool first = sim::IsCheckOnlyMode();
34+ bool second = sim::IsCheckOnlyMode();
35+ EXPECT_EQ(first, second);
36+}
Mtest/hccl_vm/test/utils/CMakeLists.txt+5-2
@@ -13,10 +13,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
13set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -D_GLIBCXX_USE_CXX11_ABI=0")13set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -D_GLIBCXX_USE_CXX11_ABI=0")
14 14 
15include_directories(15include_directories(
16- ${CMAKE_SOURCE_DIR}/include
17 ${CMAKE_SOURCE_DIR}/include16 ${CMAKE_SOURCE_DIR}/include
18 ${CMAKE_SOURCE_DIR}/src/utils17 ${CMAKE_SOURCE_DIR}/src/utils
19 ${CMAKE_SOURCE_DIR}/third_party18 ${CMAKE_SOURCE_DIR}/third_party
19+ ${CMAKE_SOURCE_DIR}/third_party/nlohmann_json
20+ ${CMAKE_SOURCE_DIR}/third_party/yaml-cpp/include
20)21)
21 22 
22link_libraries(23link_libraries(
@@ -27,8 +28,10 @@ link_libraries(
27add_executable(test_sim_log28add_executable(test_sim_log
28 sim_log_test.cc29 sim_log_test.cc
29 ${CMAKE_SOURCE_DIR}/src/utils/sim_log.cc30 ${CMAKE_SOURCE_DIR}/src/utils/sim_log.cc
31+ ${CMAKE_SOURCE_DIR}/src/utils/sim_yaml_config.cc
32+ ${CMAKE_SOURCE_DIR}/src/utils/sim_common_api.cc
30)33)
31-target_link_libraries(test_sim_log pthread)34+target_link_libraries(test_sim_log pthread yaml-cpp::yaml-cpp)
32set_target_properties(test_sim_log PROPERTIES35set_target_properties(test_sim_log PROPERTIES
33 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"36 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/bin"
34)37)