Inclusión de aportes mediante las diferentes estrategias de Merge en Git

Cada vez suele ser más normal ver la figura del ingeniero de software que se dedica a la gestión de proyectos, gestionando las ramas de desarrollo (develop & features) y la rama estable master (recientemente normalizada como main).

Imagen obtenida de la presentación Desarrollo Colaborativo de Software

Muchas veces sin querer se usan las herramientas de "merge" de código de manera incorrecta sin pararse a pensar en los beneficios y "contraprestaciones" que puede tener hacerlo de una forma u otra. Es por ello que este artículo, pretende arrojar algo de luz en qué nos puede convenir hacer en cada momento.

Antes de nada vamos a aclarar la notación para que resulte todo más sencillo. Nos referiremos a la rama Base, como la rama a la que se desea fusionar los cambios en el proceso de merge. Por otro lado nos referiremos a la rama Head como la rama que contiene los cambios que queremos incluir en la rama Base. Estos cambios en términos de plataformas para el desarrollo colaborativo de software como GitHub, GitLab, Bitbucket o Gogs, se suele aplicar con frecuencia en los conocidos como Pull Request (PR). Una solicitud formal a la inclusión de código de un desarrollador o grupo de desarrolladores externo al equipo de desarrolladores de un repositorio, la cual puede ser revisada y probada antes de realizar el merge.

La rama Base más habitual es la master o main que es asumida además como rama estable del desarrollo en un repositorio de manera generalizada. Los puntos más estables y que deberías usar son los denominados Tags de versión que son puntos específicos donde se ha trabajado una estabilidad específica que suele cerrar una iteración de desarrollo que incluye mejoras y soluciones a fallos conocidos desde la anterior versión.

Porque sale fuera de alcance de este artículo, no mencionaremos toda la problemática y gestión inherente que puede aparecer en forma de conflicto cuando realizamos un merge de código. Conflictos que deberán ser resueltos para asegurar la coherencia y estabilidad de la rama Base. Y que dicho sea de paso, suelen ser producidos muchas veces por falta de comunicación o por no ser "aséptico" en la forma de programar, refactorizando el código más de la cuenta o cuando no toca, por tener un mal diseño de clases o funciones... o incluso por aplicar la regla del scout que se resume en "Dejar las cosas mejor de como te las encontraste". Esto último puede convertir en un infierno la tarea de la persona que se dedica a integrar cambios y aportes externos si no se gestiona correctamente o falla el proceso de comunicación. Los potenciales conflictos por cambios en el código, es la causa número uno por la que una propuesta de cambio (PR) acaba por no integrarse al código finalmente y por consecuencia genera frustración y pérdida de tiempo.

Merge

La opción de hacer un merge de varios commits de una rama (Head) o hacer un merge de un PR es la acción habitual y predeterminada. Al realizar el merge todos los commits de la rama Head se fusionarán con los de la rama Base.

Estado inicial

Tras aplicar el merge

En esta ilustración, la rama Head se bifurca desde el segundo commit en la rama Base. Se sugieren algunos cambios como nuevas confirmaciones en la rama Head y ahora deben actualizarse en la rama Base. Mediante el merge, las confirmaciones se agregan a la rama Base como se muestra en la imagen superior.

No obstante el ejemplo presentado resulta una visión simplificada, existiendo dos posibles formas de hacer el merge dependiendo de la casuística en la inclusión o no del merge commit.

Fast-Forward o No Fast-Forward 🤔

En el primer caso, el merge fusiona lo cambios y añade los commits de la rama Head en la rama Base. Esto es posible siempre y cuando los commits se produzcan en la rama Head. Básicamente lo que se produce es un desplazamiento del puntero (fast-forward) de la rama Base al último commit de la rama Head.

master = Base | feature = Base

En el segundo caso, con el merge se añade un commit adicional en la rama Base que deja constancia de la unión de la rama Head con la rama Base. Este commit adicional aparece, bien porque lo forzamos para que no se produzca un fast forward (--no-ff); o bien porque la rama Base también contenía cambios, existiendo cambios en ambas ramas en el momento de la fusión.

