首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >内联函数是如何编译成程序集的?

内联函数是如何编译成程序集的?
EN

Stack Overflow用户
提问于 2019-02-19 18:40:16
回答 2查看 507关注 0票数 0

我有一些循环更新值的代码C++,出于好奇,我希望看到组成主体循环的程序集。这使我对编译后内联的外观进行了一些实验(编译器是带有O2的MSVC )。

然而,当我将指令集与我认为它实际上是内联时的样子进行比较时,我对我发现的情况感到有点困惑。以下是一些背景:

代码语言:javascript
复制
template<typename T>
struct ClassWithInline
{
    Values *v;

    ClassWithInline(Values *v) : v{ v } {}
    T inlineMe(T * const c) const
    {
        // some function of *c, using v->some_constants
    }
};

Values对象只是包含常量的东西。ClassWithInline是另一个对象Owner的成员,所有者有一个函数callTheInline

代码语言:javascript
复制
struct Owner
{

    ClassWithInline<double> a;
    Values *v;

    Owner(Values *v) : a{ ClassWithInline<double>(v) }, v{ v } {}
    void callTheInline()
    {
        double *ptr = new double[100];
        double *dptr = new double[100];

        size_t the_end = std::floor(1000 + log(100000));

        for (size_t n = 0; n < the_end; ++n)
        {
            dptr[n] = a.inlineMe(ptr + n);
        }

        ClassWithInline<double> b(v);
        for (size_t n = 0; n < the_end; ++n)
        {
            dptr[n] = b.inlineMe(ptr + n);
        }
    }
};

(不稳定的结束迭代数是因为编译器在编译时不知道循环的大小,并引入了其他一些优化。)

现在,当我查看为那些for循环生成的程序集时,它们是非常不同的;事实上,从a调用a的程序集指令的数量是以前的两倍。我该如何弥合这一差距?

a.inlineMe(ptr + n);

代码语言:javascript
复制
000000013F642094  mov         rbp,rbx  
000000013F642097  mov         qword ptr [rsp+20h],r15  
000000013F64209C  sub         rbp,rsi  
000000013F64209F  lea         r15,[r9-3]  
000000013F6420A3  mov         r14,rsi  
000000013F6420A6  lea         r10,[rbx+8]  
000000013F6420AA  sub         r14,rbx  
000000013F6420AD  nop         dword ptr [rax]  
000000013F6420B0  mov         rcx,qword ptr [rdi]  
000000013F6420B3  lea         rdx,[r14+r10]  
000000013F6420B7  movsd       xmm0,mmword ptr [r10-8]  
000000013F6420BD  movsd       xmm1,mmword ptr [rdx+rbp-10h]  
000000013F6420C3  addsd       xmm1,mmword ptr [r10]  
000000013F6420C8  movsd       xmm2,mmword ptr [rdi+8]  
000000013F6420CD  lea         rax,[rcx+r8]  
000000013F6420D1  mulsd       xmm0,xmm3  
000000013F6420D5  mulsd       xmm2,xmm2  
000000013F6420D9  addsd       xmm1,mmword ptr [rbx+rax*8]  
000000013F6420DE  mov         rax,r8  
000000013F6420E1  sub         rax,rcx  
000000013F6420E4  addsd       xmm1,mmword ptr [rbx+rax*8]  
000000013F6420E9  subsd       xmm1,xmm0  
000000013F6420ED  divsd       xmm1,xmm2  
000000013F6420F1  movsd       mmword ptr [r14+r10-8],xmm1  
000000013F6420F8  movsd       xmm1,mmword ptr [r10+8]  
000000013F6420FE  addsd       xmm1,mmword ptr [r10-8]  
000000013F642104  mov         rcx,qword ptr [rdi]  
000000013F642107  movsd       xmm0,mmword ptr [r10]  
000000013F64210C  movsd       xmm2,mmword ptr [rdi+8]  
000000013F642111  mulsd       xmm0,xmm3  
000000013F642115  lea         rax,[rcx+r8]  
000000013F642119  mulsd       xmm2,xmm2  
000000013F64211D  addsd       xmm1,mmword ptr [rbx+rax*8+8]  
000000013F642123  mov         rax,r8  
000000013F642126  sub         rax,rcx  
000000013F642129  addsd       xmm1,mmword ptr [rbx+rax*8+8]  
000000013F64212F  subsd       xmm1,xmm0  
000000013F642133  divsd       xmm1,xmm2  
000000013F642137  movsd       mmword ptr [rdx],xmm1  
000000013F64213B  movsd       xmm1,mmword ptr [r10+10h]  
000000013F642141  addsd       xmm1,mmword ptr [r10]  
000000013F642146  mov         rcx,qword ptr [rdi]  
000000013F642149  movsd       xmm0,mmword ptr [r10+8]  
000000013F64214F  movsd       xmm2,mmword ptr [rdi+8]  
000000013F642154  mulsd       xmm0,xmm3  
000000013F642158  lea         rax,[rcx+r8]  
000000013F64215C  mulsd       xmm2,xmm2  
000000013F642160  addsd       xmm1,mmword ptr [rbx+rax*8+10h]  
000000013F642166  mov         rax,r8  
000000013F642169  sub         rax,rcx  
000000013F64216C  addsd       xmm1,mmword ptr [rbx+rax*8+10h]  
000000013F642172  subsd       xmm1,xmm0  
000000013F642176  divsd       xmm1,xmm2  
000000013F64217A  movsd       mmword ptr [r14+r10+8],xmm1  
000000013F642181  movsd       xmm1,mmword ptr [r10+18h]  
000000013F642187  addsd       xmm1,mmword ptr [r10+8]  
000000013F64218D  mov         rcx,qword ptr [rdi]  
000000013F642190  movsd       xmm0,mmword ptr [r10+10h]  
000000013F642196  movsd       xmm2,mmword ptr [rdi+8]  
000000013F64219B  mulsd       xmm0,xmm3  
000000013F64219F  lea         rax,[rcx+r8]  
000000013F6421A3  mulsd       xmm2,xmm2  
000000013F6421A7  addsd       xmm1,mmword ptr [rbx+rax*8+18h]  
000000013F6421AD  mov         rax,r8  
000000013F6421B0  add         r8,4  
000000013F6421B4  sub         rax,rcx  
000000013F6421B7  addsd       xmm1,mmword ptr [rbx+rax*8+18h]  
000000013F6421BD  subsd       xmm1,xmm0  
000000013F6421C1  divsd       xmm1,xmm2  
000000013F6421C5  movsd       mmword ptr [r14+r10+10h],xmm1  
000000013F6421CC  add         r10,20h  
000000013F6421D0  cmp         r8,r15  
000000013F6421D3  jb          Owner::callTheInline+0B0h (013F6420B0h) 

b.inlineMe(ptr + n);

代码语言:javascript
复制
000000013F6422A4  movsd       xmm1,mmword ptr [rcx+r10*8-10h]  
000000013F6422AB  addsd       xmm1,mmword ptr [rdx+rcx]  
000000013F6422B0  movsd       xmm0,mmword ptr [rdx+rcx-8]  
000000013F6422B6  mulsd       xmm0,xmm3  
000000013F6422BA  addsd       xmm1,mmword ptr [rcx+r8*8-8]  
000000013F6422C1  addsd       xmm1,mmword ptr [rcx-8]  
000000013F6422C6  subsd       xmm1,xmm0  
000000013F6422CA  divsd       xmm1,xmm5  
000000013F6422CE  movsd       mmword ptr [rdi+rcx-8],xmm1  
000000013F6422D4  movsd       xmm2,mmword ptr [rdx+rcx-8]  
000000013F6422DA  addsd       xmm2,mmword ptr [rdx+rcx+8]  
000000013F6422E0  movsd       xmm0,mmword ptr [rdx+rcx]  
000000013F6422E5  mulsd       xmm0,xmm3  
000000013F6422E9  addsd       xmm2,mmword ptr [rcx+r8*8]  
000000013F6422EF  addsd       xmm2,mmword ptr [rcx]  
000000013F6422F3  subsd       xmm2,xmm0  
000000013F6422F7  divsd       xmm2,xmm5  
000000013F6422FB  movsd       mmword ptr [rdi+rcx],xmm2  
000000013F642300  movsd       xmm0,mmword ptr [rdx+rcx+8]  
000000013F642306  movsd       xmm1,mmword ptr [rdx+rcx]  
000000013F64230B  addsd       xmm1,mmword ptr [rcx+rbp]  
000000013F642310  mulsd       xmm0,xmm3  
000000013F642314  addsd       xmm1,mmword ptr [rcx+r8*8+8]  
000000013F64231B  addsd       xmm1,mmword ptr [rcx+8]  
000000013F642320  subsd       xmm1,xmm0  
000000013F642324  divsd       xmm1,xmm5  
000000013F642328  movsd       mmword ptr [rdi+rcx+8],xmm1  
000000013F64232E  movsd       xmm2,mmword ptr [rcx+r10*8+18h]  
000000013F642335  addsd       xmm2,mmword ptr [rdx+rcx+8]  
000000013F64233B  movsd       xmm0,mmword ptr [rcx+rbp]  
000000013F642340  mulsd       xmm0,xmm3  
000000013F642344  addsd       xmm2,mmword ptr [rcx+r8*8+10h]  
000000013F64234B  addsd       xmm2,mmword ptr [rcx+10h]  
000000013F642350  subsd       xmm2,xmm0  
000000013F642354  divsd       xmm2,xmm5  
000000013F642358  movsd       mmword ptr [r14+rcx],xmm2  
000000013F64235E  add         rcx,20h  
000000013F642362  sub         rax,1  
000000013F642366  jne         Owner::callTheInline+2A4h (013F6422A4h)  
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-02-19 18:54:12

功能的内衬有三大作用:

  • 它消除了函数调用的开销。
  • 它允许编译器跨越函数的边界进行优化。
  • 它允许编译器对传递给函数的硬编码参数进行硬假设。这包括指向成员函数的此指针。

内联总是在将C++代码转换为程序集之前发生。编译器本质上将内联函数视为被调用函数的源代码插入到调用的位置。差不多了。(实际上,编译器通常也会将内联函数编译成普通函数,并将弱链接分配给它,但这在以后的内联过程中就不会用到了。这在这里没有意义。)

在您的示例中,aOwner的成员,b是堆栈上的局部变量。ab都维护状态v

要寻址a,编译器需要通过Owner的这个指针来处理它。为了处理b,编译器不需要使用Owner的这个指针,它就在堆栈上。光是这一点就已经使指令的数量发生了很大的变化。实际上,这还取决于编译器是否允许内联callTheInline(),以及编译器对Owner实例的存储了解多少。

a.v的值在函数callTheInline()结束后仍然存在,而b在函数结束后不持久。这可能允许编译器省略某些计算。但是,b.v不会在函数结束后持久化,从而允许编译器省略计算inlineMe()

票数 2
EN

Stack Overflow用户

发布于 2019-02-19 19:04:10

它们不是.(特别是当它们只是模板时)。

在转换成之前,它们是内联的(通常是编译器对数据流的内部表示,通常是某种SSA)。在此之后将进行更多的优化,因此实际的asm取决于其内联点的周围代码,当然还取决于args以及返回值的处理方式。

例如,在一个调用站点中未使用的带有输出arg的函数可以优化计算它的函数的部分。或者,如果其中一个arg是编译时常量,则可以极大地简化由此产生的asm。(例如,if(x<8)可以在内联和恒定传播后变成if(false)if(true)。)

在您的例子中,一个循环使用一个类成员对象,其指针可能指向任何地方。您根本没有使用ClassWithInline::v显示这个函数,所以它是一个非静态成员函数,而不是一个模板化的免费函数,这是很奇怪的。

但是,如果ClassWithInline::v a.inlineMe(ptr + n); 确实参与其中,那么 a.inlineMe(ptr + n);将涉及 this.v this.a.v,,它们可能指向或不指向重叠内存。编译器不知道,所以必须做出保守的假设,或者发出两个版本的循环,并在运行快速或安全版本之前检查是否重叠。这将击败自动矢量化,并且需要更多的存储/重新加载来使asm即使在混叠的情况下也是正确的。

(这是一个struct,而不是class,因此这些成员是公共的,该函数的调用方可能在调用我们之前修改了这些成员。)

但是b.inlineMe(ptr + n) 使用 this.v 作为两个指针,在内联之后编译器可以看到这一点。

涉及到的另一个内存来自new,它不与其他内存重叠。也就是说,任何预先存在的指针都不能指向new[]返回的缓冲区。我认为MSVC做了足够多的别名分析来解决这个问题。但考虑到缺乏自动矢量化,也许不会。

顺便说一句,调用两个指针( v )会使思考/谈论变得非常混乱。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54772936

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档