使用 C++ 中的内联汇编对缓冲区中的数字求和

Posted

技术标签:

【中文标题】使用 C++ 中的内联汇编对缓冲区中的数字求和【英文标题】:Sum numbers in a buffer using inline assembly in c++ 【发布时间】:2014-10-07 13:12:39 【问题描述】:

我是汇编语言编程的新手,遇到的问题对于有经验的汇编语言用户来说可能很明显。我有一个 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] 应该有 buffer[0], buffer[5], buffer[10] .... 的总和,result[1] 会有 buffer[1], buffer[ 的总和6],缓冲区[11] ....等等。 我在第一条汇编指令本身(mov eax,start [j])中遇到访问冲突错误。谁能帮我找出我犯的错误?如果有人可以帮助我用汇编语言编写整个循环和求和部分,那就太好了

【问题讨论】:

你确定这是你想要的吗?因为那不是,我将其理解为每第 n 个字节的总和。 装配中有多少任务要完成?您只是在程序集中添加数字。 这是我想做的简化版。现在,如果我可以在汇编中编写循环和求和,我会很高兴。我知道与普通的 c++ 代码相比不会有太大的性能优势,但是我想使用这个概念的真实场景可能会带来一些优势 您真的想要内联汇编还是对 NASM 满意?还有,用静态数组代替动态数组怎么样? 【参考方案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。

【讨论】:

以上是关于使用 C++ 中的内联汇编对缓冲区中的数字求和的主要内容,如果未能解决你的问题,请参考以下文章

使用内联汇编器从 GCC 中的共享库调用函数

gcc 内联汇编中的 min

x86 - 使用内联汇编设置位

具有非内联汇编的 Qt C++ 项目

内联汇编中的 vpcmpeqb

如何使用 Visual C++ 的内联汇编器插入重复的 NOP 语句?