SPARC: Rendimiento del T2+

Hace algún tiempo que Sun ha puesto en escena el procesador UltraSPARC T2+, puedes leer una pequeña descripción de este procesador en el siguiente link. El procesador dispone de varios cores (dependiendo del modelo que compres) y cada uno de los cores es capaz de gestionar 8 threads hardware. Solaris trata cada uno de los threads hardware de los cores como si se tratasen de CPUs, en realidad, podemos pensar en CPUs virtuales.

Para entender como podemos medir el rendimiento de un procesador UltraSPARC T2+, es imprescindible leer los posts de Ravindra Talashikar sobre los procesadores T2 y T1, son totalmente recomendables, por no decir imprescindibles.

En este post, lo que vamos a realizar un análisis de cómo funciona el procesador UltraSPARC T2+, qué podemos medir del procesador, para identificar un posible problema de saturación y mostrar unas sencillas pruebas de carga realizadas sobre uno de estos procesadores para ver el rendimiento del procesador sometido a una pruenba de carga.

Este es el esquema básico de la arquitectura de un UltraSPARC T2+. Cada procesador puede tener hasta 8 cores y cada uno de estos cores es capaz de gestionar 8 threads hardware. Solaris, en una máquina que tenga 2 procesadores T2+, verán como 128 CPUs, en realidad está trabajando con cada uno de los threads hardware de los cores como si fuesen CPUs y aquí es donde debemos tener cuidado a la hora de medir el rendimiento de nuestro sistema, ya que NO DISPONEMOS DE 128 PROCESADORES aunque el comando mpstat nos diga lo contrario.

Cada core de un T2+ está organizado de tal forma que cada 4 threads hardware comparten un pipeline, lo que nos da como resultado que cada core dispone de 2 pipelines. El UltraSPARC T2+ es capaz de ejecutar una instrucción por ciclo, esto significa que cada grupo de 4 threads de un core será capaz de ejecutar 1 instrucción por ciclo. Por lo tanto cada core tiene la posibilidad de ejecutar 2 instrucciones por ciclo. En un procesador cuya velocidad sea de 1165 MHz, significa que cada core puede ejecutar:

1165 MHz x 1000 x 1000 = Número de instrucciones por segundo.

Cada core dispone de 2 pipelines, esto significa que el número de instrucciones por segundo es el doble.

2 pipelines x 1165 MHz x 1000 x 1000 = Número de instrucciones por segundo.

Con esta sencilla fórmula podemos calcular el número máximo de instrucciones por segundo que pueden ejecutar todos los cores de nuestra máquina, de esta forma podremos medir cómo de saturado están nuestros procesadores.

cpustat

El comando cpustat nos permite consultar una serie de contadores hardware disponible en los distintos procesadores. Cada procesador tiene sus propios contadores, por lo que es importante, que antes de utilizar el comando cpustat comprobemos cuales son los contadores disponibles en los procesadores de la máquina.

root@host # cpustat -h
Usage:
        cpustat [-c events] [-p period] [-nstD] [interval [count]]

        -c events specify processor events to be monitored
        -n        suppress titles
        -p period cycle through event list periodically
        -s        run user soaker thread for system-only events
        -t        include %tick register
        -D        enable debug mode
        -h        print extended usage information

        Use cputrack(1) to monitor per-process statistics.

        CPU performance counter interface: UltraSPARC T2+

        event specification syntax:
        [picn=][,attr[n][=]][,[picn=][,attr[n][=]],...]

        event[0-1]: Idle_strands Br_completed Br_taken Instr_FGU_arithmetic
                 Instr_ld Instr_st Instr_sw Instr_other Atomics Instr_cnt
                 IC_miss DC_miss L2_imiss L2_dmiss_ld ITLB_HWTW_ref_L2
                 DTLB_HWTW_ref_L2 ITLB_HWTW_miss_L2 DTLB_HWTW_miss_L2
                 Stream_ld_to_PCX Stream_st_to_PCX CPU_ld_to_PCX
                 CPU_ifetch_to_PCX CPU_st_to_PCX MMU_ld_to_PCX DES_3DES_op
                 AES_op RC4_op MD5_SHA-1_SHA-256_op MA_op CRC_TCPIP_cksum
                 DES_3DES_busy_cycle AES_busy_cycle RC4_busy_cycle
                 MD5_SHA-1_SHA-256_busy_cycle MA_busy_cycle CRC_MPA_cksum
                 ITLB_miss DTLB_miss TLB_miss

        attributes: hpriv l2ctl emask nouser sys

        See the "UltraSPARC T2+ User's Manual" for descriptions of these
        events. Documentation for Sun processors can be found at:
        http://www.sun.com/processors/manuals
