5 Commits
Author SHA1 Message Date
Sergio e852f48491 takana etapa 5a: los comentarios de las 741 recetas
Barrido de TEXTO puro: 1427 líneas de comentario TOML. Cero ArtifactHash
movidos, y eso está MEDIDO, no deducido: hasheé las 741 antes y después y los
ficheros de hashes son idénticos byte a byte (723 con hash real + 18 que ya no
hasheaban de antes).

El guardián valió la pena: el primer barrido, filtrando por 'la línea empieza
con #', movió el hash de helix, lsof y steam-runtime-sniper. La causa es que una
fase se escribe como compile = <triple> ... <triple> y sus comentarios de SHELL
también empiezan con #, pero viven dentro del VALOR — y las fases sí entran en
hash_inputs. El barrido ahora calcula los rangos de las cadenas multilínea de
TOML y no entra ahí.

Quedan intactos a propósito: .hammer-zig-cc y .hammer-cargo-vendor (literales
dentro de fases), hammerd, hammer-recover y toda ruta que empiece por /
2026-09-09 19:23:26 +00:00
Sergio c3413d293e jarlog APARCADO: rompe atuq, y el coste está medido mientras el beneficio no
Al reconstruir atuq sobre el firefox con jarlog, murió:

    zipfile.BadZipFile: Bad magic number for central directory  (rebrand.py)

El jarlog convierte el omni.ja al formato «jar optimizado» de Mozilla, que mueve
el directorio central al principio:

    sin jarlog:  PK\003\004    ZIP estándar — zipfile lo abre, 5306 entradas
    con jarlog:  \376\204#\0   zipfile lo rechaza

Y atuq reempaqueta el omni.ja con zipfile para su branding. O sea que el jarlog
rompe el navegador propio de la distro.

La cuenta es asimétrica: el COSTE está medido y el BENEFICIO no —el banco corre
con caché caliente, donde reordenar el omni.ja no ahorra ninguna lectura, y dio
-0,1%, por debajo del suelo de ruido del propio banco—. Cambiar algo que
funciona por una ganancia que no se pudo medir, rompiendo algo que sí
funcionaba, es mal negocio.

Revertido a los hashes YA construidos y medidos (perfil 3647c6be, firefox
3d199174, atuq fab2fbfb): cero reconstrucciones. La documentación va fuera de
los campos hasheados, verificado antes y después.

Se retoma cuando (1) se mida el arranque EN FRÍO, que es su terreno, y (2)
rebrand.py sepa leer el jar optimizado o des-optimizarlo antes. El blob
92497cdd del mirror ya trae el jarlog: retomarlo es cambiar una línea.

Y LA LECCIÓN, que es la que más vale: el jarlog parecía GRATIS. «Es lo que hace
upstream y no cuesta nada» lo escribí yo en este mismo documento hace unas
horas. El coste no apareció midiendo el jarlog sino CONSTRUYENDO LO QUE DEPENDÍA
DE ÉL. Una función que se declara gratuita sin haber reconstruido a sus
consumidores no es gratuita: es no medida.
2026-09-08 14:08:25 +00:00
Sergio d9833a581b jarlog: la mitad del PGO que faltaba — el orden de omni.ja para el arranque
El jarlog registra en qué orden se LEEN los ficheros dentro de omni.ja al
arrancar; con él, el empaquetador los reordena y el arranque hace lecturas
secuenciales en vez de saltar por el archivo.

NO hizo falta la extensión Quitter de Mozilla, que era lo que yo daba por
bloqueante: basta MOZ_JAR_LOG_FILE en el entorno del navegador. Su
profileserver.py sólo traduce JARLOG_FILE a esa variable — leerlo costó un grep
y ahorró rehacer el arnés entero.

Y se genera en una corrida APARTE, que está medido y no supuesto. Mozilla lo
emite en la misma sesión del profileserver; nuestro arnés arranca un navegador
POR PÁGINA, así que había que saber si el fichero se acumula o se pisa. Se pisa:
tras rejilla.html y tras tablas.html dio exactamente los mismos 27.652 bytes y
469 líneas. Su contenido lo domina el ARRANQUE, no la página, así que una
corrida dedicada vale igual que una de 46 — rehacer el perfilado sólo para
obtenerlo habría sido gasto sin diferencia. Se conserva el profdata ya medido
(el del -11,4%), que es lo que hace comparables los números.