Squash + Merge

Squash y merge combina todos los commits de la rama Head en un único commit y luego fusiona la rama Head con la rama Base. De esta manera, el historial de commits de la rama Head queda simplificado en un único commit en la rama Base.

Estado inicial

Tras aplicar squash + merge

Se puede observar que la rama Head se bifurca desde el segundo commit de la rama Base y se agregan dos nuevos commits que se añaden a la cabecera de la rama Base. Mediante squash y merge, ambos commits (6 y 7) se combinan en una único commit y luego se fusionan en la rama Base como se muestra en la imagen superior.

Rebase + Merge

Rebase y merge agrega todas los commits en la rama Head de manera individual a la rama Base sin un merge commit. Para todas los hotfix y commits puntuales que no se puedan fusionar con otros commits, esta es la opción de referencia. Esto es debido a que a no existe relación de parentesco con los commits preexistentes del propio árbol.

Cuando se hace una reorganización con rebase en el flujo de los commits de una rama, el SHA único de cada commit cambia debido a que además del contenido del commit para la formación del hash SHA, también se tiene en cuenta el parentesco con el commit inmediatamente anterior y por consiguiente del árbol completo. Este hecho provoca que el uso de rebase provoque más conflictos y su gestión sea más complicada. Esta gestión es complicada, es debido a que Git principalmente usa los commits comunes como base de parentesco, el contenido del propio commit y su contexto (líneas de código anteriores y posteriores a cada cambio de un commit); y en el caso del rebase por causa de la reorganización, no se cuenta con la base de parentesco.

Estado inicial

Tras aplicar rebase + merge de la rama Base a la rama Head

Tras aplicar rebase + merge de la rama head a la rama Base

Atendiendo a la ilustración, la rama Base se bifurca en el segundo commit para formar la rama Head. Posteriormente, se agrega un nuevo commit a la rama Base. Mientras tanto, los commits se realizan en la rama Head.

Por causa del rebase y merge de la rama de la Base a la rama Head, la base de la rama Head se vuelve a colocar. Es decir, ahora la rama Head se bifurca desde el nuevo tercer commit para que el nuevo commit de la rama Base se incluya en la rama Head. Y luego, se aplican los commits en la rama Head.

Ahora, para actualizar la rama Base con los últimos commits de la rama Head, el rebase se realiza de la rama Head a la rama Base como se muestra en la imagen superior.

GitHub

En GitHub podemos ver estas 3 opciones, siendo el ejemplo más habitual e ilustrativo cuando estamos revisando un PR y finalmente decidimos incorporar los cambios:

Haciendo click en la flecha que apunta hacia abajo al lado del "Merge pull request", podremos ver las tres formas de hacer merge que hemos mencionado. De manera análoga GitLab, Bitbucket o Gogs presentan las mismas opciones, o muy similares.

Conclusiones

Tras ver todas las opciones de merge, es recomendable tener en Git cuanta más traza mejor, eso incluye la creación del merge commit evitando el fast-forward si con ello podemos mejorar la traza de la rama donde existían los cambios que se fusionan a la rama Base.

Pero si trabajas en proyectos grandes o eres el gestor de la ramas de desarrollo de un repositorio con suficientes desarrolladores implicados, es más que seguro usarás squash y merge para compartimentar mejor los commits de cada desarrollador. Eso sí asegúrate de tener un buen sistema de CI como Travis CI o AppVeyor, así como suficientes pruebas unitarias, porque el problema de juntar varios commits en uno, es la imposibilidad de revertir los commits parciales una vez que se han juntado.

