首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么fastcall比stdCall慢?

为什么fastcall比stdCall慢?
EN

Stack Overflow用户
提问于 2011-03-29 21:49:34
回答 8查看 20.8K关注 0票数 28

我发现了以下问题:快打真的更快吗?

没有给出x86的明确答案,所以我决定创建基准。

以下是代码:

代码语言:javascript
复制
#include <time.h>

int __fastcall func(int i)
{   
    return i + 5;
}

int _stdcall func2(int i)
{   
    return i + 5;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int iter = 100;
    int x = 0;
    clock_t t = clock();
    for (int j = 0; j <= iter;j++)
        for (int i = 0; i <= 1000000;i++)
            x = func(x & 0xFF);
    printf("%d\n", clock() - t);
    t = clock();
    for (int j = 0; j <= iter;j++)
        for (int i = 0; i <= 1000000;i++)
            x = func2(x & 0xFF);
    printf("%d\n", clock() - t);
    printf("%d", x);
    return 0;
}

在MSVC 10中没有优化结果的情况是:

代码语言:javascript
复制
4671
4414

使用最大优化,fastcall有时更快,但我想它是多任务噪声。以下是平均结果(使用iter = 5000)

代码语言:javascript
复制
6638
6487

stdcall看起来更快!

GCC的结果如下:http://ideone.com/hHcfPfastcall输掉了比赛。

以下是fastcall情况下反汇编的一部分

代码语言:javascript
复制
011917EF  pop         ecx  
011917F0  mov         dword ptr [ebp-8],ecx  
    return i + 5;
011917F3  mov         eax,dword ptr [i]  
011917F6  add         eax,5

这是给stdcall

代码语言:javascript
复制
    return i + 5;
0119184E  mov         eax,dword ptr [i]  
01191851  add         eax,5  

i是通过ECX传递的,而不是通过堆栈传递的,但是保存到主体中的堆栈中!所以所有的影响都被忽略了!这个简单的函数只能使用寄存器来计算!两者之间没有真正的区别。

有人能解释一下fastcall的原因吗?为什么不加速呢?

编辑:通过优化,发现这两个函数都是内联的。当我关闭内联时,它们都被编译成:

代码语言:javascript
复制
00B71000  add         eax,5  
00B71003  ret  

这看起来确实是很棒的优化,但是它根本不尊重调用约定,所以测试是不公平的。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2011-03-29 22:17:27

__fastcall很久以前就被引进了。当时,Watcom C++在优化方面击败了微软,许多评审员将其基于寄存器的呼叫约定作为(可能的)原因之一。

微软的回应是添加了__fastcall,从那以后他们就一直保留着它--但我认为他们做的还不够多,以至于能够说“我们也有一个基于注册的呼叫约定……”他们的偏好(特别是自32位迁移以来)似乎是对__stdcall的偏爱。他们已经投入了大量的工作来改进他们的代码生成,但是(很明显)用__fastcall做的工作并不多。有了片上缓存,在寄存器中传递东西的收益就没有以前那么大了。

票数 31
EN

Stack Overflow用户

发布于 2011-03-29 22:07:02

你的微基准会产生不相关的结果。__fastcall在SSE指令中有特定的用途(请参阅XNAMath),clock()甚至不是一个用于基准测试的远程定时器,__fastcall也存在于多个平台,例如Itanium和其他一些平台,不仅仅是x86,此外,除了printf语句之外,您的整个程序还可以有效地被优化,从而使__fastcall__stdcall的相对性能变得非常、非常无关紧要。

最后,你忘了意识到很多事情都是以传统的方式完成的主要原因--遗产。在编译器内联变得像今天这样具有侵略性和有效性之前,__fastcall很可能是非常重要的,而且没有编译器会像依赖它的程序那样删除__fastcall。这使__fastcall成为生活中的一个事实。

票数 17
EN

Stack Overflow用户

发布于 2011-03-29 21:56:08

几个原因

  1. 至少在大多数像样的x86实现中,注册重命名实际上是有效的--通过使用寄存器而不是内存来保存的工作可能不会在硬件级别上做任何事情。
  2. 当然,您使用__fastcall节省了一些堆栈移动工作,但是在不修改堆栈的情况下,可以减少可在函数中使用的寄存器数量。

在大多数情况下,如果__fastcall速度更快,这个函数就足够简单,在任何情况下都可以内联,这意味着它在实际软件中并不重要。(这是不经常使用__fastcall的主要原因之一)

附带注意:阿农的回答有什么问题?

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5479362

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档