* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file sk_scope_info.h
* \brief Super Kernel Scope Info - data structures for scope splitting
*/
#ifndef __SK_SCOPE_INFO_H__
#define __SK_SCOPE_INFO_H__
#include <bitset>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "sk_types.h"
#include "sk_node.h"
* \struct OriginalScopeInfo
* \brief Simple structure to store original scope info (for dumping)
* Used to preserve scope information before any splitting
*/
struct OriginalScopeInfo {
uint16_t scopeId;
std::bitset<MAX_SCOPE_NUM> scopeBitFlags;
std::vector<uint64_t> nodeIds;
const std::bitset<MAX_SCOPE_NUM>& GetScopeBitFlags() const { return scopeBitFlags; }
};
* \struct ScopeStreamInfo
* \brief Information about a single stream within a scope
*/
struct ScopeStreamInfo {
uint32_t streamIdx = 0;
uint64_t headNodeIdx = INVALID_TASK_ID;
uint64_t tailNodeIdx = INVALID_TASK_ID;
uint64_t nodeSize = 0;
};
* \enum ScopeFusionStatus
* \brief Fusion status for scope after PostProcess
*/
enum class ScopeFusionStatus : uint8_t {
FAILED = 0,
SUCCESS,
};
* \brief Convert ScopeFusionStatus to string
*/
inline const char* ScopeFusionStatusToStr(ScopeFusionStatus status) {
switch (status) {
case ScopeFusionStatus::FAILED:
return "FAILED";
case ScopeFusionStatus::SUCCESS:
return "SUCCESS";
default:
return "UNKNOWN";
}
}
* \enum ScopeFailReason
* \brief Failure reasons for scope processing (when fusion failed)
*/
enum class ScopeFailReason : uint8_t {
NONE = 0,
STREAM_SYNC_FAIL,
};
* \enum ScopeBreakReason
* \brief Reasons why a scope boundary was created (scope was split)
*/
enum class ScopeBreakReason : uint8_t {
NONE,
UNFUSIBLE_NODE,
DEADLOCK_DETECTED,
SCHEMODE_CORE_DROP,
SCHEMODE_CORE_RISE,
DEBUG_PER_OP_MAX_CORE,
};
* \brief Convert ScopeBreakReason to string
*/
inline const char* ScopeBreakReasonToStr(ScopeBreakReason reason) {
switch (reason) {
case ScopeBreakReason::UNFUSIBLE_NODE:
return "There exists unfusible node in scope";
case ScopeBreakReason::DEADLOCK_DETECTED:
return "There exists deadlock in scope";
case ScopeBreakReason::SCHEMODE_CORE_DROP:
return "There exists an operator for full kernel synchronization, and the number of kernels of this operator is less than the maximum number of kernels of the fused superkernel";
case ScopeBreakReason::SCHEMODE_CORE_RISE:
return "There exists an operator for full kernel synchronization, and the number of kernels of this operator is greater than the maximum number of kernels of the previously fused superkernel";
case ScopeBreakReason::DEBUG_PER_OP_MAX_CORE:
return "Per-Op debug mode: each operator is an independent scope";
default:
return "UNKNOWN REASON";
}
}
* \class ScopeBreakInfo
* \brief Information about why a scope boundary was created
*/
class ScopeBreakInfo {
public:
ScopeBreakInfo() = default;
ScopeBreakInfo& SetReason(ScopeBreakReason r) {
reason = r;
return *this;
}
ScopeBreakInfo& SetTriggerNode(uint64_t nodeId, uint32_t streamIdx = 0) {
triggerNodeId = nodeId;
triggerStreamIdx = streamIdx;
return *this;
}
ScopeBreakInfo& SetTriggerStreamIdx(uint32_t streamIdx) {
triggerStreamIdx = streamIdx;
return *this;
}
ScopeBreakInfo& SetFusionFailReason(const FusionFailReasonInfo& failInfo) {
fusionFailReason = failInfo;
return *this;
}
ScopeBreakInfo& SetFusionFailReason(FusionFailReason failReason) {
fusionFailReason = FusionFailReasonInfo(failReason);
return *this;
}
ScopeBreakInfo& SetParentScopeId(uint16_t id) {
parentScopeId = id;
return *this;
}
ScopeBreakInfo& SetDetail(const std::string& d) {
detail = d;
return *this;
}
ScopeBreakInfo& SetDetail(std::string&& d) {
detail = std::move(d);
return *this;
}
ScopeBreakReason GetReason() const { return reason; }
uint64_t GetTriggerNodeId() const { return triggerNodeId; }
uint32_t GetTriggerStreamIdx() const { return triggerStreamIdx; }
const FusionFailReasonInfo& GetFusionFailReason() const { return fusionFailReason; }
uint16_t GetParentScopeId() const { return parentScopeId; }
const std::string& GetDetail() const { return detail; }
std::string Format() const {
std::string result;
if (reason != ScopeBreakReason::NONE) {
result = "breakReason=" + std::string(ScopeBreakReasonToStr(reason));
}
if (triggerNodeId != INVALID_TASK_ID) {
result += ", triggerNode=" + std::to_string(triggerNodeId);
}
if (triggerStreamIdx != 0 || triggerNodeId != INVALID_TASK_ID) {
result += ", triggerStream=" + std::to_string(triggerStreamIdx);
}
if (fusionFailReason != FusionFailReason::CAN_FUSE) {
result += ", fusionFailReason=" + FusionFailReasonToStr(fusionFailReason);
}
if (parentScopeId != INVALID_SCOPE_ID) {
result += ", parentScope=" + std::to_string(parentScopeId);
}
if (!detail.empty()) {
result += ", detail=\"" + detail + "\"";
}
return result;
}
private:
ScopeBreakReason reason = ScopeBreakReason::NONE;
uint64_t triggerNodeId = INVALID_TASK_ID;
uint32_t triggerStreamIdx = 0;
FusionFailReasonInfo fusionFailReason;
uint16_t parentScopeId = INVALID_SCOPE_ID;
std::string detail;
};
* \class ScopeIdGenerator
* \brief Singleton class for generating unique scope IDs
*
* This generator ensures each scope gets a unique ID at creation time.
* The ID is not recycled when scope is destroyed, allowing full lifecycle tracking.
* Uses uint16_t for compact storage; logs INFO when wraparound occurs (every 65536 scopes).
*/
class ScopeIdGenerator {
public:
static ScopeIdGenerator& Instance() {
static ScopeIdGenerator instance;
return instance;
}
uint16_t NextId() {
uint16_t id = nextId_++;
if (nextId_ == 0) {
SK_LOGI("ScopeId wraparound detected: scope ID counter reset to 0 after reaching maximum");
}
return id;
}
private:
ScopeIdGenerator() : nextId_(0) {}
ScopeIdGenerator(const ScopeIdGenerator&) = delete;
ScopeIdGenerator& operator=(const ScopeIdGenerator&) = delete;
uint16_t nextId_;
};
* \struct ScopeExtInfo
* \brief Extended information for scope post-processing and scheduling
*/
struct ScopeExtInfo {
std::vector<std::vector<aclmdlRITaskParams>> customParamsList;
std::vector<SuperKernelBaseNode*> filteredNodes;
std::vector<std::unique_ptr<SuperKernelBaseNode>> eventNodes;
uint64_t skMainNodeId = INVALID_TASK_ID;
std::string scopeName;
ScopeFusionStatus fusionStatus = ScopeFusionStatus::FAILED;
ScopeFailReason failReason = ScopeFailReason::NONE;
ScopeExtInfo() = default;
ScopeExtInfo(const ScopeExtInfo&) = delete;
ScopeExtInfo& operator=(const ScopeExtInfo&) = delete;
ScopeExtInfo(ScopeExtInfo&&) = default;
ScopeExtInfo& operator=(ScopeExtInfo&&) = default;
};
* \brief Convert ScopeFailReason to string
*/
inline const char* ScopeFailReasonToStr(ScopeFailReason reason)
{
switch (reason) {
case ScopeFailReason::STREAM_SYNC_FAIL:
return "Insufficient resources are needed for scope fusion";
default:
return "UNKNOWN REASON";
}
}
* \class SuperKernelScopeInfo
* \brief Information about a single scope in Super Kernel fusion
*
* A scope represents a collection of nodes from potentially multiple streams
* that can be fused into a single Super Kernel.
*/
class SuperKernelScopeInfo {
public:
SuperKernelScopeInfo() : scopeId_(ScopeIdGenerator::Instance().NextId()) {}
~SuperKernelScopeInfo() = default;
SuperKernelScopeInfo(const SuperKernelScopeInfo&) = delete;
SuperKernelScopeInfo& operator=(const SuperKernelScopeInfo&) = delete;
SuperKernelScopeInfo(SuperKernelScopeInfo&& other) noexcept
: scopeId_(other.scopeId_),
scopeStreamInfos_(std::move(other.scopeStreamInfos_)),
nodes_(std::move(other.nodes_)),
scopeBitFlags_(other.scopeBitFlags_),
breakInfo_(std::move(other.breakInfo_)),
extInfo_(std::move(other.extInfo_)) {}
SuperKernelScopeInfo& operator=(SuperKernelScopeInfo&& other) noexcept {
if (this != &other) {
scopeId_ = other.scopeId_;
scopeStreamInfos_ = std::move(other.scopeStreamInfos_);
nodes_ = std::move(other.nodes_);
scopeBitFlags_ = other.scopeBitFlags_;
breakInfo_ = std::move(other.breakInfo_);
extInfo_ = std::move(other.extInfo_);
}
return *this;
}
uint16_t GetScopeId() const { return scopeId_; }
const std::vector<ScopeStreamInfo>& GetScopeStreamInfos() const { return scopeStreamInfos_; }
void SetScopeStreamInfos(std::vector<ScopeStreamInfo> infos) { scopeStreamInfos_ = std::move(infos); }
void AddScopeStreamInfo(const ScopeStreamInfo& info) { scopeStreamInfos_.push_back(info); }
const std::vector<SuperKernelBaseNode*>& GetNodes() const { return nodes_; }
void SetNodes(std::vector<SuperKernelBaseNode*> nodeList) { nodes_ = std::move(nodeList); }
void AddNode(SuperKernelBaseNode* node) { nodes_.push_back(node); }
const std::bitset<MAX_SCOPE_NUM>& GetScopeBitFlags() const { return scopeBitFlags_; }
void SetScopeBitFlags(const std::bitset<MAX_SCOPE_NUM>& flags) { scopeBitFlags_ = flags; }
const ScopeBreakInfo& GetBreakInfo() const { return breakInfo_; }
ScopeBreakInfo& MutableBreakInfo() { return breakInfo_; }
void SetBreakInfo(const ScopeBreakInfo& info) { breakInfo_ = info; }
void SetBreakInfo(ScopeBreakInfo&& info) { breakInfo_ = std::move(info); }
void SetBreakReason(ScopeBreakReason reason, uint64_t triggerNodeId = INVALID_TASK_ID,
uint32_t triggerStreamIdx = 0) {
breakInfo_.SetReason(reason)
.SetTriggerNode(triggerNodeId, triggerStreamIdx);
}
const ScopeExtInfo& GetExtInfo() const { return extInfo_; }
void SetExtInfo(ScopeExtInfo&& extInfo) { extInfo_ = std::move(extInfo); }
ScopeExtInfo& MutableExtInfo() { return extInfo_; }
private:
uint16_t scopeId_;
std::vector<ScopeStreamInfo> scopeStreamInfos_;
std::vector<SuperKernelBaseNode*> nodes_;
std::bitset<MAX_SCOPE_NUM> scopeBitFlags_;
ScopeBreakInfo breakInfo_;
ScopeExtInfo extInfo_;
};
#endif