Por último el uso de rebase y merge es recomendable hacerlo como última opción y en caso de que squash y merge no sean suficiente o se quiera tener la trazabilidad manteniendo los commits parciales de las ramas que se fusionan (hotfix, commits puntales, excesiva densidad de ramas del árbol de versionado Git...), pudiendo realizar así una regresión limpia y más fácil de seguir. La principal desventaja de este método es que se pierde trazabilidad cronológica por la reorganización y potencialmente aumentan los conflictos, pero a la vez se gana simplicidad y claridad en el árbol debido a que no genera una densidad de ramas que complique su gestión. Además, también es posible revertir cualquier commit parcial, pero esto pierde relevancia, cuando se asume que existen servicios de CI que deberías estar ya usando que pueden hacer correr tus pruebas unitarias y ahorrarte sufrimiento y sobresfuerzo innecesario.

Comprobar permisos de Administrador en una ejecución (estilo 🐍 Python)

Habitualmente nos encontramos con la situación en la que debemos ejecutar ciertos programas o flujos de ejecución y necesitamos permisos de Administrador. Estas situaciones se nos presentan cuando tenemos que manejar carpetas y ficheros en acciones que impliquen lectura, escritura o ejecución en directorios protegidos del sistema. Para ello se requiere una elevación de permisos que deberemos comprobar si existe antes de lanzar nuestra ejecución.

Aprovechando que seguramente tengamos que hacer esto varias veces, creo que la mejor opción de hacerlo es con el mejor lenguaje pegamento que existe hasta la fecha, que no es ni más ni menos que Python. Por ello la solución final que vamos a plantear es excelente tanto para administradores de sistemas como para desarrolladores, siendo además multiplataforma GNU/Linux y Windows.

Tenemos que tener en cuenta que GNU/Linux y Windows no funcionan igual para la gestión de permisos. En Windows preguntaremos desde la API disponible si el usuario es administrador, mientras que en GNU/Linux preguntaremos por si el usuario tiene permisos root.

import ctypes, os
from sys import exit


def is_admin():
    is_admin = False
    try:
        is_admin = os.getuid() == 0
    except AttributeError:
        is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0

    print ("Admin privileges: {}".format(is_admin))
    return is_admin

Para mirar los permisos en Windows usaremos la librería ctypes y para los permisos en GNU/Linux usaremos la librería os. Ambas están incluida en Python. Por una razón arbitraría se ha elegido comprobar primero GNU/Linux y después Windows.

Cuando se comprueba en GNU/Linux invocamos a la función os.getuid que nos devolverá 0 en el caso de ser root. Si llamamos a la función desde Windows con Python nos elevará una excepción del tipo AttributeError, que aprovecharemos a capturar para hacer la comprobación en Windows invocando a la función ctypes.windll.shell32.IsUserAnAdmin. En ambos casos guardaremos el booleano de la evaluación y lo devolveremos al final de la función.

Hasta aquí sencillo, pero esto es código Python y es necesario tener el interprete de Python, con el que obliga al sistema a tener instalado el intérprete de Python. Para solucionarlo, deberíamos convertir nuestro código en un programa añadiendo un punto de entrada y devolviendo un código de retorno tras la ejecución.

import ctypes, os
from sys import exit


def is_admin():
    is_admin = False
    is_win = False
    try:
        is_admin = os.getuid() == 0
    except AttributeError:
        is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
        is_win = True

    print ("Admin privileges: {}".format(is_admin))
    return is_admin, is_win

Antes modificaremos un poco la función is_admin()añadiendo un valor is_win que usaremos para saber que estamos comprobando en GNU/Linux o Windows los permisos de administrador, devolviendo el valor como un segundo valor en el retorno de la función.

Recordad que Python es posible recoger múltiples valores de retorno en una variable como una tupla, en variables separadas o incluso tirar los valores que no nos interesen.

def my_function():
    a = 2
    b = 3
    return a, b

a = my_function()
print(a)  # out: (2, 3)

a, b = my_function()
print(a)  # out: 2
print(b)  # out: 3

a, _ = my_function()  #  b es ignorado
print(a)  # out: 2

Además añadimos un punto de entrada debajo de la función is_admin().

if __name__ == "__main__":
    is_admin, is_win = is_admin()
    # Converting boolean to integer 
    ret = int(is_admin == False)
    if is_win:
        exit(ret)
    else:
        exit(ret * -1)

