Sume números en un búfer usando ensamblado en línea en C++

Soy nuevo en la programación del lenguaje ensamblador y me enfrento a un problema que podría ser obvio para los usuarios experimentados del lenguaje ensamblador. Tengo un búfer de 100 bytes y necesito encontrar la suma de cada enésimo byte para n = 1 a 5 y almacenar el resultado en una matriz de 5 enteros. Necesito usar el ensamblaje en línea en mi código c ++ para hacer esto. Escribí el siguiente código:

void main()
{
    char *buffer = new char[100];
    int *result = new int[5]();

    for (int i = 0; i < 100; i++)
        buffer[i] = i;

    char *start = buffer;
    for (int k = 0; k < 20; k++)
    {
        for (int j = 0; j < 5; j++)
        {
        __asm
            {
                mov eax, start[j]
                mov ebx, result[j]
                add eax, ebx
                mov result[j], eax

            }

        }
        start += 5;
    }
}

Entonces, al final, el resultado [0] debe tener la suma del búfer [0], el búfer [5], el búfer [10] .... , el resultado [1] tendrá la suma del búfer [1], el búfer [6], búfer[11] .... y así sucesivamente. Recibo un error de infracción de acceso en la primera instrucción de ensamblaje (mov eax, start[j]). Alguien me puede ayudar a saber el error que he cometido? También sería genial si alguien pudiera ayudarme a escribir todo el bucle y la parte de suma en lenguaje ensamblador.

preguntado Oct 07 '14, 14:10

¿Estás seguro de que eso es lo que quieres? Porque eso no es, lo que yo entendería como la suma de cada enésimo byte. -

¿Qué parte de la tarea se debe realizar en el ensamblaje? Solo está agregando números en el ensamblaje. -

Esta es una versión simplificada de lo que quiero hacer. Por ahora, estaría feliz si pudiera escribir el bucle y la suma en ensamblador. Entiendo que no habría mucha ventaja de rendimiento en comparación con el código C++ normal, pero el escenario real en el que quiero usar este concepto puede resultar en alguna ventaja:

¿Realmente desea ensamblar en línea o estaría feliz con NASM? Además, ¿qué pasa con el uso de matrices estáticas en lugar de matrices dinámicas? -

1 Respuestas

Obviamente, no conozco su intención real, pero cuestiono la suposición de que "el escenario real en el que quiero usar este concepto puede resultar en alguna ventaja".

Puede que no sea 100% exacto decir que los humanos ya no pueden escribir un ensamblador eficiente para i386, pero es casi cierto. Si está familiarizado con la canalización y la ejecución fuera de orden, ya comprenderá por qué esto es así. Si no está familiarizado con estos, ya está diciendo que no sabe cómo escribir un ensamblador eficiente.

Eso no quiere decir que no deba buscar en el ensamblador los puntos de acceso de su programa. Pero debe escribir el código c más eficiente que pueda y compararlo antes de intentar ver si puede escribir algo mejor en asm. No te sorprendas si no puedes.

  • Recuerde que el hecho de que una rutina diminuta funcione mejor en un programa de prueba diminuto no garantiza que lo hará cuando se incluya de nuevo en el programa original.
  • O que funcionará mejor en todos los procesadores.
  • O en nuevas versiones del compilador.
  • Y, por supuesto, usar asm significa que pasar a nuevas plataformas (como x64) probablemente requerirá que se reescriba el asm, lo que hará que las personas que hacen el trabajo maldigan su nombre.

Dicho esto, podría intentar comparar algo como esto. Mi adivinar es que será mejor, pero eso es solo una suposición.

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100
#define MAXTOT 5

typedef unsigned char BYTE;

int main()
{
    BYTE *buffer = (BYTE *)malloc(MAXSIZE);
    const BYTE *start = buffer;
    unsigned int t0, t1, t2, t3, t4;

    for (int i = 0; i < MAXSIZE; i++)
        buffer[i] = i;

    t0 = 0;
    t1 = 0;
    t2 = 0;
    t3 = 0;
    t4 = 0;

    for (int j=0; j < (MAXSIZE / MAXTOT); j++)
    {
        t0 += start[0];
        t1 += start[1];
        t2 += start[2];
        t3 += start[3];
        t4 += start[4];

        start += MAXTOT;
    }

    printf("%u %u %u %u %u\n", t0, t1, t2, t3, t4);

    free(buffer);

    return 0;
}

El ciclo se ve así en asm (usando gcc -O2):

L3:
    movzbl  (%edx), %edi
    addl    $5, %edx
    addl    %edi, 44(%esp)
    movzbl  -4(%edx), %edi
    addl    %edi, %ebx
    movzbl  -3(%edx), %edi
    addl    %edi, %eax
    movzbl  -2(%edx), %edi
    addl    %edi, %ecx
    movzbl  -1(%edx), %edi
    addl    %edi, %esi
    cmpl    40(%esp), %edx
    jne     L3

Esto mantiene la mayor cantidad posible de "resultados" en los registros durante la duración del cálculo (en lugar de leerlos/escribirlos constantemente en la memoria como su código existente). Menos bucles también significan menos instrucciones cmp, y esto solo hace que una pase a través del búfer en lugar de 5. La compilación para x64 (mucho más fácil ahora que no hay asm) brinda un código aún mejor, ya que hay más registros disponibles.

Obviamente, esto se desmorona si MAXTOT crece. Pero solo puedo comentar sobre el código que puedo ver, y 5 es lo que usaste.

FWIW.

Respondido 08 Oct 14, 07:10

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.