¿Existen CPU modernas en las que un almacén de bytes en caché sea en realidad más lento que un almacén de palabras?

Resuelto Peter Cordes asked hace 5 años • 2 respuestas

Es una afirmación común que un almacenamiento de bytes en la caché puede resultar en un ciclo interno de lectura, modificación y escritura, o perjudicar el rendimiento o la latencia en comparación con el almacenamiento de un registro completo.

Pero nunca he visto ningún ejemplo. Ninguna CPU x86 es así, y creo que todas las CPU de alto rendimiento también pueden modificar directamente cualquier byte en una línea de caché. ¿Son diferentes algunos microcontroladores o CPU de gama baja, si es que tienen caché?

( No estoy contando las máquinas direccionables por palabras , o Alpha, que es direccionable por bytes pero carece de instrucciones de carga/almacenamiento de bytes. Estoy hablando de la instrucción de almacenamiento más estrecha que ISA admite de forma nativa).

En mi investigación mientras respondía ¿Puede el hardware x86 moderno no almacenar un solo byte en la memoria? , descubrí que las razones por las que Alpha AXP omitió los almacenes de bytes suponían que se implementarían como verdaderos almacenes de bytes en el caché, no como una actualización RMW de la palabra que los contenía. (Por lo tanto, habría encarecido la protección ECC para la caché L1d, porque necesitaría granularidad de bytes en lugar de 32 bits).

Supongo que word-RMW durante la confirmación al caché L1d no se consideró como una opción de implementación para otras ISA más recientes que implementan almacenes de bytes.

Todas las arquitecturas modernas (excepto las primeras Alpha) pueden realizar cargas/almacenamiento de bytes reales en regiones MMIO que no se pueden almacenar en caché (no ciclos RMW), lo cual es necesario para escribir controladores de dispositivos que tienen registros de E/S de bytes adyacentes. (por ejemplo, con señales externas de activación/desactivación para especificar qué partes de un bus más amplio contienen los datos reales, como el TSIZ (tamaño de transferencia) de 2 bits en esta CPU/microcontrolador ColdFire , o como transferencias de un solo byte PCI/PCIe, o como DDR Señales de control SDRAM que enmascaran bytes seleccionados).

¿Quizás hacer un ciclo RMW en caché para almacenes de bytes sería algo a considerar para el diseño de un microcontrolador, aunque no sea para un diseño canalizado superescalar de alta gama dirigido a servidores/estaciones de trabajo SMP como Alpha?

Creo que esta afirmación podría provenir de máquinas direccionables por palabras. O de almacenes de 32 bits no alineados que requieren múltiples accesos en muchas CPU, y personas que generalizan incorrectamente a partir de eso a almacenes de bytes.


Para que quede claro, espero que un bucle de almacenamiento de bytes a la misma dirección se ejecute en los mismos ciclos por iteración que un bucle de almacenamiento de palabras. Entonces, para llenar una matriz, las tiendas de 32 bits pueden ser hasta 4 veces más rápidas que las de 8 bits. (Quizás menos si las tiendas de 32 bits saturan el ancho de banda de la memoria pero las de 8 bits no). Pero a menos que las tiendas de bytes tengan una penalización adicional, no obtendrás una diferencia de velocidad superior a 4 veces. (O cualquiera que sea el ancho de la palabra).

Y estoy hablando de asm. Un buen compilador vectorizará automáticamente un bucle de almacenamiento de bytes o int en C y utilizará almacenes más amplios o lo que sea óptimo en el ISA de destino, si son contiguos.

(Y la fusión de la tienda en el búfer de la tienda también podría dar como resultado confirmaciones más amplias en la caché L1d para instrucciones de almacenamiento de bytes contiguos, por lo que esa es otra cosa a tener en cuenta al realizar microbenchmarking)

; x86-64 NASM syntax
mov   rdi, rsp
; RDI holds at a 32-bit aligned address
mov   ecx, 1000000000
.loop:                      ; do {
    mov   byte [rdi], al
    mov   byte [rdi+2], dl     ; store two bytes in the same dword
      ; no pointer increment, this is the same 32-bit dword every time
    dec   ecx
    jnz   .loop             ; }while(--ecx != 0}


    mov   eax,60
    xor   edi,edi
    syscall         ; x86-64 Linux sys_exit(0)

