tasas: los 4 experimentos que §9 dejó nombrados, medidos con root
SDD 25 §9 listó seis experimentos que no se pudieron correr y por qué. Cuatro de ellos sólo pedían privilegios, y esta sesión los tuvo. Programas y salidas crudas en docs/evidencia/tasas-kernel-2026-08-29/. 1. proc connector (bench_connector.c) — el sondeo de /proc no es lento, es CIEGO: 200 procesos de vida mínima lanzados uno cada 10 ms, sondeando cada 100 ms se ven **0 de 200**; por connector, **200 de 200**. Y esperar el evento no cuesta nada frente a waitpid (116,0 vs 114,8 µs, dentro del ruido), con 0 eventos perdidos en 1400. Sorpresa: taskstats por genetlink **no es más rápido** que parsear /proc/self/stat (2 688 vs 2 697 ns). Su valor es el CONTENIDO (delay accounting), no la velocidad. 2. reflink (bench_reflink.c) — el «copiar en O(1)» existe y NO está en ext4: FICLONE no soportado, y copy_file_range ≈ read+write (13,7 vs 14,5 ms). En xfs y btrfs es PLANO: 64 MiB en 0,010 ms y 512 MiB —ocho veces más— en 0,020 ms, contra 512,7 ms de read+write. **25 600× a 512 MiB.** 3. io_uring SQPOLL (bench_sqpoll.c) — no rescata el veredicto de T12: sobre datos cacheados pierde igual. ext4: pread 554 ns < io_uring 618 < SQPOLL 868. tmpfs: pread 749 < SQPOLL 1 409 < io_uring 2 030 (ahí sí mejora al io_uring normal, 1,4×, pero sigue perdiendo contra la syscall). Y se come un core. 4. syscalls por invocación de compilador — con el strace DEL PROPIO CORPUS (store/3c1fd8c0…-strace), que es la respuesta a «no hay strace en esta máquina». gcc: 1 803 syscalls por un `-c` trivial, **1 095 con error (61%), 919 de ellas readlink** — la canonicalización de rutas, que T8 no medía. Pero al piso de 78,7 ns son 0,14 ms sobre 30,5 ms de compilado: **el cruce al kernel es el 0,5%**. La tormenta de rutas vale milisegundos, no minutos. Condiciones distintas a la corrida original (load ~1,3 sobre 4 vCPU contra 7,3) ⇒ los absolutos NO se comparan entre corridas, sólo los cocientes dentro de una. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ACUcwo9mZsE5ocYVE9npih
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
/* bench_connector.c — proc connector (netlink) y taskstats contra sondear /proc.
|
||||
*
|
||||
* Cierra el experimento 1 de SDD 25 §9, que quedo sin medir por falta de CAP_NET_ADMIN.
|
||||
* Requiere root. Tres fases:
|
||||
* A. LATENCIA fork+_exit+waitpid vs fork+_exit+esperar el evento del connector.
|
||||
* B. CEGUERA procesos cortos vistos por el connector vs vistos sondeando /proc cada 100 ms.
|
||||
* C. TASKSTATS una consulta binaria por genetlink vs parsear /proc/<pid>/stat.
|
||||
*
|
||||
* Se compila con: cc -O2 -o bench_connector bench_connector.c
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <poll.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/connector.h>
|
||||
#include <linux/cn_proc.h>
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/taskstats.h>
|
||||
|
||||
static volatile long sink;
|
||||
static inline double now_ns(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec; }
|
||||
static int cmpd(const void*a,const void*b){ double x=*(const double*)a,y=*(const double*)b; return (x>y)-(x<y); }
|
||||
static void report(const char*n,double*r,int c,long it){ qsort(r,c,sizeof(double),cmpd);
|
||||
printf("%-44s %10.1f %10.1f (n=%ld)\n",n,r[0],r[c/2],it); fflush(stdout); }
|
||||
#define ROUNDS 7
|
||||
|
||||
/* ---------------------------------------------------------------- proc connector */
|
||||
|
||||
static int cn_open(void){
|
||||
int s = socket(PF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_CONNECTOR);
|
||||
if(s<0) return -1;
|
||||
struct sockaddr_nl a; memset(&a,0,sizeof a);
|
||||
a.nl_family=AF_NETLINK; a.nl_groups=CN_IDX_PROC; a.nl_pid=getpid();
|
||||
if(bind(s,(struct sockaddr*)&a,sizeof a)<0){ close(s); return -1; }
|
||||
|
||||
struct {
|
||||
struct nlmsghdr nl;
|
||||
struct cn_msg cn;
|
||||
enum proc_cn_mcast_op op;
|
||||
} __attribute__((packed)) m;
|
||||
memset(&m,0,sizeof m);
|
||||
m.nl.nlmsg_len=sizeof m; m.nl.nlmsg_pid=getpid(); m.nl.nlmsg_type=NLMSG_DONE;
|
||||
m.cn.id.idx=CN_IDX_PROC; m.cn.id.val=CN_VAL_PROC; m.cn.len=sizeof(enum proc_cn_mcast_op);
|
||||
m.op=PROC_CN_MCAST_LISTEN;
|
||||
if(send(s,&m,sizeof m,0)<0){ close(s); return -1; }
|
||||
/* El multicast del connector NO reintenta: si el buffer se llena, el evento se PIERDE (ENOBUFS)
|
||||
* y el que espera ese pid se queda esperando para siempre. Se agranda el buffer y, aun asi, la
|
||||
* espera lleva plazo (abajo). Es una propiedad del mecanismo, no un detalle de este programa. */
|
||||
int rb=4*1024*1024; setsockopt(s,SOL_SOCKET,SO_RCVBUFFORCE,&rb,sizeof rb);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Lee un evento. Devuelve el proc_event, o NULL si el mensaje no lo era. */
|
||||
static struct proc_event *cn_read(int s, char *buf, size_t n){
|
||||
ssize_t r = recv(s, buf, n, 0);
|
||||
if(r<=0) return NULL;
|
||||
struct nlmsghdr *nlh=(struct nlmsghdr*)buf;
|
||||
if(!NLMSG_OK(nlh,(size_t)r)) return NULL;
|
||||
struct cn_msg *cn=(struct cn_msg*)NLMSG_DATA(nlh);
|
||||
return (struct proc_event*)cn->data;
|
||||
}
|
||||
|
||||
/* Vacia lo que haya en el socket sin bloquear. */
|
||||
static void cn_drain(int s){
|
||||
int fl=fcntl(s,F_GETFL,0); fcntl(s,F_SETFL,fl|O_NONBLOCK);
|
||||
char b[4096]; while(recv(s,b,sizeof b,0)>0){}
|
||||
fcntl(s,F_SETFL,fl);
|
||||
}
|
||||
|
||||
static void fase_a(int s){
|
||||
printf("\n### A. Latencia de notificacion: waitpid contra evento del connector\n");
|
||||
printf("%-44s %10s %10s\n","BENCH (ns por proceso)","min","mediana");
|
||||
const long N=200;
|
||||
|
||||
double r[ROUNDS];
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
double t0=now_ns();
|
||||
for(long i=0;i<N;i++){ pid_t p=fork(); if(p==0) _exit(0); int st; waitpid(p,&st,0); }
|
||||
r[q]=(now_ns()-t0)/(double)N;
|
||||
}
|
||||
report("fork+_exit+waitpid (SIGCHLD, la base)",r,ROUNDS,N);
|
||||
|
||||
char buf[8192];
|
||||
long perdidos=0;
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
cn_drain(s);
|
||||
double t0=now_ns();
|
||||
for(long i=0;i<N;i++){
|
||||
pid_t p=fork(); if(p==0) _exit(0);
|
||||
double lim=now_ns()+2e8; /* 200 ms de plazo: si no llega, se PERDIO */
|
||||
for(;;){
|
||||
if(now_ns()>lim){ perdidos++; break; }
|
||||
struct pollfd pf={.fd=s,.events=POLLIN};
|
||||
if(poll(&pf,1,50)<=0) continue;
|
||||
struct proc_event *e=cn_read(s,buf,sizeof buf);
|
||||
if(!e) continue;
|
||||
if(e->what==PROC_EVENT_EXIT && e->event_data.exit.process_pid==p) break;
|
||||
}
|
||||
int st; waitpid(p,&st,0);
|
||||
}
|
||||
r[q]=(now_ns()-t0)/(double)N;
|
||||
}
|
||||
report("fork+_exit+esperar EXIT del connector",r,ROUNDS,N);
|
||||
printf("eventos EXIT que no llegaron en 200 ms: %ld de %ld\n",perdidos,(long)N*ROUNDS);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- ceguera del sondeo */
|
||||
|
||||
#define BURST 200 /* procesos cortos */
|
||||
#define SPACING_US 10000 /* uno cada 10 ms => 2 s de rafaga */
|
||||
#define POLL_MS 100 /* periodo del sondeo, generoso: un supervisor real no baja de aca */
|
||||
|
||||
/* Lanza BURST hijos que viven lo minimo, y escribe sus pids por el pipe. */
|
||||
static void spawner(int wfd){
|
||||
for(int i=0;i<BURST;i++){
|
||||
pid_t p=fork();
|
||||
if(p==0) _exit(0);
|
||||
(void)!write(wfd,&p,sizeof p);
|
||||
int st; waitpid(p,&st,0);
|
||||
usleep(SPACING_US);
|
||||
}
|
||||
close(wfd);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
static int en_lista(pid_t *v,int n,pid_t p){ for(int i=0;i<n;i++) if(v[i]==p) return 1; return 0; }
|
||||
|
||||
static double cpu_ms(void){
|
||||
struct rusage u; getrusage(RUSAGE_SELF,&u);
|
||||
return (u.ru_utime.tv_sec+u.ru_stime.tv_sec)*1e3
|
||||
+ (u.ru_utime.tv_usec+u.ru_stime.tv_usec)/1e3;
|
||||
}
|
||||
|
||||
static void fase_b(int s){
|
||||
printf("\n### B. Ceguera: %d procesos de vida minima, uno cada %d ms\n",BURST,SPACING_US/1000);
|
||||
|
||||
/* --- observador 1: sondeo de /proc cada POLL_MS --- */
|
||||
int fd[2]; if(pipe(fd)) { perror("pipe"); return; }
|
||||
pid_t sp=fork();
|
||||
if(sp==0){ close(fd[0]); spawner(fd[1]); }
|
||||
close(fd[1]);
|
||||
static pid_t lanzados[BURST]; int nl=0;
|
||||
static pid_t vistos[BURST*8]; int nv=0;
|
||||
double c0=cpu_ms(), t0=now_ns(); int barridos=0;
|
||||
while(now_ns()-t0 < (double)BURST*SPACING_US*1000.0 + 3e8){
|
||||
DIR *d=opendir("/proc"); struct dirent *e;
|
||||
while(d && (e=readdir(d))){
|
||||
if(e->d_name[0]<'0'||e->d_name[0]>'9') continue;
|
||||
pid_t p=(pid_t)atol(e->d_name);
|
||||
if(nv<BURST*8 && !en_lista(vistos,nv,p)) vistos[nv++]=p;
|
||||
}
|
||||
if(d) closedir(d);
|
||||
barridos++;
|
||||
usleep(POLL_MS*1000);
|
||||
}
|
||||
double cpu_sondeo=cpu_ms()-c0;
|
||||
{ int st; waitpid(sp,&st,0); }
|
||||
{ pid_t p; while(read(fd[0],&p,sizeof p)==sizeof p) if(nl<BURST) lanzados[nl++]=p; }
|
||||
close(fd[0]);
|
||||
int cazados=0; for(int i=0;i<nl;i++) if(en_lista(vistos,nv,lanzados[i])) cazados++;
|
||||
printf("sondeo /proc cada %d ms : %d de %d procesos vistos (%d barridos, %.1f ms de CPU)\n",
|
||||
POLL_MS,cazados,nl,barridos,cpu_sondeo);
|
||||
|
||||
/* --- observador 2: connector --- */
|
||||
if(pipe(fd)){ perror("pipe"); return; }
|
||||
cn_drain(s);
|
||||
sp=fork();
|
||||
if(sp==0){ close(fd[0]); spawner(fd[1]); }
|
||||
close(fd[1]);
|
||||
nl=0; nv=0;
|
||||
c0=cpu_ms(); t0=now_ns();
|
||||
int flags=fcntl(s,F_GETFL,0); fcntl(s,F_SETFL,flags|O_NONBLOCK);
|
||||
char buf[8192];
|
||||
while(now_ns()-t0 < (double)BURST*SPACING_US*1000.0 + 3e8){
|
||||
struct proc_event *e=cn_read(s,buf,sizeof buf);
|
||||
if(!e){ usleep(200); continue; }
|
||||
if(e->what==PROC_EVENT_EXIT){
|
||||
pid_t p=e->event_data.exit.process_pid;
|
||||
if(nv<BURST*8 && !en_lista(vistos,nv,p)) vistos[nv++]=p;
|
||||
}
|
||||
}
|
||||
fcntl(s,F_SETFL,flags);
|
||||
double cpu_conn=cpu_ms()-c0;
|
||||
{ int st; waitpid(sp,&st,0); }
|
||||
{ pid_t p; while(read(fd[0],&p,sizeof p)==sizeof p) if(nl<BURST) lanzados[nl++]=p; }
|
||||
close(fd[0]);
|
||||
cazados=0; for(int i=0;i<nl;i++) if(en_lista(vistos,nv,lanzados[i])) cazados++;
|
||||
printf("proc connector (eventos) : %d de %d procesos vistos (%.1f ms de CPU)\n",
|
||||
cazados,nl,cpu_conn);
|
||||
printf("NOTA: el connector se lee aca con espera activa de 200 us, que NO es el modo de un\n"
|
||||
" supervisor real (seria epoll) => su CPU es cota superior. Lo que cuenta: COBERTURA.\n");
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- taskstats */
|
||||
|
||||
static int genl_family(int s,const char *name){
|
||||
struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } req;
|
||||
memset(&req,0,sizeof req);
|
||||
req.n.nlmsg_type=GENL_ID_CTRL; req.n.nlmsg_flags=NLM_F_REQUEST; req.n.nlmsg_seq=1; req.n.nlmsg_pid=getpid();
|
||||
req.g.cmd=CTRL_CMD_GETFAMILY; req.g.version=1;
|
||||
struct nlattr *a=(struct nlattr*)req.buf;
|
||||
a->nla_type=CTRL_ATTR_FAMILY_NAME; a->nla_len=NLA_HDRLEN+strlen(name)+1;
|
||||
strcpy((char*)a+NLA_HDRLEN,name);
|
||||
req.n.nlmsg_len=NLMSG_LENGTH(GENL_HDRLEN)+NLA_ALIGN(a->nla_len);
|
||||
if(send(s,&req,req.n.nlmsg_len,0)<0) return -1;
|
||||
|
||||
char rb[4096]; ssize_t r=recv(s,rb,sizeof rb,0);
|
||||
if(r<0) return -1;
|
||||
struct nlmsghdr *nh=(struct nlmsghdr*)rb;
|
||||
if(nh->nlmsg_type==NLMSG_ERROR) return -1;
|
||||
struct nlattr *na=(struct nlattr*)((char*)NLMSG_DATA(nh)+GENL_HDRLEN);
|
||||
int len=NLMSG_PAYLOAD(nh,0)-GENL_HDRLEN;
|
||||
while(len>0){
|
||||
if(na->nla_type==CTRL_ATTR_FAMILY_ID) return *(__u16*)((char*)na+NLA_HDRLEN);
|
||||
len-=NLA_ALIGN(na->nla_len);
|
||||
na=(struct nlattr*)((char*)na+NLA_ALIGN(na->nla_len));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ts_query(int s,int fam,pid_t pid,unsigned long long *cpu_ns){
|
||||
struct { struct nlmsghdr n; struct genlmsghdr g; char buf[128]; } req;
|
||||
memset(&req,0,sizeof req);
|
||||
req.n.nlmsg_type=fam; req.n.nlmsg_flags=NLM_F_REQUEST; req.n.nlmsg_seq=2; req.n.nlmsg_pid=getpid();
|
||||
req.g.cmd=TASKSTATS_CMD_GET; req.g.version=TASKSTATS_VERSION;
|
||||
struct nlattr *a=(struct nlattr*)req.buf;
|
||||
a->nla_type=TASKSTATS_CMD_ATTR_PID; a->nla_len=NLA_HDRLEN+sizeof(__u32);
|
||||
*(__u32*)((char*)a+NLA_HDRLEN)=pid;
|
||||
req.n.nlmsg_len=NLMSG_LENGTH(GENL_HDRLEN)+NLA_ALIGN(a->nla_len);
|
||||
if(send(s,&req,req.n.nlmsg_len,0)<0) return -1;
|
||||
|
||||
char rb[8192]; ssize_t r=recv(s,rb,sizeof rb,0);
|
||||
if(r<0) return -1;
|
||||
struct nlmsghdr *nh=(struct nlmsghdr*)rb;
|
||||
if(nh->nlmsg_type==NLMSG_ERROR) return -1;
|
||||
struct nlattr *na=(struct nlattr*)((char*)NLMSG_DATA(nh)+GENL_HDRLEN);
|
||||
int len=NLMSG_PAYLOAD(nh,0)-GENL_HDRLEN;
|
||||
while(len>0){
|
||||
if(na->nla_type==TASKSTATS_TYPE_AGGR_PID || na->nla_type==TASKSTATS_TYPE_AGGR_TGID){
|
||||
struct nlattr *in=(struct nlattr*)((char*)na+NLA_HDRLEN);
|
||||
int il=na->nla_len-NLA_HDRLEN;
|
||||
while(il>0){
|
||||
if(in->nla_type==TASKSTATS_TYPE_STATS){
|
||||
struct taskstats *ts=(struct taskstats*)((char*)in+NLA_HDRLEN);
|
||||
*cpu_ns=ts->ac_utime*1000ULL+ts->ac_stime*1000ULL;
|
||||
return 0;
|
||||
}
|
||||
il-=NLA_ALIGN(in->nla_len);
|
||||
in=(struct nlattr*)((char*)in+NLA_ALIGN(in->nla_len));
|
||||
}
|
||||
}
|
||||
len-=NLA_ALIGN(na->nla_len);
|
||||
na=(struct nlattr*)((char*)na+NLA_ALIGN(na->nla_len));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void fase_c(void){
|
||||
printf("\n### C. Estadisticas de UNA tarea: taskstats binario contra /proc de texto\n");
|
||||
int s=socket(PF_NETLINK,SOCK_DGRAM|SOCK_CLOEXEC,NETLINK_GENERIC);
|
||||
if(s<0){ printf("taskstats: sin socket genetlink (%s)\n",strerror(errno)); return; }
|
||||
struct sockaddr_nl a; memset(&a,0,sizeof a); a.nl_family=AF_NETLINK; a.nl_pid=getpid();
|
||||
if(bind(s,(struct sockaddr*)&a,sizeof a)<0){ printf("taskstats: bind (%s)\n",strerror(errno)); close(s); return; }
|
||||
int fam=genl_family(s,TASKSTATS_GENL_NAME);
|
||||
if(fam<0){ printf("taskstats: la familia %s no esta (kernel sin TASKSTATS)\n",TASKSTATS_GENL_NAME); close(s); return; }
|
||||
unsigned long long ns=0;
|
||||
if(ts_query(s,fam,getpid(),&ns)<0){ printf("taskstats: la consulta fallo (%s)\n",strerror(errno)); close(s); return; }
|
||||
printf("familia TASKSTATS = %d, cpu de este proceso = %llu ns\n",fam,ns);
|
||||
|
||||
printf("%-44s %10s %10s\n","BENCH (ns por consulta)","min","mediana");
|
||||
const long N=2000;
|
||||
double r[ROUNDS];
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
double t0=now_ns();
|
||||
for(long i=0;i<N;i++){ unsigned long long v; sink=ts_query(s,fam,getpid(),&v); }
|
||||
r[q]=(now_ns()-t0)/(double)N;
|
||||
}
|
||||
report("taskstats por genetlink (binario)",r,ROUNDS,N);
|
||||
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
double t0=now_ns();
|
||||
for(long i=0;i<N;i++){
|
||||
int fd=open("/proc/self/stat",O_RDONLY); char b[2048];
|
||||
ssize_t n=read(fd,b,sizeof b-1); close(fd);
|
||||
if(n>0){ b[n]=0; char *p=strrchr(b,')'); for(int f=0;p&&f<12;f++) p=strchr(p+1,' ');
|
||||
sink=p?atol(p+1):0; }
|
||||
}
|
||||
r[q]=(now_ns()-t0)/(double)N;
|
||||
}
|
||||
report("open+read+parse /proc/self/stat",r,ROUNDS,N);
|
||||
close(s);
|
||||
}
|
||||
|
||||
int main(void){
|
||||
if(geteuid()!=0) fprintf(stderr,"aviso: sin root el connector no se suscribe (CAP_NET_ADMIN)\n");
|
||||
printf("# bench_connector — SDD 25 seccion 9, experimento 1\n");
|
||||
int s=cn_open();
|
||||
if(s<0){ printf("proc connector: no disponible (%s)\n",strerror(errno)); }
|
||||
else { fase_a(s); fase_b(s); close(s); }
|
||||
fase_c();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/* bench_reflink.c — copiar un fichero: reflink (FICLONE), copy_file_range y read+write.
|
||||
*
|
||||
* Cierra el experimento 2 de SDD 25 seccion 9 (T11), que quedo sin medir porque exige mkfs sobre
|
||||
* un loop, o sea root. La pregunta: el "copiar en O(1)" que la gente espera de copy_file_range,
|
||||
* cuanto vale de verdad, y en que filesystem.
|
||||
*
|
||||
* Uso: bench_reflink <dir> <etiqueta> (el dir debe estar en el FS que se quiere medir)
|
||||
* Se compila con: cc -O2 -o bench_reflink bench_reflink.c
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#define MB (1024*1024)
|
||||
static long SIZE = 64*MB; /* se puede fijar por argv[3] en MiB: la copia O(1) hay que verla PLANA */
|
||||
#define ROUNDS 7
|
||||
|
||||
static inline double now_ns(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec; }
|
||||
static int cmpd(const void*a,const void*b){ double x=*(const double*)a,y=*(const double*)b; return (x>y)-(x<y); }
|
||||
static void report(const char*n,double*r,int c){
|
||||
qsort(r,c,sizeof(double),cmpd);
|
||||
double min=r[0],med=r[c/2];
|
||||
/* El MB/s se imprime solo cuando la copia MUEVE datos. Con reflink el tiempo no depende del
|
||||
* tamano, asi que dividir por el ni significa nada ni se puede citar. */
|
||||
if(min > 1e6) printf("%-40s %9.3f ms %9.3f ms %8.0f MB/s\n",n,min/1e6,med/1e6,(double)SIZE/(min/1e9)/1e6);
|
||||
else printf("%-40s %9.3f ms %9.3f ms (plano: no mueve datos)\n",n,min/1e6,med/1e6);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static int src_fd(const char *dir,char *path,size_t n){
|
||||
snprintf(path,n,"%s/origen.bin",dir);
|
||||
int fd=open(path,O_CREAT|O_RDWR|O_TRUNC,0644);
|
||||
if(fd<0) return -1;
|
||||
char *b=malloc(MB); memset(b,0xA5,MB);
|
||||
for(long i=0;i<SIZE/MB;i++) if(write(fd,b,MB)!=MB){ free(b); close(fd); return -1; }
|
||||
free(b);
|
||||
fsync(fd);
|
||||
/* Se deja en cache a proposito: se mide el camino de COPIA, no el de lectura de disco. */
|
||||
return fd;
|
||||
}
|
||||
|
||||
int main(int argc,char**argv){
|
||||
if(argc<3){ fprintf(stderr,"uso: %s <dir> <etiqueta> [MiB]\n",argv[0]); return 2; }
|
||||
const char *dir=argv[1], *etiqueta=argv[2];
|
||||
if(argc>3) SIZE=atol(argv[3])*MB;
|
||||
char src[512],dst[512];
|
||||
int in=src_fd(dir,src,sizeof src);
|
||||
if(in<0){ fprintf(stderr,"no pude crear el origen en %s: %s\n",dir,strerror(errno)); return 1; }
|
||||
snprintf(dst,sizeof dst,"%s/copia.bin",dir);
|
||||
|
||||
printf("\n### copia de %ld MiB sobre %s\n",SIZE/MB,etiqueta);
|
||||
printf("%-40s %12s %12s\n","BENCH","min","mediana");
|
||||
|
||||
double r[ROUNDS];
|
||||
int ok=0;
|
||||
|
||||
/* 1. reflink de fichero entero: la copia O(1) de verdad (comparte extents COW). */
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
unlink(dst);
|
||||
int out=open(dst,O_CREAT|O_WRONLY|O_TRUNC,0644);
|
||||
double t0=now_ns();
|
||||
int rc=ioctl(out,FICLONE,in);
|
||||
r[q]=now_ns()-t0;
|
||||
close(out);
|
||||
if(rc<0){ printf("%-40s no soportado (%s)\n","FICLONE (reflink entero)",strerror(errno)); ok=-1; break; }
|
||||
}
|
||||
if(ok==0) report("FICLONE (reflink entero)",r,ROUNDS);
|
||||
|
||||
/* 2. copy_file_range: la API portable. En un FS con reflink cae en el mismo camino; en ext4
|
||||
* cae a una copia DENTRO del kernel. */
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
unlink(dst);
|
||||
int out=open(dst,O_CREAT|O_WRONLY|O_TRUNC,0644);
|
||||
loff_t off_in=0,off_out=0; size_t left=(size_t)SIZE;
|
||||
double t0=now_ns();
|
||||
while(left){ ssize_t c=copy_file_range(in,&off_in,out,&off_out,left,0); if(c<=0) break; left-=c; }
|
||||
r[q]=now_ns()-t0;
|
||||
close(out);
|
||||
}
|
||||
report("copy_file_range",r,ROUNDS);
|
||||
|
||||
/* 3. read+write en trozos de 1 MiB: lo que hace un cp cualquiera. */
|
||||
char *buf=malloc(MB);
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
unlink(dst);
|
||||
int out=open(dst,O_CREAT|O_WRONLY|O_TRUNC,0644);
|
||||
lseek(in,0,SEEK_SET);
|
||||
double t0=now_ns();
|
||||
for(long i=0;i<SIZE/MB;i++){ ssize_t n=read(in,buf,MB); if(n>0) (void)!write(out,buf,n); }
|
||||
r[q]=now_ns()-t0;
|
||||
close(out);
|
||||
}
|
||||
report("read+write (1 MiB)",r,ROUNDS);
|
||||
free(buf);
|
||||
|
||||
unlink(dst); unlink(src); close(in);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/* bench_sqpoll.c — io_uring con SQPOLL contra io_uring normal y contra pread.
|
||||
*
|
||||
* Cierra el experimento 4 de SDD 25 seccion 9: SQPOLL pide CAP_SYS_NICE, asi que la corrida
|
||||
* original no pudo mirarlo. La pregunta que dejo abierta T12: io_uring pierde sobre datos ya
|
||||
* cacheados porque el cruce al kernel no se amortiza — con SQPOLL NO HAY cruce (un hilo del
|
||||
* kernel consume la cola), asi que este es el caso donde deberia ganar.
|
||||
*
|
||||
* Uso: bench_sqpoll <dir> <etiqueta>
|
||||
* Se compila con: cc -O2 -o bench_sqpoll bench_sqpoll.c -luring
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <liburing.h>
|
||||
|
||||
#define MB (1024*1024)
|
||||
#define SIZE (64*MB)
|
||||
#define BS 4096
|
||||
#define ROUNDS 7
|
||||
#define DEPTH 32
|
||||
#define OPS 50000
|
||||
|
||||
static volatile long sink;
|
||||
static inline double now_ns(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec; }
|
||||
static int cmpd(const void*a,const void*b){ double x=*(const double*)a,y=*(const double*)b; return (x>y)-(x<y); }
|
||||
static void report(const char*n,double*r,int c,long it){ qsort(r,c,sizeof(double),cmpd);
|
||||
printf("%-44s %10.1f %10.1f (n=%ld)\n",n,r[0],r[c/2],it); fflush(stdout); }
|
||||
|
||||
/* Un pase de OPS lecturas de 4K en profundidad DEPTH. Devuelve ns/op. */
|
||||
static double pase(struct io_uring *ring,int fd,char *buf,long nblk){
|
||||
double t0=now_ns();
|
||||
for(long done=0;done<OPS;){
|
||||
int batch = (OPS-done<DEPTH)?(int)(OPS-done):DEPTH;
|
||||
for(int k=0;k<batch;k++){
|
||||
struct io_uring_sqe *s=io_uring_get_sqe(ring);
|
||||
io_uring_prep_read(s,fd,buf+k*BS,BS,(long)(((done+k)*7919)%nblk)*BS);
|
||||
}
|
||||
io_uring_submit_and_wait(ring,batch);
|
||||
struct io_uring_cqe *c; unsigned h; int seen=0;
|
||||
io_uring_for_each_cqe(ring,h,c){ seen++; }
|
||||
io_uring_cq_advance(ring,seen);
|
||||
done+=batch;
|
||||
}
|
||||
return (now_ns()-t0)/(double)OPS;
|
||||
}
|
||||
|
||||
int main(int argc,char**argv){
|
||||
if(argc<3){ fprintf(stderr,"uso: %s <dir> <etiqueta>\n",argv[0]); return 2; }
|
||||
char path[512]; snprintf(path,sizeof path,"%s/sqpoll.bin",argv[1]);
|
||||
int fd=open(path,O_CREAT|O_RDWR|O_TRUNC,0644);
|
||||
if(fd<0){ fprintf(stderr,"no pude crear %s: %s\n",path,strerror(errno)); return 1; }
|
||||
char *b=malloc(MB); memset(b,0x5A,MB);
|
||||
for(int i=0;i<SIZE/MB;i++) if(write(fd,b,MB)!=MB){ perror("write"); return 1; }
|
||||
free(b);
|
||||
/* Se lee entero una vez para dejarlo en cache: se mide el camino de ENTREGA, no el disco. */
|
||||
char *buf=aligned_alloc(4096,DEPTH*BS);
|
||||
for(long o=0;o<SIZE;o+=BS) sink=pread(fd,buf,BS,o);
|
||||
long nblk=SIZE/BS;
|
||||
|
||||
printf("\n### 4K aleatorios en cache sobre %s (profundidad %d)\n",argv[2],DEPTH);
|
||||
printf("%-44s %10s %10s\n","BENCH (ns/op)","min","mediana");
|
||||
|
||||
double r[ROUNDS];
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
double t0=now_ns();
|
||||
for(long i=0;i<OPS;i++) sink=pread(fd,buf,BS,(long)((i*7919)%nblk)*BS);
|
||||
r[q]=(now_ns()-t0)/(double)OPS;
|
||||
}
|
||||
report("pread 4K (una syscall por op)",r,ROUNDS,OPS);
|
||||
|
||||
struct io_uring ring;
|
||||
if(io_uring_queue_init(256,&ring,0)==0){
|
||||
for(int q=0;q<ROUNDS;q++) r[q]=pase(&ring,fd,buf,nblk);
|
||||
report("io_uring sin SQPOLL",r,ROUNDS,OPS);
|
||||
io_uring_queue_exit(&ring);
|
||||
} else printf("io_uring: no disponible (%s)\n",strerror(errno));
|
||||
|
||||
struct io_uring_params p; memset(&p,0,sizeof p);
|
||||
p.flags=IORING_SETUP_SQPOLL; p.sq_thread_idle=2000;
|
||||
if(io_uring_queue_init_params(256,&ring,&p)==0){
|
||||
/* Con SQPOLL el fd hay que registrarlo: el hilo del kernel no tiene la tabla de fds. */
|
||||
int rc=io_uring_register_files(&ring,&fd,1);
|
||||
int usar_fijo = (rc==0);
|
||||
for(int q=0;q<ROUNDS;q++){
|
||||
double t0=now_ns();
|
||||
for(long done=0;done<OPS;){
|
||||
int batch=(OPS-done<DEPTH)?(int)(OPS-done):DEPTH;
|
||||
for(int k=0;k<batch;k++){
|
||||
struct io_uring_sqe *s=io_uring_get_sqe(&ring);
|
||||
io_uring_prep_read(s,usar_fijo?0:fd,buf+k*BS,BS,(long)(((done+k)*7919)%nblk)*BS);
|
||||
if(usar_fijo) s->flags|=IOSQE_FIXED_FILE;
|
||||
}
|
||||
io_uring_submit_and_wait(&ring,batch);
|
||||
struct io_uring_cqe *c; unsigned h; int seen=0;
|
||||
io_uring_for_each_cqe(&ring,h,c){ if(c->res<0) sink=c->res; seen++; }
|
||||
io_uring_cq_advance(&ring,seen);
|
||||
done+=batch;
|
||||
}
|
||||
r[q]=(now_ns()-t0)/(double)OPS;
|
||||
}
|
||||
report(usar_fijo?"io_uring SQPOLL (fd registrado)":"io_uring SQPOLL (fd normal)",r,ROUNDS,OPS);
|
||||
io_uring_queue_exit(&ring);
|
||||
} else printf("SQPOLL: no disponible (%s) — pide CAP_SYS_NICE\n",strerror(errno));
|
||||
|
||||
unlink(path); close(fd);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# condiciones: 2026-08-29T19:27:32Z · 7.1.4-artix1-1 · load 1.25 1.30 1.93 · 4285 MiB libres · root
|
||||
# bench_connector — SDD 25 seccion 9, experimento 1
|
||||
|
||||
### A. Latencia de notificacion: waitpid contra evento del connector
|
||||
BENCH (ns por proceso) min mediana
|
||||
fork+_exit+waitpid (SIGCHLD, la base) 114825.2 123130.6 (n=200)
|
||||
fork+_exit+esperar EXIT del connector 116041.0 133773.5 (n=200)
|
||||
eventos EXIT que no llegaron en 200 ms: 0 de 1400
|
||||
|
||||
### B. Ceguera: 200 procesos de vida minima, uno cada 10 ms
|
||||
sondeo /proc cada 100 ms : 0 de 200 procesos vistos (23 barridos, 10.5 ms de CPU)
|
||||
proc connector (eventos) : 200 de 200 procesos vistos (95.4 ms de CPU)
|
||||
NOTA: el connector se lee aca con espera activa de 200 us, que NO es el modo de un
|
||||
supervisor real (seria epoll) => su CPU es cota superior. Lo que cuenta: COBERTURA.
|
||||
|
||||
### C. Estadisticas de UNA tarea: taskstats binario contra /proc de texto
|
||||
familia TASKSTATS = 41, cpu de este proceso = 198921000 ns
|
||||
BENCH (ns por consulta) min mediana
|
||||
taskstats por genetlink (binario) 2688.1 2726.3 (n=2000)
|
||||
open+read+parse /proc/self/stat 2696.7 2716.5 (n=2000)
|
||||
@@ -0,0 +1,34 @@
|
||||
# condiciones: 2026-08-29T19:29:16Z · 7.1.4-artix1-1 · load 1.30 1.30 1.87
|
||||
# xfsprogs mkfs.xfs version 7.1.1 · btrfs-progs mkfs.btrfs, part of btrfs-progs v7.1
|
||||
|
||||
### copia de 64 MiB sobre ext4
|
||||
BENCH min mediana
|
||||
FICLONE (reflink entero) no soportado (Operation not supported)
|
||||
copy_file_range 13.655 ms 15.013 ms 4914 MB/s
|
||||
read+write (1 MiB) 14.455 ms 14.911 ms 4643 MB/s
|
||||
# xfs montado: xfs rw,relatime,inode64,logbufs=8,logbsize=32k,noquota
|
||||
|
||||
### copia de 64 MiB sobre xfs (loop)
|
||||
BENCH min mediana
|
||||
FICLONE (reflink entero) 0.010 ms 0.013 ms (plano: no mueve datos)
|
||||
copy_file_range 0.010 ms 0.010 ms (plano: no mueve datos)
|
||||
read+write (1 MiB) 10.964 ms 11.290 ms 6121 MB/s
|
||||
|
||||
### copia de 512 MiB sobre xfs (loop), fichero 8x mas grande
|
||||
BENCH min mediana
|
||||
FICLONE (reflink entero) 0.020 ms 0.021 ms (plano: no mueve datos)
|
||||
copy_file_range 0.019 ms 0.020 ms (plano: no mueve datos)
|
||||
read+write (1 MiB) 512.712 ms 600.930 ms 1047 MB/s
|
||||
# btrfs montado: btrfs rw,relatime,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
|
||||
|
||||
### copia de 64 MiB sobre btrfs (loop)
|
||||
BENCH min mediana
|
||||
FICLONE (reflink entero) 0.006 ms 0.014 ms (plano: no mueve datos)
|
||||
copy_file_range 0.006 ms 0.008 ms (plano: no mueve datos)
|
||||
read+write (1 MiB) 31.323 ms 32.181 ms 2143 MB/s
|
||||
|
||||
### copia de 512 MiB sobre btrfs (loop), fichero 8x mas grande
|
||||
BENCH min mediana
|
||||
FICLONE (reflink entero) 0.015 ms 0.020 ms (plano: no mueve datos)
|
||||
copy_file_range 0.015 ms 0.020 ms (plano: no mueve datos)
|
||||
read+write (1 MiB) 236.891 ms 533.540 ms 2266 MB/s
|
||||
@@ -0,0 +1,13 @@
|
||||
# condiciones: 2026-08-29T19:30:07Z · 7.1.4-artix1-1 · load 2.23 1.61 1.95 · root
|
||||
|
||||
### 4K aleatorios en cache sobre ext4 (profundidad 32)
|
||||
BENCH (ns/op) min mediana
|
||||
pread 4K (una syscall por op) 554.4 590.2 (n=50000)
|
||||
io_uring sin SQPOLL 617.7 633.8 (n=50000)
|
||||
io_uring SQPOLL (fd registrado) 867.7 1068.6 (n=50000)
|
||||
|
||||
### 4K aleatorios en cache sobre tmpfs (profundidad 32)
|
||||
BENCH (ns/op) min mediana
|
||||
pread 4K (una syscall por op) 749.3 764.2 (n=50000)
|
||||
io_uring sin SQPOLL 2029.6 2237.6 (n=50000)
|
||||
io_uring SQPOLL (fd registrado) 1409.3 2296.8 (n=50000)
|
||||
@@ -0,0 +1,126 @@
|
||||
# condiciones: 2026-08-29T19:30:50Z · 7.1.4-artix1-1 · strace 6.19 DEL PROPIO CORPUS (store/3c1fd8c0…-strace)
|
||||
# fuente: hola.c — un main() con stdio/stdlib/string, o sea el caso MAS BARATO posible
|
||||
|
||||
=== cc -O2 -c (gcc 16) ===
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17960 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17971 attached
|
||||
% time seconds usecs/call calls errors syscall
|
||||
------ ----------- ----------- --------- --------- ----------------
|
||||
80.09 0.048227 24113 2 wait4
|
||||
7.44 0.004479 4 923 919 readlink
|
||||
3.00 0.001808 17 105 read
|
||||
2.30 0.001387 6 228 127 openat
|
||||
1.28 0.000772 8 92 mmap
|
||||
1.02 0.000614 87 7 4 execve
|
||||
0.87 0.000522 5 101 close
|
||||
0.78 0.000470 4 100 fstat
|
||||
0.77 0.000466 232 2 clone3
|
||||
0.77 0.000463 6 67 24 newfstatat
|
||||
0.37 0.000220 7 31 brk
|
||||
0.27 0.000165 8 20 mprotect
|
||||
0.17 0.000100 4 23 15 access
|
||||
0.11 0.000065 3 19 rt_sigaction
|
||||
0.10 0.000060 30 2 unlink
|
||||
0.09 0.000056 18 3 getcwd
|
||||
0.09 0.000056 5 11 prlimit64
|
||||
0.09 0.000055 6 8 write
|
||||
0.09 0.000053 3 14 rt_sigprocmask
|
||||
0.07 0.000043 3 12 lseek
|
||||
0.04 0.000026 3 7 getrandom
|
||||
0.04 0.000022 11 2 munmap
|
||||
0.04 0.000022 3 6 6 ioctl
|
||||
0.02 0.000013 4 3 sysinfo
|
||||
0.02 0.000011 3 3 rseq
|
||||
0.02 0.000010 3 3 arch_prctl
|
||||
0.02 0.000010 3 3 set_tid_address
|
||||
0.02 0.000010 3 3 set_robust_list
|
||||
0.01 0.000007 3 2 fcntl
|
||||
0.01 0.000004 3 1 getrusage
|
||||
------ ----------- ----------- --------- --------- ----------------
|
||||
100.00 0.060214 33 1803 1095 total
|
||||
|
||||
=== zig cc -O2 -c (zig 0.16.0) ===
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17979 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17980 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17984 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17988 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17989 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17990 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 17993 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 18047 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 18053 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 18054 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 18055 attached
|
||||
/mnt/vvv/hammer/store/3c1fd8c0fbb20c272c2935d5b5ca63bc51be257c271e96e312ee86ed6886f89d-strace/usr/bin/strace: Process 18056 attached
|
||||
% time seconds usecs/call calls errors syscall
|
||||
------ ----------- ----------- --------- --------- ------------------
|
||||
69.79 1.378703 1248 1104 14 futex
|
||||
25.60 0.505698 101139 5 wait4
|
||||
1.26 0.024957 2495 10 poll
|
||||
0.69 0.013629 42 318 80 openat
|
||||
0.33 0.006426 18 353 mmap
|
||||
0.31 0.006187 38 162 43 newfstatat
|
||||
0.18 0.003615 47 76 read
|
||||
0.17 0.003425 54 63 pread64
|
||||
0.17 0.003361 17 195 rt_sigprocmask
|
||||
0.15 0.003003 12 245 close
|
||||
0.13 0.002634 25 105 29 readlink
|
||||
0.13 0.002511 179 14 8 execve
|
||||
0.12 0.002410 15 159 fstat
|
||||
0.12 0.002312 28 82 mprotect
|
||||
0.09 0.001870 103 18 munmap
|
||||
0.09 0.001743 435 4 clone
|
||||
0.08 0.001520 27 56 brk
|
||||
0.08 0.001497 748 2 mkdirat
|
||||
0.07 0.001355 43 31 19 access
|
||||
0.06 0.001176 4 254 preadv
|
||||
0.06 0.001096 16 67 rt_sigaction
|
||||
0.05 0.000936 117 8 clone3
|
||||
0.03 0.000545 45 12 readv
|
||||
0.02 0.000492 28 17 set_robust_list
|
||||
0.02 0.000483 53 9 7 faccessat2
|
||||
0.02 0.000430 6 69 statx
|
||||
0.02 0.000353 25 14 write
|
||||
0.02 0.000342 15 22 prlimit64
|
||||
0.02 0.000297 22 13 rseq
|
||||
0.01 0.000277 25 11 madvise
|
||||
0.01 0.000256 32 8 pipe2
|
||||
0.01 0.000247 19 13 13 ioctl
|
||||
0.01 0.000242 16 15 getrandom
|
||||
0.01 0.000210 30 7 sched_getaffinity
|
||||
0.01 0.000155 25 6 dup2
|
||||
0.01 0.000149 18 8 sigaltstack
|
||||
0.01 0.000147 13 11 lseek
|
||||
0.01 0.000134 22 6 getdents64
|
||||
0.00 0.000088 29 3 1 unlinkat
|
||||
0.00 0.000078 12 6 arch_prctl
|
||||
0.00 0.000075 18 4 gettid
|
||||
0.00 0.000073 12 6 set_tid_address
|
||||
0.00 0.000060 29 2 rename
|
||||
0.00 0.000048 16 3 ftruncate
|
||||
0.00 0.000045 22 2 pwritev
|
||||
0.00 0.000036 17 2 flock
|
||||
0.00 0.000028 28 1 readlinkat
|
||||
0.00 0.000022 22 1 renameat
|
||||
0.00 0.000020 20 1 unlink
|
||||
0.00 0.000016 5 3 sysinfo
|
||||
0.00 0.000008 7 1 uname
|
||||
0.00 0.000005 4 1 getcwd
|
||||
------ ----------- ----------- --------- --------- ------------------
|
||||
100.00 1.975424 547 3608 214 total
|
||||
|
||||
=== reparto de fallos del gcc (ENOENT por buscar cabeceras) ===
|
||||
166
|
||||
|
||||
=== tiempo de pared del MISMO compilado, sin strace (mínimo/mediana de 7) ===
|
||||
cc -O2 -c hola.c min 30.5 ms mediana 33.3 ms
|
||||
zig cc -O2 -c hola.c min 53.6 ms mediana 58.9 ms
|
||||
|
||||
=== lectura ===
|
||||
gcc: 1803 syscalls, 1095 con error (61%) — 919 de 923 readlink FALLAN.
|
||||
Al piso medido de 78,7 ns por syscall, 1803 cruces = 0,14 ms sobre 30,5 ms de compilado
|
||||
=> el cruce al kernel es el 0,5% del compilado. La tormenta de rutas de T8 vale
|
||||
MILISEGUNDOS por invocación, no minutos.
|
||||
zig cc: 3608 syscalls, sólo 214 con error, pero 1104 futex (70% del tiempo EN syscall) y 5 wait4.
|
||||
Su perfil no es de búsqueda de rutas sino de coordinación entre hilos; el tiempo de
|
||||
syscall que reporta strace -c es espera BLOQUEADA, no CPU.
|
||||
Reference in New Issue
Block a user