Comprobar si __name__ es __main__, es una excelente forma de hacer que cada unidad de fichero Python sea ejecutable si se quiere. El nombre de __main__ sólo es dado al fichero Python que es dado al intérprete para ser ejecutado y en caso de no serlo, siempre puede ser importado en otro fichero Python, sin que el punto de entrada afecte.

Como vemos, ahora estamos recogiendo en el retorno is_admin e is_win. Necesitamos el segundo valor para decidir el valor de retorno, ya que los códigos de error en Windows son enteros positivos y en GNU/Linux son enteros negativos. El único punto en común es que devolver un 0 es señal de que la ejecución fue correcta. Sabiendo esto debemos adaptar el retorno de los errores dependiendo el sistema.

💾 Binarizando nuestra aplicación Python

Para convertirlo en un ejecutable usaremos PyInstaller, el cual podéis instalar con un simple pip install pyinstaller. Para binarizar nuestra aplicación en un ejecutable autocontenido con el interprete de Python ejecutaremos:

pyinstaller -F is_admin.py

De esta forma resultará, mucho mas sencilla la distribución de nuestra aplicación.

👔 Puesta en Producción

Ahora vamos a plantear los dos casos de uso para Windows y GNU/Linux. Como aclaración, decir que los ejemplos expuestos se pueden usar tanto con la versión binarizada o con la de código invocando al intérprete de Python.

Windows

@echo off

cd
cd /D "%~dp0"
cd

#REM python is_admin.py
is_admin.exe

if errorlevel 1 (
   echo Exit Code is %errorlevel%
   echo.
   echo Admin privileges required
   echo Run it again as Administrator
   echo.
   pause
   exit /b %errorlevel%
)

#REM [your_executable].exe

pause

👀 Puedes descargar la versión binarizada de is_admin.exe

GNU/Linux

#!/bin/bash

pwd
cd `dirname $0`
SCRIPTDIR=`pwd`
pwd

#python is_admin.py
is_admin


if [ $? -eq 0 ]
then
  # [your_executable]
else
  echo Exit Code is $?
  echo.
  echo Admin privileges required
  echo Run it again as Administrator
  echo.
  read -rsp $'Press any key to continue...\n' -n 1 key
  exit $?
fi

read -rsp $'Press any key to continue...\n' -n 1 key

Si no estás tan familiarizado con el Bash te invito a que visites el proyecto ExplainShell para que puedas obtener la explicación de los diferentes comandos y argumentos.

🥅 Conclusiones

La solución presentada, sin ser seguramente perfecta, asegura tanto a administradores de sistemas como desarrolladores, comprobar si existen los permisos de administrador necesarios de manera sencilla; pudiendo hacerlo con una versión binarizada autocontenida que incluya el intérpretete Python, ejecutándolo usando el intérprete de Python del sistema gracias a ser todo dependencias internas, o llamando directamente a la función is_admin() si se quiere integrar en un desarrollo.

El código completo puedes encontrarlo en el siguiente gist de GitHub:

Espero que esta forma de trabajar te resulte útil y puedas extrapolarla y usar para otros casos Python como tu lenguaje pegamento generando código y aplicaciones altamente reutilizables.

Feliz Navidad y Próspero Año 2021

Este año ha sido raro y quizás especialmente duro para algunos con todo el tema del COVID19. Pero como se suele decir, no hay mal que por bien no venga. En mi caso, como supongo que el de muchas otras personas, me ha llevado a replantearme la escala de lo que considero realmente importante, dándome cuenta de que ciertas cosas que consideraba importantes no lo eran tanto y a otras que sí lo eran, no les estaba dedicando el tiempo necesario.

Una de esas cosas que había dejado un poco de lado, era Mascando Bits; el cual tras el último mes creo haber resucitado, nutriendo el blog de nuevo con artículos, que han sido un poco distintos a los que suelo hacer pero que me he sentido a gusto haciendo. Creo que forma parte de la evolución que debe tener un blog de carácter personal como este.

