news 2026/9/20 22:30:51

CANN Runtime Adump 实战指南:从算子信息统计到异常算子 Dump 的四种落盘方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN Runtime Adump 实战指南:从算子信息统计到异常算子 Dump 的四种落盘方案
  • CANN
  • Ascend
  • 人工智能
  • 任务调度

【免费下载链接】runtime

本项目提供CANN运行时组件和维测功能组件。

项目地址:https://gitcode.com/cann/runtime
点击查看免费下载

导读

adump是 CANN Runtime 仓库中聚焦 Dump 能力的示例集合,位于example/5_performance/adump,以"算子信息统计 → 回调 Dump → 配置式 Dump → 异常算子 Dump"四条主线,覆盖了 Runtime 侧数据采集的完整手段。读完本文,你将掌握aclopStartDumpArgs/aclopStopDumpArgsacldumpRegCallback/acldumpUnregCallbackaclmdlInitDump/aclmdlSetDump/aclmdlFinalizeDump以及acldumpGetExceptionInfoPath/acldumpSaveExceptionInfo四组接口的调用顺序、参数约束与适用场景,并能够独立完成 Dump 文件的路径定位、格式转换与字段解读。

总览:adump 样例集的组织脉络

adump目录下共有 4 个自包含的样例工程,每个样例都是一个可独立编译运行的main.cpp程序,公共工具(tensor 创建、运行时初始化)抽取在example/5_performance/adump/adump_tensor_utils.h中:

样例目录核心接口数据形态典型场景
0_adump_argsaclopStartDumpArgs/aclopStopDumpArgs/acldumpGetPath算子信息文件(算子类型、属性、输入输出 format/数据类型/shape)排查算子编译选型、shape 适配
1_adump_callbackacldumpRegCallback/acldumpUnregCallback回调函数接收的分块(chunk)Dump 数据不想落盘、希望直接在进程内消费 Dump 数据
2_model_dump_configaclmdlInitDump/aclmdlSetDump/aclmdlFinalizeDump/acldumpGetPath配置文件驱动的 tensor 数据落盘按配置文件精确控制 Dump 范围与路径
3_save_exception_infoacldumpGetExceptionInfoPath/acldumpSaveExceptionInfo自定义 tensor 主动落盘到 Exception Dump 路径算子异常时主动保存自定义维测数据

四个样例全部围绕同一个计算场景展开:构造shape = {4, 2}的 float 输入,通过aclnnAdd单算子接口执行向量加法(self + alpha * other),并在算子执行前后穿插 Dump 的开启、查询、关闭逻辑。这样读者可以把注意力完全放在 Dump 接口的调用时序上,而不被算子逻辑干扰。

一键运行全部样例

顶层提供了串联脚本 example/5_performance/adump/run.sh,会按固定顺序依次执行 4 个子样例并汇总结果:

samples=("0_adump_args" "1_adump_callback" "2_model_dump_config" "3_save_exception_info")

脚本使用set -euo pipefail严格模式,通过source ../../common/resolve_cann_env.sh解析 CANN 环境(参见 example/common/resolve_cann_env.sh)。每个子样例运行成功打印[SUCCESS],任一样例失败则failed=1,最终以非 0 退出码退出,便于接入 CI 流水线。

环境准备与编译运行

四个样例的编译运行方式完全一致,以任意一个子目录为例:

# ${install_root} 替换为 CANN 安装根目录,默认安装在 /usr/local/Ascend source ${install_root}/cann/set_env.sh # 自动识别 SOC_VERSION 和 ASCENDC_CMAKE_DIR source ${git_clone_path}/example/set_sample_env.sh # 编译运行 bash run.sh

其中每个子目录的 run.sh 完成cmake -B build -DASCEND_CANN_PACKAGE_PATH=${ASCEND_INSTALL_PATH}→ 编译 → 安装 → 运行二进制并将输出写入output_msg.txt的完整流程。环境安装与运行的更多细节参见 example/README.md。

从源码看,公共初始化逻辑封装在 adump_tensor_utils.h 的InitRuntime中,内部调用runtime::InitRuntimeAndCreateStream,等价于aclInit+aclrtSetDevice+aclrtCreateStream的组合;CreateAclTensor则完成aclrtMallocACL_MEM_MALLOC_HUGE_FIRST)、aclrtMemcpy(H2D)以及aclCreateTensor的完整封装。这意味着样例可以直接从"算子信息统计"开始讲起,而无需关心样板代码。

