代码之家  ›  专栏  ›  技术社区  ›  amit kumar

C++中的AI应用程序:虚拟函数的成本有多高?可能的优化是什么?

  •  16
  • amit kumar  · 技术社区  · 16 年前

    在AI应用程序中,我用C++编写,

    1. 没有太多的数值计算
    2. 有许多结构需要运行时多态性
    3. 在计算过程中,许多多态结构经常相互作用

    在这种情况下,是否有任何优化技术?虽然我不想现在优化应用程序,但是在项目中选择C++来代替java的一个方面是使更多的杠杆优化和能够使用非面向对象的方法(模板、过程、重载)。

    特别是,与虚拟函数相关的优化技术有哪些?虚拟函数是通过内存中的虚拟表实现的。是否有某种方法可以将这些虚拟表预取到二级缓存上(从内存/二级缓存获取的成本正在增加)?

    除此之外,C++中是否存在对数据局部性技术的良好参考?这些技术将减少将数据提取到二级缓存中进行计算所需的等待时间。

    :另请参阅以下相关论坛: Performance Penalty for Interface , Several Levels of Base Classes

    15 回复  |  直到 8 年前
        1
  •  28
  •   loudej    16 年前

    虚拟函数非常有效。假设32位指针,内存布局约为:

    classptr -> [vtable:4][classdata:x]
    vtable -> [first:4][second:4][third:4][fourth:4][...]
    first -> [code:x]
    second -> [code:x]
    ...
    

    classptr指向通常在堆上的内存,偶尔在堆栈上,并以指向该类vtable的四字节指针开始。但是需要记住的重要一点是vtable本身并没有分配内存。它是一个静态资源,同一类类型的所有对象都将指向其vtable数组的完全相同的内存位置。调用不同的实例不会将不同的内存位置拉入二级缓存。

    example from msdn 显示具有虚拟func1、func2和func3的类A的vtable。不超过12个字节。不同类的vtables很有可能在编译库中物理上相邻(您需要验证这一点,这是您特别关心的),这可以从微观上提高缓存效率。

    CONST SEGMENT
    ??_7A@@6B@
       DD  FLAT:?func1@A@@UAEXXZ
       DD  FLAT:?func2@A@@UAEXXZ
       DD  FLAT:?func3@A@@UAEXXZ
    CONST ENDS
    

    :

    ; A* pa;
    ; pa->func3();
    mov eax, DWORD PTR _pa$[ebp]
    mov edx, DWORD PTR [eax]
    mov ecx, DWORD PTR _pa$[ebp]
    call  DWORD PTR [edx+8]
    

    在本例中,堆栈帧基指针ebp具有变量 A* pa 在零偏移处。寄存器eax在位置[ebp]处加载值,因此它具有A*,而edx在位置[eax]处加载值,因此它具有A类vtable。然后用[ebp]加载ecx,因为ecx代表“this”,它现在保存A*,最后调用位置[edx+8]处的值,该位置是vtable中的第三个函数地址。

    如果此函数调用不是虚拟的,则不需要mov eax和mov edx,但性能上的差异将非常小。

        2
  •  11
  •   Xavier Nodet    16 年前

    draft Technical Report on C++ Performance 完全用于虚拟函数的开销。

        3
  •  3
  •   Chris Mayer    16 年前

    您是否实际分析并找到了需要优化的位置和内容?

        4
  •  3
  •   user3458 user3458    16 年前

    我能想到的唯一优化是Java的JIT编译器。如果我理解正确,它会在代码运行时监视调用,如果大多数调用只转到特定的实现,它会在类正确时插入条件跳转到实现。这样,大多数情况下,没有vtable查找。当然,对于通过不同课程的罕见情况,vtable仍然使用。

        5
  •  3
  •   MSN    16 年前

    虚拟函数往往是查找和间接函数调用。在某些平台上,这很快。在其他情况下,例如,控制台中使用的一种流行的PPC体系结构,这并没有那么快。

    优化通常围绕在调用堆栈的更高层表达可变性,这样您就不需要在热点中多次调用虚拟函数。

        6
  •  2
  •   iafonov    16 年前

    您可以在运行时使用虚拟函数实现polymorfism,在编译时使用模板实现polymorfism。可以用模板替换虚拟函数。有关更多信息,请参阅本文- http://www.codeproject.com/KB/cpp/SimulationofVirtualFunc.aspx

        7
  •  2
  •   paercebal    16 年前

    http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

    维基百科上的解释很清楚,也许是这样 能够 如果你真的下定决心 虚拟方法调用是性能瓶颈的根源。

        8
  •  2
  •   Stack Overflow is garbage    16 年前

    正如其他答案所述,虚拟函数调用的实际开销相当小。它可能会在一个被称为每秒数百万次的紧密循环中产生影响,但很少有什么大不了的。

    当然,这也取决于CPU架构。在某些情况下,它可能变得相当昂贵。

    但首先,确保你有问题。代码是否真的太慢而无法接受? 其次,通过探查器找出是什么使它变慢。 第三,修复它。

        9
  •  2
  •   Mike Dunlavey    16 年前

    • 如果你不知道这是一个问题,任何关于修复它的担心都可能是错误的。

    你想知道的是:

    • 调用方法的过程中花费了多少执行时间(当它实际运行时),特别是哪些方法的成本最高(按此衡量)。

    如果虚拟函数调用过程中花费的时间很长,比如说20%,那么平均五分之一的示例将在反汇编窗口的调用堆栈底部显示跟随虚拟函数指针的指令。

    如果你没有看到,这不是问题。

    在这个过程中,您可能会看到调用堆栈上更高的其他东西,这些东西实际上是不需要的,并且可以为您节省大量时间。

        10
  •  2
  •   Hernán    15 年前

    静态多态性,正如一些用户在这里回答的那样。例如,WTL使用这种方法。有关WTL实现的详细说明,请参见 http://www.codeproject.com/KB/wtl/wtl4mfc1.aspx#atltemplates

        11
  •  2
  •   mac OldMan    12 年前

    在某些情况下,可以做一些事情来防止该功能的浪费,即声明函数inline virtual。

    Class A {
       inline virtual int foo() {...}
    };
    

    当您在代码的某一点上确定要调用的对象的类型时,您可以进行内联调用,以避免多态系统,并通过编译器启用内联。

    class B : public A {
         inline virtual int foo() 
         {
             //...do something different
         }
    
         void bar()
         {
          //logic...
          B::foo();
          // more  logic
         }
    };
    

    在本例中,调用 foo() 将被设为非多态并绑定到 B 实施 foo() . 但是,只有在您确实知道实例类型是什么的情况下,才可以这样做,因为自动多态特性将消失,这对于以后的代码读者来说不是很明显。

        12
  •  1
  •   Dark Shikari    16 年前

    缓存通常仅在处理以下大型数据结构时才会出现问题:

    1. 足够大,并且由单个函数使用很长时间,以便该函数可以将您需要的所有其他内容从缓存中推出,或者

    Vtables之类的东西通常不会成为性能/缓存/内存问题;通常,每个对象类型只有一个Vtable,并且对象包含指向Vtable的指针,而不是Vtable本身。所以,除非你有几千种类型的对象,否则我不认为Vtables会破坏你的缓存。

    1) 顺便说一句,这就是为什么像memcpy这样的函数使用缓存绕过像movnt(dq | q)这样的流式指令来进行超大(数兆字节)数据输入的原因。

        13
  •  1
  •   David Cournapeau    16 年前

        14
  •  1
  •   Jimmy J    16 年前

    使用现代的、前瞻性的多个调度CPU,虚拟函数的开销很可能为零。娜达。拉链

        15
  •  0
  •   nikhilbelsare    16 年前

    如果 人工智能 应用程序不需要大量的数字运算,我不会担心虚拟函数的性能缺点。只有当它们出现在重复评估的复杂计算中时,性能才会受到轻微的影响。我认为您也不能强制虚拟表留在二级缓存中。

    虚拟函数有两种优化方法,

    1. 人们已经编写了编译器,可以对程序进行代码分析和转换。但是,这些不是生产级编译器。