No quisiera dejar de agradecer a todos aquellos que habéis dedicado unos minutos durante este año, para plasmar en comentarios de los distintos artículos que os han gustado, os han sido de ayuda, o incluso algunas correcciones y aportes... Anima un montón a seguir escribiendo cuando ves que hay alguien al otro lado que responde. 😊

Sólo me resta desearos con mis mejores deseos ¡Felices Fiestas! (para los agnósticos y ateos), ¡Feliz Navidad! (para los creyentes) y ¡Feliz Año 2021! (para TODOS 😉 ). ¡Nos vemos a la vuelta!

Historia del impresionante resurgir de AMD y de cómo Intel no lo vio venir – Acto2

Buscando el trasfondo que permite entender la situación actual de Intel y el tremendo auge de AMD, en la anterior entrada contextualizábamos en grandes términos la situación de Intel. Relatando cómo Intel se encontraba en una situación de dominio absoluto, y cómo esa falsa seguridad de no tener competencia relevante que empañase sus ingentes beneficios, aderezado con unas serie de eventualidades, hacen tropezar a Intel dejando vía libre a AMD.

En 2019 llega Zen2. Zen 1 fue una zancadilla que le puso AMD a Intel pero Zen2 es un rodillazo en la cabeza en toda regla. Mientras Intel sigue sin ver la luz, AMD dobla su apuesta, 16 núcleos para la gama doméstica y 64 para servidores. Una vez más AMD vuelve a duplicar el número de núcleos. Intel definitivamente no puede seguir el ritmo. Se espera que su respuesta sea un procesador de 10 núcleos para la gama doméstica y uno de 48 para la gama profesional, que en realidad no son más que 24 núcleos unidos. Ninguno de ellos es suficiente para superar el rendimiento AMD, además se espera que los precios de estos nuevos procesadores sean estratosféricos y su disponibilidad tan limitada que será como si prácticamente no existieran.

En este momento AMD ya se encuentra en un proceso fabricación de 7 nanómetros que les permite hacer procesadores más pequeños, o dicho de otra forma, obtener más procesadores de la misma oblea de silicio. De esta forma el procesador va a ser más barato de fabricar, además de que se va a necesitar menos corriente eléctrica para alimentarlo y va a generar menos temperatura. Pero sobre todo, al ser más corto el circuito, menos tiempo tardará la corriente en recorrerlo de punta a punta, por lo cual el procesador también va a ser algo más rápido.

Pero os estáis preguntando cómo es que AMD puede aumentar el número de núcleos casi sin esfuerzo. ¿No sufren los mismos problemas que Intel a la hora de fabricar procesadores? ¿Cómo es que Intel no puede hacer procesadores con muchos núcleos pero AMD sí? La respuesta a estas preguntas son los chiplets.

¿Qué son los chiplets?

Para abaratar los costes los nuevos procesadores están hechos de partes más pequeñas, llamadas chiplets, en vez de un chip único y grande (monolítico).

  • Los chiplets son componentes independientes que se agrupan para construir un chip más grande.
  • Los chiplets son más pequeños y son más fáciles y baratos de fabricar.
Procesador compuesto por varios chiplets

Hasta ahora, los procesadores se fabricaban con arquitectura monolítica. El diseño monolítico significa que todos los componentes del procesador (ALU, AGU, CU, cache, etc.) están fabricados juntos y en contacto. Como consecuencia, si alguno de esos circuitos falla, todos quedan inservibles.

A medida que los transistores se han hecho más pequeños, el número de fallos por oblea ha ido aumentando; haciendo que el coste de fabricación de circuitos de manera monolítica sea cada vez mayor. Por otro lado, como los chiplets son fabricados de manera independiente del resto de componentes del procesador, si un chiplet falla, no hay que tirar todo el procesador entero, se sustituye por otro y solucionado.

Apuesta de AMD en los procesos de fabricación para abaratar costes y mejorar su competitividad