root@host #

En el ejemplo anterior, podemos ver la salida del comando cpustat ejecutado en una máquina T5140. Entre los contadores disponibles podemos destacar:

  • Instr_cnt. Contador de instrucciones.
  • IC_miss. Fallos en la cache de instrucciones.
  • DC_miss. Fallos en la cache de datos.
  • L2_imiss. Fallos en la cache L2 de instrucciones.
  • ITLB_miss. Fallos en la TLB de instrucciones.
  • DTLB_miss. Fallos en la TLB de datos.

Para medir la saturación del procesador, vamos a utilizar el contador Instr_cnt que nos devuelve el número de instrucciones ejecutadas en un intervalo determinado.

Vamos a ejecutar el comando cpustat para que nos muestre el contado Instr_cnt en intervalos de 1 segundo.

root@jcapp03 # cpustat  -t -c pic0=Instr_cnt 1
   time cpu event     %tick      pic0
  1.003   8  tick 1152567284      1595
  1.003  11  tick 1165544138      1604
  1.003  10  tick 1166027871      1604
  1.003   9  tick 1166414820      9775
  1.013  24  tick 1171174233       426
  1.013  25  tick 1170740458       426
  1.013  16  tick 1174728555       443
  1.013  33  tick 1167129500       426
  1.013  17  tick 1174284219       443
  1.013  26  tick 1170273285       467
  1.013  18  tick 1173889975       426
  1.013  35  tick 1166250128       443
  1.013  34  tick 1166707237       426
  1.013  27  tick 1169819819       451
  1.013  19  tick 1173460552       426
  1.013  36  tick 1165817219       475
  1.013  20  tick 1173041754       426
  1.013  12  tick 1176732505       426
  1.013  28  tick 1169421351       426
  1.013  37  tick 1165409112       426
  1.013  13  tick 1176270535       426
  1.013  21  tick 1172615507       426
  1.013  29  tick 1169000478       426
  1.013  14  tick 1175823140       426
  1.013  30  tick 1168532323       426
  1.013  22  tick 1172181934       426
  1.013  31  tick 1168069751       467
  1.013  23  tick 1171720735       426
  1.013  15  tick 1175328250     20102
  1.014  32  tick 1168564242       426
  1.023  49  tick 1174019314       426
  1.023  48  tick 1174641224       426
  1.023  50  tick 1173488538       426
  1.023  51  tick 1172939043       426
  1.023  52  tick 1172390077       426
  1.023  53  tick 1171815546       426
  1.023  68  tick 1167820934     14190
  1.023  72  tick 1165704565    325052
  1.023  67  tick 1168338027       426
  1.023  38  tick 1176636092       426
  1.023  54  tick 1171010154       426
  1.023  64  tick 1145953368       627
  1.023  69  tick 1167302036       426
  1.023  39  tick 1175947237       426
  1.023  70  tick 1166799597       426
  1.023  55  tick 1170458103       426
  1.023  65  tick 1169289137       603
  1.023  71  tick 1166291126       426
  1.023  66  tick 1168887198       426
  1.033 104  tick 1165315947       426
  1.033  88  tick 1173301192     20669
...

La salida del comando cpustat muestra varias columnas, la primera es el tiempo en el que se ha consultado el contador, la segunda columna muestra el ID de la CPU virtual, recordemos que cada CPU en realidad es un thread hardware. La cuarta columna muestra el número de ciclos ejecutados y la última columna muestra el número de instrucciones ejecutadas durante la muestra.

Para analizar el rendimiento que está dando cada uno de los cores de los procesadores, solo tenemos que agrupar todas las CPUs que ve Solaris en grupos de 8, cada grupo correspondería a un core y cada uno de estos grupos de 8, podríamos dividirlos en 2 grupos de 4 CPUs cada uno.

Podemos decir que un core está saturado cuando la suma de todas las instrucciones ejecutadas por todos sus threads se acercan al número máximo de intrucciones ejecutadas por core:

2 pipelines x 1165 MHz x 1000 x 1000 = Número máximo de instrucciones por segundo ejecutadas por un core.

Prueba de carga

Hemos realizado una sencilla prueba de carga, para ver el comportamiento de uno de los cores del procesador UltraSPARC T2+. La prueba ha sido realizada en una máquina T5140. Se ha creado en el sistema operativo un pool de CPUs, al que hemos asignado 8 CPUs pertenecientes a un mismo core.

root@host # pooladm 

system default
        string  system.comment
        int     system.version 1
        boolean system.bind-default true
        string  system.poold.objectives wt-load

        pool test1
                int     pool.sys_id 2
                boolean pool.active true
                boolean pool.default false
                int     pool.importance 1
                string  pool.comment
                pset    ptest1

        pset ptest1
                int     pset.sys_id 1
                boolean pset.default false
                uint    pset.min 1
                uint    pset.max 16
                string  pset.units population
                uint    pset.load 0
                uint    pset.size 8
                string  pset.comment

                cpu
                        int     cpu.sys_id 5
                        string  cpu.comment
                        string  cpu.status on-line

                cpu
                        int     cpu.sys_id 4
                        string  cpu.comment
                        string  cpu.status on-line

                cpu
                        int     cpu.sys_id 7
                        string  cpu.comment
                        string  cpu.status on-line

                cpu
                        int     cpu.sys_id 6
                        string  cpu.comment
                        string  cpu.status on-line

                cpu
                        int     cpu.sys_id 1
                        string  cpu.comment
                        string  cpu.status on-line

                cpu
                        int     cpu.sys_id 0
                        string  cpu.comment
                        string  cpu.status on-line

                cpu
                        int     cpu.sys_id 3
                        string  cpu.comment
                        string  cpu.status on-line

                cpu
                        int     cpu.sys_id 2
                        string  cpu.comment
                        string  cpu.status on-line
...

Para la carga hemos creado el siguiente programa en C.

root@host # cat test_int.c
#include 

void main()
{
int i,n;

        while(1)
        {
        i=0;
        for(n=0;n<60000;n++)
                {
                i=i+1;
                }
        }
}

La prueba ha consistido en lanzar hasta 8 procesos test_int, uno cada 4 minutos, de esta forma el SO ha asignado una VCPU a cada proceso nuevo. El gráfico siguiente muestra el porcentaje de instrucciones ejecutadas por cada CPU virtual o thread hardware.

resultados_t2.JPG

De la gráfica podemos obtener como conclusión que el número total de instrucciones por segundo ejecutadas por un core aumenta con cada nuevo thread hardware que comienza a trabajar, lo curioso es que el primer thread hardware que comienza a trabajar lo hace al ~45% de su capacidad, cuando entra el segundo thread hardware, es decir cuando lanzamos el segundo proceso, el uso de los 2 threads hardware baja hasta ~35%, cuando se lanza el tercer proceso, el uso de cada thread hardware baja hasta el ~29% de su capacidad, cuando se lanza el cuarto proceso, los cuatro threads hardware trabajan al ~24% de su capacidad, lo que significa que entre los 4 thread hardware están utilizando al 100% un pipeline. Lo mismo ocurre cuando seguimos lanzando procesos y entra en escena el segundo grupo de threads hardware, lo más curioso es que el primer thread hardware del segundo grupo comienza con una utilización del ~45% de sus capacidad.

Como podemos ver en la línea que representa el número de instrucciones TOTALES ejecutadas por segundo, esta línea crece mas o menos linealmente, hasta llegar al ~90%. en este punto hemos conseguido sacar el máximo de rendimiento al core con el que estamos realizando la prueba.

Conclusión

Para ver el rendimiento de nuestro sistema, debemos analizar el comportamiento de cada uno de los cores que forman parte de los distintos procesadores, esto nos podrá ayudar, tanto a identificar posibles cuellos de botella, como para ayudarnos a tomar decisiones sobre la forma de crecimiento mas optima.

En resumen, con la nueva tecnología de procesadores multicores/multithreads , los administradores de sistemas nos enfrentamos a problemas que nos obligarán a tomar decisiones, dependiendo del nivel de comprensión que tengamos de nuestros sistemas, estas decisiones serán mas o menos acertadas, pero tenemos que tener mucho cuiadado, porque ahora no todo se soluciones con mas CPU.