产品支持方面,四个样例均支持 Ascend 950PR/Ascend 950DT、Atlas A3 训练/推理系列、Atlas A2 训练/推理系列产品。

样例一:aclopStartDumpArgs 算子信息统计

功能与接口

0_adump_args 演示了单算子执行场景下的"算子信息 Dump":调用aclopStartDumpArgs(uint32_t dumpType, const char *path)开启算子信息统计,将算子信息文件输出到path参数指定的目录,一个 shape 对应一个算子信息文件,文件中包含算子类型、算子属性、算子输入&输出的 format/数据类型/shape 等信息;调用aclopStopDumpArgs(uint32_t dumpType)关闭统计。

与 tensor 数据 Dump 不同,算子信息统计只输出算子信息文件,不输出 tensor 数据文件,因此不能使用后续的 tensor Dump 转换命令处理。

调用时序

以 main.cpp 为参照,核心调用链如下:

// 1. 初始化(aclInit + aclrtSetDevice + aclrtCreateStream) CHECK_ERROR(adump::InitRuntime(deviceId, &stream, dumpCfgPath)); // 2. 开启算子信息 Dump,输出到 /tmp CHECK_ERROR(aclopStartDumpArgs(ACL_OP_DUMP_OP_AICORE_ARGS, dumpPath)); // 3. 查询当前 Dump 输出路径(本示例新增覆盖点) LogDumpPath(DATA_DUMP, dumpPath); // acldumpGetPath(dumpType) 按 Dump 类型返回路径,为空时回退到调用方传入的路径 // 4. 构造 aclTensor / aclScalar,执行 aclnnAddGetWorkspaceSize + aclnnAdd // 5. aclrtSynchronizeStream 等待任务完成 // 6. aclrtMemcpy 将结果从 Device 拷贝回 Host 并打印 // 7. 关闭算子信息 Dump,再销毁 Stream/Device/去初始化 CHECK_ERROR(aclopStopDumpArgs(ACL_OP_DUMP_OP_AICORE_ARGS));

ACL_OP_DUMP_OP_AICORE_ARGS定义在 include/external/acl/acl_dump.h,值为0x00000001U,表示开启 AICore 算子参数(算子信息)的 Dump。acldumpGetPath的入参acldumpType是一个枚举,本示例传入DATA_DUMP(值为 4),对应模型 Dump 或单算子 Dump 类型,见 include/external/acl/acl_dump.h。

配置与输出

acl.json 用于aclInit初始化阶段的基础 Dump 配置:

{"dump": {"dump_path": "./", "dump_list": [], "dump_op_switch": "on", "dump_data": "tensor"}}
  • dump_path:Dump 落盘根路径;
  • dump_list:为空数组表示不针对特定模型/算子做白名单筛选;
  • dump_op_switch: "on":开启单算子 Dump 开关;
  • dump_data: "tensor":Dump 数据类型为 tensor 数据。

运行成功后,样例会在/tmp下生成算子信息文件,并打印:

[INFO] The dump data path is /tmp. [INFO] acldumpGetPath returned dump path: ... [INFO] result[0] is: 1.000000 ... [INFO] Run the device_normal sample successfully. please make sure dump data has been in path: /tmp

样例二:acldumpRegCallback 回调式 Dump

回调机制

1_adump_callback 演示了"不落盘、直接在进程内接收 Dump 数据"的回调模式:通过acldumpRegCallback注册回调函数,Dump 数据以acldumpChunk分块结构传入回调;通过acldumpUnregCallback取消注册。

acldumpChunk是回调数据的最小载体,其结构定义在 include/external/acl/acl_dump.h:

字段含义使用建议
fileNameDump 数据原计划写入的绝对文件名用于按文件名归并同一个 tensor 或同一次 Dump 的数据块
bufLendataBuf的字节长度判断本次回调携带的数据大小
isLastChunk是否为最后一个数据块,0否 /1只有收到最后一块后,才应认为该文件的数据接收完整
offset当前数据块写入文件时的偏移,-1表示追加写用于按偏移还原分块数据
flag预留标志位当前未定义具体标志,通常仅记录
dataBufDump 数据内容地址(柔性数组)bufLen读取字节数据,再结合文件名或元信息解析

回调实现要点

