Jam*_*tta 2 c++ assembly inline-assembly
我编写了一个程序来处理以big-endian格式写入磁盘的一些数据,因此程序需要交换字节才能执行其他操作.在分析代码后,我发现我的字节交换功能占用了30%的执行时间.所以我想,我怎么能加快速度呢?所以我决定写一小块内联汇编.
我想取代这个:
void swapTwoByte(char* array, int numChunks)
{
for(int i= (2*numChunks-1); i>=0; i-=2)
{
char temp=array[i];
array[i]=array[i-1];
array[i-1]=temp;
}
}
Run Code Online (Sandbox Code Playgroud)
有了这个:
void swapTwoByte(int16* array, int numChunks)
{
for(int i= (numChunks-1); i>=0; --i)
{
asm("movw %1, %%ax;"
"rorw %%ax;"
"rorw %%ax;"
"rorw %%ax;"
"rorw %%ax;"
"rorw %%ax;"
"rorw %%ax;"
"rorw %%ax;"
"rorw %%ax;"
"movw %%ax, %0;"
: "=r" ( array[i] )
: "r" (array[i])
:"%ax"
);
}
}
Run Code Online (Sandbox Code Playgroud)
这是预期的工作,但这是很多旋转操作.
所以这是我的问题:根据这个来源, rorw可以采用两个操作数,而在gas sytax中,源操作数应该是要旋转的位数,但每次我尝试用类似的东西替换8个旋转权限的列表
".set rotate, 0x0008"
"rorw rotate, %%ax"
Run Code Online (Sandbox Code Playgroud)
我收到一个汇编错误说明:
"Error: number of operands mismatch for `ror'"
Run Code Online (Sandbox Code Playgroud)
为什么是这样?我错过了什么?
Nem*_*emo 10
首先,使用
#include <arpa/inet.h>
little_endian = ntohs(big_endian);
Run Code Online (Sandbox Code Playgroud)
这将在您正在使用的任何系统上编译成最佳代码,如果您碰巧将代码移植到big-endian平台,它甚至可以工作.
但是,这不会解决您的性能问题,因为我认为您错误地识别了该问题.Nemo的第一个微优化规则:"数学很快;记忆很慢".
迭代一大块内存并交换其字节对缓存不友好.字节交换是一个周期; 内存读取或写入是数百个周期,除非它在缓存中命中.
因此,在使用它们之前不要交换字节.我个人最喜欢的方法是:
class be_uint16_t {
public:
be_uint16_t() : be_val_(0) {
}
be_uint16_t(const uint16_t &val) : be_val_(htons(val)) {
}
operator uint16_t() const {
return ntohs(be_val_);
}
private:
uint16_t be_val_;
} __attribute__((packed));
Run Code Online (Sandbox Code Playgroud)
这定义了一个双字节类,表示内存中的大端数字.它根据需要隐式地转换为uint16_t.因此,将内存指针转换为a be_uint16 *
,只需像数组一样访问它; 忘记字节交换,因为类会为你做:
const be_uint16_t *p = (be_uint16 *)my_block;
unsigned val = p[37]; // or whatever
Run Code Online (Sandbox Code Playgroud)
请注意,您甚至可以执行以下操作:
be_uint16_t x = 12;
x = x + 1;
write(fd, &x, sizeof(x)); // writes 13 to file in big-endian form
Run Code Online (Sandbox Code Playgroud)
根据我的经验,在使用之前立即交换值的开销是不可检测的.地方是游戏的名称......
考虑重新组织一下C++代码.正如所写,g ++ 4.5.2为我编译了一个无聊的紧密循环,有四个8位mov
s和两个指针递减.
.L3:
movzbl (%rdi), %eax
movzbl -1(%rdi), %edx
movb %al, -1(%rdi)
movb %dl, (%rdi)
subq $2, %rdi
subl $2, %esi
jns .L3
Run Code Online (Sandbox Code Playgroud)
把它重写为
void swapTwoByte(char* array, int numChunks)
{
for(int i = 0; i<numChunks*2; i+=2)
std::swap(array[i], array[i+1]);
}
Run Code Online (Sandbox Code Playgroud)
让编译器实现您正在做的事情并打开完整的SIMD电源,核心循环现在一次处理32个字节:
.L4:
movdqu (%rdx), %xmm1
movdqu (%rax), %xmm2
movdqa %xmm1, %xmm0
movdqa %xmm2, %xmm3
pshufb %xmm7, %xmm0
pshufb %xmm4, %xmm2
pshufb %xmm6, %xmm3
pshufb %xmm5, %xmm1
por %xmm3, %xmm0
por %xmm2, %xmm1
incl %ecx
movdqa %xmm1, %xmm2
punpckhbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqu %xmm2, (%rdx)
movdqu %xmm1, (%rax)
addq $32, %rdx
addq $32, %rax
cmpl %ecx, %r8d
ja .L4
Run Code Online (Sandbox Code Playgroud)
a rorw
不会打败那个.