Guardián propio: un jarlog vacío o que no nombre los archivos reales NO rompe el
build de firefox, sólo deja el omni.ja sin ordenar — o sea que se pierde justo
lo que se vino a buscar, en silencio. Se exige que mencione los DOS archivos que
el navegador abre (omni.ja y browser/omni.ja).
2026-09-08 02:16:53 +00:00
Sergio a5bcf4fd3e perfil PGO v2: 46 páginas y 6,5× más ejecuciones registradas
El corpus se amplió por una MEDICIÓN, no por corazonada. Con las 36 de Mozilla
la ganancia era -9,4% en DOM/maquetación y -1,0% en 3d-raytrace, y la razón es
que un benchmark JIT-bound es casi ciego al PGO: el bucle caliente lo ejecuta
código que el JIT emite en runtime, y el PGO optimiza el intérprete, el GC y el
propio JIT, no lo que el JIT produce. El corpus de Mozilla está dominado en
número de páginas por SunSpider, o sea que entrenaba justo donde menos rinde.

Con las 10 de scripts/pgo-corpus/ sumadas, el efecto se ve EN EL PROPIO PERFIL:

    ejecuciones registradas   5.885.254.799 -> 38.498.366.335   (6,5x)
    máximo por función          416.219.136 ->  1.552.416.768   (3,7x)

Funciones y bloques totales no cambian (501.521 / 3.734.991) porque son la
estructura estática del binario instrumentado, no lo que se ejecutó.

Blob nuevo publicado y verificado bajándolo del mirror por el mismo camino que
usa hammer: 17.525.708 bytes, sha256 95472411.
2026-09-07 20:43:49 +00:00
Sergio e6e4d9260d PGO: el perfil sellado y firefox consumiéndolo (SDD 26, unidad 3.a)
merged.profdata: 501.521 funciones, 3.734.991 bloques, 5.885.254.799 ejecuciones
registradas. Recogido corriendo firefox-instrumentado sobre el corpus de
entrenamiento de Mozilla (build/pgo: blueprint para maquetación, js-input y
sunspider para el motor JS), 35 de 36 páginas servidas.

VIVE COMO FUENTE PINEADA, NO COMO RECETA QUE LO GENERA. El perfil NO es
determinista —los contadores dependen del timing—, así que una receta que lo
produjera tendría un ArtifactHash estable sobre bytes cambiantes: firefox
consumiría cosas distintas en la misma dirección y dejaría de reproducir sin que
nada lo dijera. Es el modo de fallo del lab fuera de hash_inputs, fabricado a
propósito. Sellarlo una vez y pinearlo por sha256 hace que el insumo sea no
determinista y el build vuelva a serlo.

El blob (17.426.112 bytes, xz) está publicado en hammer/fuentes/<sha256>.tar del
Storage Box y verificado bajándolo de vuelta por el mismo camino que usa hammer.
La URL upstream no resuelve por DNS a propósito: es el patrón que el ADR 0013 ya
prueba, y como la URL nunca estuvo en hash_inputs, mover el objeto de origen no
re-hashea nada.

Dos cosas que costaron y quedan escritas:

 · strip_components = 0. hammer recorta un componente al extraer (los releases
   GNU traen un proyecto-version/ de más) y este tarball lleva el fichero en la
   raíz, así que el default se llevaba lo único que había. El error era
   «cp: cannot stat merged.profdata» y no menciona el recorte por ningún lado.

 · SIN --with-pgo-jarlog, y es una mitad que falta, no un olvido. Alpine pasa
   además un jarlog que reordena omni.ja para acelerar el ARRANQUE; lo emite el
   profileserver.py de Mozilla con su extensión Quitter, que la corrida headless
   no tiene. Hoy se gana la disposición de CÓDIGO y no el orden del omni.ja.

Y VIAJA UN ARREGLO QUE NO ES DE PGO, porque firefox se reconstruye igual: EL
ARTEFACTO NO ARRANCABA. Medido sobre el sellado: `firefox --version` moría con
«Error loading shared library libnspr4.so / Couldn't load XPCOM». mach install
deja /usr/bin/firefox como symlink pelado, el binario no trae RUNPATH y el
cierre no publica ld-musl-x86_64.path, así que las librerías que viven junto al
binario no las encuentra nadie. atuq andaba sólo porque su receta añade este
mismo lanzador; firefox, que va en las CUATRO imágenes de escritorio, no lo
tenía. Es el NEEDED colgante un escalón más allá —no falta la librería, falta el
modo de encontrarla— y por eso vigia-sonames.py no puede verlo: las librerías SÍ
están en el artefacto y las cuenta como propias.

firefox: b3:8116bdec -> b3:1d730334.
2026-09-07 10:04:42 +00:00