Как измерить CPU и память изнутри процесса на C++: полный гайд

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Слепая зона производительности: почему ваш процесс не знает, сколько он ест?

Представьте: вы написали сложное приложение на C++, оно работает, но начинает тормозить. Вы запускаете top, htop или диспетчер задач — и видите, что ваш процесс пожирает 90% CPU или гигабайты памяти. Но как заставить сам процесс осознать это и, например, динамически снизить нагрузку или сбросить кэш?

Это не просто академический интерес. В production-среде, на серверах без GUI, вы не можете полагаться на внешние утилиты. Нужно, чтобы программа сама следила за собой. Именно эту задачу решает популярный вопрос на Stack Overflow: "How to determine CPU and memory consumption from inside a process". Давайте разберем его на реальных примерах на C++.

Кроссплатформенная дилемма: Linux vs Windows

Операционные системы по-разному предоставляют доступ к статистике процессов. Под капотом Linux лежит виртуальная файловая система /proc, а Windows использует Win32 API. Мы напишем универсальный класс, который работает на обеих платформах.

Основные метрики, которые мы будем добывать:

  • Использование CPU (в процентах или долях от ядра)
  • Физическая память (RSS) — реально занятая RAM
  • Виртуальная память (VSZ) — общий объем адресного пространства

Считываем CPU на Linux: магия /proc/self/stat

Для получения времени CPU процесса читаем файл /proc/self/stat. Он содержит десятки полей, нас интересуют поля 13-16 (utime, stime, cutime, cstime) — время в тиках (jiffies). Чтобы перевести в проценты, нужно знать частоту тиков (sysconf(_SC_CLK_TCK)) и общее время работы системы.

#ifdef __linux__
#include <unistd.h>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

double getCpuUsageLinux() {
    static unsigned long long prevTotal = 0, prevIdle = 0;
    static double prevProcessTicks = 0;
    static bool first = true;

    // Время процесса
    std::ifstream procStat("/proc/self/stat");
    std::string line;
    std::getline(procStat, line);
    std::istringstream iss(line);
    std::vector<std::string> fields;
    std::string field;
    while (iss >> field) fields.push_back(field);

    unsigned long utime = std::stoul(fields[13]);
    unsigned long stime = std::stoul(fields[14]);
    unsigned long cutime = std::stoul(fields[15]);
    unsigned long cstime = std::stoul(fields[16]);
    double processTicks = utime + stime + cutime + cstime;

    // Системное время
    std::ifstream statFile("/proc/stat");
    std::getline(statFile, line);
    statFile.close();
    std::istringstream iss2(line);
    std::string cpuLabel;
    iss2 >> cpuLabel;
    unsigned long long user, nice, system, idle, iowait, irq, softirq, steal;
    iss2 >> user >> nice >> system >> idle >> iowait >> irq >> softirq >> steal;
    unsigned long long total = user + nice + system + idle + iowait + irq + softirq + steal;

    if (first) {
        prevTotal = total;
        prevIdle = idle;
        prevProcessTicks = processTicks;
        first = false;
        return 0.0;
    }

    unsigned long long totalDelta = total - prevTotal;
    double processDelta = processTicks - prevProcessTicks;
    double cpuPercent = (processDelta / totalDelta) * 100.0;

    prevTotal = total;
    prevIdle = idle;
    prevProcessTicks = processTicks;

    return cpuPercent;
}
#endif

Этот код вычисляет процент CPU, занятый процессом с момента первого вызова. Обратите внимание: мы берем разницу между двумя замерами — это стандартный подход для точности.

Память на Linux: RSS и VSZ

Для памяти читаем /proc/self/status. Нас интересуют строки VmRSS и VmSize.

#ifdef __linux__
#include <fstream>
#include <string>

struct MemoryInfo {
    long vmPeak;  // пиковая виртуальная
    long vmSize;  // текущая виртуальная
    long vmRSS;   // физическая в RAM
    long vmData;  // данные (heap + mmap)
};

MemoryInfo getMemoryUsageLinux() {
    MemoryInfo info = {0,0,0,0};
    std::ifstream statusFile("/proc/self/status");
    std::string line;
    while (std::getline(statusFile, line)) {
        if (line.find("VmPeak:") == 0) sscanf(line.c_str(), "VmPeak: %ld kB", &info.vmPeak);
        else if (line.find("VmSize:") == 0) sscanf(line.c_str(), "VmSize: %ld kB", &info.vmSize);
        else if (line.find("VmRSS:") == 0) sscanf(line.c_str(), "VmRSS: %ld kB", &info.vmRSS);
        else if (line.find("VmData:") == 0) sscanf(line.c_str(), "VmData: %ld kB", &info.vmData);
    }
    return info;
}
#endif

Важно: Все значения в килобайтах. VmRSS — это реальное потребление RAM, а VmSize — виртуальное адресное пространство (может быть огромным из-за memory-mapped файлов).

Windows: Win32 API и структуры PROCESS_MEMORY_COUNTERS

На Windows подход другой — используем GetProcessMemoryInfo из psapi.h и GetSystemTimes для CPU.

#ifdef _WIN32
#include <windows.h>
#include <psapi.h>

double getCpuUsageWindows() {
    static ULARGE_INTEGER prevKernel, prevUser, prevIdle;
    static double prevProcessTime = 0;
    static bool first = true;

    FILETIME ftCreation, ftExit, ftKernel, ftUser;
    GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, &ftKernel, &ftUser);

    ULARGE_INTEGER kernel, user;
    kernel.LowPart = ftKernel.dwLowDateTime;
    kernel.HighPart = ftKernel.dwHighDateTime;
    user.LowPart = ftUser.dwLowDateTime;
    user.HighPart = ftUser.dwHighDateTime;
    double processTime = (kernel.QuadPart + user.QuadPart) / 10000.0; // в миллисекундах

    FILETIME ftIdle, ftKernelSys, ftUserSys;
    GetSystemTimes(&ftIdle, &ftKernelSys, &ftUserSys);
    ULARGE_INTEGER idle, kernelSys, userSys;
    idle.LowPart = ftIdle.dwLowDateTime;
    idle.HighPart = ftIdle.dwHighDateTime;
    kernelSys.LowPart = ftKernelSys.dwLowDateTime;
    kernelSys.HighPart = ftKernelSys.dwHighDateTime;
    userSys.LowPart = ftUserSys.dwLowDateTime;
    userSys.HighPart = ftUserSys.dwHighDateTime;

    double totalSys = (                

Блоги

Рекомендаций по книгам