Как определить потребление CPU и памяти изнутри процесса на C++?
Системные вызовы: /proc/self/stat (Linux) и GetProcessTimes (Windows)
Ваша проблема типична для начинающих разработчиков. clock() измеряет только процессорное время текущего потока, что не даёт полной картины. Для получения реальных метрик CPU и памяти изнутри процесса нужно использовать системные API. Рассмотрим два основных подхода для Linux и Windows.
1. Linux: чтение /proc/self/stat и /proc/self/status
В Linux виртуальная файловая система /proc предоставляет информацию о каждом процессе. Файл /proc/self/stat содержит данные о CPU, а /proc/self/status — о памяти. Пример получения загрузки CPU:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <unistd.h>
double get_cpu_usage() {
std::ifstream stat_file("/proc/self/stat");
std::string line;
std::getline(stat_file, line);
std::istringstream iss(line);
// Пропускаем первые 13 полей (pid, имя, состояние и т.д.)
std::string tmp;
for (int i = 0; i < 13; ++i) iss >> tmp;
long utime, stime; // пользовательское и системное время (в тиках)
iss >> utime >> stime;
long ticks_per_sec = sysconf(_SC_CLK_TCK);
double total_time = (utime + stime) / (double)ticks_per_sec;
// Для получения загрузки за интервал нужно два замера
return total_time;
}
long get_memory_usage() {
std::ifstream status_file("/proc/self/status");
std::string line;
while (std::getline(status_file, line)) {
if (line.find("VmRSS:") != std::string::npos) {
// VmRSS — физическая память, используемая процессом
std::istringstream iss(line);
std::string key;
long value;
std::string unit;
iss >> key >> value >> unit;
return value; // в килобайтах
}
}
return 0;
}
int main() {
std::cout << "Memory: " << get_memory_usage() << " KB" << std::endl;
double start = get_cpu_usage();
// Ваш код
for (int i = 0; i < 100000000; ++i) {}
double end = get_cpu_usage();
std::cout << "CPU time used: " << end - start << " seconds" << std::endl;
return 0;
}
2. Windows: GetProcessTimes и GetProcessMemoryInfo
На Windows используйте WinAPI. Для памяти потребуется библиотека Psapi.h:
#include <windows.h>
#include <psapi.h>
#include <iostream>
double get_cpu_usage() {
FILETIME create_time, exit_time, kernel_time, user_time;
GetProcessTimes(GetCurrentProcess(), &create_time, &exit_time, &kernel_time, &user_time);
// Преобразуем FILETIME в 64-битное значение (100-наносекундные интервалы)
ULARGE_INTEGER kt, ut;
kt.LowPart = kernel_time.dwLowDateTime;
kt.HighPart = kernel_time.dwHighDateTime;
ut.LowPart = user_time.dwLowDateTime;
ut.HighPart = user_time.dwHighDateTime;
double total_time = (kt.QuadPart + ut.QuadPart) / 10000000.0; // в секунды
return total_time;
}
size_t get_memory_usage() {
PROCESS_MEMORY_COUNTERS pmc;
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
return pmc.WorkingSetSize; // в байтах
}
int main() {
std::cout << "Memory: " << get_memory_usage() / 1024 << " KB" << std::endl;
double start = get_cpu_usage();
// Ваш код
for (int i = 0; i < 100000000; ++i) {}
double end = get_cpu_usage();
std::cout << "CPU time used: " << end - start << " seconds" << std::endl;
return 0;
}
3. Кроссплатформенное решение с препроцессором
Объедините оба подхода с помощью условной компиляции:
#ifdef _WIN32
// Windows код
#else
// Linux код
#endif
Для более сложных метрик (например, процент загрузки CPU за интервал) нужно делать два замера с паузой и вычислять разницу, делённую на реальное время. Помните: эти методы не показывают «процент» напрямую — они дают процессорное время, которое нужно нормировать.
- VmRSS — физическая память, занятая процессом (реальная, без свопа).
- utime + stime — общее процессорное время (пользовательское + системное).
- WorkingSetSize — аналог VmRSS на Windows.
Этот подход надёжен и используется в production-системах. Для учебных целей он идеален, но для продакшена рассмотрите готовые библиотеки, например, getrusage() в Linux.
Альтернатива: библиотека getrusage (Linux) и QueryProcessCycleTime (Windows)
Если вам не хочется парсить текстовые файлы, можно использовать системные вызовы напрямую. В Linux функция getrusage() из sys/resource.h возвращает структуру rusage, содержащую процессорное время и другие метрики:
#include <sys/resource.h>
#include <iostream>
double get_cpu_time() {
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
double user = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1e6;
double sys = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1e6;
return user + sys;
}
Для памяти можно использовать /proc/self/status или mallinfo() (если используете malloc). На Windows есть QueryProcessCycleTime(), которая дает количество тактов процессора, затраченных процессом. Это более точный способ для CPU, чем GetProcessTimes, так как он не зависит от системных часов. Однако для памяти всё равно придётся использовать GetProcessMemoryInfo.
Минус getrusage — он не даёт процент загрузки, только абсолютное время. Для вычисления процента нужно замерить интервал реального времени (например, через clock_gettime) и разделить процессорное время на реальное. Этот подход проще в реализации, но менее гибок, чем чтение /proc.