已关闭
[RFC]: HIXL支持host api profiling #128
zhaozhihui创建于  3月4日关闭于  4月14日
zhaozhihui成员
3月4日 创建

HIXL支持Host Profiling能力

介绍

需求的背景:

  1. 当前HIXL缺少对传输接口进行profiling的能力,无法对Host侧传输API调用进行性能分析和优化。
  2. 参考GE仓库中ACL的profiling实现(api/acl/common/prof_api_reg.h),需要为hixl的传输接口提供Profiling能力。

设计目标:

  • 为hixl的传输接口提供Profiling能力,支持对传输API调用进行耗时统计和性能分析
  • 提供简洁易用的宏接口,方便在传输API中集成profiling
  • 支持同步和异步传输接口的profiling
  • 异步接口在获取传输完成时进行打点,支持传入起始时间
处理

整体架构:

graph TB
    A[hixl传输接口层<br/>- 同步传输接口<br/>- 异步传输接口] --> B[HixlProfilingReporter RAII<br/>- 构造: 记录开始时间<br/>- 析构: 计算耗时并上报<br/>- 支持传入外部起始时间异步接口]
    B --> C[Profiling数据上报层<br/>- MsprofReportApi<br/>- MsprofRegTypeInfo]
    C --> D[profiling框架]

核心组件设计:

  1. 修改原有的私有成员req2client_为req_map_,用于存储req和开始时间,操作类型,对端ip等,后续如果还有扩展信息可以直接在这个结构体里添加,新增结构体TransferInfo记录每次传输的开始时间和操作类型对端ip等
classDiagram
    class HixlEngine {
        -req_map_: std::map~uint64_t, TransferInfo~
    }
    
    class TransferInfo {
        <<struct>>
        +start_time: uint64_t
        +op_type: TransferOp
        +remote_engine: AscendString
    }
    
    HixlEngine --> TransferInfo : use
  1. 在aprof_pub.h中加入MSPROF_REPORT_ACL_HIXL_BASE_TYPE
#define MSPROF_REPORT_ACL_HIXL_BASE_TYPE  0x0C0000U
  1. 在log_types.h中加入module id HIXL = 12
// module id
enum {
    HIXL = 12,     /* HIXL */             
}

API类型枚举定义:

namespace hixl {
enum HixlProfType {
    // 基础类型起始偏移(参考ACL设计)
    kProfTypeStart = 0x10000000U,
    // 传输接口
    HixlProfTypeRead,
    HixlProfTypeWrite,
    // End标记
    kProfTypeEnd
};
}

Profiling Reporter类:

namespace hixl {
class HixlProfilingReporter {
public:
  // 同步接口:构造时自动记录开始时间
    explicit HixlProfilingReporter(const HixlProfType apiId);

    // 异步接口:支持传入外部记录的起始时间
    HixlProfilingReporter(const HixlProfType apiId, uint64_t startTime);

    virtual ~HixlProfilingReporter() noexcept;
    
private:
    uint64_t startTime_ = 0UL;
    const HixlProfType hixlApi_;
};
}

Profiling宏定义:

// 同步接口宏:自动记录开始时间
#define HIXL_API_PROFILING(apiId) \
    const hixl::HixlProfilingReporter profilingReporter(apiId)
// 异步接口宏:使用外部传入的起始时间
#define HIXL_API_PROFILING_WITH_TIME(apiId, startTime) \
    const hixl::HixlProfilingReporter profilingReporter(apiId, startTime)

Profiling实现文件:

主要功能:

  1. 全局profiling状态管理(是否启用)
  2. 设备列表管理
  3. API类型注册
  4. Profiling数据上报
  5. 回调函数注册

关键实现:

// API类型名称映射
static const std::map<HixlProfType, std::string> PROF_TYPE_TO_NAMES = {
    {HixlProfType::HixlProfTypeRead, "HixlProfTypeRead"},
    {HixlProfType::HixlProfTypeWrite, "HixlProfTypeWrite"},
};

// 同步接口构造函数:记录开始时间
HixlProfilingReporter::HixlProfilingReporter(const HixlProfType apiId) 
    : hixlApi_(apiId) {
    if (g_profRun) {
        startTime_ = MsprofSysCycleTime();
    }
}

// 异步接口构造函数:使用外部传入的起始时间
HixlProfilingReporter::HixlProfilingReporter(const HixlProfType apiId, uint64_t startTime) 
    : startTime_(startTime), hixlApi_(apiId) {
    // 不需要记录时间,使用传入的startTime
}

// 析构函数:计算耗时并上报
HixlProfilingReporter::~HixlProfilingReporter() noexcept {
    if (g_profRun && (startTime_ != 0UL)) {
        const uint64_t endTime = MsprofSysCycleTime();
        MsprofApi api{};
        api.beginTime = startTime_;
        api.endTime = endTime;
        api.threadId = static_cast<uint32_t>(mmGetTid());
        api.level = MSPROF_REPORT_ACL_LEVEL;
        api.type = static_cast<uint32_t>(hixlApi_);
        (void)MsprofReportApi(true, &api);
    }
}

