【问题标题】:Can C++ compilers optimize repeated virtual function calls on the same pointer? [duplicate]C++ 编译器可以优化对同一指针的重复虚函数调用吗? [复制]
【发布时间】:2013-02-07 17:12:56
【问题描述】:

假设我有以下代码

void f(PolymorphicType *p)
{
    for (int i = 0; i < 1000; ++i)
    {
        p->virtualMethod(something);
    }
}

编译器生成的代码是否会对pvtable 条目取消引用virtualMethod 1 或1000 次?我正在使用微软的编译器。

编辑

这是为我正在查看的真实案例生成的程序集。 line-&gt;addPoint() 是关注的虚拟方法。我没有组装经验,所以慢慢复习……

; 369  :        for (int i = 0; i < numPts; ++i)

    test    ebx, ebx
    je  SHORT $LN1@RDS_SCANNE
    lea edi, DWORD PTR [ecx+32]
    npad    2
$LL3@RDS_SCANNE:

; 370  :        {
; 371  :            double *pts = pPoints[i].SystemXYZ;
; 372  :            line->addPoint(pts[0], pts[1], pts[2]);

    fld QWORD PTR [edi+8]
    mov eax, DWORD PTR [esi]
    mov edx, DWORD PTR [eax+16]
    sub esp, 24                 ; 00000018H
    fstp    QWORD PTR [esp+16]
    mov ecx, esi
    fld QWORD PTR [edi]
    fstp    QWORD PTR [esp+8]
    fld QWORD PTR [edi-8]
    fstp    QWORD PTR [esp]
    call    edx
    add edi, 96                 ; 00000060H
    dec ebx
    jne SHORT $LL3@RDS_SCANNE
$LN314@RDS_SCANNE:

; 365  :        }

【问题讨论】:

  • 要求编译器生成汇编代码并检查。
  • 用优化编译它并查看生成的代码。
  • 添加到 Joachim 的评论中 - 除了检查之外没有其他方法
  • 我不确定编译器在这里也可以假设什么。调用 ->virtualFunction 可以 改变 p 指向的东西,如果 p 指向的对象被删除并在不同子类型的该位置创建一个新对象怎么办? (我不知道这是否可行或合法,但这是编译器编写者在生成代码时必须考虑的事情......),他们必须非常小心他们的假设......
  • 好的,所以 mov edx, DWORD PTR [eax+16] ... call edx 在循环内。我猜它每次都在看 vtable。

标签: c++ performance polymorphism vtable


【解决方案1】:

一般来说,不,这是不可能的。该函数可能会破坏 *this 并放置新的一些其他从该空间中的同一基础派生的对象。

编辑:更简单的是,函数可以更改p。编译器不可能知道谁拥有p 的地址,除非它是相关优化单元的本地地址。

【讨论】:

    【解决方案2】:

    一般来说不可能,但也有可以优化的特殊情况,尤其是跨过程分析。 VS2012全优化和全程序优化编译此程序:

    #include <iostream>
    
    using namespace std;
    
    namespace {
    struct A {
      virtual void foo() { cout << "A::foo\n"; }
    };
    
    struct B : public A {
      virtual void foo() { cout << "B::foo\n"; }
    };
    
    void test(A& a) {
      for (int i = 0; i < 100; ++i)
        a.foo();
    }
    }
    
    int main() {
      B b;
      test(b);
    }
    

    到:

    01251221  mov         esi,64h  
    01251226  jmp         main+10h (01251230h)  
    01251228  lea         esp,[esp]  
    0125122F  nop  
    01251230  mov         ecx,dword ptr ds:[1253044h]  
    01251236  mov         edx,12531ACh  
    0125123B  call        std::operator<<<std::char_traits<char> > (012516B0h)  
    01251240  dec         esi  
    01251241  jne         main+10h (01251230h)  
    

    所以它有效地优化了循环:

    for(int i = 0; i < 100; ++i)
      cout << "B::foo()\n";
    

    【讨论】:

    • 加 1 用于整个程序/链接时间优化,这使得“不可能”通常变得微不足道。
    猜你喜欢
    • 2017-06-26
    • 1970-01-01
    • 2013-01-08
    • 1970-01-01
    • 2020-11-29
    • 2018-11-21
    • 2011-06-19
    相关资源
    最近更新 更多