C no es un lenguaje de bajo nivel: las CPU ya no son PDP-11 rápidos

Fuentes: C Is Not a Low-level Language: Your Computer Is Not a Fast PDP-11

Este artículo, publicado en ACM Queue por David Chisnall (Universidad de Cambridge), sostiene que C dejó de ser un lenguaje de bajo nivel hace décadas y que las vulnerabilidades Spectre y Meltdown son consecuencia directa de los esfuerzos de la industria por mantener esa ilusión. La definición de Alan Perlis —"un lenguaje es de bajo nivel cuando sus programas exigen atención a lo irrelevante"— describe C con precisión, pero los autores prefieren una visión más útil: un lenguaje de bajo nivel debe ofrecer una máquina abstracta fácilmente trasladable a las abstracciones del hardware.

Para los autores, C fue de bajo nivel en la era del PDP-11: ambos compartían ejecución secuencial, memoria plana y modos de direccionamiento idénticos. Sin embargo, los procesadores modernos ya no exponen esa máquina abstracta. Las CPU actuales ejecutan hasta 180 instrucciones en vuelo, frente a la expectativa secuencial del modelo C, y mantienen hasta tres niveles de caché invisibles para el programador. Para preservar la ilusión, los fabricantes añaden paralelismo a nivel de instrucción (ILP), predicción de saltos y motores de renombrado de registros, mecanismos cuya complejidad y consumo eléctrico son enormes. La ejecución especulativa que originó Spectre y Meltdown es, precisamente, el subproducto de intentar adivinar el destino de unos 25 saltos por iteración y mantener el pipeline lleno desde un único hilo.

El texto también desmonta la idea de que C sea intrínsecamente rápido: el compilador Clang/LLVM, con unos dos millones de líneas de código y alrededor de 200.000 dedicadas a pases de análisis y transformación, aplica optimizaciones cada vez más elaboradas —aut vectorización, desenrollado, SROA— para acercarse al rendimiento que los programadores esperan. Limitaciones como la palabra clave restrict, las garantías de disposición de estructuras o el relleno obligatorio al final de cada struct obligan al compilador a malgastar ciclos copiando espacio muerto. Los autores concluyen que, si C quiere seguir siendo cercano al metal, el estándar debe evolucionar; de lo contrario, seguirá arrastrando decisiones de diseño de los años setenta sobre hardware que ya no existe.