人工智能在C++;:虚拟功能的成本有多高?可能的优化是什么? 在AI应用程序中,我用C++编写, 没有太多的数值计算 有许多结构需要运行时多态性 在计算过程中,许多多态结构经常相互作用

人工智能在C++;:虚拟功能的成本有多高?可能的优化是什么? 在AI应用程序中,我用C++编写, 没有太多的数值计算 有许多结构需要运行时多态性 在计算过程中,许多多态结构经常相互作用,c++,optimization,C++,Optimization,在这种情况下,是否有任何优化技术?虽然我不想现在优化应用程序,但是在项目中选择C++来代替java是为了使更多的杠杆优化和能够使用非面向对象的方法(模板、过程、重载)。p> 特别是,与虚拟函数相关的优化技术有哪些?虚拟函数是通过内存中的虚拟表实现的。是否有某种方法可以将这些虚拟表预取到二级缓存上(从内存/二级缓存获取的成本正在增加) 除此之外,C++中是否存在对数据局部性技术的良好参考?这些技术将减少将数据提取到二级缓存中进行计算所需的等待时间 更新:也可以查看以下相关论坛:,您很少需要担心这些

在这种情况下,是否有任何优化技术?虽然我不想现在优化应用程序,但是在项目中选择C++来代替java是为了使更多的杠杆优化和能够使用非面向对象的方法(模板、过程、重载)。p> 特别是,与虚拟函数相关的优化技术有哪些?虚拟函数是通过内存中的虚拟表实现的。是否有某种方法可以将这些虚拟表预取到二级缓存上(从内存/二级缓存获取的成本正在增加)

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


更新:也可以查看以下相关论坛:,

您很少需要担心这些常用项目的缓存,因为它们只提取一次并保存在那里

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

  • 足够大,并且由单个函数使用很长时间,以便该函数可以将您需要的所有其他内容从缓存中推出,或者
  • 随机访问的数据量足够大,因此从数据结构加载时,数据结构本身不一定在缓存中
  • Vtables之类的东西通常不会成为性能/缓存/内存问题;通常,每个对象类型只有一个Vtable,并且对象包含指向Vtable的指针,而不是Vtable本身。所以,除非你有几千种类型的对象,否则我不认为Vtables会破坏你的缓存


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

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

    虚拟函数有两种优化方法

  • 人们已经编写了编译器,可以对程序进行代码分析和转换。但是,这些不是生产级编译器
  • 您可以使用等效的“switch…case”块替换所有虚拟函数,以根据层次结构中的类型调用适当的函数。这样,您将摆脱编译器管理的虚拟表,并且您将拥有自己的虚拟表,其形式为switch…case块。现在,您自己的虚拟表在二级缓存中的可能性与在代码路径中的可能性一样高。记住,您需要RTTI或您自己的“typeof”函数来实现这一点

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


    当您发现虚拟函数调用实际上是瓶颈时,请实际优化它们。

    虚拟函数非常有效。假设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数组的完全相同的内存位置。调用不同的实例不会将不同的内存位置拉入二级缓存

    这显示了具有虚拟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
    
    另一个性能问题是通过vtable函数调用的指令开销。这也是非常有效的。与调用非虚函数几乎相同。再次从以下方面:

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


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

    您可以在运行时使用虚拟函数实现polymorfism,在编译时使用模板实现polymorfism。可以用模板替换虚拟函数。查看本文以了解更多信息-

    动态多态性的解决方案可以是静态多形性,如果您的类型在编译类型(CRTP(奇怪的循环模板模式)中已知,则可以使用它


    Wikipedia上的解释非常清楚,如果您真的确定虚拟方法调用是性能瓶颈的来源,那么它可能会帮助您。

    虚拟调用不会比普通函数带来更大的开销。尽管如此,最大的损失是以多态方式调用虚函数时无法内联。在很多情况下,内联将代表性能上的一些实际收益

    你可以做些什么来防止浪费
    ; 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]
    
    Class A {
       inline virtual int foo() {...}
    };
    
    class B : public A {
         inline virtual int foo() 
         {
             //...do something different
         }
    
         void bar()
         {
          //logic...
          B::foo();
          // more  logic
         }
    };