From a6d136f0c34f6bb3de6b86ce7fe3ab608bd61283 Mon Sep 17 00:00:00 2001 From: verdant Date: Wed, 22 Jul 2026 14:44:38 +0800 Subject: Implement Exponential Moving Average algorithm For solving that CPU status updating is so fast, use EMA to calc a smooth value --- stat.c | 340 +++++++++++++++++++++++++++++++++++++---------------------------- stat.h | 70 +++++++------- 2 files changed, 229 insertions(+), 181 deletions(-) diff --git a/stat.c b/stat.c index c0d94a3..17f9af3 100644 --- a/stat.c +++ b/stat.c @@ -12,190 +12,238 @@ #include "log.h" #include "mem.h" -stat* stat_init() { - stat* s = xmalloc(sizeof(stat)); - *s = (stat){ - .sysver = "", - .meminfo = {.mem_available = 0, .mem_total = 0}, - .diskinfo = {.disk_available = 0, .disk_total = 0}, - }; - - return s; +stat *stat_init() +{ + stat *s = xmalloc(sizeof(stat)); + *s = (stat){ + .sysver = "", + .meminfo = {.mem_available = 0, .mem_total = 0}, + .diskinfo = {.disk_available = 0, .disk_total = 0}, + }; + s->cpu_occ = xmalloc(sizeof(cpu_occupy)); + + if (s->cpu_occ) + *s->cpu_occ = (cpu_occupy){0}; + return s; } -void stat_get_sysver(stat* s) { - FILE* fp = fopen(PROC_PATH_VERSION, "r"); - if (!fp) die("Cannot open %s", PROC_PATH_VERSION); +void stat_get_sysver(stat *s) +{ + FILE *fp = fopen(PROC_PATH_VERSION, "r"); + if (!fp) + die("Cannot open %s", PROC_PATH_VERSION); - if (fgets(s->sysver, sizeof(s->sysver), fp) != NULL) { - s->sysver[strcspn(s->sysver, "\n")] = '\0'; - } - fclose(fp); + if (fgets(s->sysver, sizeof(s->sysver), fp) != NULL) { + s->sysver[strcspn(s->sysver, "\n")] = '\0'; + } + fclose(fp); } -void stat_print_cpu_info(WINDOW* w, int y, int maxx, stat* s) { - mvwprintw(w, y, maxx - STAT_INFO_WIDTH, " %.2f %%", s->cpu_occupy.cpu); +void stat_print_cpu_info(WINDOW *w, int y, int maxx, stat *s) +{ + mvwprintw(w, y, maxx - STAT_INFO_WIDTH, " %.2f %%", + s->cpu_occ->smooth_cpu); } -void stat_print_info(WINDOW* w, int y, int maxx, const char* label, - unsigned long total, unsigned long ava) { - unsigned long long t_bytes = total; - unsigned long long a_bytes = ava; +void stat_print_info(WINDOW *w, int y, int maxx, const char *label, + unsigned long total, unsigned long ava) +{ + unsigned long long t_bytes = total; + unsigned long long a_bytes = ava; - if (strcmp(label, "Mem") == 0 || strcmp(label, "Mem ") == 0) { - t_bytes *= 1024; - a_bytes *= 1024; - } + if (strcmp(label, "Mem") == 0 || strcmp(label, "Mem ") == 0) { + t_bytes *= 1024; + a_bytes *= 1024; + } - const char* units[] = {"B", "K", "M", "G", "T"}; + const char *units[] = {"B", "K", "M", "G", "T"}; - int t_idx = 0; - int a_idx = 0; + int t_idx = 0; + int a_idx = 0; - double d_total = (double)t_bytes; - double d_ava = (double)a_bytes; + double d_total = (double)t_bytes; + double d_ava = (double)a_bytes; - while (d_total >= 1024.0 && t_idx < 4) { - d_total /= 1024.0; - t_idx++; - } + while (d_total >= 1024.0 && t_idx < 4) { + d_total /= 1024.0; + t_idx++; + } - while (d_ava >= 1024.0 && a_idx < 4) { - d_ava /= 1024.0; - a_idx++; - } + while (d_ava >= 1024.0 && a_idx < 4) { + d_ava /= 1024.0; + a_idx++; + } - int startx = maxx - STAT_INFO_WIDTH; + int startx = maxx - STAT_INFO_WIDTH; - mvwprintw(w, y, startx, " %.2f%s/%.2f%s", d_ava, units[t_idx], d_total, - units[t_idx]); + mvwprintw(w, y, startx, " %.2f%s/%.2f%s", d_ava, units[t_idx], d_total, + units[t_idx]); - /* wrefresh(w); */ + /* wrefresh(w); */ } int stat_get_bar_ticks(int maxx, unsigned long long total, - unsigned long long ava) { - if (total == 0) return 0; - if (ava >= total) return 0; + unsigned long long ava) +{ + if (total == 0) + return 0; + if (ava >= total) + return 0; - unsigned long long used = total - ava; + unsigned long long used = total - ava; - unsigned long long slots = (unsigned long long)(maxx); + unsigned long long slots = (unsigned long long)(maxx); - return (int)((slots * used) / total); + return (int)((slots * used) / total); } -void stat_draw_cpu_bar(WINDOW* w, int maxx, int y, int x, stat* s) { - int ava_to_draw_x = maxx - STAT_INFO_WIDTH - BAR_LABEL_WIDTH; - int total_hashes = ava_to_draw_x * s->cpu_occupy.cpu; - log_write(LOG_INFO, "stat_draw_cpu_bar: ava_to_draw = %d", ava_to_draw_x); - log_write(LOG_INFO, "s->cpu_occupy.cpu = %f", s->cpu_occupy.cpu); - log_write(LOG_INFO, "CPU Hashes = %d", total_hashes); - char hashes[2048] = {0}; +void stat_draw_cpu_bar(WINDOW *w, int maxx, int y, int x, stat *s) +{ + int ava_to_draw_x = maxx - STAT_INFO_WIDTH - BAR_LABEL_WIDTH; + int total_hashes = ava_to_draw_x * (s->cpu_occ->smooth_cpu * 0.1); - if (total_hashes > ava_to_draw_x) total_hashes = ava_to_draw_x; - memset(hashes, '#', total_hashes); - hashes[total_hashes] = '\0'; + char hashes[2048] = {0}; - mvwprintw(w, y, x, "%-*s [", 4, "CPU"); - wprintw_colorful(w, COLOR_PAIR_PROGRESS, "%.*s", total_hashes, hashes); + if (total_hashes > ava_to_draw_x) + total_hashes = ava_to_draw_x; + memset(hashes, '#', total_hashes); + hashes[total_hashes] = '\0'; - int remaining_spaces = ava_to_draw_x - total_hashes; - if (remaining_spaces > 0) { - wprintw(w, "%*s", remaining_spaces, ""); - } + mvwprintw(w, y, x, "%-*s [", 4, "CPU"); + wprintw_colorful(w, COLOR_PAIR_PROGRESS, "%.*s", total_hashes, hashes); - waddch(w, ']'); -} + int remaining_spaces = ava_to_draw_x - total_hashes; + if (remaining_spaces > 0) { + wprintw(w, "%*s", remaining_spaces, ""); + } -void stat_draw_bar(WINDOW* w, const char* label, int maxx, int y, int x, - unsigned long total, unsigned long ava) { - int ava_to_draw_x = maxx - STAT_INFO_WIDTH - BAR_LABEL_WIDTH; - int total_hashes = stat_get_bar_ticks(ava_to_draw_x, total, ava); - char hashes[2048] = {0}; + waddch(w, ']'); +} - init_pair(COLOR_PAIR_PROGRESS, COLOR_GREEN, COLOR_BLACK); +void stat_draw_bar(WINDOW *w, const char *label, int maxx, int y, int x, + unsigned long total, unsigned long ava) +{ + int ava_to_draw_x = maxx - STAT_INFO_WIDTH - BAR_LABEL_WIDTH; + int total_hashes = stat_get_bar_ticks(ava_to_draw_x, total, ava); + char hashes[2048] = {0}; - if (total_hashes > ava_to_draw_x) total_hashes = ava_to_draw_x; - memset(hashes, '#', total_hashes); - hashes[total_hashes] = '\0'; + init_pair(COLOR_PAIR_PROGRESS, COLOR_GREEN, COLOR_BLACK); - mvwprintw(w, y, x, "%-*s [", 4, label); - wprintw_colorful(w, COLOR_PAIR_PROGRESS, "%.*s", total_hashes, hashes); + if (total_hashes > ava_to_draw_x) + total_hashes = ava_to_draw_x; + memset(hashes, '#', total_hashes); + hashes[total_hashes] = '\0'; - int remaining_spaces = ava_to_draw_x - total_hashes; - if (remaining_spaces > 0) { - wprintw(w, "%*s", remaining_spaces, ""); - } + mvwprintw(w, y, x, "%-*s [", 4, label); + wprintw_colorful(w, COLOR_PAIR_PROGRESS, "%.*s", total_hashes, hashes); - waddch(w, ']'); + int remaining_spaces = ava_to_draw_x - total_hashes; + if (remaining_spaces > 0) { + wprintw(w, "%*s", remaining_spaces, ""); + } - /* wrefresh(w); */ -} + waddch(w, ']'); -void stat_get_meminfo(stat* s) { - static char buf[128]; - char key[32] = ""; - unsigned long long val = 0; - - FILE* fp = fopen(PROC_PATH_MEMINFO, "r"); - if (!fp) die("Cannot open %s", PROC_PATH_MEMINFO); - - while (fgets(buf, sizeof(buf), fp)) { - if (sscanf(buf, "%31[^:]: %llu", key, &val) == 2) { - if (strcmp(key, "MemTotal") == 0) { - s->meminfo.mem_total = val; - } else if (strcmp(key, "MemAvailable") == 0) { - s->meminfo.mem_available = val; - break; - } - } - } - - fclose(fp); + /* wrefresh(w); */ } -void stat_get_diskinfo(stat* s) { - struct statvfs vfs; - if (statvfs("/", &vfs) != 0) { - s->diskinfo.disk_total = 0; - s->diskinfo.disk_available = 0; - log_write(LOG_WARN, "Cannot get disk info"); - } - - s->diskinfo.disk_total = (unsigned long long)vfs.f_blocks * vfs.f_frsize; - s->diskinfo.disk_available = - (unsigned long long)vfs.f_bavail * vfs.f_frsize; +void stat_get_meminfo(stat *s) +{ + static char buf[128]; + char key[32] = ""; + unsigned long long val = 0; + + FILE *fp = fopen(PROC_PATH_MEMINFO, "r"); + if (!fp) + die("Cannot open %s", PROC_PATH_MEMINFO); + + while (fgets(buf, sizeof(buf), fp)) { + if (sscanf(buf, "%31[^:]: %llu", key, &val) == 2) { + if (strcmp(key, "MemTotal") == 0) { + s->meminfo.mem_total = val; + } else if (strcmp(key, "MemAvailable") == 0) { + s->meminfo.mem_available = val; + break; + } + } + } + + fclose(fp); } -void stat_get_cpuinfo(stat* s) { - char buf[128]; - FILE* fp = fopen(PROC_PATH_STAT, "r"); - if (!fp) { - log_write(LOG_WARN, "Cannot open %s", PROC_PATH_STAT); - return; - } - - fgets(buf, sizeof(buf), fp); - sscanf(buf, "%s %llu %llu %llu %llu", s->cpu_occupy.name, - &s->cpu_occupy.user, &s->cpu_occupy.nice, &s->cpu_occupy.system, - &s->cpu_occupy.idle); - fclose(fp); +void stat_get_diskinfo(stat *s) +{ + struct statvfs vfs; + if (statvfs("/", &vfs) != 0) { + s->diskinfo.disk_total = 0; + s->diskinfo.disk_available = 0; + log_write(LOG_WARN, "Cannot get disk info"); + } + + s->diskinfo.disk_total = + (unsigned long long)vfs.f_blocks * vfs.f_frsize; + s->diskinfo.disk_available = + (unsigned long long)vfs.f_bavail * vfs.f_frsize; } -void stat_calc_cpuocc(stat* s) { - cpu_occupy* co = xmalloc(sizeof(cpu_occupy)); - stat_get_cpuinfo(s); - co->idle = s->cpu_occupy.idle; - co->nice = s->cpu_occupy.nice; - co->system = s->cpu_occupy.system; - co->user = s->cpu_occupy.user; - usleep(200000); - stat_get_cpuinfo(s); - unsigned long long t1 = co->user + co->nice + co->system + co->idle; - unsigned long long t2 = s->cpu_occupy.user + s->cpu_occupy.nice + - s->cpu_occupy.system + s->cpu_occupy.idle; - unsigned long long user = s->cpu_occupy.user - co->user; - unsigned long long system = s->cpu_occupy.system - co->system; - - s->cpu_occupy.cpu = (user + system) * 1.0 / (t2 - t1); - free(co); +void stat_get_cpuinfo(stat *s) +{ + char buf[256]; + FILE *fp = fopen(PROC_PATH_STAT, "r"); + if (!fp) { + log_write(LOG_WARN, "Cannot open %s", PROC_PATH_STAT); + return; + } + + if (!fgets(buf, sizeof(buf), fp)) { + fclose(fp); + return; + } + fclose(fp); + + unsigned long long user = 0, nice = 0, system = 0, idle = 0; + unsigned long long iowait = 0, irq = 0, softirq = 0; + + sscanf(buf, "%*s %llu %llu %llu %llu %llu %llu %llu", &user, &nice, + &system, &idle, &iowait, &irq, &softirq); + + s->cpu_occ->user = user; + s->cpu_occ->nice = nice; + s->cpu_occ->system = system; + s->cpu_occ->idle = idle; + s->cpu_occ->iowait = iowait; + s->cpu_occ->irq = irq; + s->cpu_occ->softirq = softirq; + + unsigned long long curr_idle = idle + iowait; + unsigned long long curr_total = + user + nice + system + idle + iowait + irq + softirq; + + unsigned long long total_diff = curr_total - s->cpu_occ->prev_total; + unsigned long long idle_diff = curr_idle - s->cpu_occ->prev_idle; + + if (total_diff > 0) { + s->cpu_occ->cpu = (double)(total_diff - idle_diff) / + (double)total_diff * 100.0; + } else { + s->cpu_occ->cpu = 0.0; // 防零除 + } + + s->cpu_occ->prev_total = curr_total; + s->cpu_occ->prev_idle = curr_idle; + + /* EMA - Exponential Moving Average */ + /* DisplayCPU = alpha * raw_cpu + (1 - alpha) * pre_cpu */ + double alpha = 0.1; + if (total_diff > 0) { + double raw_cpu = (double)(total_diff - idle_diff) / + (double)total_diff * 100.0; + if (s->cpu_occ->smooth_cpu == 0.0) { + s->cpu_occ->smooth_cpu = raw_cpu; + } else { + s->cpu_occ->smooth_cpu = + alpha * raw_cpu + + (1 - alpha) * s->cpu_occ->smooth_cpu; + } + + s->cpu_occ->cpu = raw_cpu; + } } diff --git a/stat.h b/stat.h index 81d84cc..5b14226 100644 --- a/stat.h +++ b/stat.h @@ -12,46 +12,46 @@ #define STAT_BAR_HEIGHT 1 typedef struct { - char name[16]; - unsigned int user; /* User mode */ - unsigned int nice; /* Low-priority user mode */ - unsigned int system; /* Kernel mode */ - unsigned int idle; /* Free time of CPU */ + char name[16]; + unsigned int user; /* User mode */ + unsigned int nice; /* Low-priority user mode */ + unsigned int system; /* Kernel mode */ + unsigned int idle; /* Free time of CPU */ + unsigned long long iowait; + unsigned long long irq; + unsigned long long softirq; + + unsigned long long prev_total; + unsigned long long prev_idle; + double cpu; + double smooth_cpu; } cpu_occupy; typedef struct { - char sysver[30]; - struct { - unsigned long mem_total; - unsigned long mem_available; - } meminfo; - struct { - unsigned long long disk_total; - unsigned long long disk_available; - } diskinfo; - struct { - char name[16]; - unsigned long long user; /* User mode */ - unsigned long long nice; /* Low-priority user mode */ - unsigned long long system; /* Kernel mode */ - unsigned long long idle; /* Free time of CPU */ - double cpu; - } cpu_occupy; + char sysver[30]; + struct { + unsigned long mem_total; + unsigned long mem_available; + } meminfo; + struct { + unsigned long long disk_total; + unsigned long long disk_available; + } diskinfo; + cpu_occupy *cpu_occ; } stat; -void stat_get_sysver(stat* s); -stat* stat_init(); -void stat_print_info(WINDOW* w, int y, int maxx, const char* label, - unsigned long total, unsigned long ava); -void stat_print_cpu_info(WINDOW* w, int y, int maxx, stat* s); -void stat_draw_bar(WINDOW* w, const char* label, int maxx, int y, int x, - unsigned long total, unsigned long ava); -void stat_draw_cpu_bar(WINDOW* w, int maxx, int y, int x, stat* s); -void stat_get_meminfo(stat* s); -void stat_get_diskinfo(stat* s); -void stat_get_cpuinfo(stat* s); -void stat_calc_cpuocc(stat* s); +void stat_get_sysver(stat *s); +stat *stat_init(); +void stat_print_info(WINDOW *w, int y, int maxx, const char *label, + unsigned long total, unsigned long ava); +void stat_print_cpu_info(WINDOW *w, int y, int maxx, stat *s); +void stat_draw_bar(WINDOW *w, const char *label, int maxx, int y, int x, + unsigned long total, unsigned long ava); +void stat_draw_cpu_bar(WINDOW *w, int maxx, int y, int x, stat *s); +void stat_get_meminfo(stat *s); +void stat_get_diskinfo(stat *s); +void stat_get_cpuinfo(stat *s); int stat_get_bar_ticks(int maxx, unsigned long long total, - unsigned long long ava); + unsigned long long ava); #endif /* SF_STAT_H */ -- cgit v1.2.3