使用 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++ 中的内联汇编对缓冲区中的数字求和的主要内容,如果未能解决你的问题,请参考以下文章