Profiling注册所有类型名称

static Status RegisterProfType() {
    for(auto &iter: PROF_TYPE_TO_NAMES) {
        uint32_t typeId = static_cast<uint32_t>(iter.first);
        const auto ret = MsprofRegTypeInfo(MSPROF_REPORT_HIXL_LEVEL, typeId, iter.second.c_str())
        HIXL_CHK_BOOL_RET_STATUS(ret == MSPROF_ERROR_NONE, FAILED, "Registered api type [%u] failed = %d", typeId, ret);
    }
    return SUCCESS;
}

Profiling控制:
通过回调机制控制profiling的启动和停止:

static Status ProcessProfData(void *const data, const uint32_t len) {
    HIXL_LOGI("start to execute ProcessProfData");
    const std::lock_guard<std::mutex> lk(g_profMutex);
    HIXL_CHECK_NOTNULL(data);
    constexpr size_t commandLen = sizeof(MsprofCommandHandle);
    if (len < commandLen) {
        HIXL_LOGE("[Check][Len]len[%u] is invalid, it should not be smaller than %zu", len, commandLen);
        return PARAM_INVALID;
    }
    MsprofCommandHandle *const profilerConfig = static_cast<MsprofCommandHandle *>(data);
    Status ret = SUCCESS;
    const uint64_t profSwitch = profilerConfig->profSwitch;
    const uint32_t type = profilerConfig->type;
    if (((profSwitch & HIXL_PROF_HIXL_API) != 0U) && (type == START_PROFILING)) {
        ret = ProfInnerStart(profilerConfig);
    }
    if (((profSwitch & HIXL_PROF_HIXL_API) != 0U) && (type == STOP_PROFILING)) {
        ret = ProfInnerStop(profilerConfig);
    }

    return ret;
}

static Status HixlProfCtrlHandle(uint32_t dataType, void *data, uint32_t dataLen) {
    if (dataType == PROF_CTRL_SWITCH) {
        return ProcessProfData(data, dataLen);
    }
    return SUCCESS;
}

// 注册回调
class HixlRegProfCallback {
public:
    HixlRegProfCallback() {
        const auto profRet = MsprofRegisterCallback(HIXL, &HixlProfCtrlHandle);
        if (profRet != 0) {
            // 错误处理
        }
    }
};
static HixlRegProfCallback g_profCbReg;

时序图(初始化阶段):

sequenceDiagram
    participant App as 用户应用
    participant Hixl as hixl库
    participant Reporter as HixlProfilingReporter
    participant Aprof as profiling框架
    participant Msprof as Msprof接口

    Note over App,Msprof: 库加载时执行全局构造
    Hixl->>Reporter: 创建HixlRegProfCallback静态对象
    Reporter->>Msprof: MsprofRegisterCallback(HIXL, &HixlProfCtrlHandle)
    Msprof-->>Reporter: 返回注册结果
    Note over Reporter: 回调函数注册完成

    Note over App,Msprof: Profiling启动
    App->>Aprof: 发送profiling启动命令
    Aprof->>Msprof: 调用已注册的回调函数
    Msprof->>Hixl: HixlProfCtrlHandle(PROF_CTRL_SWITCH, data, dataLen)
    Hixl->>Hixl: ProcessProfData(data, dataLen)
    Hixl->>Msprof: MsprofRegTypeInfo(MSPROF_REPORT_HIXL_LEVEL, typeId, apiName)
    Note over Msprof: 注册所有API类型信息
    Msprof-->>Hixl: 返回注册结果
    Hixl->>Hixl: 设置g_profRun = true
    Hixl-->>Aprof: 返回成功
    Aprof-->>App: Profiling启动成功

时序图(同步传输接口调用):

