Sobre el tema de turnos, se帽ales y velocidad MK

"Encuentra una raz贸n para todo y entender谩s mucho"


Quiz谩s mis lectores habituales (bueno, no puede ser que no lo fueran) recuerden que en mi publicaci贸n me qued茅 perplejo porque el atributo unsigned se us贸 para describir los registros de dispositivos externos. En los comentarios, se sugiri贸 que esto se hiciera para evitar un comportamiento indefinido durante los turnos y acept茅. Como descubr铆 recientemente, hay otra raz贸n para este uso del atributo, y puede aplicarse no solo a los registros, sino tambi茅n a las variables ordinarias.

Entonces, estamos empezando.

Para empezar, una peque帽a introducci贸n al hierro.
Como plataforma de destino, consideraremos un MK de 8 bits sin bater铆a (este es un intento tan pat茅tico de ocultar el nombre AVR comprometido), que tiene los siguientes comandos implementados en hardware:

lsl / lsr desplazamiento l贸gico izquierda / derecha, se borra el bit bajo / alto;
rol / ror desplazamiento c铆clico izquierda / derecha mediante transferencia (desplazamiento de 9 bits);
asr cambio aritm茅tico a la derecha, se guarda el bit m谩s significativo (con signo) (prestamos atenci贸n al hecho de que realizar este tipo de cambio a la izquierda es generalmente imposible en principio).

Todos estos comandos se ejecutan en el byte operando y son la base para la implementaci贸n de todos los otros cambios posibles. Por ejemplo, la siguiente secuencia implementa un cambio de palabra (2 bytes rh, rl) con un signo a la derecha de 1 d铆gito:

asr rh; ror rl;

Considere un ejemplo de c贸digo simple y el c贸digo de ensamblador correspondiente para MK con el sistema de comando AVR, como siempre, obtenido en godbolt.org. (implica que la optimizaci贸n est谩 habilitada y la variable se encuentra en el registro r24)

int8_t byte; byte = byte << 1; 

 clr r25 sbrc r24,7 com r25 lsl r24 rol r25 

y ves que la operaci贸n lleva cinco equipos?

Nota: Si alguien en los comentarios le dice c贸mo organizar este fragmento (y los siguientes) en 2 columnas, se lo agradecer茅.

Se puede ver en el c贸digo del ensamblador que la variable de byte se expande a un tipo entero (16 bits) en los primeros tres comandos, y en los dos siguientes, el n煤mero de doble byte se desplaza realmente; de 鈥嬧媋lguna manera es extra帽o, por decir lo menos.

Desplazarse a la derecha no es mejor

 byte = byte >> 1; clr r25 sbrc r24,7 com r25 asr r25 ror r24 

- Los mismos cinco equipos. Mientras tanto, es obvio que, de hecho, para realizar la 煤ltima operaci贸n, necesita un solo comando

 sr r24 

y para la primera operaci贸n no m谩s. En repetidas ocasiones he declarado que el compilador actualmente est谩 creando un c贸digo de ensamblador no peor que un programador (aunque era un sistema de comando ARM), especialmente si lo ayudas un poco, y de repente es un fastidio. Pero intente ayudar al compilador a crear el c贸digo correcto, tal vez se trate de mezclar tipos en una operaci贸n de turno e intente

 byte = byte >> (int8_t) 1; 

- no ayud贸, de la palabra "completamente", pero la opci贸n

  byte=(uint8_t) byte >> 1; 

da un resultado ligeramente mejor

 ldi r25,lo8(0) asr r25 ror r24 

- tres equipos, dado que la expansi贸n general ahora ocupa un equipo - es mejor, aunque no perfecto, la misma imagen para

 byte=(uint8_t) byte << 1; 

- tres equipos Bueno, para no escribir lanzamientos adicionales, hacemos que la variable en s铆 misma no est茅 firmada

 uint8_t byteu; 

y BINGO: el c贸digo de ensamblador cumple totalmente con nuestras expectativas

 byteu = byteu << 1; lsr r24 

Es extra帽o c贸mo parecer铆a, qu茅 diferencia, indicar el tipo correcto de una variable de inmediato, o llevarlo directamente a una operaci贸n, pero resulta que hay una diferencia.

Otros estudios mostraron que el c贸digo del ensamblador tiene en cuenta el tipo de variable a la que se asigna el resultado, ya que

 byteu = byte << 1; 

funciona bien y produce un c贸digo m铆nimo, y la opci贸n

 byte = byteu << 1; 

no puede prescindir de tres equipos.

Seguramente este comportamiento se describe en el est谩ndar del lenguaje, les pregunto a los que saben en el comentario, pero una vez m谩s declarar茅 con orgullo que "el Chukchi no es un lector" y continuar茅 la historia.