O un bucle sobre una matriz de 8 kiB como esta, almacenando 1 byte o 1 palabra de cada 8 bytes (para una implementación de C con sizeof(unsigned int)=4 y CHAR_BIT=8 para 8 kiB, pero debería compilarse en funciones comparables en cualquier Implementación de C, con solo un sesgo menor si sizeof(unsigned int)no es una potencia de 2). ASM en Godbolt para algunas ISA diferentes , sin desenrollar o con la misma cantidad de desenrollado para ambas versiones.

// volatile defeats auto-vectorization
void byte_stores(volatile unsigned char *arr) {
    for (int outer=0 ; outer<1000 ; outer++)
        for (int i=0 ; i< 1024 ; i++)      // loop over 4k * 2*sizeof(int) chars
            arr[i*2*sizeof(unsigned) + 1] = 123;    // touch one byte of every 2 words
}

// volatile to defeat auto-vectorization: x86 could use AVX2 vpmaskmovd
void word_stores(volatile unsigned int *arr) {
    for (int outer=0 ; outer<1000 ; outer++)
        for (int i=0 ; i<(1024 / sizeof(unsigned)) ; i++)  // same number of chars
            arr[i*2 + 0] = 123;       // touch every other int
}

Ajustando los tamaños según sea necesario, tendría mucha curiosidad si alguien pudiera señalar un sistema que word_store()sea más rápido que byte_store(). (Si realmente realiza una evaluación comparativa, tenga cuidado con los efectos de calentamiento, como la velocidad del reloj dinámico y el primer paso que provoca errores de TLB y errores de caché).

O si los compiladores de C reales para plataformas antiguas no existen o generan código subóptimo que no obstaculiza el rendimiento de la tienda, entonces cualquier conjunto hecho a mano que muestre un efecto.

Cualquier otra forma de demostrar una desaceleración para los almacenes de bytes está bien, no insisto en bucles sobre matrices o escrituras spam dentro de una palabra.

También estaría bien con documentación detallada sobre los componentes internos de la CPU o los números de tiempo del ciclo de la CPU para diferentes instrucciones. Sin embargo, desconfío de los consejos o guías de optimización que podrían basarse en esta afirmación sin haberlo probado.

  • ¿Alguna CPU o microcontrolador aún relevante donde los almacenes de bytes almacenados en caché tengan una penalización adicional?
  • ¿Alguna CPU o microcontrolador aún relevante donde los almacenes de bytes que no se pueden almacenar en caché tengan una penalización adicional?
  • ¿Alguna CPU histórica que aún no sea relevante (con o sin cachés de reescritura o escritura directa) donde cualquiera de las situaciones anteriores sea cierta? ¿Cuál es el ejemplo más reciente?

Por ejemplo, ¿es este el caso de un ARM Cortex-A? o Cortex-M? ¿Alguna microarquitectura ARM anterior? ¿Algún microcontrolador MIPS o una de las primeras CPU de servidor/estación de trabajo MIPS? ¿Algo más RISC aleatorio como PA-RISC o CISC como VAX o 486? (CDC6600 era direccionable por palabra).

O construya un caso de prueba que involucre cargas además de almacenes, por ejemplo, mostrando la palabra RMW de los almacenes de bytes que compiten con el rendimiento de la carga.

(No estoy interesado en mostrar que el reenvío de almacenamiento desde almacenes de bytes a cargas de palabras es más lento que palabra->palabra, porque es normal que SF solo funcione eficientemente cuando una carga está completamente contenida en el almacén más reciente para tocar cualquiera de los bytes relevantes. Pero algo que mostrara que el reenvío byte->byte es menos eficiente que palabra->palabra SF sería interesante, tal vez con bytes que no comienzan en un límite de palabra).


( No mencioné las cargas de bytes porque generalmente es fácil : acceda a una palabra completa desde el caché o la RAM y luego extraiga el byte que desea. Ese detalle de implementación es indistinguible excepto para MMIO, donde las CPU definitivamente no leen la palabra que contiene. )