AMD es una compañía mucho más pequeña que sus competidores (Intel o Nvidia). No pueden permitirse gastarse tanto dinero como estas, desarrollando arquitecturas, por lo tanto siguen la filosofía de hacer las cosas para durar. AMD no desarrolla una arquitectura pensando que dure dos años y luego desarrolla otra. No puede permitirse hacer esto, AMD desarrolla arquitecturas pensando en que duren al menos una década y fue esa filosofía la que siguieron para crear la arquitectura Zen. ¿Cómo podemos crear una arquitectura que nos dure muchos años y que no tengamos que invertir en dos años un dineral para crear otra arquitectura?

Además por aquel entonces ya se olían lo que ocurriría con los procesos de fabricación. Esperaban que la ley de Moore dejase de estar vigente pronto, por lo tanto su arquitectura tenía que ser capaz de lidiar también con esto y no depender de procesos de fabricación para aumentar el número de núcleos. ¿Y cómo lo hicieron? De una forma muy inteligente, desarrollar procesadores como si fueran un Lego, los chiplets. 😉

El diseño de los procesadores de AMD orientados a la fabricación

En AMD crearon un procesador de cuatro núcleos, este procesador va a ser nuestra pieza de lego. Igual que los legos, podemos juntar piezas para crear procesadores más grandes. ¿Quieres un procesador de ocho núcleos? Pues te junto dos piezas. ¿Quieres uno de 16 núcleos? Pues te junto cuatro piezas. Esto para una empresa las características de AMD es extremadamente interesante porque no tienen que desarrollar toda una gama de procesadores desde 4 a 64 núcleos. No, sólo tienen que centrarse en desarrollar un procesador de cuatro núcleos y luego los unen como si fuesen piezas de lego para crear el resto de la gama. La salsa secreta que hace a Zen funcionar es Infinity Fabric.

SCF: Scalable Control Fabric
SDF: Scalable Data Farbic
A. SCF conecta los elementos de procesamiento
B. SDF permite comunicaciones coherentes a lo largo de las caches y entre la caché y la memoria.
C. Ambos, SCF y SDF escalan mediante MCM (Multi-Chip Module) y sockets

Infinity Fabric es la conexión que ha desarrollado AMD para unir esos procesadores de cuatro núcleos, para crear procesadores más grandes. Esta es la primera ventaja de Zen. Desarrollar estos procesadores es más barato y sencillo. Pero pensemos un poco más en esto de las piezas de lego. ¿Realmente me sirven para algo?

Si me ahorro dinero a la hora de diseñar procesadores, pero a la hora de fabricar un procesador de 64 núcleos, sigo teniendo un procesador enorme, es súper difícil y carísimo de fabricar. ¿Cuál es la forma de resolver este problema? Bien, ¿es posible no unir las piezas de lego en el diseño y luego fabricar el procesador; sino hacerlo al revés, fabricar las piezas de lego por separado y después una vez fabricadas unirlas? Es decir, en vez de tener lo que se conoce como un chip monolítico, que es la forma tradicional de fabricar procesadores, no imprimir todo el procesador del tirón. Fabricar cada una de las piezas que forman el procesador por separado y luego soldarlas. La respuesta es, sí se puede y esa es la clave por la que AMD está superando a Intel. Fabricar un procesador de 64 núcleos es muy difícil, es un chip demasiado grande, no es viable fabricar algo así.

Sin embargo AMD no fabrica procesadores de 64 núcleos, AMD fábrica procesadores de 8 núcleos, procesadores pequeños fáciles y baratos de fabricar. Después AMD suelda estos chips de 8 núcleos en un mismo PCB. Para unirlos entre ellos, el primer intento de hacer esto fueron los procesadores Threadripper y EPYC basados en ZEN 1 de hasta 32 núcleos. Eran literalmente cuatro procesadores de 8 núcleos soldados en un mismo PCB y cada uno de esos procesadores de 8 núcleos en realidad internamente eran dos procesadores de cuatro núcleos unidos.

Zeppelin de 2 procesadores y 8 núcleos

