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…
⎇ 84e3737e10e17927380ed3e232e50cebfdd39123
docs/evidencia/tasas-kernel-2026-08-29/bench_io.c
Historia
Quién tocó qué
crudo
SDD 25: las tasas del kernel medidas, y el MEMCG que falta en las recetas
1/* bench_io.c — metadatos de FS, resolucion de rutas, copia, io_uring, fallos de pagina, namespaces. */
2#define _GNU_SOURCE
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7#include <fcntl.h>
8#include <time.h>
9#include <errno.h>
10#include <sched.h>
11#include <sys/syscall.h>
12#include <sys/stat.h>
13#include <sys/mman.h>
14#include <sys/wait.h>
15#include <sys/sendfile.h>
16#include <dirent.h>
17#include <linux/openat2.h>
18#include <liburing.h>
19
20extern char **environ;
21static volatile long sink;
22static inline double now_ns(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec; }
23static int cmpd(const void*a,const void*b){ double x=*(const double*)a,y=*(const double*)b; return (x>y)-(x<y); }
24#define ROUNDS 7
25static void report(const char*n,double*r,int c,long it){ qsort(r,c,sizeof(double),cmpd);
26 printf("%-40s %10.1f %10.1f (n=%ld)\n",n,r[0],r[c/2],it); fflush(stdout); }
27#define BENCH(name,iters,...) do{ double _r[ROUNDS]; long _N=(iters); \
28 for(int _q=0;_q<ROUNDS;_q++){ double _t0=now_ns(); for(long i=0;i<_N;i++){ __VA_ARGS__; } \
29 _r[_q]=(now_ns()-_t0)/(double)_N; } report(name,_r,ROUNDS,_N);}while(0)
30
31#define NFILES 4000
32static char root[512];
33
34static void mk_tree(const char *base){
35 mkdir(base,0755);
36 char p[600]; snprintf(p,sizeof p,"%s/flat",base); mkdir(p,0755);
37 for(int i=0;i<NFILES;i++){ char f[700]; snprintf(f,sizeof f,"%s/flat/f%05d",base,i);
38 int fd=open(f,O_CREAT|O_WRONLY|O_TRUNC,0644); if(fd>=0){ (void)!write(fd,"x",1); close(fd);} }
39 /* ruta profunda: base/d/d/d/... (12 niveles) con un fichero al fondo */
40 char deep[600]; snprintf(deep,sizeof deep,"%s/deep",base); mkdir(deep,0755);
41 for(int i=0;i<12;i++){ strcat(deep,"/d"); mkdir(deep,0755); }
42 char df[700]; snprintf(df,sizeof df,"%s/x",deep);
43 int fd=open(df,O_CREAT|O_WRONLY,0644); if(fd>=0){(void)!write(fd,"x",1);close(fd);}
44}
45static void rm_tree(const char *base){ char c[700]; snprintf(c,sizeof c,"rm -rf '%s'",base); (void)!system(c); }
46
47static void metadata(const char *base,const char *etiqueta){
48 char flat[600]; snprintf(flat,sizeof flat,"%s/flat",base);
49 int dfd=open(flat,O_RDONLY|O_DIRECTORY);
50 printf("\n### metadatos sobre %s (%d ficheros en un directorio)\n",etiqueta,NFILES);
51 printf("%-40s %10s %10s\n","BENCH (ns/fichero)","min","mediana");
52 BENCH("stat() por ruta completa", NFILES, {struct stat s;char f[700];snprintf(f,sizeof f,"%s/f%05d",flat,(int)(i%NFILES));sink=stat(f,&s);});
53 BENCH("fstatat(dirfd, nombre)", NFILES, {struct stat s;char f[32];snprintf(f,sizeof f,"f%05d",(int)(i%NFILES));sink=fstatat(dfd,f,&s,0);});
54 BENCH("statx(dirfd,nombre,DONT_SYNC)", NFILES, {struct statx s;char f[32];snprintf(f,sizeof f,"f%05d",(int)(i%NFILES));sink=syscall(SYS_statx,dfd,f,AT_STATX_DONT_SYNC,STATX_BASIC_STATS,&s);});
55 BENCH("openat(dirfd,nombre)+close", NFILES, {char f[32];snprintf(f,sizeof f,"f%05d",(int)(i%NFILES));int fd=openat(dfd,f,O_RDONLY);if(fd>=0)close(fd);});
56 { struct open_how how; memset(&how,0,sizeof how); how.flags=O_RDONLY; how.resolve=RESOLVE_BENEATH|RESOLVE_NO_SYMLINKS;
57 int probe=syscall(SYS_openat2,dfd,"f00000",&how,sizeof how);
58 if(probe>=0){ close(probe);
59 BENCH("openat2 RESOLVE_BENEATH+NO_SYMLINKS",NFILES,{char f[32];snprintf(f,sizeof f,"f%05d",(int)(i%NFILES));int fd=syscall(SYS_openat2,dfd,f,&how,sizeof how);if(fd>=0)close(fd);});
60 } else printf("openat2: no disponible (%s)\n",strerror(errno)); }
61 /* getdents64: barrido completo, coste por entrada */
62 { double r[ROUNDS]; long cnt=0;
63 for(int q=0;q<ROUNDS;q++){ char buf[65536]; long c=0; double t0=now_ns();
64 int fd=open(flat,O_RDONLY|O_DIRECTORY); long n;
65 while((n=syscall(SYS_getdents64,fd,buf,sizeof buf))>0){ long off=0; while(off<n){ struct dirent64 *e=(void*)(buf+off); c++; off+=e->d_reclen; } }
66 close(fd); r[q]=(now_ns()-t0)/(double)c; cnt=c; }
67 report("getdents64 (barrido, sin stat)",r,ROUNDS,cnt); }
68 close(dfd);
69 /* profundidad de ruta */
70 { char shallow[700],deep[700]; snprintf(shallow,sizeof shallow,"%s/flat/f00000",base);
71 snprintf(deep,sizeof deep,"%s/deep",base); for(int i=0;i<12;i++) strcat(deep,"/d"); strcat(deep,"/x");
72 BENCH("open ruta corta (2 componentes bajo base)",20000,{int fd=open(shallow,O_RDONLY);if(fd>=0)close(fd);});
73 BENCH("open ruta 13 componentes mas profunda", 20000,{int fd=open(deep,O_RDONLY);if(fd>=0)close(fd);}); }
74}
75
76static void copia(const char *base){
77 const long SZ = 64L*1024*1024;
78 char src[700],dst[700]; snprintf(src,sizeof src,"%s/src.bin",base); snprintf(dst,sizeof dst,"%s/dst.bin",base);
79 int fd=open(src,O_CREAT|O_WRONLY|O_TRUNC,0644);
80 char *buf=malloc(1<<20); memset(buf,0xA5,1<<20);
81 for(long w=0;w<SZ;w+=1<<20) (void)!write(fd,buf,1<<20);
82 close(fd); free(buf);
83 printf("\n### copiar %ld MB (fichero en cache de pagina)\n",SZ>>20);
84 printf("%-40s %10s %10s\n","BENCH (MB/s)","max","mediana");
85 double r[ROUNDS];
86 for(int q=0;q<ROUNDS;q++){ int s=open(src,O_RDONLY),d=open(dst,O_CREAT|O_WRONLY|O_TRUNC,0644);
87 char b[65536]; ssize_t n; double t0=now_ns(); while((n=read(s,b,sizeof b))>0) (void)!write(d,b,n);
88 r[q]=SZ/((now_ns()-t0)/1e9)/1e6; close(s);close(d);}
89 qsort(r,ROUNDS,sizeof(double),cmpd); printf("%-40s %10.1f %10.1f\n","read()+write() 64K",r[ROUNDS-1],r[ROUNDS/2]);
90 for(int q=0;q<ROUNDS;q++){ int s=open(src,O_RDONLY),d=open(dst,O_CREAT|O_WRONLY|O_TRUNC,0644);
91 off_t off=0; double t0=now_ns(); while(off<SZ){ ssize_t n=sendfile(d,s,&off,SZ-off); if(n<=0)break; }
92 r[q]=SZ/((now_ns()-t0)/1e9)/1e6; close(s);close(d);}
93 qsort(r,ROUNDS,sizeof(double),cmpd); printf("%-40s %10.1f %10.1f\n","sendfile()",r[ROUNDS-1],r[ROUNDS/2]);
94 for(int q=0;q<ROUNDS;q++){ int s=open(src,O_RDONLY),d=open(dst,O_CREAT|O_WRONLY|O_TRUNC,0644);
95 loff_t io=0,oo=0; double t0=now_ns(); long left=SZ;
96 while(left>0){ ssize_t n=syscall(SYS_copy_file_range,s,&io,d,&oo,left,0); if(n<=0)break; left-=n; }
97 r[q]=SZ/((now_ns()-t0)/1e9)/1e6; close(s);close(d);}
98 qsort(r,ROUNDS,sizeof(double),cmpd); printf("%-40s %10.1f %10.1f\n","copy_file_range()",r[ROUNDS-1],r[ROUNDS/2]);
99 /* materializacion de arbol: hardlink vs copia, 2000 ficheros pequeños */
100 char lk[700]; snprintf(lk,sizeof lk,"%s/mat",base); mkdir(lk,0755);
101 printf("\n### materializar 2000 ficheros de 1 byte (el problema del store)\n");
102 BENCH("link() [hardlink]",2000,{char a[700],b[700];snprintf(a,sizeof a,"%s/flat/f%05d",base,(int)i);snprintf(b,sizeof b,"%s/mat/l%05d",base,(int)i);unlink(b);sink=link(a,b);});
103 BENCH("open+read+write+close [copia]",2000,{char a[700],b[700];char c[64];snprintf(a,sizeof a,"%s/flat/f%05d",base,(int)i);snprintf(b,sizeof b,"%s/mat/c%05d",base,(int)i);int s=open(a,O_RDONLY),d=open(b,O_CREAT|O_WRONLY|O_TRUNC,0644);ssize_t n=read(s,c,sizeof c);(void)!write(d,c,n);close(s);close(d);});
104 unlink(src); unlink(dst);
105}
106
107static void uring(const char *base){
108 const long SZ=64L*1024*1024; const int BS=4096; const int DEPTH=32;
109 char src[700]; snprintf(src,sizeof src,"%s/u.bin",base);
110 int fd=open(src,O_CREAT|O_RDWR|O_TRUNC,0644);
111 char *b=malloc(1<<20); memset(b,7,1<<20); for(long w=0;w<SZ;w+=1<<20)(void)!write(fd,b,1<<20); free(b);
112 fsync(fd);
113 printf("\n### 4K aleatorios sobre 64MB en cache: syscall vs io_uring\n");
114 printf("%-40s %10s %10s\n","BENCH (ns/lectura)","min","mediana");
115 char *buf=aligned_alloc(4096,BS*DEPTH);
116 long nblk=SZ/BS;
117 BENCH("pread() 4K",50000,{sink=pread(fd,buf,BS,(long)((i*7919)%nblk)*BS);});
118 struct io_uring ring;
119 if(io_uring_queue_init(256,&ring,0)==0){
120 double r[ROUNDS];
121 for(int q=0;q<ROUNDS;q++){ long done=0,total=50000; double t0=now_ns();
122 while(done<total){ int batch=DEPTH; if(total-done<batch)batch=total-done;
123 for(int k=0;k<batch;k++){ struct io_uring_sqe*s=io_uring_get_sqe(&ring);
124 io_uring_prep_read(s,fd,buf+k*BS,BS,(long)(((done+k)*7919)%nblk)*BS); }
125 io_uring_submit_and_wait(&ring,batch);
126 struct io_uring_cqe*c; unsigned h; int seen=0;
127 io_uring_for_each_cqe(&ring,h,c){ seen++; } io_uring_cq_advance(&ring,seen);
128 done+=batch; }
129 r[q]=(now_ns()-t0)/total; }
130 report("io_uring read 4K (profundidad 32)",r,ROUNDS,50000);
131 /* piso de submission: NOP en lote */
132 for(int q=0;q<ROUNDS;q++){ long done=0,total=200000; double t0=now_ns();
133 while(done<total){ int batch=64; if(total-done<batch)batch=total-done;
134 for(int k=0;k<batch;k++){ struct io_uring_sqe*s=io_uring_get_sqe(&ring); io_uring_prep_nop(s); }
135 io_uring_submit_and_wait(&ring,batch);
136 struct io_uring_cqe*c; unsigned h; int seen=0;
137 io_uring_for_each_cqe(&ring,h,c){ seen++; } io_uring_cq_advance(&ring,seen);
138 done+=batch; }
139 r[q]=(now_ns()-t0)/total; }
140 report("io_uring NOP (piso de operacion)",r,ROUNDS,200000);
141 io_uring_queue_exit(&ring);
142 } else printf("io_uring: no disponible (%s)\n",strerror(errno));
143 free(buf); close(fd); unlink(src);
144}
145
146static void memoria(void){
147 const long SZ=128L*1024*1024;
148 printf("\n### fallos de pagina: tocar 128MB anonimos (ns por pagina de 4K)\n");
149 printf("%-40s %10s %10s\n","BENCH","min","mediana");
150 double r[ROUNDS]; long pages=SZ/4096;
151 for(int q=0;q<ROUNDS;q++){ char*m=mmap(0,SZ,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
152 madvise(m,SZ,MADV_HUGEPAGE); double t0=now_ns();
153 for(long o=0;o<SZ;o+=4096) m[o]=1; r[q]=(now_ns()-t0)/pages; munmap(m,SZ);}
154 report("MADV_HUGEPAGE (THP)",r,ROUNDS,pages);
155 for(int q=0;q<ROUNDS;q++){ char*m=mmap(0,SZ,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
156 madvise(m,SZ,MADV_NOHUGEPAGE); double t0=now_ns();
157 for(long o=0;o<SZ;o+=4096) m[o]=1; r[q]=(now_ns()-t0)/pages; munmap(m,SZ);}
158 report("MADV_NOHUGEPAGE (4K de a uno)",r,ROUNDS,pages);
159 for(int q=0;q<ROUNDS;q++){ double t0=now_ns();
160 char*m=mmap(0,SZ,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE,-1,0);
161 for(long o=0;o<SZ;o+=4096) m[o]=1; r[q]=(now_ns()-t0)/pages; munmap(m,SZ);}
162 report("MAP_POPULATE (prefault)",r,ROUNDS,pages);
163 BENCH("mmap+munmap 4K (churn)",100000,{void*m=mmap(0,4096,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);munmap(m,4096);});
164}
165
166static void namespaces(void){
167 printf("\n### coste del sandbox: crear namespaces (us por proceso)\n");
168 printf("%-40s %10s %10s\n","BENCH","min","mediana");
169 double r[ROUNDS];
170 for(int q=0;q<ROUNDS;q++){ int N=100; double t0=now_ns();
171 for(int i=0;i<N;i++){ pid_t p=fork(); if(p==0){ char*a[]={"/bin/true",0}; execve(a[0],a,environ); _exit(127);} int st; waitpid(p,&st,0);}
172 r[q]=(now_ns()-t0)/N/1000; } report("fork+exec /bin/true (base)",r,ROUNDS,100);
173 int fallo=0;
174 for(int q=0;q<ROUNDS;q++){ int N=100; double t0=now_ns();
175 for(int i=0;i<N;i++){ pid_t p=fork();
176 if(p==0){ if(unshare(CLONE_NEWUSER|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWUTS|CLONE_NEWNET)<0) _exit(9);
177 char*a[]={"/bin/true",0}; execve(a[0],a,environ); _exit(127);}
178 int st; waitpid(p,&st,0); if(WEXITSTATUS(st)==9) fallo=1; }
179 r[q]=(now_ns()-t0)/N/1000; }
180 if(fallo) printf("unshare completo: no permitido para este uid\n");
181 else report("fork+unshare(6 ns)+exec",r,ROUNDS,100);
182 for(int q=0;q<ROUNDS;q++){ int N=100; double t0=now_ns();
183 for(int i=0;i<N;i++){ pid_t p=fork();
184 if(p==0){ unshare(CLONE_NEWUSER|CLONE_NEWNS); char*a[]={"/bin/true",0}; execve(a[0],a,environ); _exit(127);}
185 int st; waitpid(p,&st,0); }
186 r[q]=(now_ns()-t0)/N/1000; } report("fork+unshare(USER|MNT)+exec",r,ROUNDS,100);
187}
188
189int main(int argc,char**argv){
190 const char *base = argc>1?argv[1]:"/tmp/bench-linux";
191 snprintf(root,sizeof root,"%s",base);
192 rm_tree(root); mk_tree(root);
193 metadata(root, base);
194 copia(root);
195 uring(root);
196 if(argc<=2){ memoria(); namespaces(); }
197 rm_tree(root);
198 return 0;
199}