使用c ++中的内联汇编在缓冲区中求和数

时间:2014-10-07 13:12:39

标签: c++ x86 inline-assembly

我是汇编语言编程的新手,面对的问题对于有经验的汇编语言用户来说可能是显而易见的。我有一个100字节的缓冲区,我需要找到n = 1到5的每个第n个字节的总和,并将结果存储在5个整数的数组中。我需要在我的c ++代码中使用内联汇编来执行此操作。我写了以下代码:

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;
    }
}

所以最后,result [0]应该有缓冲区[0],缓冲区[5],缓冲区[10] ....之和,结果[1]将有缓冲区[1],缓冲区[ 6],缓冲[11] ....等等。 我在第一个汇编指令本身遇到了Access Violation错误(mov eax,start [j])。任何人都可以帮我找出我所犯的错误吗?如果有人可以帮我用汇编语言编写整个循环和求和部分,那就太棒了

1 个答案:

答案 0 :(得分:1)

显然我不知道你的实际意图,但我质疑“我想使用这个概念的真实场景可能带来一些好处。”

说人类不能再为i386编写高效的汇编程序可能不是100%准确,但这几乎是正确的。如果您熟悉流水线操作和乱序执行,那么您已经理解为什么会这样。如果你不熟悉这些,你已经说过你不知道如何编写高效的汇编程序。

这并不是说您不应该查看程序热点的汇编程序。但是你应该编写最有效的c代码并对其进行基准测试,然后再尝试查看是否可以在asm中编写更好的东西。如果你不能,不要感到惊讶。

  • 请记住,仅仅因为一些微小的例程在一个微小的测试程序中表现得更好,这并不能保证它会在原始程序中包含它时会这样做。
  • 或者它会在所有处理器上表现更好。
  • 或者在新版本的编译器中。
  • 当然使用asm意味着迁移到新平台(如x64)可能需要重新编写asm,导致工作人员诅咒你的名字。

那就是说,你可以尝试这样的基准测试。我的猜测是它会更好,但这只是猜测。

#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;
}

循环在asm中看起来像这样(使用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

这样可以在计算期间尽可能多地保留寄存器中的“结果”(而不是像现有代码那样不断地将它们全部读取/写入内存)。更少的循环也意味着更少的cmp指令,这只通过缓冲区而不是5次。编译x64(现在没有asm更容易)提供更好的代码,因为有更多的寄存器可供使用。

如果MAXTOT变大,显然会崩溃。但我只能对我能看到的代码发表评论,而5就是你所使用的代码。

FWIW。