Pero pegar directamente procesadores de ocho núcleos (conocidos como zeppelin), no es demasiado eficiente, esto tiene muchos problemas como lo que estamos duplicando parte del hardware. Un procesador no sólo son núcleos, hay mucho más, como por ejemplo el controlador de memorias para comunicar el procesador con la memoria RAM. Si junto a cuatro procesadores tengo cuatro controladores de memoria. ¿Realmente necesito cuatro? Tengo mucho hardware innecesario. Además esto de tener hardware duplicado crea muchos problemas, porque por ejemplo el controlador de memoria de un núcleo es posible que para acceder a la memoria tenga que acceder en realidad al controlador de memorias de otro núcleo y que ese controlador de memoria sea el que acceda a la memoria. Vamos un lío tremendo y una cosa nada eficiente.

Refinando la fórmula del diseño

Zen 2 la segunda iteración de la arquitectura Zen, es donde está lo realmente interesante. Con Zen 2 en vez de fabricar procesadores completos de ocho núcleos que luego soldamos, vamos a fabricar chipplets, es decir unos chips donde sólo tenemos ocho núcleos. Nada de controlador de memorias, nada más... sólo ocho núcleos y las memorias que necesitan para trabajar. Después vamos a crear otro chip con el controlador de memoria y todas las cosas que necesitamos para conectar el procesador con el resto de componentes del PC. Además este chip va a ser el punto de encuentro de los chiplets, es decir, todos los procesadores de 8 núcleos van a estar unidos a este núcleo central donde habrá una memoria caché dedicada para que los núcleos se comuniquen entre ellos.

IOD: proceso de fabricación 12 nm
Procesador de8 núcleos: proceso de fabricación 7 nm

¿Cuál es el resultado de todo esto? El resultado es que AMD a diferencia de Intel, no fabrica un procesador de 4 núcleos, otro de 6, otro de 8, otro de 10... y así hasta 28 núcleos. Se necesitan un montón de líneas diferentes para fabricar todos estos procesadores y es mucho lío. No, AMD sólo fábrica chiplets, procesadores de 8 núcleos. Un sólo procesador, todos idénticos y además los chipplets son enanos. Acceso a núcleos y memoria caché es una cosa muy pequeñita, esto hace que se puedan producir chiplets en masa de forma muy barata. Producir chips pequeñitos es rápido y fácil; y además si sólo tienes un modelo de procesador, puedes optimizar toda la cadena de producción para ese procesador haciéndolo de la forma más barata y rápida posible.

Y diréis: pero AMD también tiene procesadores de seis núcleos, por ejemplo los Ryzen 5. Bien, los Ryzen 5 en realidad son procesadores de 8 núcleos pero dos de ellos están desactivados

¿Qué pasa si hay un fallo en la oblea y tienes algunos chiplets donde ha quedado por ejemplo un núcleo defectuoso? ¿Se tira a la basura? No, si sólo hay un núcleo defectuoso desactivas dos y se convierte el chiplet en un chiplet de 6 núcleos y con él creas un Ryzen 5. Y si en vez de fallarte un núcleo, te han fallado tres o cuatro, pues creas un chiplet de cuatro núcleos y creas con él un Ryzen 3. Aquí se aprovecha todo, no se tira nada a la basura.

Ryzen 3: 4 núcleos 8 hilos
Ryzen 5: 6 núcleos 12 hilos
Ryzen 7: 8 núcleos 16 hilos

¿Pero no es esto un desperdicio de dinero? Es decir, desactivar algunos núcleos porque sí. Bueno es que no todos los procesadores salen igual. Hacer procesadores es como hornear galletas, no todas son iguales, algunas salen más grandes otras más pequeñas, algunas más tostadas y otras más crudas. Lo mismo pasa con el silicio, el proceso de creación de los procesadores que se conoce como fotolitografía es imperfecto. Por eso no todos los procesadores quedan iguales. Algunos necesitan más voltaje para funcionar, otros no consiguen alcanzar ciertas frecuencias no importa lo que hagas y algunos directamente no funcionan. Así pues, ¿qué hacen con los procesadores que no son perfectos? Pues los venden como gamas más bajas con algunos núcleos activados y frecuencia más baja, así es cómo nacen las distintas gamas de Ryzen en los procesadores que se fabrican. Son todos iguales, pero los mejores van para un modelo y los peores para otro.

