* -------------------------------------------------------------------------
* 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.
* -------------------------------------------------------------------------
*/
#ifndef PROFILER_SERVER_SEARCHSLICECACHEMANAGER_H
#define PROFILER_SERVER_SEARCHSLICECACHEMANAGER_H
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <shared_mutex>
#include <functional>
#include <numeric>
#include <algorithm>
#include <list>
#include "TimelineParamStrcut.h"
namespace Dic {
namespace Module {
namespace Timeline {
enum class SliceTableType : int8_t {
TASK = 0,
CANN_API = 1,
PYTORCH_API = 2,
COMMUNICATION_OP = 3,
MSTX = 4,
UNKNOWN = -1
};
struct TargetRow {
SliceTableType tableType;
uint64_t rowId;
};
struct PagedResult {
std::vector<TargetRow> rows;
uint64_t totalCount;
};
struct LightSliceCache {
std::string rankId;
std::string primarySearchContent;
std::unordered_map<int32_t, std::string> dictMap;
std::vector<int8_t> tableTypes;
std::vector<int32_t> stringIds;
std::vector<uint64_t> rowIds;
std::vector<uint64_t> timestamps;
std::vector<uint64_t> durations;
std::vector<uint32_t> sortedIndices;
std::string orderBy;
std::string order;
void clear() {
rankId.clear();
primarySearchContent.clear();
dictMap.clear();
tableTypes.clear();
stringIds.clear();
rowIds.clear();
timestamps.clear();
durations.clear();
sortedIndices.clear();
orderBy.clear();
order.clear();
}
bool isDataValid(const std::string &rId, const std::string &content) const {
return rankId == rId && content == primarySearchContent;
}
bool isOrderValid(const std::string &orderField, const std::string &orderDirection) const {
return orderBy == orderField && order == orderDirection;
}
size_t size() const { return tableTypes.size(); }
};
class CacheGuard {
public:
CacheGuard() : cache_(nullptr) {}
CacheGuard(std::shared_mutex &mutex,
std::unordered_map<std::string, std::pair<LightSliceCache, std::list<std::string>::iterator>> &caches,
const std::string &key)
: lock_(mutex), cache_(nullptr)
{
auto it = caches.find(key);
if (it != caches.end()) {
cache_ = &it->second.first;
}
}
CacheGuard(const CacheGuard &) = delete;
CacheGuard &operator=(const CacheGuard &) = delete;
CacheGuard(CacheGuard &&) = default;
CacheGuard &operator=(CacheGuard &&) = default;
LightSliceCache *get() const { return cache_; }
LightSliceCache &operator*() const { return *cache_; }
LightSliceCache *operator->() const { return cache_; }
explicit operator bool() const { return cache_ != nullptr; }
private:
std::shared_lock<std::shared_mutex> lock_;
LightSliceCache *cache_;
};
class SearchSliceCacheManager {
private:
static constexpr size_t MAX_CACHE_SIZE = 5;
mutable std::shared_mutex cacheMutex_;
std::list<std::string> lruList_;
std::unordered_map<std::string, std::pair<LightSliceCache, std::list<std::string>::iterator>> caches_;
SearchSliceCacheManager() = default;
SearchSliceCacheManager(const SearchSliceCacheManager &) = delete;
SearchSliceCacheManager &operator=(const SearchSliceCacheManager &) = delete;
public:
static SearchSliceCacheManager &Instance() {
static SearchSliceCacheManager instance;
return instance;
}
CacheGuard get(const std::string &key) {
return CacheGuard(cacheMutex_, caches_, key);
}
CacheGuard getOrCreate(const std::string &key) {
{
std::unique_lock<std::shared_mutex> writeLock(cacheMutex_);
auto it = caches_.find(key);
if (it != caches_.end()) {
auto listIt = it->second.second;
lruList_.splice(lruList_.begin(), lruList_, listIt);
} else {
while (caches_.size() >= MAX_CACHE_SIZE) {
std::string oldestKey = lruList_.back();
lruList_.pop_back();
caches_.erase(oldestKey);
}
lruList_.push_front(key);
caches_.emplace(key, std::make_pair(LightSliceCache(), lruList_.begin()));
}
}
return CacheGuard(cacheMutex_, caches_, key);
}
void clear(const std::string &key) {
std::unique_lock<std::shared_mutex> writeLock(cacheMutex_);
auto it = caches_.find(key);
if (it != caches_.end()) {
lruList_.erase(it->second.second);
caches_.erase(it);
}
}
void clearAll() {
std::unique_lock<std::shared_mutex> writeLock(cacheMutex_);
caches_.clear();
lruList_.clear();
}
bool exists(const std::string &key) const {
std::shared_lock<std::shared_mutex> readLock(cacheMutex_);
return caches_.find(key) != caches_.end();
}
size_t size() const {
std::shared_lock<std::shared_mutex> readLock(cacheMutex_);
return caches_.size();
}
static std::string makeKey(const std::string &rankId, const std::string &searchContent) {
return rankId + "|" + searchContent;
}
static void InitializeSortedIndices(LightSliceCache &cache) {
cache.sortedIndices.resize(cache.size());
std::iota(cache.sortedIndices.begin(), cache.sortedIndices.end(), 0);
}
static void SortCache(LightSliceCache &cache, const std::string &orderBy, const std::string &order) {
if (cache.isOrderValid(orderBy, order)) {
return;
}
InitializeSortedIndices(cache);
std::function<bool(size_t, size_t)> comparator;
if (orderBy == "name") {
comparator = [&](size_t a, size_t b) {
const std::string &nameA = cache.dictMap[cache.stringIds[a]];
const std::string &nameB = cache.dictMap[cache.stringIds[b]];
return order == "ascend" ? nameA < nameB : nameA > nameB;
};
} else if (orderBy == "timestamp") {
comparator = [&](size_t a, size_t b) {
return order == "ascend" ? cache.timestamps[a] < cache.timestamps[b]
: cache.timestamps[a] > cache.timestamps[b];
};
} else if (orderBy == "duration") {
comparator = [&](size_t a, size_t b) {
return order == "ascend" ? cache.durations[a] < cache.durations[b]
: cache.durations[a] > cache.durations[b];
};
} else {
return;
}
std::sort(cache.sortedIndices.begin(), cache.sortedIndices.end(), comparator);
cache.orderBy = orderBy;
cache.order = order;
}
static std::unordered_set<int32_t> GetMatchedStringIds(
const LightSliceCache &cache, const std::string &nameFilter) {
std::unordered_set<int32_t> result;
if (nameFilter.empty()) {
return result;
}
std::string lowerFilter = nameFilter;
std::transform(lowerFilter.begin(), lowerFilter.end(), lowerFilter.begin(), ::tolower);
for (const auto &[id, name] : cache.dictMap) {
std::string lowerName = name;
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
if (lowerName.find(lowerFilter) != std::string::npos) {
result.insert(id);
}
}
return result;
}
static PagedResult FilterAndPage(
LightSliceCache &cache, const std::string &nameFilter, uint64_t current, uint64_t pageSize) {
PagedResult result;
result.totalCount = 0;
if (current == 0 || pageSize == 0) {
return result;
}
std::unordered_set<int32_t> matchedStringIds;
if (!nameFilter.empty()) {
matchedStringIds = GetMatchedStringIds(cache, nameFilter);
if (matchedStringIds.empty()) {
return result;
}
}
uint64_t offset = (current - 1) * pageSize;
uint64_t limit = pageSize;
for (size_t i = 0; i < cache.sortedIndices.size(); ++i) {
uint32_t realIdx = cache.sortedIndices[i];
bool matches = nameFilter.empty() || matchedStringIds.count(cache.stringIds[realIdx]) > 0;
if (matches) {
if (result.totalCount >= offset && result.totalCount < offset + limit) {
TargetRow row;
row.tableType = static_cast<SliceTableType>(cache.tableTypes[realIdx]);
row.rowId = cache.rowIds[realIdx];
result.rows.push_back(row);
}
result.totalCount++;
}
}
return result;
}
};
}
}
}
#endif