Entonces, tal t茅cnica no ayud贸 a cambiar a la derecha, como antes, hab铆a 3 equipos (bueno, que no son 5, en cuanto a la versi贸n de signos) y no pude mejorar el resultado de ninguna manera.
Pero en cualquier caso, vemos que las operaciones de turno con un n煤mero sin signo se llevan a cabo m谩s r谩pido que con su oponente. Por lo tanto, si no vamos a tratar el bit de orden superior de un n煤mero como un signo (y en el caso de los registros, este suele ser el caso), entonces definitivamente debemos agregar el atributo sin signo, que haremos en el futuro.

Resulta que con los cambios en general, todo es extremadamente interesante, comencemos a aumentar el n煤mero de posiciones al cambiar a la izquierda y ver los resultados: << 1 toma 1 ciclo de reloj, << 2 - 2, << 3 - 3, 4 - 2 inesperadamente, el compilador aplic贸 una optimizaci贸n dif铆cil

 swap r24 andi r24,lo8(-16) 

donde el comando s wap intercambia dos mordiscos en un byte. Adem谩s, basado en la 煤ltima optimizaci贸n << 5 - 3, << 6 - 4, << 7 - 3 nuevamente inesperadamente, hay otra optimizaci贸n

 ror r24 clr r24 ror r24 

se utiliza el bit de transferencia, << 8 - 0 medidas, ya que simplemente resulta 0, no tiene sentido buscar m谩s.

Por cierto, aqu铆 hay una tarea interesante para usted: por qu茅 tiempo m铆nimo puede realizar una operaci贸n

 uint16_t byteu; byteu = byteu << 4; 

que traduce 0x1234 a 0x2340. La soluci贸n obvia es ejecutar un par de comandos 4 veces

 lsl rl rol rh 

conduce a 4 * 2 = 8 medidas, r谩pidamente se me ocurri贸 una opci贸n

 swap rl ; 1243 swap rh ; 2143 andi rh,0xf0 ; 2043 mov tmp,rl andi tmp,0x0f or rh,tmp ; 2343 andi rl,0xf0 ; 2340 

que requiere 7 medidas y un registro intermedio. Entonces, el compilador genera un c贸digo de 6 comandos y no hay registros intermedios, genial, s铆.

Oculto este c贸digo debajo del spoiler; intente encontrar una soluci贸n usted mismo.
Sugerencia: en el conjunto de comandos MK hay un comando O EXCLUSIVO o una CANTIDAD TOTAL DOS o

Aqu铆 est谩, este maravilloso c贸digo
 swap rl ; 1243 swap rh ; 2143 andi rh,0xf0 ; 2043 eor rh,rl ; 6343 andi r2l,0xf0 ; 6340 eor rh,rl ; 2340 


Solo obtengo placer est茅tico de este fragmento.

Por lo general, para los n煤meros de 16 bits, la diferencia entre el c贸digo para los n煤meros con signo y sin signo desapareci贸 cuando se desplaz贸 hacia la izquierda, es extra帽o as铆.

Volvamos a nuestros bytes y comencemos a movernos a la derecha. Como recordamos, para un byte firmado tenemos 5 ciclos de reloj, para un byte sin signo - 3 y este tiempo no se puede reducir. O de todos modos, puede, s铆, puede, pero es una forma muy extra帽a (GCC con optimizaciones activadas: "este es un lugar muy extra帽o"), es decir

 byteu = (byteu >> 1) & 0x7F; 

que genera exactamente un comando para ambas variantes del signo. Conveniente y opci贸n

  byteu = (byteu & 0xFE) >> 1; 

pero solo para un n煤mero sin signo, con un signo todo se vuelve a煤n m谩s deprimente: 7 medidas, por lo que continuamos explorando solo la primera opci贸n.

No puedo decir que entiendo lo que est谩 sucediendo, porque es obvio que la multiplicaci贸n l贸gica (&) por una constante despu茅s de tal cambio no tiene ning煤n sentido (y no lo hace), pero la presencia de la operaci贸n & afecta el c贸digo del cambio en s铆. "Ves al gopher, no, y yo no veo, pero 茅l s铆".

Los cambios en 2 y as铆 sucesivamente mostraron que es importante pagar el bit de signo, pero el n煤mero inicialmente no est谩 firmado, en general, se obtiene algo de basura, "pero funciona", es lo 煤nico que se puede decir al respecto.

Sin embargo, es seguro decir que interpretar el contenido de los registros y la memoria como n煤meros sin signo le permite realizar una serie de operaciones (por ejemplo, desplazar o expandir un valor) con ellos m谩s r谩pido y genera un c贸digo m谩s compacto, por lo que puede ser muy recomendable para escribir programas para MK, a menos que sea diferente (la interpretaci贸n como un n煤mero es familiar) no es un requisito previo.

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


All Articles