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_sqpoll.c
History
Who touched what
raw
tasas: los 4 experimentos que §9 dejó nombrados, medidos con root
1/* bench_sqpoll.c — io_uring con SQPOLL contra io_uring normal y contra pread.
2 *
3 * Cierra el experimento 4 de SDD 25 seccion 9: SQPOLL pide CAP_SYS_NICE, asi que la corrida
4 * original no pudo mirarlo. La pregunta que dejo abierta T12: io_uring pierde sobre datos ya
5 * cacheados porque el cruce al kernel no se amortiza — con SQPOLL NO HAY cruce (un hilo del
6 * kernel consume la cola), asi que este es el caso donde deberia ganar.
7 *
8 * Uso: bench_sqpoll <dir> <etiqueta>
9 * Se compila con: cc -O2 -o bench_sqpoll bench_sqpoll.c -luring
10 */
11#define _GNU_SOURCE
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <unistd.h>
16#include <fcntl.h>
17#include <time.h>
18#include <errno.h>
19#include <liburing.h>
20
21#define MB (1024*1024)
22#define SIZE (64*MB)
23#define BS 4096
24#define ROUNDS 7
25#define DEPTH 32
26#define OPS 50000
27
28static volatile long sink;
29static inline double now_ns(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec; }
30static int cmpd(const void*a,const void*b){ double x=*(const double*)a,y=*(const double*)b; return (x>y)-(x<y); }
31static void report(const char*n,double*r,int c,long it){ qsort(r,c,sizeof(double),cmpd);
32 printf("%-44s %10.1f %10.1f (n=%ld)\n",n,r[0],r[c/2],it); fflush(stdout); }
33
34/* Un pase de OPS lecturas de 4K en profundidad DEPTH. Devuelve ns/op. */
35static double pase(struct io_uring *ring,int fd,char *buf,long nblk){
36 double t0=now_ns();
37 for(long done=0;done<OPS;){
38 int batch = (OPS-done<DEPTH)?(int)(OPS-done):DEPTH;
39 for(int k=0;k<batch;k++){
40 struct io_uring_sqe *s=io_uring_get_sqe(ring);
41 io_uring_prep_read(s,fd,buf+k*BS,BS,(long)(((done+k)*7919)%nblk)*BS);
42 }
43 io_uring_submit_and_wait(ring,batch);
44 struct io_uring_cqe *c; unsigned h; int seen=0;
45 io_uring_for_each_cqe(ring,h,c){ seen++; }
46 io_uring_cq_advance(ring,seen);
47 done+=batch;
48 }
49 return (now_ns()-t0)/(double)OPS;
50}
51
52int main(int argc,char**argv){
53 if(argc<3){ fprintf(stderr,"uso: %s <dir> <etiqueta>\n",argv[0]); return 2; }
54 char path[512]; snprintf(path,sizeof path,"%s/sqpoll.bin",argv[1]);
55 int fd=open(path,O_CREAT|O_RDWR|O_TRUNC,0644);
56 if(fd<0){ fprintf(stderr,"no pude crear %s: %s\n",path,strerror(errno)); return 1; }
57 char *b=malloc(MB); memset(b,0x5A,MB);
58 for(int i=0;i<SIZE/MB;i++) if(write(fd,b,MB)!=MB){ perror("write"); return 1; }
59 free(b);
60 /* Se lee entero una vez para dejarlo en cache: se mide el camino de ENTREGA, no el disco. */
61 char *buf=aligned_alloc(4096,DEPTH*BS);
62 for(long o=0;o<SIZE;o+=BS) sink=pread(fd,buf,BS,o);
63 long nblk=SIZE/BS;
64
65 printf("\n### 4K aleatorios en cache sobre %s (profundidad %d)\n",argv[2],DEPTH);
66 printf("%-44s %10s %10s\n","BENCH (ns/op)","min","mediana");
67
68 double r[ROUNDS];
69 for(int q=0;q<ROUNDS;q++){
70 double t0=now_ns();
71 for(long i=0;i<OPS;i++) sink=pread(fd,buf,BS,(long)((i*7919)%nblk)*BS);
72 r[q]=(now_ns()-t0)/(double)OPS;
73 }
74 report("pread 4K (una syscall por op)",r,ROUNDS,OPS);
75
76 struct io_uring ring;
77 if(io_uring_queue_init(256,&ring,0)==0){
78 for(int q=0;q<ROUNDS;q++) r[q]=pase(&ring,fd,buf,nblk);
79 report("io_uring sin SQPOLL",r,ROUNDS,OPS);
80 io_uring_queue_exit(&ring);
81 } else printf("io_uring: no disponible (%s)\n",strerror(errno));
82
83 struct io_uring_params p; memset(&p,0,sizeof p);
84 p.flags=IORING_SETUP_SQPOLL; p.sq_thread_idle=2000;
85 if(io_uring_queue_init_params(256,&ring,&p)==0){
86 /* Con SQPOLL el fd hay que registrarlo: el hilo del kernel no tiene la tabla de fds. */
87 int rc=io_uring_register_files(&ring,&fd,1);
88 int usar_fijo = (rc==0);
89 for(int q=0;q<ROUNDS;q++){
90 double t0=now_ns();
91 for(long done=0;done<OPS;){
92 int batch=(OPS-done<DEPTH)?(int)(OPS-done):DEPTH;
93 for(int k=0;k<batch;k++){
94 struct io_uring_sqe *s=io_uring_get_sqe(&ring);
95 io_uring_prep_read(s,usar_fijo?0:fd,buf+k*BS,BS,(long)(((done+k)*7919)%nblk)*BS);
96 if(usar_fijo) s->flags|=IOSQE_FIXED_FILE;
97 }
98 io_uring_submit_and_wait(&ring,batch);
99 struct io_uring_cqe *c; unsigned h; int seen=0;
100 io_uring_for_each_cqe(&ring,h,c){ if(c->res<0) sink=c->res; seen++; }
101 io_uring_cq_advance(&ring,seen);
102 done+=batch;
103 }
104 r[q]=(now_ns()-t0)/(double)OPS;
105 }
106 report(usar_fijo?"io_uring SQPOLL (fd registrado)":"io_uring SQPOLL (fd normal)",r,ROUNDS,OPS);
107 io_uring_queue_exit(&ring);
108 } else printf("SQPOLL: no disponible (%s) — pide CAP_SYS_NICE\n",strerror(errno));
109
110 unlink(path); close(fd);
111 return 0;
112}