当前位置: 首页 > 知识库问答 >
问题:

调用空函数会有多浪费?

戚弘和
2023-03-14

这三点都与相同的“空函数”问题有关:

  • 调用空函数时浪费了多少处理时间

重要编辑调用空虚函数是否相同?

编辑SO基本上你都在说在大多数情况下编译器会优化它。

但现在我很好奇,因为这仍然适用于这个问题。如果出现这样的情况,在编译时不知道何时调用空函数,该怎么办?它会立即进入堆栈然后退出吗?

class base{
public:
    virtual void method() = 0;
};

class derived1: public base{
public:
    void method() { }
};

class derived2: public base{
public:
    void method(){ std::cout << " done something  "; }
};


int main()
{
    derived1 D1;
    derived2 D2;
    base* array[] = { &D1, &D2 };
    for( int i = 0; i < 1000; i ++)
    {
        array[0]->method();// nothing
        array[1]->method();// Something
    }
    return 0;
}

共有3个答案

蒋权
2023-03-14

尝试g-4.8-O2-S-otest.asmmain.cpp

例如,使用以下代码:

void empty_function(int a, int b, int c) { }

int main() {
    int a = 42, b = a, c = a;
    empty_function(a, b, c); 
}

我没有看到它在程序集输出中被调用。

我听从了pmr的建议,做了如下准备:

<代码>对象。h

#ifndef _OBJECT_H_
#define _OBJECT_H_

class Object {
 public:
  Object() { }
  void empty_function();
};

#endif

<代码>对象。cpp

#include "Object.h"

void Object::empty_function() { }

<代码>主。cpp

#include "Object.h"

int main() {
  Object object;
  object.empty_function();
}

有趣的是,它确实调用了空函数:

call    __main
leaq    47(%rsp), %rcx
call    _ZN6Object14empty_functionEv
xorl    %eax, %eax
addq    $56, %rsp
ret

严亦
2023-03-14

调用空函数时浪费了多少处理时间?

如果可以让编译器保留空函数,那么调用函数和返回值的开销都会增加。调用的开销取决于参数的数量和传递方式。

实际开销取决于处理器以及编译器如何生成代码。
例如,处理器可能有足够的寄存器来包含每个参数,或者编译器可能必须在堆栈上推送参数,并在函数内部的堆栈上访问它们。

Edit1:
处理器喜欢处理顺序数据指令。分支、跳转或函数调用迫使处理器更改它们的指令指针,并可能重新加载它们的指令缓存。由于这些操作不是处理数据指令,它们正在浪费时间并降低程序的性能。这些分支的性能下降只有在高性能程序(需要处理大量数据)或需要满足关键截止日期的程序(例如通过打印机移动纸张以避免卡纸)中才会明显。

调用100个甚至1000个空函数会产生巨大影响吗?

取决于您对“影响”的定义。以目前桌面处理器的速度,在不到一秒钟(更像是不到1毫秒)的时间内,可以重复100或1000次。

您将花费更多时间编译(翻译和链接)这些函数。

如果这些空函数需要参数怎么办?

正如我在顶部所述,这取决于编译器和处理器。

调用空的虚拟函数是否相同?

虚拟函数调用可能需要一个或两个以上的处理器指令;取决于处理器。

不要假设Profile是你的朋友。分析将告诉您程序中每个函数花费了多少时间。

不要进行微优化,你所关心的就是所谓的微优化。您可能正在优化或担心只执行20%或更少时间的代码。或者它在等待外部进程(如鼠标单击或文件I/O)时卡住。

把性能问题放在一边,关注程序的正确性和健壮性。一个运行不正确的程序是不好的,无论它有多快或多慢。一个很容易被破坏或崩溃的程序是毫无价值的,无论它离崩溃还有多久。

只有当用户投诉或您的程序错过了关键的实时截止日期(例如缺少来自流式输入源的数据)时,才需要担心性能。

颜镜
2023-03-14

毫无疑问,任何像样的编译器都会对其进行完全优化,但这里有一个比较。

void EmptyFunction() {}
void EmptyFunctionWithArgs(int a, int b, int c, int d, int e) {}
int EmptyFunctionRet() {return 0;}
int EmptyFunctionWithArgsRet(int a, int b, int c, int d, int e) {return 0;}

int main() {   
    EmptyFunction();
    EmptyFunctionWithArgs(0, 1, 2, 3, 4);
    EmptyFunctionRet();
    EmptyFunctionWithArgsRet(5, 7, 6, 8, 9);
}

在VC 11的调试模式下(如果有任何优化的话,也很少),调用如下:

EmptyFunction()

call    ?EmptyFunction@@YAXXZ            ; EmptyFunction

EmptyFunctionWithArgs(0、1、2、3、4)

push    4
push    3
push    2
push    1
push    0
call    ?EmptyFunctionWithArgs@@YAXHHHHH@Z    ; EmptyFunctionWithArgs
add     esp, 20                    ; 00000014H