En una arquitectura de carga/almacenamiento como MIPS, trabajar con datos de bytes solo significa usar lbo lbupara cargarlos y ponerlos a cero o firmarlos, extenderlos y luego almacenarlos nuevamente con sb. (Si necesita truncamiento a 8 bits entre pasos en los registros, es posible que necesite una instrucción adicional, por lo que las variables locales generalmente deben tener el tamaño de un registro. A menos que desee que el compilador vectorice automáticamente con SIMD con elementos de 8 bits, a menudo uint8_t los locales son buenos...) Pero de todos modos, si lo haces bien y tu compilador es bueno, no debería costar ninguna instrucción adicional tener matrices de bytes.

Noto que gcc tiene sizeof(uint_fast8_t) == 1ARM, AArch64, x86 y MIPS. Pero no sé cuánto valor podemos poner en eso. La ABI x86-64 System V se define uint_fast32_tcomo un tipo de 64 bits en x86-64. Si van a hacer eso (en lugar de 32 bits, que es el tamaño de operando predeterminado de x86-64), uint_fast8_ttambién debería ser un tipo de 64 bits. ¿Quizás para evitar la extensión cero cuando se usa como índice de matriz? Si se pasó como una función arg en un registro, ya que podría extenderse a cero de forma gratuita si tuviera que cargarlo desde la memoria de todos modos.

Peter Cordes avatar Jan 16 '19 19:01 Peter Cordes
Aceptado

Mi suposición estaba equivocada. Las microarquitecturas x86 modernas realmente son diferentes en este sentido de algunas (¿la mayoría?) de otras ISA.

Puede haber una penalización por almacenamientos estrechos en caché incluso en CPU de alto rendimiento que no sean x86. Sin embargo , la reducción en el espacio de la caché aún puede hacer que int8_tvalga la pena usar las matrices. (Y en algunos ISA como MIPS, ayuda no tener que escalar un índice para un modo de direccionamiento).

Fusionar/unir en el búfer de almacenamiento entre instrucciones de almacenamiento de bytes en la misma palabra antes de la confirmación real a L1d también puede reducir o eliminar la penalización. (A veces, x86 no puede hacer tanto de esto porque su modelo de memoria sólida requiere que todos los almacenes se comprometan en el orden del programa).


La documentación de ARM para Cortex-A15 MPCore (de ~2012) dice que usa granularidad ECC de 32 bits en L1d y, de hecho, hace una palabra-RMW para tiendas estrechas para actualizar los datos.

La caché de datos L1 admite lógica opcional de corrección de errores de detección de un solo bit y de detección de doble bit tanto en la etiqueta como en las matrices de datos. La granularidad ECC para la matriz de etiquetas es la etiqueta para una única línea de caché y la granularidad ECC para la matriz de datos es una palabra de 32 bits.

Debido a la granularidad de ECC en la matriz de datos, una escritura en la matriz no puede actualizar una parte de una ubicación de memoria alineada de 4 bytes porque no hay suficiente información para calcular el nuevo valor de ECC. Este es el caso de cualquier instrucción de almacenamiento que no escriba una o más regiones de memoria alineadas de 4 bytes. En este caso, el sistema de memoria de datos L1 lee los datos existentes en la caché, fusiona los bytes modificados y calcula el ECC a partir del valor fusionado. El sistema de memoria L1 intenta fusionar varios almacenes para cumplir con la granularidad ECC alineada de 4 bytes y evitar el requisito de lectura, modificación y escritura.

(Cuando dicen "el sistema de memoria L1", creo que se refieren al búfer de almacenamiento, si tiene almacenes de bytes contiguos que aún no se han comprometido con L1d).

Tenga en cuenta que el RMW es atómico y solo implica la modificación de la línea de caché de propiedad exclusiva. Este es un detalle de implementación que no afecta el modelo de memoria. Entonces, mi conclusión sobre ¿Puede el hardware x86 moderno no almacenar ni un solo byte en la memoria? Todavía es (probablemente) correcto que x86 pueda, al igual que cualquier otro ISA que proporcione instrucciones de almacenamiento de bytes.


Cortex-A15 MPCore es una CPU de ejecución desordenada de 3 vías, por lo que no es un diseño ARM simple/de potencia mínima, sin embargo, eligieron gastar transistores en OoO exec pero no en almacenes de bytes eficientes.

Presumiblemente, sin la necesidad de admitir almacenes no alineados eficientes (que es más probable que el software x86 asuma o aproveche), se consideró que valía la pena tener almacenes de bytes más lentos para la mayor confiabilidad de ECC para L1d sin una sobrecarga excesiva.

Cortex-A15 probablemente no sea el único núcleo ARM, ni el más reciente, que funciona de esta manera.


Otros ejemplos (encontrados por @HadiBrais en los comentarios):

  1. Alpha 21264 (consulte la Tabla 8-1 del Capítulo 8 de este documento) tiene una granularidad ECC de 8 bytes para su caché L1d. Las tiendas más estrechas (incluidas las de 32 bits) dan como resultado un RMW cuando se comprometen con L1d, si no se combinan primero en el búfer de la tienda. El documento explica todos los detalles de lo que L1d puede hacer por reloj. Y documenta específicamente que el buffer de tiendas fusiona tiendas.

  2. PowerPC RS64-II y RS64-III (consulte la sección de errores en este documento). Según este resumen , L1 del procesador RS/6000 tiene 7 bits de ECC por cada 32 bits de datos.

Alpha era agresivamente de 64 bits desde cero, por lo que la granularidad de 8 bytes tiene cierto sentido, especialmente si el búfer de la tienda puede ocultar/absorber el costo de RMW en su mayor parte. (por ejemplo, tal vez los cuellos de botella normales estuvieran en otros lugares para la mayor parte del código en esa CPU; su caché multipuerto normalmente podría manejar 2 operaciones por reloj).

POWER / PowerPC64 surgió de PowerPC de 32 bits y probablemente se preocupa por ejecutar código de 32 bits con enteros y punteros de 32 bits. (Por lo tanto, es más probable que se realicen almacenes de 32 bits no contiguos en estructuras de datos que no se pudieron fusionar). Por lo tanto, la granularidad ECC de 32 bits tiene mucho sentido allí.

Peter Cordes avatar Jan 16 '2019 21:01 Peter Cordes

cortex-m7 trm, sección de memoria caché del manual:

En un sistema libre de errores, el mayor impacto en el rendimiento es el costo del esquema de lectura-modificación-escritura para almacenes no completos en el lado de los datos. Si una ranura del búfer de almacenamiento no contiene al menos una palabra completa de 32 bits, debe leer la palabra para poder calcular los bits de verificación. Esto puede ocurrir porque el software solo escribe en un área de la memoria con instrucciones de almacenamiento de bytes o media palabra. Luego los datos se pueden escribir en la RAM. Esta lectura adicional puede tener un impacto negativo en el rendimiento porque impide que la ranura se utilice para otra escritura.

.

El almacenamiento en búfer y las excelentes capacidades del sistema de memoria enmascaran parte de la lectura adicional, y es insignificante para la mayoría de los códigos. Sin embargo, ARM recomienda utilizar la menor cantidad posible de instrucciones STRB y STRH almacenables en caché para reducir el impacto en el rendimiento.

Tengo cortex-m7 pero hasta la fecha no he realizado ninguna prueba para demostrarlo.

Lo que se entiende por "leer la palabra" es una lectura de una ubicación de almacenamiento en una SRAM que forma parte de la caché de datos. No es una cuestión de memoria del sistema de alto nivel.

El núcleo del caché está construido a partir de bloques SRAM y alrededor de ellos, que son la SRAM rápida que hace que un caché sea lo que es, más rápido que la memoria del sistema, rápido para devolver respuestas al procesador, etc. Esta lectura-modificación-escritura (RMW) No es una política de escritura de alto nivel. Lo que dicen es que si hay un acierto y la política de escritura dice que se guarde la escritura en el caché, entonces el byte o media palabra debe escribirse en una de estas SRAM. El ancho de la memoria caché de datos SRAM con ECC, como se muestra en este documento, es de 32+7 bits. 32 bits de datos 7 bits de bits de verificación ECC. Debes mantener los 39 bits juntos para que ECC funcione. Por definición, no se pueden modificar sólo algunos de los bits, ya que eso provocaría una falla ECC.

Siempre que sea necesario cambiar cualquier cantidad de bits en esa palabra de 32 bits almacenada en la memoria caché de datos SRAM, 8, 16 o 32 bits, se deben volver a calcular los 7 bits de verificación y escribir los 39 bits a la vez. Para una escritura de 8 o 16 bits, STRB o STRH, es necesario leer los 32 bits de datos, modificar los 8 o 16 bits con los bits de datos restantes en esa palabra sin cambios, calcular los 7 bits de verificación ECC y escribir los 39 bits en el sram. .

Lo ideal/probable es que el cálculo de los bits de verificación se realice dentro del mismo ciclo de reloj que configura la escritura, pero la lectura y la escritura no están en el mismo ciclo de reloj, por lo que deberían tomarse al menos dos ciclos separados para escribir los datos que llegaron al caché. en un ciclo de reloj. Hay trucos para retrasar la escritura que a veces también pueden resultar perjudiciales, pero normalmente la mueven a un ciclo que no se habría utilizado y la liberan, por así decirlo. Pero no será el mismo ciclo de reloj que la lectura.

Dicen que si mantienes la boca bien y logras que suficientes tiendas más pequeñas accedan al caché lo suficientemente rápido, detendrán el procesador hasta que puedan alcanzarlo.

El documento también describe la SRAM sin ECC como de 32 bits de ancho, lo que implica que esto también es cierto cuando se compila el núcleo sin soporte ECC. No tengo acceso a las señales para esta interfaz de memoria ni a la documentación, por lo que no puedo decirlo con certeza, pero si se implementa como una interfaz de 32 bits de ancho sin controles de carril de bytes, entonces tiene el mismo problema, solo puede escribir un Elemento completo de 32 bits para esta SRAM y no fracciones, por lo que para cambiar 8 o 16 bits tienes que RMW, en las entrañas del caché.

La respuesta corta a por qué no usar una memoria más estrecha es el tamaño del chip, con ECC el tamaño se duplica ya que hay un límite en la cantidad de bits de verificación que puede usar incluso con el ancho cada vez más pequeño (7 bits por cada 8 bits es mucho más). bits para ahorrar que 7 bits por cada 32). Cuanto más estrecha sea la memoria, también tendrá muchas más señales para enrutar y no podrá empaquetar la memoria con tanta densidad. Un apartamento frente a un conjunto de casas individuales para albergar el mismo número de personas. Caminos y aceras hasta la puerta de entrada en lugar de pasillos.

Y especialmente con un procesador de un solo núcleo como este, a menos que lo intente intencionalmente (lo cual haré), es poco probable que lo alcance accidentalmente y ¿por qué aumentar el costo del producto en un: probablemente no sucederá?

Tenga en cuenta que incluso con un procesador multinúcleo verá las memorias construidas así.

Editar

Bien, me puse a hacer una prueba.

0800007c <lwtest>:
 800007c:   b430        push    {r4, r5}
 800007e:   6814        ldr r4, [r2, #0]

08000080 <lwloop>:
 8000080:   6803        ldr r3, [r0, #0]
 8000082:   6803        ldr r3, [r0, #0]
 8000084:   6803        ldr r3, [r0, #0]
 8000086:   6803        ldr r3, [r0, #0]
 8000088:   6803        ldr r3, [r0, #0]
 800008a:   6803        ldr r3, [r0, #0]
 800008c:   6803        ldr r3, [r0, #0]
 800008e:   6803        ldr r3, [r0, #0]
 8000090:   6803        ldr r3, [r0, #0]
 8000092:   6803        ldr r3, [r0, #0]
 8000094:   6803        ldr r3, [r0, #0]
 8000096:   6803        ldr r3, [r0, #0]
 8000098:   6803        ldr r3, [r0, #0]
 800009a:   6803        ldr r3, [r0, #0]
 800009c:   6803        ldr r3, [r0, #0]
 800009e:   6803        ldr r3, [r0, #0]
 80000a0:   3901        subs    r1, #1
 80000a2:   d1ed        bne.n   8000080 <lwloop>
 80000a4:   6815        ldr r5, [r2, #0]
 80000a6:   1b60        subs    r0, r4, r5
 80000a8:   bc30        pop {r4, r5}
 80000aa:   4770        bx  lr

Hay versiones de palabra de carga (ldr), byte de carga (ldrb), palabra de almacenamiento (str) y byte de almacenamiento (strb), cada una de las cuales está alineada en límites de al menos 16 bytes hasta la parte superior de la dirección del bucle.

con icache y dcache habilitados

    ra=lwtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=lwtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=lbtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=lbtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=swtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=swtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=sbtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=sbtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);


0001000B                                                                        
00010007                                                                        
0001000B                                                                        
00010007                                                                        
0001000C                                                                        
00010007                                                                        
0002FFFD                                                                        
0002FFFD  

Las cargas están a la par como se esperaba, sin embargo, las tiendas, cuando las agrupas de esta manera, una escritura de byte es 3 veces más larga que una escritura de palabra.

Pero si no golpeas el caché con tanta fuerza

0800019c <nbtest>:
 800019c:   b430        push    {r4, r5}
 800019e:   6814        ldr r4, [r2, #0]

080001a0 <nbloop>:
 80001a0:   7003        strb    r3, [r0, #0]
 80001a2:   46c0        nop         ; (mov r8, r8)
 80001a4:   46c0        nop         ; (mov r8, r8)
 80001a6:   46c0        nop         ; (mov r8, r8)
 80001a8:   7003        strb    r3, [r0, #0]
 80001aa:   46c0        nop         ; (mov r8, r8)
 80001ac:   46c0        nop         ; (mov r8, r8)
 80001ae:   46c0        nop         ; (mov r8, r8)
 80001b0:   7003        strb    r3, [r0, #0]
 80001b2:   46c0        nop         ; (mov r8, r8)
 80001b4:   46c0        nop         ; (mov r8, r8)
 80001b6:   46c0        nop         ; (mov r8, r8)
 80001b8:   7003        strb    r3, [r0, #0]
 80001ba:   46c0        nop         ; (mov r8, r8)
 80001bc:   46c0        nop         ; (mov r8, r8)
 80001be:   46c0        nop         ; (mov r8, r8)
 80001c0:   3901        subs    r1, #1
 80001c2:   d1ed        bne.n   80001a0 <nbloop>
 80001c4:   6815        ldr r5, [r2, #0]
 80001c6:   1b60        subs    r0, r4, r5
 80001c8:   bc30        pop {r4, r5}
 80001ca:   4770        bx  lr

entonces la palabra y el byte toman la misma cantidad de tiempo

    ra=nwtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=nwtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=nbtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
    ra=nbtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);

0000C00B                                                                        
0000C007                                                                        
0000C00B                                                                        
0000C007

Todavía se necesitan 4 veces más tiempo para hacer bytes versus palabras y todos los demás factores se mantienen constantes, pero ese fue el desafío de que los bytes tomaran más de 4 veces más.

Entonces, como estaba describiendo antes de esta pregunta, verá que los srams tienen un ancho óptimo en el caché, así como otros lugares y las escrituras de bytes sufrirán una lectura-modificación-escritura. Ahora bien, si eso es visible o no debido a otros gastos generales u optimizaciones o no, es otra historia. ARM indicó claramente que puede ser visible y creo que lo he demostrado. Esto no es negativo para el diseño de ARM de ninguna manera; de hecho, al revés, RISC se mueve por encima en general en lo que respecta al lado de instrucción/ejecución, se necesitan más instrucciones para realizar la misma tarea.

Las eficiencias en el diseño permiten que cosas como ésta sean visibles. Hay libros completos escritos sobre cómo hacer que su x86 vaya más rápido, no realice operaciones de 8 bits para esto o aquello, o se prefieren otras instrucciones, etc. Lo que significa que debería poder escribir un punto de referencia para demostrar esos avances en el rendimiento. Al igual que este, incluso si calcula cada byte en una cadena a medida que lo mueve a la memoria, esto debería estar oculto, necesita escribir un código como este y si fuera a hacer algo como esto, podría considerar grabar las instrucciones combinando los bytes. en una palabra antes de escribir, puede o no ser más rápido... depende.

Si tuviera media palabra (strh), entonces no es de extrañar, también sufre la misma lectura-modificación-escritura ya que la memoria RAM tiene 32 bits de ancho (más los bits ecc, si los hubiera).

0001000C   str                                                                      
00010007   str                                                                      
0002FFFD   strh                                                                     
0002FFFD   strh                                                                     
0002FFFD   strb                                                                     
0002FFFD   strb

las cargas toman la misma cantidad de tiempo que el ancho de sram se lee en su totalidad y se coloca en el bus, el procesador extrae los carriles de bytes de interés de eso, por lo que no hay costo de tiempo/reloj para hacerlo.

old_timer avatar Jan 17 '2019 03:01 old_timer