* This file is part of the MindStudio project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MindStudio is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*/
#include "inefficient_analyzer.h"
#include <string>
#include "event_dispatcher.h"
namespace MemScope {
InefficientAnalyzer& InefficientAnalyzer::GetInstance()
{
static InefficientAnalyzer analyzer{};
return analyzer;
}
InefficientAnalyzer::InefficientAnalyzer()
{
InefficientAnalyzer::Subscribe();
}
void InefficientAnalyzer::EventHandle(std::shared_ptr<EventBase>& event, MemoryState* state)
{
const auto eventType = event->eventType;
const auto eventSubType = event->eventSubType;
if (eventType == EventBaseType::OP_LAUNCH) {
std::unordered_map<uint64_t, PidState>& pidStatesMap =
(eventSubType == EventSubType::ATEN_START || eventSubType == EventSubType::ATEN_END) ? PTAPidStatesMap : ATBPidStatesMap;
Init(event->pid, pidStatesMap);
HandleOpLaunchEvent(event, pidStatesMap);
return;
}
if (eventType == EventBaseType::MALLOC || eventType == EventBaseType::FREE || eventType == EventBaseType::ACCESS) {
if ((event->poolType != PoolType::PTA_CACHING && event->poolType != PoolType::ATB)) {
return;
}
std::unordered_map<uint64_t, PidState>& pidStatesMap =
(event->poolType == PoolType::PTA_CACHING) ? PTAPidStatesMap : ATBPidStatesMap;
Init(event->pid, pidStatesMap);
HandleMemoryEvent(event, state, pidStatesMap);
}
}
void InefficientAnalyzer::Init(const uint64_t pid, std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
if (pidStatesMap.find(pid) == pidStatesMap.end()) {
pidStatesMap[pid].apiId = 0;
pidStatesMap[pid].mallocApiTmpId = MAX_UNIT64;
pidStatesMap[pid].freeApiTmpId = MAX_UNIT64;
pidStatesMap[pid].isOpStart = false;
}
}
void InefficientAnalyzer::HandleOpLaunchEvent(std::shared_ptr<EventBase>& event, std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
const auto eventSubType = event->eventSubType;
const uint64_t pid = event->pid;
auto& pidState = pidStatesMap[pid];
if (eventSubType == EventSubType::ATB_START || eventSubType == EventSubType::ATEN_START) {
pidState.isOpStart = true;
UpdateApiId(pid, pidStatesMap);
return;
}
if (eventSubType == EventSubType::ATB_END || eventSubType == EventSubType::ATEN_END) {
pidState.isOpStart = false;
ClassifyEventsTmp(pid, pidStatesMap);
return;
}
}
void InefficientAnalyzer::HandleMemoryEvent(std::shared_ptr<EventBase>& event,
MemoryState* state,
std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
const uint64_t pid = event->pid;
auto& pidState = pidStatesMap[pid];
auto memEvent = std::dynamic_pointer_cast<MemoryEvent>(event);
if (!memEvent || !state) {
return;
}
if (!pidState.isOpStart) {
UpdateApiId(pid, pidStatesMap);
AddEventToTmps(memEvent, pidStatesMap);
AddApiIdToState(memEvent, state, pidStatesMap);
ClassifyEventsTmp(pid, pidStatesMap);
} else {
AddEventToTmps(memEvent, pidStatesMap);
AddApiIdToState(memEvent, state, pidStatesMap);
}
InefficientAnalysis(memEvent, state, pidStatesMap);
}
void InefficientAnalyzer::ClassifyEventsTmp(const uint64_t pid, std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
auto& pidState = pidStatesMap[pid];
if (pidState.apiTmp.empty()) {
return;
}
std::vector<std::shared_ptr<MemoryEvent>> local_events;
local_events = std::move(pidState.apiTmp);
bool hasMalloc = false;
bool hasFree = false;
for (const auto& event : local_events) {
if (event->eventType == EventBaseType::MALLOC) {
hasMalloc = true;
}
if (event->eventType == EventBaseType::FREE) {
hasFree = true;
}
}
if (hasMalloc) {
pidState.mallocApiTmpId = pidState.apiId;
}
if (hasFree) {
pidState.freeApiTmpId = pidState.apiId;
}
}
void InefficientAnalyzer::UpdateApiId(const uint64_t pid, std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
pidStatesMap[pid].apiId.fetch_add(1, std::memory_order_relaxed);
}
void InefficientAnalyzer::AddEventToTmps(const std::shared_ptr<MemoryEvent>& event,
std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
pidStatesMap[event->pid].apiTmp.push_back(event);
}
void InefficientAnalyzer::AddApiIdToState(std::shared_ptr<MemoryEvent>& event,
MemoryState* state,
std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
state->apiId.push_back(pidStatesMap[event->pid].apiId);
}
void InefficientAnalyzer::InefficientAnalysis(std::shared_ptr<MemoryEvent>& event,
MemoryState* state,
std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
if (state->events.at(0)->eventType != EventBaseType::MALLOC || state->events.size() != state->apiId.size()) {
return;
}
if (event->eventType == EventBaseType::ACCESS) {
TemporaryIdleness(event, state);
if (pidStatesMap[event->pid].freeApiTmpId == MAX_UNIT64) {
return;
}
EarlyAllocation(event, state, pidStatesMap);
}
if (event->eventType == EventBaseType::FREE && pidStatesMap[event->pid].mallocApiTmpId != MAX_UNIT64) {
LateDeallocation(event, state, pidStatesMap);
}
}
void InefficientAnalyzer::EarlyAllocation(std::shared_ptr<MemoryEvent>& event,
MemoryState* state,
std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
if (state->inefficientType.find("early_allocation") != std::string::npos) {
return;
}
uint64_t eventsLen = state->events.size();
uint64_t firstAccessApiId = MAX_UNIT64;
uint64_t mallocApiId = 0;
auto& pidState = pidStatesMap[event->pid];
for (uint64_t i = 0; i < eventsLen; i++) {
if (firstAccessApiId < MAX_UNIT64) {
break;
}
if (state->events[i]->eventType == EventBaseType::MALLOC) {
mallocApiId = state->apiId[i];
} else if (state->events[i]->eventType == EventBaseType::ACCESS &&
state->apiId[i] != mallocApiId) {
firstAccessApiId = state->apiId[i];
}
}
if (firstAccessApiId == MAX_UNIT64 || mallocApiId == pidState.freeApiTmpId) {
return;
}
if (firstAccessApiId > pidState.freeApiTmpId && mallocApiId < pidState.freeApiTmpId) {
if (!state->inefficientType.empty()) {
state->inefficientType += ",";
}
state->inefficientType += "early_allocation";
}
}
void InefficientAnalyzer::LateDeallocation(std::shared_ptr<MemoryEvent>& event,
MemoryState* state,
std::unordered_map<uint64_t, PidState>& pidStatesMap)
{
uint64_t eventsLen = state->events.size();
uint64_t lastAccessApiId = MAX_UNIT64;
if (eventsLen >= MIN_EVENTS_NUM) {
if (state->events[eventsLen - MIN_EVENTS_NUM]->eventType == EventBaseType::ACCESS) {
lastAccessApiId = state->apiId[eventsLen - MIN_EVENTS_NUM];
}
}
auto& pidState = pidStatesMap[event->pid];
if (lastAccessApiId == MAX_UNIT64 || lastAccessApiId == pidState.apiId) {
return;
}
if (lastAccessApiId < pidState.mallocApiTmpId && pidState.apiId > pidState.mallocApiTmpId) {
if (!state->inefficientType.empty()) {
state->inefficientType += ",";
}
state->inefficientType += "late_deallocation";
}
}
void InefficientAnalyzer::TemporaryIdleness(std::shared_ptr<MemoryEvent>& event, MemoryState* state)
{
if (state->inefficientType.find("temporary_idleness") != std::string::npos) {
return;
}
uint64_t eventLen = state->events.size();
if (eventLen < MIN_EVENTS_NUM || state->events[eventLen - MIN_EVENTS_NUM]->eventType != EventBaseType::ACCESS) {
return;
}
if (state->apiId[eventLen - LAST_EVENTS_NUM] > state->apiId[eventLen - MIN_EVENTS_NUM] &&
state->apiId[eventLen - LAST_EVENTS_NUM] - state->apiId[eventLen - MIN_EVENTS_NUM] > THREHOLD) {
if (!state->inefficientType.empty()) {
state->inefficientType += ",";
}
state->inefficientType += "temporary_idleness";
}
}
void InefficientAnalyzer::Subscribe()
{
auto func = std::bind(&InefficientAnalyzer::EventHandle, this, std::placeholders::_1, std::placeholders::_2);
std::vector<EventBaseType> eventTypes{
EventBaseType::MALLOC,
EventBaseType::ACCESS,
EventBaseType::FREE,
EventBaseType::OP_LAUNCH};
EventDispatcher::GetInstance().Subscribe(
SubscriberId::INEFFICIENT_ANALYZER, eventTypes, EventDispatcher::Priority::High, func);
}
void InefficientAnalyzer::UnSubscribe() const
{
EventDispatcher::GetInstance().UnSubscribe(SubscriberId::INEFFICIENT_ANALYZER);
}
}