sequenceDiagram
    participant App as 用户应用
    participant API as 同步传输API
    participant Macro as HIXL_API_PROFILING
    participant Reporter as HixlProfilingReporter
    participant Msprof as Msprof接口
    participant Aprof as profiling框架

    Note over App,Aprof: 同步传输API调用流程
    App->>API: TransferSync(...)
    API->>Macro: HIXL_API_PROFILING(HixlProfTypeRead)
    Macro->>Reporter: 创建HixlProfilingReporter对象(构造函数)
    
    alt Profiling已启用
        Reporter->>Msprof: MsprofSysCycleTime()
        Msprof-->>Reporter: 返回startTime
        Note over Reporter: 保存startTime_
    else Profiling未启用
        Note over Reporter: startTime_ = 0
    end
    
    Reporter-->>Macro: 返回对象
    Macro-->>API: 继续执行API逻辑
    API->>API: 执行同步传输业务逻辑
    
    Note over API: API函数返回,触发析构函数
    API->>Reporter: ~HixlProfilingReporter() (析构函数)
    
    alt Profiling已启用且startTime_ != 0
        Reporter->>Msprof: MsprofSysCycleTime()
        Msprof-->>Reporter:MSprof: 返回endTime
        Reporter->>Reporter: 计算耗时 = endTime - startTime_
        Reporter->>Msprof: MsprofReportApi(, &api)
        Note over Msprof: 上报profiling数据到profiling
        Msprof->>Aprof: 写入profiling数据
        Aprof-->>Msprof: 返回成功
        Msprof-->>Reporter: 返回上报结果
    else Profiling未启用或startTime_ == 0
        Note over Reporter: 跳过profiling上报
    end
    
    Reporter-->>API: 析构完成
    API-->>App: 返回SUCCESS

时序图(异步传输接口调用):

sequenceDiagram
    participant App as 用户应用
    participant API as 异步传输API
    participant Macro as HIXL_API_PROFILING
    participant Reporter as HixlProfilingReporter
    participant Msprof as Msprof接口
    participant Aprof as profiling框架
    participant AsyncOp as 异步操作

    Note over App,AsyncOp: 异步传输API调用流程
    App->>API: TransferAsync(...)
    API->>Msprof: MsprofSysCycleTime()
    Msprof-->>API: 返回startTime
    Note over API: 保存startTime到异步操作上下文
    
    API->>API: 发起异步传输请求
    API->>AsyncOp: 创建异步操作对象
    API-->>App: 返回异步操作句柄
    
    Note over App,AsyncOp: 异步传输执行中...
    AsyncOp->>AsyncOp: 执行异步传输逻辑
    
    Note over App,AsyncOp: 获取传输完成状态
    App->>API: GetTransferStatus(req)
    API->>AsyncOp: 检查异步操作完成状态
    AsyncOp-->>API: 返回完成状态和startTime
    
    alt 传输完成
        API->>Macro: HIXL_API_PROFILING_WITH_TIME(HixlProfTypeWrite, startTime)
        Macro->>Reporter: 创建HixlProfilingReporter对象(构造函数,传入startTime)
        Note over Reporter: 使用外部传入的startTime_
        
        Reporter-->>Macro: 返回对象
        Macro-->>API: 继续执行API逻辑
        
        Note over API: API函数返回,触发析构函数
        API->>Reporter: ~HixlProfilingReporter() (析构函数)
        
        Reporter->>Msprof: MsprofSysCycleTime()
        Msprof-->>Reporter: 返回endTime
        Reporter->>Reporter: 计算耗时 = endTime - startTime_
        Reporter->>Msprof: MsprofReportApi(true, &api)
        Note over Msprof: 上报profiling数据到profiling
        Msprof->>Aprof: 写入profiling数据
        Aprof-->>Msprof: 返回成功
        Msprof-->>Reporter: 返回上报结果
        
        Reporter-->>API: 析构完成
        API-->>App: 返回SUCCESS
    else 传输未完成
        API-->>App: 返回HIXX_ERROR_NOT_READY
    end

时序图(Profiling停止阶段):

sequenceDiagram
    participant App as 用户应用
    participant Aprof as profiling框架
    participant Msprof as Msprof接口
    participant Hixl as hixl库

    Note over App,Hixl: Profiling停止
    App->>Aprof: 发送profiling停止命令
    Aprof->>Msprof: 调用已注册的回调函数
    Msprof->>Hixl: HixlProfCtrlHandle(PROF_CTRL_SWITCH, data, dataLen)
    Hixl->>Hixl: ProcessProfData(data, dataLen)
    Hixl->>Hixl: 从设备列表中移除设备
    Hixl->>Hixl: 检查设备列表是否为空
    alt 设备列表为空
        Hixl->>Hixl: 设置g_profRun = false
        Note over Hixl: Profiling完全停止
    else 设备列表不为空
        Note over Hixl: 仅移除指定设备,profiling继续
    end
    Hixl-->>Aprof: 返回成功
    Aprof-->>App: Profiling停止成功

时序图说明:

  1. 初始化阶段:
    • 库加载时自动注册回调函数
    • 用户通过profiling框架发送启动命令
    • 回调函数注册所有API类型信息
    • 设置全局profiling运行标志
  2. 同步传输接口调用阶段:
    • API函数开始时通过宏创建Reporter对象
    • 构造函数记录开始时间(仅在profiling启用时)
    • API执行同步传输业务逻辑
    • 析构函数计算耗时并上报数据(仅在profiling启用时)
  3. 异步传输接口调用阶段:
    • API函数开始时记录开始时间并保存到异步操作上下文
    • 发起异步传输请求,返回异步操作句柄
    • 用户调用获取完成状态接口
    • 使用外部传入的起始时间创建Reporter对象
    • 析构函数计算从起始时间到当前的耗时并上报数据
  4. Profiling停止阶段:
    • 用户通过profiling框架发送停止命令
    • 回调函数移除指定设备
    • 当所有设备都移除时,完全停止profiling
