我很好奇是否有人可以为我阐明这一点。我正在研究一些数字数据转换的东西,我有几个执行数据转换的函数,我用两个宏定义:
#define CONV_VIA_CAST(name, dtype, vtype) \
static inline void name(void *data, void *view, size_t len) { \
vtype *vptr = (vtype*)view; \
dtype *dptr = (dtype*)data; \
for (size_t ii=0; ii < len/sizeof(vtype); ii++) { \
*vptr++ = (vtype)*dptr++; \
} \
}
#define CONV_VIA_FUNC(name, dtype, vtype, via) \
static inline void name(void *data, void *view, size_t len) { \
vtype *vptr = (vtype*)view; \
dtype *dptr = (dtype*)data; \
for (size_t ii=0; ii < len/sizeof(vtype); ii++) { \
*vptr++ = (vtype)via(*dptr++); \
} \
}
当我将float转换为int转换时:
CONV_VIA_FUNC(f_to_i, float, int16_t, lrintf);
我在-O3上获得了一个很好的简洁小装配:
0x0000000000401fb0 <+0>: shr %rdx
0x0000000000401fb3 <+3>: je 0x401fd3 <f_to_i+35>
0x0000000000401fb5 <+5>: xor %eax,%eax
0x0000000000401fb7 <+7>: nopw 0x0(%rax,%rax,1)
0x0000000000401fc0 <+16>: cvtss2si (%rdi,%rax,4),%rcx
0x0000000000401fc6 <+22>: mov %cx,(%rsi,%rax,2)
0x0000000000401fca <+26>: add $0x1,%rax
0x0000000000401fce <+30>: cmp %rdx,%rax
0x0000000000401fd1 <+33>: jne 0x401fc0 <f_to_i+16>
0x0000000000401fd3 <+35>: repz retq
但是,当我定义float-&gt; double(或double-&gt; float)函数时:
CONV_VIA_CAST(f_to_d, float, double);
我得到了这个怪物:
0x0000000000402040 <+0>: mov %rdx,%r8
0x0000000000402043 <+3>: shr $0x3,%r8
0x0000000000402047 <+7>: test %r8,%r8
0x000000000040204a <+10>: je 0x402106 <f_to_d+198>
0x0000000000402050 <+16>: shr $0x5,%rdx
0x0000000000402054 <+20>: lea 0x0(,%rdx,4),%r9
0x000000000040205c <+28>: test %r9,%r9
0x000000000040205f <+31>: je 0x402108 <f_to_d+200>
0x0000000000402065 <+37>: lea (%rdi,%r8,4),%rax
0x0000000000402069 <+41>: cmp $0xb,%r8
0x000000000040206d <+45>: lea (%rsi,%r8,8),%r10
0x0000000000402071 <+49>: seta %cl
0x0000000000402074 <+52>: cmp %rax,%rsi
0x0000000000402077 <+55>: seta %al
0x000000000040207a <+58>: cmp %r10,%rdi
0x000000000040207d <+61>: seta %r10b
0x0000000000402081 <+65>: or %r10d,%eax
0x0000000000402084 <+68>: test %al,%cl
0x0000000000402086 <+70>: je 0x402108 <f_to_d+200>
0x000000000040208c <+76>: xorps %xmm3,%xmm3
0x000000000040208f <+79>: xor %eax,%eax
0x0000000000402091 <+81>: xor %ecx,%ecx
0x0000000000402093 <+83>: nopl 0x0(%rax,%rax,1)
0x0000000000402098 <+88>: movaps %xmm3,%xmm0
0x000000000040209b <+91>: add $0x1,%rcx
0x000000000040209f <+95>: movlps (%rdi,%rax,1),%xmm0
0x00000000004020a3 <+99>: movhps 0x8(%rdi,%rax,1),%xmm0
0x00000000004020a8 <+104>: movhlps %xmm0,%xmm1
0x00000000004020ab <+107>: cvtps2pd %xmm0,%xmm2
0x00000000004020ae <+110>: cvtps2pd %xmm1,%xmm0
0x00000000004020b1 <+113>: movlpd %xmm2,(%rsi,%rax,2)
0x00000000004020b6 <+118>: movhpd %xmm2,0x8(%rsi,%rax,2)
0x00000000004020bc <+124>: movlpd %xmm0,0x10(%rsi,%rax,2)
0x00000000004020c2 <+130>: movhpd %xmm0,0x18(%rsi,%rax,2)
0x00000000004020c8 <+136>: add $0x10,%rax
0x00000000004020cc <+140>: cmp %rcx,%rdx
0x00000000004020cf <+143>: ja 0x402098 <f_to_d+88>
0x00000000004020d1 <+145>: cmp %r9,%r8
0x00000000004020d4 <+148>: lea (%rsi,%r9,8),%rsi
0x00000000004020d8 <+152>: lea (%rdi,%r9,4),%rdi
0x00000000004020dc <+156>: je 0x40210d <f_to_d+205>
0x00000000004020de <+158>: mov %r9,%rdx
0x00000000004020e1 <+161>: mov %r9,%rax
0x00000000004020e4 <+164>: neg %rdx
0x00000000004020e7 <+167>: lea (%rsi,%rdx,8),%rcx
0x00000000004020eb <+171>: lea (%rdi,%rdx,4),%rdx
0x00000000004020ef <+175>: nop
0x00000000004020f0 <+176>: movss (%rdx,%rax,4),%xmm0
0x00000000004020f5 <+181>: cvtps2pd %xmm0,%xmm0
0x00000000004020f8 <+184>: movsd %xmm0,(%rcx,%rax,8)
0x00000000004020fd <+189>: add $0x1,%rax
0x0000000000402101 <+193>: cmp %rax,%r8
0x0000000000402104 <+196>: ja 0x4020f0 <f_to_d+176>
0x0000000000402106 <+198>: repz retq
0x0000000000402108 <+200>: xor %r9d,%r9d
0x000000000040210b <+203>: jmp 0x4020de <f_to_d+158>
0x000000000040210d <+205>: nopl (%rax)
0x0000000000402110 <+208>: retq
任何人都可以了解浮动 - >双重转换的内幕动态吗?也许如何编写它以获得更高效的装配?如果重要的话,我正在使用gcc 4.6.3。
答案 0 :(得分:7)
你所谓的“怪物”实际上看起来像automatically vectorized code。在开始运行良好并且在通用编译器中有用之前,20年的研究已经进入了这种技术。
它可能不太漂亮,但GCC实现者认为它对于长阵列会更快。如果你的数组实际上不长,或者如果你不能理解看起来像这样的编译代码,那就禁用那个特定的优化。使用-O2
进行编译应该进行(未尝试)。
答案 1 :(得分:6)
我可以快速看到这里发生的一些事情(代码有点长,时间有点晚,而且我不喜欢AT&amp; T语法)。
首先,第二个循环被矢量化(但很糟糕,见下文)。这固有地导致一些代码膨胀 - 它现在必须处理比矢量等更短的“尾端”。
其次,浮动加倍是一个扩大的转换。这对于标量来说并不重要,但是使用向量意味着你不能只读取一些数据,将其转换并将其写回 - 在某些地方你最终会得到两倍的字节并且它们必须被处理掉用。 (因此movhlps %xmm0,%xmm1
)
实际循环仅从402098h跨越到4020cfh,低于“尾部处理”,并且在上面是一个怪物,测试它是否完全跳过主循环以及我还没有想到的一些事情 - 它是有意义的,如果它是为了对齐,但我没有看到任何test rdi, 15
在那里,也没有任何明显的东西会摆脱未对齐的开始。
第三,海湾合作委员会正在跛脚。这并不罕见。它似乎认为xmm3是以某种方式参与的,它不是,它似乎忘记了矢量可以从一个存储器加载到一个 - 然后再次这可能是因为开头的怪物真的没有测试对齐,这是它对未对齐指针的防御。无论如何,GCC在这里做得不好。