Qué hace tu computadora mientras esperas (que termine de cargar)

Este artículo lo encontré en el blog de Gustavo Duarte y me pareció tan bueno y completo, que no dudé ni un segundo en pedirle los permisos necesarios para traducirlo.

De hecho, el artículo es algo más complejo a lo que estamos acostumbrados a publicar aquí, pero Gustavo lo detalla todo de tal manera, que todos entenderemos por qué demonios, con tremendas PCs multinúcleo que tenemos hoy en día, todavía tenemos que esperar buen rato a que la computadora termine, vulgarmente, de “pensar”. El artículo me pareció enormemente útil como referencia para cualquier persona de cómo trabaja, internamente, nuestra PC.

Pueden leer el artículo original en el blog de Gustavo Duarte (que incluso, tuvo la gentileza de facilitarme sus gráficos para también traducirlos, y lo haré en el transcurso de la semana).

 

Este post le da un vistazo a velocidad – latencia y throughput (volumen de trabajo o de información que fluye a través de un sistema) – de varios subsistemas en una moderna PC de casa, una Intel Core 2 Duo a 3.0 Ghz. He tratado de mostrar throughputs del mundo real (las fuentes están puestas como comentario), en lugar de máximos teóricos.

Las unidades temporales utilizadas son nanosegundos (ns, 10-9 segundos), milisegundos (ms, ns, 10-3 segundos) y segundos (s). Las unidades de throughput están en megabytes y gigabytes por segundo. Empecemos con el CPU y la memoria, “el norte del northbridge”:

latencyAndThroughputNorth[1]

Lo primero que salta a la vista es lo absurdamente veloz que son nuestros procesadores hoy en día. La mayoría de instrucciones simples en el Core 2 toman un ciclo de reloj en ejecutar, por lo tanto, un tercio de nanosegundo a 3.0 Ghz.  Como referencia, la luz sólo viaja ~10 cms en el lapso de tiempo en el que transcurre un ciclo de reloj. Vale la pena tener esto en mente cuando pensemos en optimizar – las instrucciones son cómicamente baratas de ejecutar hoy en día.

Mientras el CPU trabaja, tiene que escribir y leer de la memoria de sistema, a la cual accede vía los cachés L1 y L2. El caché usa memoria estática, un tipo de memora mucho más veloz (y costosa) que la memoría DRAM utilizada como la principal. Los caches son parte del procesador y por esa costosa memoria, obtenemos una menor latencia. Una razón por la que la optimización a nivel de instrucción es bastante relevante. Dado al uso del cache, pueden darse diferencias enormes de rendimiento entre el código que entra por completo en los cachés de L1/L2 y el código que necesita ser metido / sacado de los caches mientras es ejecutado.

Normalmente, cuando el CPU necesita tocar el contenido de la región de la memoria, debe estar en ya en los L1/L2 caches, o ser jalados de la memoria principal. Aquí vemos el primer golpe, un enorme ~250 ciclos de latencia que generalmente nos lleva a un pare, cuando el CPU no tiene ninguna tarea por realizar mientras espera.

Para poner esto en perspectiva, leer de la memoria cache L1 es como coger un pedazo de papel de nuestro escritorio (3 segundos). El cache L2, es como coger un libro de un estante cercano (14 segundos), y la memoria regular de sistema, es como realizar una caminata de 4 minutos para ir a comprar una barra Twix.

La latencia exacta de la memoria principal es variable, y depende de la aplicación y muchos otros factores. Por ejemplo, depende de las especificaciones del mismo RAM que está en la PC. También depende de qué tan exitoso es el procesador al hacer pre-fetching – adivinar qué partes de la memoria serán necesarias basado en el código que está siendo ejecutado, y traerlas al cache antes de tiempo.

Mirando al rendimiento de los caches L1 / L2 contra el rendimiento de la memoria de sistema, queda claro todo lo que se puede ganar de un cache L2 de mayor tamaño, y de aplicaciones que hayan sido diseñadas de utilizarlo correctamente. Para una discusión sobre todo lo relacionado a la memoria, vean What Every Programmer Should Know About Memory (pdf)

 

Las personas se refieren al cuello de botella entre el CPU y la memoria como el von Neumann bottleneck. Ahora, el ancho de banda del bus frontal (Front Side Bus, FSB) alrededor de 10GB/s, se ve decente. A esa velocidad, uno podría leer 8GBs de la memoria del sistema en menos de un segundo, o leer 100 bytes en 10ns. Lamentablemente, este throughput es un máximo teórico (a diferencia del resto en el diagrama) y no puede ser alcanzado debido a las demoras en el RAM. Se requieren múltiples periodos de espera cuando se accede a la memoria.

En el southbridge tenemos otro número de buses (PCIe, USB) y periféricos conectados:

latencyAndThroughputSouth[1]

Lamentablemente, el soutbridge aloja a varios componentes lentos, porque incluso el RAM es extremadamente veloz comparado a los discos duros. Manteniéndonos con la analogía de oficina, esperar a la búsqueda del disco duro es como dejar el edificio, para ir a recorrer la  tierra por un año y tres meses. Esta es la razón por la cual muchas cargas de trabajo son dominadas por la entrada y salida del disco duro, y por qué el rendimiento de la base de datos puede caer una vez que se acaban los búfferes de memoria. Es otra razón por la cual bastante RAM (para buffering) y veloces discos duros son tan importantes para un rendimiento óptimo.

Mientras que el volumen de trabajo “sostenido” es real, en el sentido en que realmente es alcanzado por el disco en casos de la vida real, no nos cuenta la historia completa.

El problema del rendimiento del disco duro, son las búsquedas de información, que involucran mover el cabezal de lectura / escritura en el disco a la pista correcta, y luego esperar a que el disco empiece a girar alrededor de la posición correcta (imagínense un disco de vinilo). [Nota de arturo: Curiosamente, esto lo vimos hace poco en el artículo sobre las diferencias de los discos duros tradicionales, y los SSDs ]. Las revoluciones por minuto (RPM) de los discos duros se refieren a la velocidad de rotación de los “platos”: mientras mayor sea el RPM, menos tiempo tendremos que esperar a que la rotación nos de el sector deseado. Un buen sitio donde podemos informarnos sobre la velocidad e impacto de las búsquedas, es el reporte donde un par de estudiantes de Stanford describen la Anatomia de un Motor Buscador a Larga Escala (pdf)

Cuando el disco está leyendo un archivo grande y continuo, consigue alcanzar una mayor velocidad sostenida debido a la falta de búsquedas. La defragmentación de los archivos de sistema ayuda a mantener a los archivos en pedazos continuos en el disco para minimizar las búsquedas y mejorar el rendimiento. Cuando vamos al punto de qué tan rápido una computadora “se siente”, el volumen de información sostenida es menos importante que los tiempos de búsqueda, y el número de operaciones I / O (lectura / escritura) que una unidad puede hacer por unidad temporal. Los discos de estado sólido (SSD) son una gran opción aquí.

Los cachés de discos duros ayudan también al rendimiento. Sus pequeños tamaños – un caché de 16 MB en un disco de 750 GB cubre sólo el 0.002/ del disco – los pueden hacer parecer inútiles, pero en realidad, su contribución, es la de permitir al disco a poner en cola escrituras, y luego realizarlas de golpe, por lo tanto permitiéndole al disco administrar el orden de las escrituras en una manera que – sorpresa – minimiza las búsquedas. Las lecturas también pueden ser agrupadas de esta manera para mejorar el performance, y tanto el sistema operativo como el firmware de la unidad realizan estas optimizaciones.

Espero que este diagrama resulte de utilidad. Resulta fascinante, para mí, mirar a todos estos números reunidos y ver qué tan lejos hemos llegado.

 

 

Link al artículo original