以 main.cpp 中的回调实现为参考,一个健壮的回调函数需要做三件事:

  1. 空指针与长度校验data == nullptr直接返回失败;sizeof(acldumpChunk) +>// 注册回调(示例 flag 传 0) CHECK_ERROR(acldumpRegCallback(DumpTensorCallback, 0)); LogDumpPath(DATA_DUMP, "./"); // ... 执行 aclnnAdd 算子 ... CHECK_ERROR(aclrtSynchronizeStream(stream)); // 同步等待,确保回调数据全部到达 PrintCallbackSummary(); // 打印汇总统计 acldumpUnregCallback(); // 去注册

    回调模式下,acldumpGetPath返回的路径可用于与回调收到的fileName做对应,判断数据原计划写入哪个目录。示例输出形如:

    [INFO] Receive dump tensor data success. file=..., bufLen=..., isLastChunk=..., offset=..., flag=..., preview=... [INFO] Dump callback summary: total chunks=..., total bytes=..., total files=... [INFO] Run the device_normal sample successfully.

    分块还原原则:对分块回调数据,必须按fileNameoffsetisLastChunk三个字段组合还原完整文件后再分析;只收到中间块就下结论是不安全的。完整字段说明也可参考 dump_artifact_analysis.md 的"回调字段"一节。

    样例三:aclmdlInitDump 配置式 Dump

    配置接口组合

    2_model_dump_config 演示了模型级 Dump 配置接口aclmdlInitDumpaclmdlSetDump(dumpCfgPath)aclmdlFinalizeDump的组合用法。该接口本是模型场景的标准配置入口,本示例将其用于单算子 API 场景,恰好补全了adump目录中的"配置式 Dump"拼图。

    从 main.cpp 可以看到核心调用:

    CHECK_ERROR(adump::InitRuntime(deviceId, &stream)); // 注意:此样例初始化不传 acl.json CHECK_ERROR(aclmdlInitDump()); // Dump 初始化 CHECK_ERROR(aclmdlSetDump(dumpCfgPath)); // 加载 Dump 配置文件 const char* dumpPath = acldumpGetPath(DATA_DUMP); // 查询当前生效的 Dump 输出路径 // ... 执行 aclnnAdd 算子,数据自动按配置落盘 ... CHECK_ERROR(aclmdlFinalizeDump()); // Dump 去初始化

    关键约束(依据 docs/zh/api_ref/18_dump_configuration.md):

    • 必须在aclInit之后、模型加载之前调用aclmdlInitDump
    • 如果已通过aclInit的 json 配置了 Dump 信息,再调用aclmdlInitDump会返回失败——即配置文件式(aclInit)与接口配置式(aclmdl 系列)两种方式互斥
    • 同一进程内可按需多次执行aclmdlInitDump/aclmdlSetDump/aclmdlFinalizeDump循环,为不同模型或不同执行阶段切换 Dump 配置;多次aclmdlSetDump对同一模型采用覆盖策略。

    配置文件解析

    acl.json 采用单算子 Dump 场景推荐的配置:

    {"dump": {"dump_path": "./", "dump_list": [{}], "dump_op_switch": "on", "dump_data": "tensor"}}

    与样例一相比,dump_list从空数组[]变为[{}]——在单算子调用场景下,dump_list: [{}]表示对当前进程内所有单算子调用开启 Dump,这也是官方文档"单算子调用场景下 Dump 配置示例"的推荐写法,参见 docs/zh/api_ref/18_dump_configuration.md。dump_op_switch: "on"dump_data: "tensor"共同保证算子输入/输出 tensor 数据被落盘到dump_path(当前目录./)。

    示例输出确认了配置生效:

    [INFO] Configured model dump path is: ... [INFO] result[0] is: 1.000000 ... [INFO] Run the model dump config sample successfully.

    样例四:acldumpSaveExceptionInfo 异常算子自定义落盘

    场景与接口

    3_save_exception_info 面向"部分组件或框架在算子异常时需要落盘自定义数据"的诉求:通过acl.json使能 Exception Dump 后,组合使用acldumpGetExceptionInfoPathacldumpSaveExceptionInfo,把自定义 tensor 数据主动写入异常 Dump 路径。

    两个接口的函数原型(include/external/acl/acl_dump.h):

    ACL_DUMP_WEAK ACL_FUNC_VISIBILITY aclError acldumpSaveExceptionInfo( const char* fileName, const char* userTag, const acldumpTensorInfo* tensors, size_t tensorCount); ACL_DUMP_WEAK ACL_FUNC_VISIBILITY aclError acldumpGetExceptionInfoPath(char* path, size_t maxLen);

    先决条件:两个接口仅在不低于 25.0.RC1 版本的驱动、且已使能 Exception Dump 的平台上可用。由于它们以 weak 符号导出(ACL_DUMP_WEAK),示例在调用前显式判空——若当前安装的 CANN 包不提供该接口,则打印告警并跳过,保证样例在旧环境上不至于崩溃:

    if (acldumpGetExceptionInfoPath == nullptr || acldumpSaveExceptionInfo == nullptr) { WARN_LOG("Exception Dump APIs are unavailable in the installed CANN package; skip this sample."); return 0; }

    调用流程与参数约束

    核心流程(main.cpp):

    1. 初始化并加载开启 Exception Dump 的配置InitRuntime(deviceId, &stream, "./acl.json")
    2. 查询落盘根路径acldumpGetExceptionInfoPath(excDumpPath, sizeof(excDumpPath)),成功返回形如<dumpPath>/extra-info/data-dump/<deviceId>/的路径,注意路径已携带 deviceId;
    3. 构造acldumpTensorInfo:复用算子的 device 地址、shape、dataType,填充输入/输出三个 tensor;
    4. 落盘acldumpSaveExceptionInfo("save_exception_info", userTag, tensors.data(), tensors.size())

    acl.json 使用异常 Dump 专用配置:

    { "dump": { "dump_path": "./", "dump_scene": "aic_err_brief_dump" } }

    dump_scene是异常算子 Dump 的开关,aic_err_brief_dump表示轻量化 exception dump,导出 AI Core 错误算子的输入&输出、workspace 数据。其他可选值(依据 docs/zh/api_ref/18_dump_configuration.md):

    • aic_err_norm_dump:普通 exception dump,在轻量化基础上额外导出 Shape、Data Type、Format 及属性信息;
    • aic_err_detail_dump:在轻量化基础上额外导出 AI Core 内部存储、寄存器与调用栈,仅支持 Atlas A2/A3 系列且需配套 25.0.RC1 或更高版本驱动,配置后须在aclrtSetDevice之前调用本接口,且aclmdlFinalizeDump无法完成 Dump 去初始化;
    • lite_exception:旧版本兼容写法,效果等同于aic_err_brief_dump

    关键字段语义

    acldumpTensorInfo的完整定义在 include/external/acl/acl_dump.h,落盘时必须满足的硬性约束包括:

    • addrType必须为ACL_DUMP_ADDR_RAW:表示tensorAddr是 Device 上的原始数据地址(接口直接以该地址读取数据);
    • placement必须为ACL_DUMP_PLACEMENT_DEVICE:表示读取 Device 侧数据;
    • tensorSize必须大于 0,tensorAddr不得为空;
    • shapeNum/originShapeNum不得超过ACL_DUMP_MAX_SHAPE_NUM(值为 25),否则返回ACL_ERROR_INVALID_PARAM;未设置或为 0 时,落盘文件中当前 shape 为空;
    • shape/originShape为定长数组,仅前shapeNum/originShapeNum个维度有效;动态 shape 或格式转换场景下,originShapeNum需与originShape有效维度保持一致。

    示例中的FillTensorInfo辅助函数(main.cpp)用memset_s清零结构体后逐字段赋值,并同步填充shapeoriginShape,是构造该结构体的标准写法。

    文件命名与 userTag

    fileName参数有严格的路径约束:必须是相对路径,不能为空、不能包含..,最终落盘位置被限定在 Exception Dump 根路径之内。为避免重复运行时覆盖已有文件,落盘文件名会在fileName基础上自动追加.custom.{timestamp}后缀timestamp为毫秒级时间戳),例如传入save_exception_info,实际文件名形如save_exception_info.custom.20260721153012345

    userTag是用户自定义附加维测信息,允许为 NULL;非空时会被写入 Dump 文件 proto 头的OpAttr字段中。示例传入"component=demo;stage=forward;note=save_exception_info_example",演示了用键值对形式携带组件名、执行阶段等上下文信息。由于userTag会进入 proto 头,落盘后通过异常 Dump 分析工具即可还原这些附加信息。

    示例输出:

    [INFO] acldumpGetExceptionInfoPath success, exception dump path is: .../extra-info/data-dump/0/ [INFO] acldumpSaveExceptionInfo success, data has been saved under exception dump path: .../extra-info/data-dump/0/ ... [INFO] Run the save_exception_info sample successfully.

    Dump 文件路径定位规则

    无论使用哪种 Dump 方式,定位文件落盘位置是第一优先级问题。综合 dump_artifact_analysis.md 的说明,各类 Dump 的路径定位方式如下:

    类型说明路径定位方式
    模型 Dump导出模型中算子的输入/输出数据配置文件dump_path(环境变量不覆盖)
    单算子数据 Dump导出单算子的输入/输出 tensor 数据配置文件dump_path
    算子信息统计算子类型、属性、输入输出 format/数据类型/shapeaclopStartDumpArgspath参数
    溢出算子 Dump导出溢出算子的输入和输出数据配置文件dump_path
    异常算子 Dump导出异常算子的输入输出、workspace、Tiling 等信息<dumpPath>/extra-info/data-dump/<deviceId>/

    异常算子 Dump 的根路径存在优先级:ASCEND_DUMP_PATH>ASCEND_WORK_PATH> 配置文件dump_path> 当前目录./。也可调用acldumpGetExceptionInfoPath直接查询。所有类型都可以用acldumpGetPath(acldumpType)按类型查询当前生效路径。

    文件转换与字段解读

    tensor Dump 文件转换

    aclopStartDumpArgs生成的算子信息文件不适用于 tensor Dump 转换命令;模型 Dump、单算子数据 Dump、异常算子 Dump 产生的 tensor 文件可用msaccucmp.py转换为 numpy 格式:

    msaccucmp.py convert -d <dump_file> -out <output_dir>

    转换完成后用 Python 快速查看数据:

    import numpy as np data = np.load("<converted_file>.npy") print(data.shape) print(data.dtype) print(data)

    数据量较大时,建议先查看 shape、dtype、极值以及是否存在 NaN/Inf,再决定是否全量展开:

    import numpy as np data = np.load("<converted_file>.npy") print("shape:", data.shape) print("dtype:", data.dtype) print("min:", np.nanmin(data)) print("max:", np.nanmax(data)) print("has_nan:", np.isnan(data).any()) print("has_inf:", np.isinf(data).any())

    Tensor 元信息字段

    异常算子 Dump 或自定义 tensor 落盘场景中,acldumpTensorInfo的关键字段解读如下(详见 dump_artifact_analysis.md):

    字段含义使用建议
    typeTensor 类型,区分输入、输出等判断数据在算子中的角色
    tensorSizeTensor 数据大小(字节)与 shape×dataType 计算结果比对,判断数据是否完整
    formatTensor 格式与算子期望格式比对,排查格式不匹配
    dataTypeTensor 数据类型转换或查看数据时须按该类型解释原始字节
    tensorAddrTensor 数据地址落盘时标识待读取的数据地址
    addrType地址类型调用acldumpSaveExceptionInfo时必须为ACL_DUMP_ADDR_RAW
    placement数据所在位置调用acldumpSaveExceptionInfo时必须为ACL_DUMP_PLACEMENT_DEVICE
    shapeNumshape有效维度个数不得超过 25,否则返回ACL_ERROR_INVALID_PARAM
    originShapeNumoriginShape有效维度个数不得超过 25;动态 shape 或格式转换场景需与originShape有效维度一致
    shape/originShape当前 shape / 原始 shape仅前shapeNum/originShapeNum个维度有效

    分析建议

    • 先根据路径优先级、dump_path或接口返回路径确认文件是否生成,再选择需要转换和查看的文件;
    • 比对精度问题时,优先确认 shape、dataType 和 format 是否符合预期,再比较数值差异;
    • 对异常 Dump,先查看异常算子名称、输入输出、workspace 和 Tiling 信息,再结合 Runtime 日志或 AI Core Error 信息定位;
    • 对分块回调数据,必须按fileNameoffsetisLastChunk还原完整文件后再分析。

    接口调用顺序对照与选型建议

    最后将四组接口的调用骨架汇总如下,便于直接对照落地:

    场景开启/初始化使用关闭/去初始化
    算子信息统计aclopStartDumpArgs(type, path)执行算子aclopStopDumpArgs(type)
    回调式 DumpacldumpRegCallback(cb, flag)执行算子 + 回调消费 chunkacldumpUnregCallback()
    配置式 DumpaclmdlInitDump()+aclmdlSetDump(cfg)执行算子,数据按配置落盘aclmdlFinalizeDump()
    异常自定义落盘aclInit(acl.json 使能dump_sceneacldumpGetExceptionInfoPath+acldumpSaveExceptionInfoaclFinalize结束

    选型参考:

    • 只需确认"算子以什么 shape/format/数据类型被编译执行"→ 用aclopStartDumpArgs算子信息统计,开销最小、不含 tensor 数据;
    • 需要拿到 tensor 数据又不想产生磁盘文件(如直接送入比对工具或日志系统)→ 用acldumpRegCallback回调模式;
    • 需要精细控制 Dump 范围、路径,或模型与单算子混合场景 → 用aclmdlInitDump配置式接口;
    • 算子异常时希望额外把自定义数据(如中间变量、上下文 tag)保存下来辅助定位 → 用acldumpSaveExceptionInfo写入 Exception Dump 路径。

    注意配置入口互斥:aclInit的 json 配置与aclmdlSetDump的配置接口不可同时使用(先aclInit配置 Dump 后再aclmdlInitDump会失败),规划工程时要二选一。

    延伸阅读

    • Dump 配置接口完整文档:aclmdlInitDump/aclmdlSetDump/acldumpRegCallback/acldumpGetPath/aclopStartDumpArgs/acldumpGetExceptionInfoPath/acldumpSaveExceptionInfo的完整参数与约束说明;
    • Dump 文件解读:文件目录规则、转换命令与字段含义;
    • 接口头文件:acldumpChunkacldumpTensorInfoacldumpTypeACL_DUMP_MAX_SHAPE_NUM等类型与宏的精确定义;
    • 四个样例的完整源码与英文版说明:0_adump_args、1_adump_callback、2_model_dump_config、3_save_exception_info。
    • CANN
    • Ascend
    • 人工智能
    • 任务调度

    【免费下载链接】runtime

    本项目提供CANN运行时组件和维测功能组件。

    项目地址:https://gitcode.com/cann/runtime
    点击查看免费下载

    相关推荐

    上一篇:PingFangSC苹果平方字体:3分钟掌握专业字体应用全攻略
    下一篇:Mautic审计日志:操作记录与追踪

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 22:28:34

STM32F407移植Zephyr:设备树配置与定时器触发DAC实战

当年我从 Keil 切到 Zephyr 的时候&#xff0c;第一反应是“这也太复杂了吧”。一个简单的串口打印&#xff0c;FreeRTOS 工程里配个库调用就行&#xff0c;Zephyr 里要折腾 west、SDK、设备树、Kconfig&#xff0c;还没跑通就先被工具链劝退。但等我把整个流程走通之后&#x…

作者头像 李华
网站建设 2026/9/20 22:27:38

Qt表格控件实战:QTableWidget无存储场景用法与避坑指南

简介&#xff1a;面向Qt初学者的表格功能演示案例&#xff0c;重点展示在Qt环境中如何创建可交互的表格界面&#xff0c;包括添加、删除行列与单元格编辑&#xff0c;但所有修改仅在内存中生效&#xff0c;不写入磁盘。压缩包共6个文件&#xff0c;类型涵盖cpp源文件、h头文件、…

作者头像 李华
网站建设 2026/9/20 22:27:02

OpenMMO交易系统详解:单一金币货币的经济设计分析

OpenMMO交易系统详解&#xff1a;单一金币货币的经济设计分析 【免费下载链接】OpenMMO 项目地址: https://gitcode.com/GitHub_Trending/open/OpenMMO OpenMMO 是一款 AI 智能体与人类玩家平等共处的 3D MMORPG&#xff0c;其交易系统围绕"单一金币货币 买卖价差…

作者头像 李华
网站建设 2026/9/20 22:25:45

PETS5口语备考全攻略:历年真题高效使用与评分规则详解

简介&#xff1a;这是一份面向PETS5&#xff08;全国外语水平考试英语五级&#xff09;考生的口语历年真题PDF&#xff0c;覆盖自我介绍、家庭背景、工作与学习经历、兴趣爱好、合作讨论等核心环节&#xff0c;并附有考官引导语与话题卡示例&#xff0c;可用于熟悉真实考场流程…

作者头像 李华