sergio/takana
> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
SDD 25: las tasas del kernel medidas, y el MEMCG que falta en las recetas
1/* bench_proc.c — piso de syscall, control de procesos, /proc, IPC, scheduler.
2 * Método: R rondas; se reporta MIN (mejor caso, robusto al ruido) y MEDIANA. */
3#define _GNU_SOURCE
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8#include <fcntl.h>
9#include <time.h>
10#include <errno.h>
11#include <signal.h>
12#include <sched.h>
13#include <pthread.h>
14#include <spawn.h>
15#include <dirent.h>
16#include <sys/syscall.h>
17#include <sys/wait.h>
18#include <sys/resource.h>
19#include <sys/eventfd.h>
20#include <sys/socket.h>
21#include <sys/stat.h>
22
23extern char **environ;
24static volatile long sink;
25
26static inline double now_ns(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec; }
27static int cmpd(const void*a,const void*b){ double x=*(const double*)a,y=*(const double*)b; return (x>y)-(x<y); }
28
29#define ROUNDS 9
30static void report(const char*name,double*r,int n,long it){
31 qsort(r,n,sizeof(double),cmpd);
32 printf("%-38s %10.1f %10.1f (n=%ld)\n",name,r[0],r[n/2],it); fflush(stdout);
33}
34#define BENCH(name,iters,...) do{ double _r[ROUNDS]; long _N=(iters); \
35 for(int _q=0;_q<ROUNDS;_q++){ double _t0=now_ns(); for(long i=0;i<_N;i++){ __VA_ARGS__; } \
36 _r[_q]=(now_ns()-_t0)/(double)_N; } report(name,_r,ROUNDS,_N);}while(0)
37
38/* ---------- helpers ---------- */
39static void pin(int cpu){ cpu_set_t s; CPU_ZERO(&s); CPU_SET(cpu,&s); sched_setaffinity(0,sizeof s,&s); }
40
41static long read_stat_utime(int fd){
42 char buf[1024]; ssize_t n=pread(fd,buf,sizeof buf-1,0); if(n<=0) return -1; buf[n]=0;
43 char *p=strrchr(buf,')'); if(!p) return -1; p+=2;
44 int f=1; long v=0; while(*p&&f<12){ if(*p==' ')f++; p++; }
45 v=strtol(p,NULL,10); return v;
46}
47static long parse_vmrss(void){
48 int fd=open("/proc/self/status",O_RDONLY); if(fd<0) return -1;
49 char buf[4096]; ssize_t n=read(fd,buf,sizeof buf-1); close(fd); if(n<=0)return -1; buf[n]=0;
50 char*p=strstr(buf,"VmRSS:"); return p?strtol(p+6,NULL,10):-1;
51}
52
53static volatile sig_atomic_t got;
54static void h(int s){ (void)s; got=1; }
55
56/* RTT entre dos hilos pinneados al MISMO cpu */
57struct rtt { int a[2], b[2]; int kind; long iters; };
58static void rd1(int fd,int kind){ if(kind==2){uint64_t v;(void)!read(fd,&v,8);} else {char c;(void)!read(fd,&c,1);} }
59static void wr1(int fd,int kind){ if(kind==2){uint64_t v=1;(void)!write(fd,&v,8);} else {char c=1;(void)!write(fd,&c,1);} }
60static void*echo(void*p){ struct rtt*r=p; pin(3);
61 for(long i=0;i<r->iters;i++){ rd1(r->a[0],r->kind); wr1(r->b[1],r->kind); } return 0; }
62
63static double rtt_bench(int kind,long iters){
64 struct rtt r; r.kind=kind; r.iters=iters;
65 if(kind==0){ if(pipe(r.a)||pipe(r.b)) return -1; }
66 else if(kind==1){ if(socketpair(AF_UNIX,SOCK_STREAM,0,r.a)||socketpair(AF_UNIX,SOCK_STREAM,0,r.b)) return -1; }
67 else { r.a[0]=r.a[1]=eventfd(0,0); r.b[0]=r.b[1]=eventfd(0,0); }
68 pthread_t t; pthread_create(&t,0,echo,&r); pin(3);
69 double best=1e18;
70 for(int q=0;q<3;q++){ long n=iters/3; double t0=now_ns();
71 for(long i=0;i<n;i++){ wr1(r.a[1],kind); rd1(r.b[0],kind); }
72 double v=(now_ns()-t0)/n; if(v<best)best=v; }
73 pthread_join(t,0);
74 return best;
75}
76
77int main(void){
78 pin(3);
79 printf("%-38s %10s %10s\n","BENCH (ns/op)","min","mediana");
80 printf("--- A. piso de syscall ---\n");
81 BENCH("getppid (syscall nulo)", 300000, sink=syscall(SYS_getppid));
82 BENCH("clock_gettime MONOTONIC (vDSO)", 300000, {struct timespec t;clock_gettime(CLOCK_MONOTONIC,&t);sink=t.tv_nsec;});
83 BENCH("clock_gettime MONOTONIC (syscall)", 200000, {struct timespec t;syscall(SYS_clock_gettime,CLOCK_MONOTONIC,&t);sink=t.tv_nsec;});
84 BENCH("getpid (vDSO no; glibc syscall)", 300000, sink=getpid());
85 BENCH("sched_yield", 200000, sched_yield());
86
87 printf("--- B. contabilidad de proceso propio ---\n");
88 BENCH("getrusage(RUSAGE_SELF)", 200000, {struct rusage u;getrusage(RUSAGE_SELF,&u);sink=u.ru_utime.tv_usec;});
89 BENCH("clock_gettime(PROCESS_CPUTIME)", 200000, {struct timespec t;clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t);sink=t.tv_nsec;});
90 { int fd=open("/proc/self/stat",O_RDONLY);
91 BENCH("pread+parse /proc/self/stat (fd abierto)", 100000, sink=read_stat_utime(fd));
92 close(fd); }
93 BENCH("open+read+parse /proc/self/stat", 50000, {int fd=open("/proc/self/stat",O_RDONLY);sink=read_stat_utime(fd);close(fd);});
94 BENCH("open+read+parse /proc/self/status(RSS)",30000, sink=parse_vmrss());
95
96 printf("--- C. liveness de otro proceso ---\n");
97 { pid_t me=getpid(); char p[64]; snprintf(p,sizeof p,"/proc/%d",me);
98 int pfd=syscall(SYS_pidfd_open,me,0);
99 BENCH("access(/proc/<pid>) [lo que se usa hoy]",200000, sink=access(p,F_OK));
100 BENCH("kill(pid,0)", 300000, sink=kill(me,0));
101 if(pfd>=0){ BENCH("pidfd_send_signal(fd,0) [sin carrera]",300000, sink=syscall(SYS_pidfd_send_signal,pfd,0,NULL,0)); }
102 else printf("pidfd_send_signal: no disponible (%s)\n",strerror(errno));
103 BENCH("pidfd_open+close", 100000, {int f=syscall(SYS_pidfd_open,me,0); if(f>=0)close(f);});
104 if(pfd>=0) close(pfd); }
105
106 printf("--- D. barrido de /proc (enumeracion) ---\n");
107 { int nproc=0;
108 double r1[ROUNDS],r2[ROUNDS];
109 for(int q=0;q<ROUNDS;q++){
110 double t0=now_ns(); int c=0;
111 DIR*d=opendir("/proc"); struct dirent*e;
112 while((e=readdir(d))){ if(e->d_name[0]<'0'||e->d_name[0]>'9')continue; c++; }
113 closedir(d); r1[q]=now_ns()-t0; nproc=c;
114 t0=now_ns(); d=opendir("/proc");
115 while((e=readdir(d))){ if(e->d_name[0]<'0'||e->d_name[0]>'9')continue;
116 char p[512]; snprintf(p,sizeof p,"/proc/%s/stat",e->d_name);
117 int fd=open(p,O_RDONLY); if(fd<0)continue; sink=read_stat_utime(fd); close(fd); }
118 closedir(d); r2[q]=now_ns()-t0; }
119 qsort(r1,ROUNDS,sizeof(double),cmpd); qsort(r2,ROUNDS,sizeof(double),cmpd);
120 printf("procesos vivos = %d\n",nproc);
121 printf("%-38s %10.1f %10.1f (us/barrido)\n","solo readdir(/proc)",r1[0]/1000,r1[ROUNDS/2]/1000);
122 printf("%-38s %10.1f %10.1f (us/barrido)\n","readdir + stat de cada pid",r2[0]/1000,r2[ROUNDS/2]/1000);
123 printf("%-38s %10.1f %10.1f (ns/proceso)\n"," => coste por proceso",r2[0]/nproc,r2[ROUNDS/2]/nproc); }
124
125 printf("--- E. creacion de procesos ---\n");
126 BENCH("fork + _exit + waitpid", 400, {pid_t p=fork(); if(p==0)_exit(0); int st;waitpid(p,&st,0);});
127 BENCH("vfork + execve(/bin/true) + wait", 300, {pid_t p=vfork(); if(p==0){char*a[]={"/bin/true",0};execve(a[0],a,environ);_exit(127);} int st;waitpid(p,&st,0);});
128 { BENCH("posix_spawn(/bin/true) + wait", 300, {pid_t p;char*a[]={"/bin/true",0};if(posix_spawn(&p,a[0],0,0,a,environ)==0){int st;waitpid(p,&st,0);} }); }
129
130 printf("--- F. IPC: round-trip entre 2 hilos (mismo cpu) ---\n");
131 { pthread_mutex_t m=PTHREAD_MUTEX_INITIALIZER;
132 BENCH("pthread_mutex lock+unlock (sin disputa)",1000000,{pthread_mutex_lock(&m);pthread_mutex_unlock(&m);}); }
133 printf("%-38s %10.1f (RTT)\n","pipe read/write", rtt_bench(0,60000));
134 printf("%-38s %10.1f (RTT)\n","socketpair AF_UNIX", rtt_bench(1,60000));
135 printf("%-38s %10.1f (RTT)\n","eventfd", rtt_bench(2,60000));
136 { signal(SIGUSR1,h);
137 BENCH("kill(self,SIGUSR1) + handler", 200000, {got=0;kill(getpid(),SIGUSR1);sink=got;}); }
138
139 printf("--- G. temporizadores ---\n");
140 { double r[ROUNDS];
141 for(int q=0;q<ROUNDS;q++){ struct timespec req={0,1000000},rem; double t0=now_ns();
142 for(int i=0;i<20;i++) nanosleep(&req,&rem);
143 r[q]=(now_ns()-t0)/20 - 1000000.0; }
144 qsort(r,ROUNDS,sizeof(double),cmpd);
145 printf("%-38s %10.1f %10.1f (sobrecoste ns sobre 1ms)\n","nanosleep(1ms) overshoot",r[0],r[ROUNDS/2]); }
146 return 0;
147}