Versionado de modelos de procesadores AMD Ryzen

Después también hay que fabricar ese núcleo central, pero la gracia es que ese núcleo central no necesita estar hecho en un proceso de fabricación moderno, ahí no hay núcleos haciendo operaciones. No hace falta un máximo rendimiento o la máxima eficiencia a las máximas frecuencias. No, es una cosa sencilla donde hay controladores y una memoria caché, por lo que se puede fabricar con un proceso de fabricación diferente. Mientras los chiplets se fabrican a 7 nanómetros, el núcleo central se puede fabricar a 12 nanómetros que es más barato. De esta forma, gracias a los chiplets, AMD es capaz de fabricar de forma barata, procesadores con un gran número de núcleos; y es la razón por la que AMD puede vender un procesador de 64 núcleos a mitad de precio de lo que Intel vende un pensador de 28 núcleos. Y también pueden hacer locuras como procesadores de 16 núcleos para la gama doméstica.

En realidad el diseño Mesh de Intel que hemos visto en el anterior artículo es más eficiente que Infinity Fabric de AMD. Las latencias en las comunicaciones entre núcleos son inferiores en el diseño de Intel, pero el diseño de Intel está limitado por los procesos de fabricación y el silicio, por lo tanto aunque en la teoría sea superior, en la práctica Zen 2 le pasa por encima. Ahora bien, no cometamos el error de subestimar a Intel.

Intel han sido los líderes en procesadores durante los últimos 50 años. Es una empresa gigante con muchísimo dinero y los mejores equipos ingenieros del mundo. Además recientemente se han dedicado a fichar talentos de otras empresas. Por ejemplo, Jim Keller el cerebro que estuvo detrás de la arquitectura Zen, tras acabar el desarrollo de Zen, abandonó a AMD y se pasó a Intel.

Jim Keller

Intel ya está trabajando en futuras arquitecturas y ha mostrado algunas de ellas, como por ejemplo Foveros, el cual es un diseño de chiplets similar a Zen pero en 3D. Es decir, los chiplets en vez de soldarse todos en un mismo PCB, se pueden apilar unos encima de otros. De esta forma se puede hacer que la conexión entre chiplets sea extremadamente rápida y así el diseño sea mucho más eficiente.

Diseño Foveros

Intel va a responder. AMD les ha pillado en el peor momento que podían pillarles, pero responderán. Tardarán tres o cinco años, pero entre 2021 y 2026 veremos la respuesta de Intel, y de hecho la última vez que AMD puso en aprietos a Intel, la respuesta de Intel fue la arquitectura Core que mandó a AMD al olvido durante casi una década. Pero a diferencia que en aquella época, AMD a día de hoy tiene una hoja de ruta. Es decir, AMD no ha lanzado ZEN 2 y se van a quedar ya ahí, no se van a sentar en el sofá a vender y punto. No, AMD ha anunciado que el diseño la tercera generación de la arquitectura Zen ya ha terminado y la cuarta generación de la arquitectura Zen está ya en fase de diseño. Por lo tanto, en los próximos años vamos a ver una pelea apasionante entre AMD e Intel, lo cual es extremadamente bueno para los consumidores.

Que AMD e Intel compitan va a provocar que lancen mejores productos a precios más bajos. De hecho antes de que la arquitectura Zen llegase al mercado, un procesador de 8 núcleos 16 hilos te costaba 1000€. A día de hoy se han llegado a ver Ryzen 7 1700 por hasta 100€. Se ha dividido entre 10 el precio de ese tipo de procesadores. También antes de que empezase esta competencia entre Intel y AMD el estándar en PC era entre dos y cuatro núcleos. A día de hoy la gama media ya son seis núcleos, y eso tan sólo en 23 años de competencia. Esperemos que AMD e Intel compitan durante muchos años porque nos va a venir muy bien a los consumidores.