Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions module/common/include/oops/str.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ class SplitView {
}

bool operator==(const Iterator &other) const {
// 短路和迭代器的比较
// 尾迭代器参与的快速比较
if ((token_.data() == nullptr) ^ (other.token_.data() == nullptr)) {
return false; // 一个迭代器是尾迭代器,另一个不是
}
if (token_.data() == nullptr) {
return true; // 均是尾迭代器
}
// 非尾迭代器的严格匹配
// 两个正常迭代器的严格比较
return (token_.data() == other.token_.data()) && (token_.size() == other.token_.size()) &&
(s_.data() == other.s_.data()) && (s_.size() == other.s_.size()) && (delim_ == other.delim_) &&
(any_of_delims_ == other.any_of_delims_) && (skip_empty_ == other.skip_empty_);
Expand Down
41 changes: 35 additions & 6 deletions module/profiling/include/oops/cpu_timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ inline auto GetTicksPerSec() {
class CpuTimer {
struct CpuTicks {
std::uintmax_t TotalTicks() const { return user_ticks + kernel_ticks; };
std::uintmax_t user_ticks;
std::uintmax_t kernel_ticks;
std::uintmax_t user_ticks{};
std::uintmax_t kernel_ticks{};
};

using CpuTimePoint = std::variant<struct timespec, CpuTicks, std::monostate>;
Expand All @@ -34,10 +34,17 @@ class CpuTimer {
double elapsed_time{};
};

static constexpr pid_t SYSTEM{-1};
static std::string GetPath(pid_t pid) {
if (pid == SYSTEM) {
return "/proc/stat";
}
return "/proc/" + std::to_string(pid) + "/stat";
}

CpuTimer() : cpu_t0_{GetCpuTimePoint()}, elapsed_t0_{std::chrono::steady_clock::now()} {}
CpuTimer(pid_t pid)
: stat_path_{"/proc/" + std::to_string(pid) + "/stat"}, cpu_t0_{GetCpuTimePoint()},
elapsed_t0_{std::chrono::steady_clock::now()} {}
: stat_path_{GetPath(pid)}, cpu_t0_{GetCpuTimePoint()}, elapsed_t0_{std::chrono::steady_clock::now()} {}

void Reset() {
cpu_t0_ = GetCpuTimePoint();
Expand Down Expand Up @@ -76,20 +83,42 @@ class CpuTimer {
return spec;
}

// 文件快照(/proc/pid/stat)获取cpu时间,Ticks级精度(约10ms)
// 文件快照(/proc/*/stat)获取进程或system cpu时间,Ticks级精度(约10ms)
// 解析效率要求高,使用栈缓冲
char buf[512];
std::FILE *f{std::fopen(stat_path_.c_str(), "r")};
if (!f) {
return std::monostate{}; // 无法打开文件,返回空类型
}

auto res{std::fgets(buf, sizeof(buf), f)};
std::fclose(f);
if (res == nullptr) {
return std::monostate{}; // 读取失败,返回空类型
}

if (stat_path_.size() == 10) {
// 文件快照(/proc/stat)获取系统级cpu时间
char *p{buf};
while ((*p < '0') || (*p > '9')) {
++p;
}
CpuTicks cpu_ticks;
cpu_ticks.user_ticks += std::strtoull(p, &p, 10); // 匹配第1个字段user
cpu_ticks.user_ticks += std::strtoull(p + 1, &p, 10); // 匹配第2个字段nice

cpu_ticks.kernel_ticks += std::strtoull(p + 1, &p, 10); // 匹配第3个字段system

// 跳过第4个字段idle和第5个字段iowait
int rem{2};
while (rem-- > 0) {
p = std::strchr(p + 1, ' ');
assert(p != nullptr);
}

cpu_ticks.kernel_ticks += std::strtoull(p + 1, &p, 10); // 匹配第6个字段irq
cpu_ticks.kernel_ticks += std::strtoull(p + 1, &p, 10); // 匹配第7个字段softirq
return cpu_ticks;
}
// 查找第2个字段的右括号
char *p{std::strrchr(buf, ')')};
assert(p != nullptr);
Expand Down
64 changes: 31 additions & 33 deletions module/profiling/tool/monitor/monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ void Measure() {
std::cout << MakeHeaderRow() << std::endl;

std::vector<double> values(oops::ToUnderlying(Metrics::COUNT));
while (!STOP.load(std::memory_order_relaxed) && ProcExist(ARGS.measure.pid)) {
while (!STOP.load(std::memory_order_relaxed) && (ProcExist(ARGS.measure.pid) || ARGS.measure.pid == -1)) {
std::this_thread::sleep_until(next_time);

// 根据选项配置完成测量
Expand Down Expand Up @@ -433,9 +433,6 @@ void AlignTimeline() {
double range_l_fidx{std::ceil(std::chrono::duration<double>{range_l - raw_l}.count()) / r_entry.interval};
double range_r_fidx{std::ceil(std::chrono::duration<double>{range_r - raw_l}.count()) / r_entry.interval};

std::cout << "lidx: " << range_l_fidx << std::endl;
std::cout << "ridx: " << range_r_fidx << std::endl;

// 计算range对应的下标,左闭右开
std::size_t range_l_idx{std::max<std::ptrdiff_t>(range_l_fidx, 0)};
std::size_t range_r_idx{std::min<std::ptrdiff_t>(range_r_fidx, size)};
Expand Down Expand Up @@ -469,78 +466,77 @@ void AlignTimeline() {
}
}

void PlotCpu() {
if (RAW_DATA_TABLE.empty()) {
return;
}
auto GetMetricEntries(Metrics m) {
struct Entries {
RawDataEntry &r_entry;
MetricDataEntry &m_entry;
};

// 识别记录了CPU利用率的raw data idx及对应CPU利用率的metric data dix
std::vector<std::pair<std::size_t, std::size_t>> valid_idxs;
for (std::size_t i{0}; i < RAW_DATA_TABLE.size(); ++i) {
const auto &r_entry{RAW_DATA_TABLE[i]};
for (std::size_t j{0}; j < r_entry.metric_data_table.size(); ++j) {
const auto &m_entry{r_entry.metric_data_table.at(j)};
if (m_entry.name == METRIC_TABLE[oops::ToUnderlying(Metrics::CPU_USAGE)].name && !m_entry.values.empty()) {
valid_idxs.emplace_back(i, j);
std::vector<Entries> res;
for (auto &r_entry : RAW_DATA_TABLE) {
for (auto &m_entry : r_entry.metric_data_table) {
if (m_entry.name == METRIC_TABLE[oops::ToUnderlying(m)].name && !m_entry.values.empty()) {
res.push_back({r_entry, m_entry});
}
}
}
if (valid_idxs.empty()) {
return res;
}

void PlotCpu() {
assert(!RAW_DATA_TABLE.empty());

auto entries{GetMetricEntries(Metrics::CPU_USAGE)};
if (entries.empty()) {
return;
}

// 根据downsample_rate,合并数据点,cpu利用率采取平均值
for (const auto &[r_idx, m_idx] : valid_idxs) {
auto &r_entry{RAW_DATA_TABLE[r_idx]};
for (const auto &[r_entry, m_entry] : entries) {
if (r_entry.downsample_rate <= 1) {
// 不需要合并
continue;
}

std::cout << r_entry.downsample_rate << std::endl;

// 计算合并后的新数据点数
auto &m_entry{r_entry.metric_data_table[m_idx]};
std::size_t new_i{0};
for (std::size_t i{0}; i + r_entry.downsample_rate - 1 < m_entry.values.size(); i += r_entry.downsample_rate) {
double sum{};
for (std::size_t j{i}; j < i + r_entry.downsample_rate; j++) {
sum += m_entry.values[j];
}
m_entry.values[new_i++] = sum / r_entry.downsample_rate;
std::cout << "avg: " << sum / r_entry.downsample_rate << std::endl;
}
m_entry.values.resize(new_i);
r_entry.interval *= r_entry.downsample_rate;
}

// 最大数值
double max_value{std::numeric_limits<double>::min()};
for (const auto &[r_idx, m_idx] : valid_idxs) {
for (double value : RAW_DATA_TABLE[r_idx].metric_data_table[m_idx].values) {
for (const auto &e : entries) {
for (double value : e.m_entry.values) {
max_value = std::max(max_value, value);
}
}
std::cout << "max_value: " << max_value << std::endl;

py::scoped_interpreter guard{}; // 必须放在try-catch块外侧,否则无法捕获py::error_already_set异常
try {
auto plt{py::module_::import("matplotlib.pyplot")};
auto subplots{plt.attr("subplots")(
valid_idxs.size(), 1, py::arg("sharex") = true,
entries.size(), 1, py::arg("sharex") = true,
py::arg("figsize") = py::make_tuple(
16, std::max<std::size_t>(std::min<std::size_t>(9, 3 * valid_idxs.size()), valid_idxs.size())))};
16, std::max<std::size_t>(std::min<std::size_t>(9, 3 * entries.size()), entries.size())))};

py::list axes; // 存储所有子图,使用vector反而会有拷贝和引用计数开销
if (valid_idxs.size() == 1) {
if (entries.size() == 1) {
axes.append(subplots[py::int_(1)]);
} else {
axes = subplots[py::int_(1)].cast<py::list>();
}

for (std::size_t i{0}; i < valid_idxs.size(); ++i) {
const auto &r_entry{RAW_DATA_TABLE[valid_idxs[i].first]};
const auto &m_entry{r_entry.metric_data_table[valid_idxs[i].second]};
for (std::size_t i{0}; i < entries.size(); ++i) {
const auto &r_entry{entries[i].r_entry};
const auto &m_entry{entries[i].m_entry};
double offset{std::chrono::duration<double>{r_entry.start_time - TOTAL_START_TIME}.count()};

// 构造时间数组,由于offset差异,每个raw data需要单独构造
Expand All @@ -557,7 +553,6 @@ void PlotCpu() {
ax.attr("margins")(py::arg("x") = 0);
ax.attr("grid")(true, py::arg("linestyle") = "--", py::arg("alpha") = 0.5);
double ylim{std::max(100., max_value)};
std::cout << "ylim " << ylim << std::endl;
ax.attr("set_ylim")(-ylim / 20, ylim + ylim / 20); // 对齐每个子图y坐标范围
// 绘制每个子图左侧描述raw data的标签
ax.attr("set_ylabel")(
Expand Down Expand Up @@ -592,6 +587,9 @@ void Report() {

// 对齐时间轴,range裁剪,分辨率缩放
AlignTimeline();
if (RAW_DATA_TABLE.empty()) {
return;
}

// 绘制CPU利用率折线图
PlotCpu();
Expand Down
Loading