输入&输出

使用时的输入介绍:

  1. 配置选项:通过profiling框架控制profiling的启动和停止
  2. API类型:使用HixlProfType枚举定义需要profiling的API类型
  3. Profiling宏:使用HIXL_API_PROFILING和HIXL_API_PROFILING_WITH_TIME宏在API中集成profiling

使用example:

// 同步接口集成profiling
Status TransferSync(const AscendString &remote_engine, TransferOp operation,
                                const std::vector<TransferOpDesc> &op_descs, int32_t timeout_in_millis) {
    if (operation == READ) {
        HIXL_API_PROFILING(hixl::HixlProfType::HixlProfTypeRead);
    } else {
        HIXL_API_PROFILING(hixl::HixlProfType::HixlProfTypeWrite);
    }
    
    // 执行同步读取逻辑
    // ...
    
    return SUCCESS;
}

// 异步接口集成profiling
Status TransferAsync(const AscendString &remote_engine, TransferOp operation,
                                  const std::vector<TransferOpDesc> &op_descs, const TransferArgs &optional_args,
                                  TransferReq &req) {
    // 记录开始时间
    auto id = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(req));
    uint64_t startTime = 0;
    startTime = MsprofSysCycleTime();
    TransferInfo transfer_info = {startTime, operation, remote_engine};
    std::lock_guard<std::mutex> lock(mutex_);
    req_map_.emplace(id, transfer_info);
    // 创建异步操作并保存startTime
    // ...
    
    return SUCCESS;
}

Status GetTransferStatus(const TransferReq &req, TransferStatus &status) {
    std::lock_guard<std::mutex> lock(mutex_);
    auto id = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(req));
    auto it = req_map_.find(id);
    if (it == req_map_.cend()) {
        status = TransferStatus::FAILED;
        HIXL_LOGE(PARAM_INVALID, "[HixlEngine] Request not found, request has been completed or does not exist, req: %p", req);
        return PARAM_INVALID;
    }
    // 查找对应的client
    ...
    if (status != TransferStatus::WAITING) {
        auto op_type = it->second.op_type;
        auto start_time = it->second.start_time;
        // 使用异步操作中保存的起始时间进行profiling
        if (op_type == READ) {
            HIXL_API_PROFILING_WITH_TIME(hixl::HixlProfType::HixlProfTypeRead, start_time);
        } else {
            HIXL_API_PROFILING_WITH_TIME(hixl::HixlProfType::HixlProfTypeWrite, start_time);
        
        // 清理异步操作
        req_map_.erase(it);
    }
    
    return SUCCESS;
}

使用时传出的输出介绍:

  1. Profiling数据:通过MsprofReportApi上报API调用数据到profiling框架
  2. API类型信息:通过MsprofRegTypeInfo注册API类型信息
  3. 回调响应:通过回调机制处理profiling控制命令
关键检查点

技术要点:

性能考虑:

  • 仅在profiling启用时记录时间戳
  • 使用RAII模式,无需手动管理生命周期
  • 异步接口的startTime在发起请求时记录一次,避免重复记录
  • 避免在热路径上增加过多开销

兼容性:

  • 与现有profiling框架保持兼容
  • 保持接口简洁,使用成本低
  • 异步接口支持传入外部起始时间,适应异步场景

错误处理:

  • Profiling失败不应影响API正常功能
  • 记录profiling相关错误日志
  • 异步操作失败时,startTime可能为0,需要正确处理

风险与依赖:

风险:

  1. profiling框架依赖:需要profiling团队支持定义新的reporting level
  2. 性能开销:需要评估profiling对传输API性能的影响
  3. 兼容性:需要确保与现有profiling机制不冲突
  4. 异步操作生命周期:需要确保异步操作完成前startTime不会被错误释放

依赖:

  1. profiling框架
  2. MMPA接口

验收标准:

测试场景
需要编写用例并用profiling接口开启能力,看是否有性能数据生成,需要验证:

  1. 同步传输read
  2. 同步传输write
  3. 异步传输read
  4. 异步传输write

具体开启profiling能力采集性能数据的方式可以参考使用acl C&C++接口采集性能数据

参考资料:

  • GE ACL Profiling实现:api/acl/common/prof_api_reg.h
  • profiling框架接口:profiling_pub.h
likedislike
Zzhaozhihui成员
3月4日 将 zhaozhihui 设为负责人
此处折叠了23条事件消息 查看更多
Zzhaozhihui成员
5月6日 关联了里程碑:release plan Apr.2026