空函数Ret();

call    ?EmptyFunctionRet@@YAHXZ        ; EmptyFunctionRet

EmptyFunctionWithArgsRet(5、7、6、8、9)

push    9
push    8
push    6
push    7
push    5
call    ?EmptyFunctionWithArgsRet@@YAHHHHHH@Z    ; EmptyFunctionWithArgsRet
add     esp, 20                    ; 00000014H

int返回函数如下所示:

int EmptyFunctionRet()int EmptyFunctionWithArgsRet()

push   ebp
mov    ebp, esp
sub    esp, 192                ; 000000c0H
push   ebx
push   esi
push   edi
lea    edi, DWORD PTR [ebp-192]
mov    ecx, 48                    ; 00000030H
mov    eax, -858993460                ; ccccccccH
rep    stosd
xor    eax, eax
pop    edi
pop    esi
pop    ebx
mov    esp, ebp
pop    ebp
ret    0

没有返回值的函数是相同的,只是它们没有异或eax,eax。

在发布模式下,VC 11不会费心调用任何函数。但它确实为它们生成定义。int返回函数如下所示:

int EmptyFunctionRet()int EmptyFunctionWithArgsRet()

xor    eax, eax
ret    0

而非返回函数再次删除xor eax、eax。

您可以很容易地看到,未优化的函数非常臃肿,在循环中运行它们无论多少次都会耗费大量时间,而如果启用优化,则会删除循环本身。所以,帮自己一个忙吧,伙计们,发布时进行优化。

您不应该担心这个,因为它违反了“暂不优化”。如果您使用的是一个模糊的平台(如27位MCU),并且您的编译器是垃圾,并且您的性能需要改进,那么您应该分析您的代码以查看您需要改进的地方(它可能不是空函数调用)。就目前而言,这实际上只是一个练习,表明我们应该让编译器做多少繁重的工作。

编辑您的编辑:

有趣的是,虚拟函数是优化器的一个已知问题,因为它们很难确定指针的实际去向。一些人为的示例可能会消除该调用,但如果通过指针进行调用,则可能不会删除该调用。

在VC 11发布模式下编译以下内容:

class Object {
public:
    virtual void EmptyVirtual() {}
};

int main() {   
    Object * obj = new Object();
    obj->EmptyVirtual();
}

在安装程序代码中生成以下代码段:

mov     DWORD PTR [ecx], OFFSET ??_7Object@@6B@
mov     eax, DWORD PTR [ecx]
call    DWORD PTR [eax]

这是对虚函数的调用。

 类似资料:
  • 问题内容: 假设我有以下重载函数 为什么要调用带有参数签名的方法?这里重载的优先顺序是什么? 问题答案: 在这种情况下,类层次结构中较低的类将具有优先权。换句话说,更具体的类类型,在这种情况下将是因为从技术上扩展了。 如果您有以下内容 然后,当您定义如下的重载方法时: 然后调用将调用第二个方法,因为它在类层次结构中较低。

  • 问题内容: 我正在研究要点:PasteboardWatcher.swift,在其中我像这样调用了NSTimer对象: 函数的定义是: 我不想将函数公开给其他类,因此我想将其标记为私有,但是由于某种原因,它抛出以下异常: 如果我不将其标记为私有,那么它会完美运行。 我有什么办法可以将此方法保留为私有方法,对其他类隐藏呢? 问题答案: 根据将 Swift与Cocoa和Objective-C结合使用 :

  • 我试图将一个向量元组转换成一个向量元组(反之亦然)。调用函数时遇到问题。当我用一个参数调用它时,我得到一个错误: prog。cpp:在函数“int main()”中: prog。cpp:44:24:错误:调用“tuple_transpose(std::tuple)”时没有匹配函数 这是一个演示,其中包含错误:http://ideone.com/7AWiQQ#view_edit_box 我做错了什么

  • 问题内容: 我正在使用Ionic,并且想要根据数据动态更改每个项目的背景色。我以为我可以通过函数调用的方式返回正确的类 目前是控制器 在检查控制台时,列表中的每个函数均被调用7次。这是为什么,我应该避免在其中使用函数调用吗? 问题答案: AngularJS可以进行脏检查:它需要在 每个循环中 调用该函数,以确保它不返回新值,并且不需要更新DOM。 这是框架典型过程的一部分,调用像您这样简单的函数不

  • 问题内容: 该功能运行什么?它只会运行吗? 问题答案: setState()将按以下顺序运行函数: 如果您的组件正在接收道具,它将使用上述功能运行该功能。

  • 问题内容: 我知道在香草js中,我们可以做到 在ReactJS中调用onClick的两个函数等效于什么? 我知道调用一个函数是这样的: 问题答案: 将两个函数调用包装在另一个函数/方法中。这是该想法的几个变体: 1)分开的方法 或使用ES6类: 2)内联 或等效于ES6: