使用汇编代码从内存复制到c++中的寄存器

copy from memory to register in c++ using assembly code

本文关键字:c++ 寄存器 复制 内存 汇编 代码      更新时间:2023-10-16

我在将c++程序转换为程序集时遇到问题我必须为做

这是我的c++代码

for(int i=0;i<rows-4;i++,a+=4,b+=4,c+=4,d+=4,e+=4,f+=4,x+=4,o+=4){
  for(int j=0;j<cols-4;j++,a++,b++,c++,d++,e++,f++,x++,o++){
    *o=*a>*x;
    *o=*b>*x|(*o<<1);
    *o=*c>*x|(*o<<1);
    *o=*d>*x|(*o<<1);
    *o=*e>*x|(*o<<1);
    *o=*f>*x|(*o<<1);
    }
}

o是输出数据的指针,而a、b、c、d、e、f和x是输入数据的指针。我想要的只是将输入数据的比较保存到一个变量中,但当处理的数据很大时,上面的代码就没有效率了。与在寄存器中保存临时数据相比,该程序需要更多的时间将数据保存到内存中。

所以我想做的就是在寄存器中完成这个过程。我所尝试的是将x引用的数据存储在EBX中,将EBX与ECX进行比较,ECX保存a引用的值(以及b、c、d、e、f顺序),将比较结果保存到EAX,并将EAX寄存器向左移位,以便所有比较都存储在一个变量中。在已经处理了所有6个比较之后,将来自ECX的值复制到存储器中。

这就是我所做的,我的程序运行速度可以快两倍,但我得到的所有值都为零。也许我做得不对?

      __asm__(
"xorl %%eax,%%eax;"
"xorl %%ebx,%%ebx;"
"xorl %%ecx,%%ecx;"
"movl %1, %%ebx;"
//start here
"movl %2,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .one;"
"orl $0x1,%%eax;"
".one:;"
"shll $1,%%eax;"
"movl %3,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .two;"
"orl $0x1,%%eax;"
".two:;"
"shll $1,%%eax;"
"movl %4,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .three;"
"orl $0x1,%%eax;"
".three:;"
"shll $1,%%eax;"
"movl %5,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .four;"
"orl $0x1,%%eax;"
".four:"
"shll $1,%%eax;"
"movl %6,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .five;"
"orl $0x1,%%eax;"
".five:"
"shll $1,%%eax;"
"movl %7,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .six;"
"orl $0x1,%%eax;"
".six:"
//output
"movl %%eax,%0;"
:"=r"(sett)
:"r"((int)*x),"r"((int)*a) ,"r"((int)*b) ,"r"((int)*c) ,"r"((int)*d),"r"((int)*e),"r"((int)*f) /* input */
  );

几个选项:

1) 扔掉手工制作的程序集代码。你说C代码很慢,告诉我们多少。我看不出如何以任何有意义的方式测量差异,因为asm版本甚至没有产生正确的结果。换言之,试试asm("nop;");,这是一种更快地产生错误结果的方法。

2) 重写您的C代码,使其只读取*x一次;将结果保存在一个临时变量中,最后只写入*o

3) 如果适合您的语义(并且得到编译器的支持),则使用restrict/__restrict/__restrict__(来自C99,通常在C++中作为扩展提供)来装饰指针,这样编译器就知道在您写入*o时,输入变量都不会更改。

4) 编译器非常擅长自动展开循环。它可能需要命令行选项、#pragma指令或扩展/属性的组合。

编辑

这就是我重写它以使用临时性的意思:

for(int i=0;i<rows-4;i++,a+=4,b+=4,c+=4,d+=4,e+=4,f+=4,x+=4,o+=4){
    for(int j=0;j<cols-4;j++,a++,b++,c++,d++,e++,f++,x++,o++){
        uint32_t tmp_x = *x;
        *o = (*a > tmp_x ? 0x20 : 0)
          |  (*b > tmp_x ? 0x10 : 0)
          |  (*c > tmp_x ? 0x08 : 0)
          |  (*d > tmp_x ? 0x04 : 0)
          |  (*e > tmp_x ? 0x02 : 0)
          |  (*f > tmp_x ? 0x01 : 0);
    }
}

这有什么区别?在原始版本中,x在每个赋值中都会被读取。编译器不知道ox指向不同的位置;在最坏的情况下,编译器每次都必须再次从x读取,因为通过写入ox中的值可能会发生变化。

当然,这段代码有不同的语义:如果你真的让o别名另一个指针,它会做一些与原来不同的事情。

我假设您使用的是最新的英特尔芯片。。。我认为你真正想使用的是(如果用Cray:-来形容的话,相当有限)向量功能,这些被称为AVX。还有一些库可以在C/C++下做到这一点,从谷歌搜索AVX和C.开始

话虽如此,您还可以告诉编译器使用"register"关键字将一些变量存储在寄存器中,请参阅C++中的register关键字