【问题标题】:copy from memory to register in c++ using assembly code使用汇编代码从内存复制到 C++ 中的寄存器
【发布时间】:2013-08-10 01:54:45
【问题描述】:

我在将 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 */
  );

【问题讨论】:

  • 我相信有些工具非常擅长将 C 和 C++ 代码转换为相当优化的程序集,而不会引入人为错误。要是能记得他们叫什么就好了……

标签: c++ assembly cpu-registers


【解决方案1】:

几个选项:

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为其他指针中的任何一个起别名,它会做一些与原来不同的事情。

【讨论】:

  • 程序运行1M数据约33ms(我的图像是1200x1100),最耗时的是存储到*o,当它不存储到*o时时间大约是5ms,使用那个汇编代码大约需要 15 毫秒……所以问题不在于读取部分,而是写入部分……我需要修改我的程序,以便它只存储一次结果……存储到临时变量也无济于事。 ..
  • 如果您没有向*o 写入任何内容,编译器可能正在优化循环。正如我所说,如果您的 ASM 代码产生了不正确的结果,那么比较没有意义。我的 ASM 代码比你的要高出无数倍。
  • 哇...非常感谢..它真的让我的程序显着加速,现在它可以工作大约9ms(之前是33ms),并结合上面提到的register关键字提高速度约为 7 毫秒...非常感谢:D
  • 我从来没有找到 register 关键字来更改生成程序集中的任何内容。一些messages from the GCC mailing list 建议,除非您禁用所有优化,否则这无关紧要(实际上它在 C++1y 中已被弃用,因为它没有用处)。您是否在启用优化的情况下进行编译?我想restrict 会产生更大的影响。
  • 对我来说 register 关键字确实在速度上有所不同,大约 1ms 进行 100 万次迭代,我认为优化没有启用,我的编译器很奇怪,今天早上代码运行得很慢,我新建代码,一模一样,速度又正常了,现在8.5ms左右,明天可能又慢了,不知道怎么回事,我用的是gcc 4.7.2
【解决方案2】:

我假设您使用的是最新的英特尔芯片。 ...而且我认为您真正想要使用的是(如果有人用来说 Cray 的话,则相当有限:-)矢量功能,这些功能称为 AVX。还有一些库可以在 C/C++ 下执行此操作,从谷歌搜索 AVX 和 C 开始。

话虽如此,您也可以使用“register”关键字告诉编译器将一些变量存储在寄存器中,请参阅Register keyword in C++

【讨论】:

  • 是的...你给了我一个很好的建议,我按照 DanielKO 的建议编辑了我的程序,我还使用了 register 关键字,现在我的程序可以在大约 7 毫秒内运行(之前是 33 毫秒!)我还会检查AVX,非常感谢:-)
猜你喜欢
  • 2019-03-24
  • 2014-11-21
  • 1970-01-01
  • 2015-09-25
  • 2015-11-01
  • 1970-01-01
  • 2019-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多