这三点都与相同的“空函数”问题有关:
重要编辑调用空虚函数是否相同?
编辑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;
}
尝试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
调用空函数时浪费了多少处理时间?
如果可以让编译器保留空函数,那么调用函数和返回值的开销都会增加。调用的开销取决于参数的数量和传递方式。
实际开销取决于处理器以及编译器如何生成代码。
例如,处理器可能有足够的寄存器来包含每个参数,或者编译器可能必须在堆栈上推送参数,并在函数内部的堆栈上访问它们。
Edit1:
处理器喜欢处理顺序数据指令。分支、跳转或函数调用迫使处理器更改它们的指令指针,并可能重新加载它们的指令缓存。由于这些操作不是处理数据指令,它们正在浪费时间并降低程序的性能。这些分支的性能下降只有在高性能程序(需要处理大量数据)或需要满足关键截止日期的程序(例如通过打印机移动纸张以避免卡纸)中才会明显。
调用100个甚至1000个空函数会产生巨大影响吗?
取决于您对“影响”的定义。以目前桌面处理器的速度,在不到一秒钟(更像是不到1毫秒)的时间内,可以重复100或1000次。
您将花费更多时间编译(翻译和链接)这些函数。
如果这些空函数需要参数怎么办?
正如我在顶部所述,这取决于编译器和处理器。
调用空的虚拟函数是否相同?
虚拟函数调用可能需要一个或两个以上的处理器指令;取决于处理器。
不要假设Profile是你的朋友。分析将告诉您程序中每个函数花费了多少时间。
不要进行微优化,你所关心的就是所谓的微优化。您可能正在优化或担心只执行20%或更少时间的代码。或者它在等待外部进程(如鼠标单击或文件I/O)时卡住。
把性能问题放在一边,关注程序的正确性和健壮性。一个运行不正确的程序是不好的,无论它有多快或多慢。一个很容易被破坏或崩溃的程序是毫无价值的,无论它离崩溃还有多久。
只有当用户投诉或您的程序错过了关键的实时截止日期(例如缺少来自流式输入源的数据)时,才需要担心性能。
毫无疑问,任何像样的编译器都会对其进行完全优化,但这里有一个比较。
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: