Descripción general y pruebas comparativas de PC "Elbrus 401 - PC". Tercera parte: herramientas de desarrollo

Continuamos revisando la nueva computadora doméstica. Después de un breve conocimiento de las características de la arquitectura de Elbrus, consideraremos las herramientas de desarrollo de software que se nos ofrecen.

Vista frontal y lateral de la unidad del sistema Elbrus 401-PCPrograma de lenguaje de máquina de muestra E2K



Recordemos la estructura del artículo:

  1. revisión de hardware :
    • proceso de adquisición;
    • hardware
  2. revisión de software :
    • lanzamiento del sistema operativo;
    • software regular;
  3. Resumen de herramientas de desarrollo:
  4. rendimiento de evaluación comparativa :
    • Descripción de computadoras rivales
    • resultados de referencia;
    • resumiendo

Que tengas una buena lectura!

Características de la arquitectura


La esencia de la arquitectura E2K en una oración se puede formular de la siguiente manera: registros de 64 bits, paralelismo explícito de ejecución de instrucciones y acceso estrictamente controlado a la memoria.

Por ejemplo, los procesadores de arquitectura x86 o SPARC que son capaces de ejecutar más de una instrucción por ciclo (superescalar), y a veces también fuera de orden, están implícitosparalelismo: el procesador directamente en tiempo real analiza las dependencias entre instrucciones en una pequeña sección de código y, si lo considera posible, carga ciertos actuadores al mismo tiempo. A veces actúa de manera demasiado optimista, especulativamente, con descartar el resultado o revertir la transacción en caso de predicción fallida. A veces, por el contrario, es demasiado pesimista, suponiendo dependencias entre los valores de los registros o partes de los registros, que en realidad no son desde el punto de vista del programa ejecutable.

Si es  explícitoparalelismo (computación de instrucción explícitamente paralela, EPIC), el mismo análisis tiene lugar en la etapa de compilación, y todas las instrucciones de máquina definidas para ejecución paralela se escriben en una palabra de instrucción muy grande (VLIW), y Elbrus tiene la longitud de esto Las "palabras" no son fijas y pueden tener de 1 a 8 palabras dobles (en este contexto, una sola palabra tiene una capacidad de 32 bits).

Sin lugar a dudas, el compilador tiene muchas más oportunidades en términos de la cantidad de código cubierto, el tiempo y la memoria gastados, y al escribir el código de la máquina manualmente, el programador puede llevar a cabo una optimización aún más inteligente. Pero esto es en teoría, y en la práctica es poco probable que use el ensamblador, y por lo tanto todo depende de cuán bueno sea el compilador de optimización, y escribir uno no es una tarea fácil, por decir lo menos. Además, si, con paralelismo implícito, las instrucciones "lentas" pueden continuar funcionando sin bloquear la recepción de las siguientes instrucciones en otros actuadores, entonces, con un paralelismo explícito, todo el comando ancho esperará la finalización completa. Finalmente, un compilador optimizador ayudará poco en la interpretación de lenguajes dinámicos.

Todo esto se entiende bien en el MCST, por supuesto, y por lo tanto, Elbrus también implementa tecnologías de ejecución especulativa, código y datos precargados y operaciones computacionales combinadas. Por lo tanto, en lugar de teorizar y preguntarse infinitamente cuántos gigaflops hipotéticos puede ofrecer esta o aquella plataforma en un conjunto de circunstancias exitosas, en la cuarta parte del artículo simplemente tomamos y evaluamos el rendimiento real de los programas reales, aplicados y sintéticos.

VLIW: ¿un avance o un callejón sin salida?
, VLIW   : ,   ‑ Transmeta Crusoe —  «». ,     Efficeon (   )    .  ,   x86-   ,   . ,  Pentium M  ,   Pentium 4 , .      VIA C3, x86.

Debido a su naturaleza exótica, la tecnología de ejecución protegida de programas en lenguajes C / C ++, donde el uso de punteros proporciona una amplia gama de oportunidades para dispararse en el pie, no es menos interesante. El concepto de protección contextual, implementado conjuntamente por el compilador en la etapa de ensamblaje y el procesador en tiempo de ejecución, así como el sistema operativo en términos de gestión de memoria, no permitirá violar el alcance de las variables, ya sea acceder a una variable de clase privada, datos privados de otro módulo, variables locales de la función de llamada . Cualquier manipulación con el cambio del nivel de acceso está permitida solo en la dirección de reducir los derechos. El bloqueo de enlaces a objetos de corta duración en estructuras de larga duración está bloqueado. También se evita que los intentos utilicen enlaces muertos: si el objeto al que se recibió el enlace ya se ha eliminado,incluso la ubicación de otra instalación nueva en la misma dirección no se considerará una excusa para acceder a su contenido. Se alienta a utilizar los datos como código y transferir el control a cualquier lugar.

De hecho, tan pronto como pasamos de modismos de alto nivel a punteros de bajo nivel, todas estas áreas de visibilidad resultan ser nada más que sal sintáctica. Algunos casos (más simples) de mal uso de punteros a veces pueden ayudar a detectar analizadores de código fuente estático. Pero cuando el programa ya está traducido a instrucciones de máquina x86 o SPARC, nada impedirá que lea o escriba el valor de la celda de memoria incorrecta o el tamaño incorrecto, lo que provocará un bloqueo en un lugar completamente diferente, y aquí está sentado, mirando la pila dañada. y no tiene idea de dónde comenzar a depurar, porque en la otra máquina el mismo código se cumple con éxito. Y el desbordamiento de la pila y las vulnerabilidades resultantes son solo el flagelo de las plataformas populares. Es gratificante que nuestros desarrolladores aborden sistemáticamente estos problemas,y no se limita a organizar más y más muletas, cuyo efecto todavía se parece más bien a un rastrillo. Después de todo, a nadie le importa qué tan rápido funciona su programa si no funciona correctamente. Además, un control más estricto por parte del compilador lo obliga a reescribir el código "maloliente" e intolerable, lo que significa que indirectamente mejora la cultura de programación.

El orden de bytes cuando se almacenan números en la memoria de Elbrus, a diferencia de SPARC, es little endian (el byte inferior viene primero), es decir, como en x86. Del mismo modo, dado que la plataforma tiene como objetivo admitir el código x86, no hay restricciones en la alineación de los datos en la memoria.

Orden, alineación y portabilidad
 , Intel, ,       (, 32‑   0x04000005) —   , ,   ,   . - ,   ,    , — ,   (   , UTF‑16),   , ,   ,    ‑. ,   , — , SPARC, —   .

Puede leer más sobre el diseño de las computadoras MCST en arquitecturas SPARC y E2K en el libro "Microprocesadores y complejos de computación de la familia Elbrus", que fue publicado por Peter Publishing House en una  tirada mínima y se ha vendido durante mucho tiempo, pero está disponible de forma gratuita como PDF ( 6 MB ) y por una pequeña tarifa en  Google Play . En el contexto de la falta de otra información detallada en el dominio público, esta publicación es solo un depósito de conocimiento. Pero el texto se concentra principalmente en el hardware, los algoritmos de operación de las memorias intermedias y las tuberías, los cachés y los dispositivos de lógica aritmética: el tema de escribir programas [efectivos] no se aborda por completo, e incluso solo mencionar las instrucciones de la máquina se puede contar con los dedos.

Lenguaje de máquina


Además de compilar los lenguajes de alto nivel C, C ++, Fortran, la documentación en cada oportunidad no olvida mencionar la posibilidad de escribir programas directamente en Assembler, pero en ninguna parte se especifica cómo exactamente puede involucrarse en este arte de filigrana, donde al menos puede obtener una referencia a las instrucciones de la máquina. Afortunadamente, el sistema tiene un depurador GDB que puede desmontar el código de los programas compilados previamente. Para no ir más allá del alcance del artículo, escribimos una función aritmética simple que tiene una buena acumulación de trabajos paralelos.

uint64_t CalcParallel(
	uint64_t a,
	uint64_t b,
	uint64_t c,
	uint32_t d,
	uint32_t e,
	uint16_t f,
	uint16_t g,
	uint8_t h
) {
	return (a * b) + (c * d) - (e * f) + (g / h);
}

Esto es lo que se traduce al compilar en modo -O3 :

0x0000000000010490 <+0>:
	muld,1 %dr0, %dr1, %dg20
	sxt,2 6, %r3, %dg19
	getfs,3 %r6, _f32,_lts2 0x2400, %g17
	getfs,4 %r5, _lit32_ref, _lts2 0x00002400, %g18
	getfs,5 %r7, _f32,_lts3 0x200, %g16
	return %ctpr3
	setwd wsz = 0x5, nfx = 0x1
	setbp psz = 0x0
0x00000000000104c8 <+56>:
	nop 5
	muld,0 %dr2, %dg19, %dg18
	muls,3 %r4, %g18, %g17
	sdivs,5 %g17, %g16, %g16
0x00000000000104e0 <+80>:
	sxt,0 6, %g17, %dg17
	addd,1 %dg20, %dg18, %dg18
0x00000000000104f0 <+96>:
	nop 5
	subd,0 %dg18, %dg17, %dg17
0x00000000000104f8 <+104>:
	sxt,0 2, %g16, %dg16
0x0000000000010500 <+112>:
	ct %ctpr3
	ipd 3
	addd,0 %dg17, %dg16, %dr0

Lo primero que llama la atención es que cada comando se decodifica inmediatamente en varias instrucciones ejecutadas en paralelo. La designación mnemónica de instrucciones es generalmente intuitiva, aunque algunos nombres parecen inusuales después de Intel: por ejemplo, una instrucción de extensión sin signo aquí se llama sxt , no  movzx . El parámetro de muchos comandos computacionales, además de los operandos mismos, es el número del dispositivo ejecutivo; no sin razón, ELBRUS significa programación explícita de utilización de recursos básicos, es decir, "planificación explícita para el uso de recursos básicos".

Para acceder al valor de registro completo de 64 bits, el prefijo " d"; en teoría, también es posible acceder a los 16 y 8 bits inferiores del valor. La designación de registros globales de propósito general, de los cuales hay 32 piezas, tiene el prefijo " g " antes del número , y los registros de procedimientos locales con el prefijo " r ". El tamaño de la ventana de los registros locales solicitados por la instrucción setwd puede alcanzar 224, y la bomba se empuja a la pila automáticamente según sea necesario.

La forma en que aplica ciertas instrucciones es confusa: por ejemplo, regresarComo puede suponer, sirve para devolver el control al procedimiento de llamada, sin embargo, en todos los ejemplos de código estudiados, esta instrucción ocurre mucho antes del último comando (donde también está presente algún tipo de manipulación del contexto), a veces incluso en la primera palabra de comando, como aquí. Aunque el libro mencionado paga un párrafo completo sobre este tema, aún no nos queda claro. Actualización a partir del 9 de febrero de 2016: los comentarios sugieren que la instrucción return solo prepara el camino para regresar del subprograma y permite que el procesador comience a cargar los siguientes comandos del procedimiento de llamada, y el control en sí mismo regresa cuando la ejecución alcanza la instrucción  ct .

Sin embargo, "código fácil de leer" y "código eficiente" están lejos de ser lo mismo cuando se trata de instrucciones de la máquina. Si compila sin optimización, entonces el código es más consistente y similar al cálculo de la frente, pero a costa de alargarlo: en lugar de 6 palabras de comando saturadas, se generan 8 dispersas.

Sesión de adivinación en café molido para el sim, terminemos antes de fantasear con suposiciones completamente ridículas. Esperemos que algún día la referencia de comandos y la guía de programación y optimización se hagan públicos.

Herramientas de desarrollo


El compilador de lenguaje C / C ++ estándar en el sistema operativo Elbrus es LCC, un desarrollo patentado de la compañía MCST, compatible con GCC. La información detallada sobre la estructura y los principios de este compilador no se publica, pero según una entrevista con un desarrollador anterior de una de varias subespecies desarrolladas del compilador, Edison Design Group utiliza una interfaz para el análisis de alto nivel de los códigos fuente , y la traducción de bajo nivel a las instrucciones de la máquina se puede realizar de diferentes maneras, sin optimización o con optimización. Es el compilador de optimización que se entrega a los usuarios finales, no solo en la plataforma E2K, para la que simplemente no hay generadores de código de máquina alternativos, sino también en la familia de plataformas SPARC, donde también está disponible el GCC habitual como parte del sistema operativo MSVS.

Teniendo en cuenta las características arquitectónicas enumeradas anteriormente (concurrencia obvia, ejecución segura de programas), el compilador LCC obviamente implementa muchas soluciones únicas dignas del estudio y las pruebas más rigurosos en la práctica. Desafortunadamente, al momento de escribir estas líneas, el autor no tiene calificaciones suficientes para esto, ni tiempo para tales estudios; Espero que tarde o temprano este problema sea abordado por un círculo mucho más amplio de representantes de la comunidad de TI, incluidos los más competentes.

Por lo que logró notar a simple vista al crear programas para probar el rendimiento, LCC en E2K con más frecuencia que otros brinda advertencias sobre posibles errores, construcciones analfabetas o simplemente lugares sospechosos en el código. Es cierto que el autor no está tan familiarizado con GCC para garantizar la distinción entre mensajes únicos de LCC en ruso y simplemente traducidos (además, la traducción es selectiva), y no estoy seguro de que un flujo más intenso de advertencias no sea el resultado de una configuración de ensamblaje completada automáticamente. Además, al no conocer la semántica de una sección de código en particular, a veces es difícil entender cuán inteligente es el compilador para encontrar errores ocultos o generar una falsa alarma. Por ejemplo, en el código Postgresql, la misma construcción se encuentra cuatro veces en el mismo archivo con ligeras variaciones:

for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) {

	//....//

	/* is string only whitespace? */
	if ((*ptr)[strspn(*ptr, " \t")] == '\0')
		fputs("&nbsp; ", fout);
	else
		html_escaped_print(*ptr, fout);

	//....//
}


El compilador predice una posible salida de la matriz unidimensional en la cadena con la llamada de la función strspn . En qué circunstancias puede suceder esto, el autor no comprende (y no hubo tal advertencia en otras plataformas, aunque el modo de verificación es Warray-limitses estándar para GCC), sin embargo, es notable que la replicación múltiple del mismo diseño no trivial (ya que era necesario explicar su propósito en el comentario), en lugar de ponerlo en una función separada con un nombre elocuente que no requiere explicación. Incluso si la alarma resultó ser falsa, detectar un código maloliente es un efecto útil; los autores del analizador estático PVS - Studio se quedarán sin trabajo. Pero en serio, sería divertido y útil comparar qué errores adicionales en el código el LCC es realmente capaz de detectar debido a las características únicas de la arquitectura E2K; al mismo tiempo, el mundo del software libre podría recibir otro lote de informes de errores.

Otro resultado de un conocido curiosa con LCC locuaz fue el autor de la educación, y luego sus colegas más experimentados, por lo que trigrafos ( trigrafos ) en lenguajes C / C ++, y por qué no están soportados por defecto, por suerte. Así es como vives y no sospeches que la combinación aparentemente inocua de puntuación en textos literales o comentarios puede resultar una bomba de tiempo, o un excelente material para un marcador de programa, dependiendo de qué lado de la barricada estés.

Una consecuencia desagradable de la autosuficiencia de LCC es que su formato de mensaje es diferente al de GCC, y cuando se compila desde un entorno de desarrollo (por ejemplo, Qt Creator), estos mensajes se encuentran solo en el registro general de trabajo, pero no en la lista de problemas reconocidos. Quizás esto se pueda personalizar de alguna manera, ya sea desde el lado del compilador o en el entorno de desarrollo, pero al menos desde el primer momento no se comprende entre sí.

Tradicionalmente grave para las plataformas domésticas, dado su rendimiento relativamente bajo, existe el problema de la compilación cruzada, es decir, el ensamblaje de programas para la arquitectura de destino y un conjunto específico de bibliotecas del sistema, utilizando recursos de computadoras más potentes, con arquitectura diferente y otro software. A juzgar por las líneas de identificación en el núcleo del sistema Elbrus y en el compilador LCC, están ensambladas en Linux i386, pero este kit de herramientas para x86, por supuesto, no está incluido en el paquete de distribución del sistema en sí. Es interesante, pero ¿es posible hacer lo contrario: en Elbrus para recopilar programas para otras plataformas? (El autor no tuvo éxito más allá de la primera fase del ensamblaje de GCC para i386).

Versiones de los paquetes más importantes para el desarrollador:

  • compiladores: lcc 1.19.18 (compatible con gcc 4.4.0);
  • : erlang 15.b.1, gawk 4.0.2, lua 5.1.4, openjdk 1.6.0_27 (jvm 20.0‑b12), perl 5.16.3, php 5.4.11, python 2.7.3, slang 2.2.4, tcl 8.6.1;
  • : autoconf 2.69, automake 1.13.1, cmake 2.8.10.2, distcc 3.1, m4 1.4.16, make 3.81, makedepend 1.0.4, pkgtools 13.1, pmake 1.45;
  • : binutils 2.23.1, elfutils 0.153, patchelf 0.6;
  • : boost 1.53.0, qt 4.8.4, qt 5.2.1;
  • : expat 2.1.0, ffi 3.0.10, gettext 0.18.2, glib 2.36.3, glibc 2.16.0, gmp 4.3.1, gtk+ 2.24.17, mesa 10.0.4, ncurses 5.9, opencv 2.4.8, pcap 1.3.0, popt 1.7, protobuf 2.4.1, sdl 1.2.13, sqlite 3.6.13, tk 8.6.0, usb 1.0.9, wxgtk 2.8.12, xml‑parser 2.41, zlib 1.2.7;
  •  : cppunit 1.12.1, dprof 1.3, gdb 7.2, perf 3.5.7;
  • : anjuta 2.32.1.1, glade 2.12.0, glade 3.5.1, qt‑creator 2.7.1;
  • : bzr 2.2.4, cvs 1.11.22, git 1.8.0, patch 2.7, subversion 1.7.7.

Nuevamente, si esperaba GCC 5, PHP 7 y Java 9, entonces estos son sus problemas, como dice un famoso futbolista. En este caso, también debo agradecer que al menos no GCC 3.4.6 (LCC 1.16.12), como en las versiones anteriores del sistema Elbrus, o GCC 3.3.6 en la composición del MSVS 3.0; Por cierto, el compilador principal en MSVS 3.0 sigue siendo GCC 2.95.4 (¿y por qué sorprenderse cuando hay un núcleo de una rama 2.4?). En comparación con la situación anterior, cuando era posible tropezar con un error de GCC corregido en el flujo ascendente hace diez años, el nuevo sistema tiene condiciones casi celestiales: incluso puede deslizarlo en C ++ 11 si no desea mantener la compatibilidad con versiones anteriores.

La aparición de OpenJDK, al menos en alguna forma, ya se puede llamar un gran avance, porque no les gusta Java y  Monoen tales sistemas se conoce desde hace mucho tiempo; y esta aversión se puede entender cuando incluso los programas nativos apenas se mueven. Dado que hay muchos Javists entre los colegas del autor, debido a las circunstancias anteriores, obligados a contener los maravillosos impulsos de las almas, se decidió dedicar una serie separada de pruebas de rendimiento para dedicar Java. Mirando hacia el futuro, notamos que los resultados fueron desalentadores incluso en términos relativos: con el mismo éxito, puede escribir scripts interpretados en PHP o Python, probablemente.

El soporte para C y C ++ solo no se limita a la compatibilidad con la Colección de compiladores GNU: el sistema todavía tiene un traductor Fortran. Como el autor solo está familiarizado con el profesor Fortran, cualquier persona interesada puede recomendar el tema de diciembre sobre "Hecho con nosotros", donde los comentarios se refieren al uso de este lenguaje como punto de referencia.

Para el postre, almacenamos lo más delicioso: la última parte del artículo está dedicada al estudio del desempeño de Elbrus en comparación con una variedad de plataformas de hardware y software, incluidas las domésticas.

Source: https://habr.com/ru/post/es389927/


All Articles