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_extra.c — lo que las dos sorpresas piden aislar:
2 * a) coste de fork EN FUNCION del espacio de direcciones del padre
3 * b) contabilidad por cgroup v2 vs sumar /proc
4 * c) tasa del enlazado dinamico en el exec
5 * d) cierre de descriptores en el spawn */
6#define _GNU_SOURCE
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11#include <fcntl.h>
12#include <time.h>
13#include <errno.h>
14#include <spawn.h>
15#include <dirent.h>
16#include <sched.h>
17#include <sys/syscall.h>
18#include <sys/wait.h>
19#include <sys/mman.h>
20#include <sys/stat.h>
21
22extern char **environ;
23static volatile long sink;
24static inline double now_ns(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec; }
25static int cmpd(const void*a,const void*b){ double x=*(const double*)a,y=*(const double*)b; return (x>y)-(x<y); }
26#define ROUNDS 7
27static void rep(const char*n,double*r,int c){ qsort(r,c,sizeof(double),cmpd);
28 printf("%-44s %10.1f %10.1f\n",n,r[0],r[c/2]); fflush(stdout); }
29
30static double *area=NULL; static long area_sz=0;
31static void grow(long mb){
32 if(area) munmap(area,area_sz);
33 area_sz=mb*1024L*1024; if(mb==0){area=NULL;return;}
34 area=mmap(0,area_sz,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
35 madvise(area,area_sz,MADV_NOHUGEPAGE); /* tablas de paginas reales, no THP */
36 for(long o=0;o<area_sz;o+=4096) ((char*)area)[o]=1;
37}
38static double t_fork(int n){ double t0=now_ns();
39 for(int i=0;i<n;i++){ pid_t p=fork(); if(p==0)_exit(0); int st; waitpid(p,&st,0);} return (now_ns()-t0)/n/1000; }
40static double t_vfork_exec(int n,const char*prog){ double t0=now_ns();
41 for(int i=0;i<n;i++){ pid_t p=vfork(); if(p==0){char*a[]={(char*)prog,0};execve(prog,a,environ);_exit(127);} int st; waitpid(p,&st,0);} return (now_ns()-t0)/n/1000; }
42static double t_fork_exec(int n,const char*prog){ double t0=now_ns();
43 for(int i=0;i<n;i++){ pid_t p=fork(); if(p==0){char*a[]={(char*)prog,0};execve(prog,a,environ);_exit(127);} int st; waitpid(p,&st,0);} return (now_ns()-t0)/n/1000; }
44static double t_spawn(int n,const char*prog){ double t0=now_ns();
45 for(int i=0;i<n;i++){ pid_t p; char*a[]={(char*)prog,0}; if(posix_spawn(&p,prog,0,0,a,environ)==0){int st;waitpid(p,&st,0);} } return (now_ns()-t0)/n/1000; }
46
47int main(int argc,char**argv){
48 const char *sta = argc>1?argv[1]:"/bin/true";
49 const char *dyn = argc>2?argv[2]:"/bin/true";
50 printf("%-44s %10s %10s\n","BENCH (us/op)","min","mediana");
51 printf("--- a) fork segun el espacio de direcciones del padre ---\n");
52 long mbs[]={0,16,64,256};
53 for(unsigned k=0;k<sizeof mbs/sizeof*mbs;k++){
54 grow(mbs[k]); double r[ROUNDS],r2[ROUNDS],r3[ROUNDS];
55 for(int q=0;q<ROUNDS;q++) r[q]=t_fork(150);
56 for(int q=0;q<ROUNDS;q++) r2[q]=t_fork_exec(120,sta);
57 for(int q=0;q<ROUNDS;q++) r3[q]=t_spawn(120,sta);
58 char n1[80],n2[80],n3[80];
59 snprintf(n1,sizeof n1," padre %3ld MB tocados: fork+_exit",mbs[k]);
60 snprintf(n2,sizeof n2," padre %3ld MB tocados: fork+exec",mbs[k]);
61 snprintf(n3,sizeof n3," padre %3ld MB tocados: posix_spawn(CLONE_VFORK)",mbs[k]);
62 rep(n1,r,ROUNDS); rep(n2,r2,ROUNDS); rep(n3,r3,ROUNDS); }
63 grow(0);
64
65 printf("--- c) tasa del enlazado dinamico en exec ---\n");
66 { double r[ROUNDS]; for(int q=0;q<ROUNDS;q++) r[q]=t_vfork_exec(150,sta); rep(" vfork+exec binario ESTATICO",r,ROUNDS);
67 for(int q=0;q<ROUNDS;q++) r[q]=t_vfork_exec(150,dyn); rep(" vfork+exec binario DINAMICO",r,ROUNDS); }
68
69 printf("--- d) cierre de descriptores antes del exec ---\n");
70 { int base=open("/dev/null",O_RDONLY); int fds[512]; for(int i=0;i<512;i++) fds[i]=dup(base);
71 double r[ROUNDS];
72 for(int q=0;q<ROUNDS;q++){ double t0=now_ns(); for(int i=0;i<512;i++){int f=dup(base); close(f);} r[q]=(now_ns()-t0)/512/1000; }
73 rep(" dup+close x512, uno a uno (us/fd)",r,ROUNDS);
74 for(int q=0;q<ROUNDS;q++){ int a[512]; for(int i=0;i<512;i++) a[i]=dup(base);
75 double t0=now_ns(); sink=syscall(SYS_close_range,a[0],a[511],0); r[q]=(now_ns()-t0)/512/1000; }
76 rep(" close_range(lo,hi) (us/fd)",r,ROUNDS);
77 for(int i=0;i<512;i++) close(fds[i]); close(base); }
78
79 printf("--- b) contabilidad: cgroup v2 vs sumar /proc ---\n");
80 { char cg[512]="", path[600]; FILE*f=fopen("/proc/self/cgroup","r");
81 if(f){ char line[512]; while(fgets(line,sizeof line,f)){ char*p=strstr(line,"0::"); if(p){ p+=3; p[strcspn(p,"\n")]=0; snprintf(cg,sizeof cg,"/sys/fs/cgroup%s",p);} } fclose(f);}
82 if(cg[0]){ printf(" cgroup: %s\n",cg);
83 const char*campos[]={"cpu.stat","memory.current","pids.current"};
84 double r[ROUNDS];
85 for(int q=0;q<ROUNDS;q++){ int N=2000; double t0=now_ns();
86 for(int i=0;i<N;i++){ for(int c=0;c<3;c++){ snprintf(path,sizeof path,"%s/%s",cg,campos[c]);
87 int fd=open(path,O_RDONLY); if(fd<0)continue; char b[512]; sink=read(fd,b,sizeof b); close(fd);} }
88 r[q]=(now_ns()-t0)/N/1000; }
89 rep(" leer cpu.stat+memory.current+pids.current",r,ROUNDS);
90 } else printf(" sin cgroup v2 legible\n"); }
91 return 0;
92}