#!/usr/bin/env python3 """ 004 · Cuánto duerme de verdad un sleep. Mide nanosleep (C), time.sleep (Python) y setTimeout (Node) sobre los mismos objetivos, en la misma máquina, y escribe medicion_004.json. python3 medir_004.py # 400 muestras por celda python3 medir_004.py --n 1000 # más muestras, mejor cola python3 medir_004.py --solo py # py | node | c Compila el binario de C solo si hay cc disponible. Node y C son opcionales: lo que falte se marca como no disponible y el resto sigue. """ import argparse, json, os, platform, shutil, statistics, subprocess, sys, tempfile, time TARGETS = [0.1, 1, 5, 16] C_SRC = r''' #include #include #include static double ns_now(void){struct timespec t;clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec;} int main(int argc,char**argv){ double ms=atof(argv[1]); int n=atoi(argv[2]); struct timespec req; req.tv_sec=(time_t)(ms/1000.0); req.tv_nsec=(long)((ms/1000.0-req.tv_sec)*1e9); printf("["); for(int i=0;i{const xs=[]; for(let i=0;isetTimeout(r,ms)); xs.push(Number(process.hrtime.bigint()-a)/1e6);} console.log(JSON.stringify(xs.map(x=>+x.toFixed(5))));})(); ''' def stats(xs): s = sorted(xs) n = len(s) return { "min": round(s[0], 4), "med": round(statistics.median(s), 4), "mean": round(statistics.mean(s), 4), "p95": round(s[min(n - 1, int(n * 0.95))], 4), "p99": round(s[min(n - 1, int(n * 0.99))], 4), "max": round(s[-1], 4), "n": n, "hist": hist(s), } def hist(s, nb=24): lo, hi = s[0], s[-1] w = (hi - lo) / nb if hi > lo else 1.0 b = [0] * nb for x in s: b[min(nb - 1, int((x - lo) / w))] += 1 return {"lo": round(lo, 4), "hi": round(hi, 4), "bins": b} def py_bench(ms, n): t = ms / 1000.0 xs = [] for _ in range(n): a = time.perf_counter() time.sleep(t) xs.append((time.perf_counter() - a) * 1000.0) return xs def run_json(cmd): out = subprocess.check_output(cmd, timeout=600).decode() return json.loads(out.strip().splitlines()[-1]) def cpu_name(): try: if sys.platform == "darwin": return subprocess.check_output( ["sysctl", "-n", "machdep.cpu.brand_string"]).decode().strip() if sys.platform == "linux": for line in open("/proc/cpuinfo"): if "model name" in line: return line.split(":", 1)[1].strip() except Exception: pass return platform.processor() or platform.machine() def main(): ap = argparse.ArgumentParser() ap.add_argument("--n", type=int, default=400) ap.add_argument("--solo", choices=["py", "node", "c"], default=None) args = ap.parse_args() N = args.n want = {args.solo} if args.solo else {"py", "node", "c"} tmp = tempfile.mkdtemp() cbin = jsfile = None if "c" in want: cc = shutil.which("cc") or shutil.which("gcc") or shutil.which("clang") if cc: csrc = os.path.join(tmp, "s.c") cbin = os.path.join(tmp, "s") open(csrc, "w").write(C_SRC) try: subprocess.check_call([cc, "-O2", "-o", cbin, csrc], stderr=subprocess.DEVNULL) except Exception: cbin = None if "node" in want and shutil.which("node"): jsfile = os.path.join(tmp, "s.js") open(jsfile, "w").write(JS_SRC) meta = { "fecha": time.strftime("%Y-%m-%d"), "cpu": cpu_name(), "so": f"{platform.system()} {platform.release()}", "python": platform.python_version(), "node": (subprocess.check_output(["node", "-v"]).decode().strip() if jsfile else None), "c": bool(cbin), "muestras_por_celda": N, "reloj": "perf_counter / hrtime.bigint / CLOCK_MONOTONIC", } print(f" {meta['cpu']} · {meta['so']} · {N} muestras/celda\n") print(f" {'obj':>7} {'lenguaje':<8} {'med':>9} {'p95':>9} {'max':>9}") res = {} for tgt in TARGETS: for lang in ("c", "py", "node"): if lang not in want: continue try: if lang == "py": xs = py_bench(tgt, N) elif lang == "c": if not cbin: continue xs = run_json([cbin, str(tgt), str(N)]) else: if not jsfile: continue xs = run_json(["node", jsfile, str(tgt), str(N)]) except Exception as e: print(f" {tgt:>7} {lang:<8} error: {e}") continue st = stats(xs) res[f"{lang}:{tgt}"] = {"target": tgt, "lang": lang, **st} print(f" {tgt:>7} {lang:<8} {st['med']:>9.4f} " f"{st['p95']:>9.4f} {st['max']:>9.4f}") with open("medicion_004.json", "w") as f: json.dump({"meta": meta, "resultados": res}, f, indent=2, ensure_ascii=False) print("\n medicion_004.json escrito") # el titular: ¿redondea alguien el objetivo submilisegundo? for lang in ("c", "py", "node"): k = f"{lang}:0.1" if k in res: r = res[k] factor = r["med"] / 0.1 print(f" {lang:<5} pidió 0,1 ms y durmió {r['med']:.3f} " f"({factor:.1f}×)") if __name__ == "__main__": main()