Avatar ·

Как определить потребление CPU и памяти изнутри процесса на C++?

Всем привет! Пишу на C++ уже около года, и столкнулся с задачей, которая поставила меня в тупик. Мне нужно, чтобы моя программа (сервер для обработки изображений) в реальном времени отслеживала, сколько она потребляет памяти и CPU. Проблема в том, что я не понимаю, как получить эти данные изнутри самого процесса. Например, я хочу выводить в лог что-то вроде: "Пиковое потребление памяти: 150 МБ, загрузка CPU: 45%". Я пытался использовать простые переменные вроде `sizeof()` для объектов, но это не даёт реальной картины — ведь память выделяется кучами (heap), плюс есть накладные расходы на стек и системные ресурсы. С CPU ещё сложнее: я могу замерить время выполнения куска кода с помощью `clock()`, но это время только моего потока, а не всей программы, и не учитывает время ожидания ввода-вывода. Подскажите, пожалуйста, есть ли стандартный способ или библиотеки, которые позволяют получить эти метрики изнутри процесса? Желательно кроссплатформенно, но хотя бы для Linux и Windows. Привожу пример моего текущего (неправильного) подхода: ```cpp #include #include int main() { clock_t start = clock(); // долгая операция for (int i = 0; i < 1000000; ++i) {} clock_t end = clock(); double cpu_time = double(end - start) / CLOCKS_PER_SEC; std::cout << "CPU time: " << cpu_time << " seconds" << std::endl; return 0; } ``` Этот код показывает только время выполнения, а не общую загрузку CPU процессом. Как быть? <<>>

Системные вызовы: /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.