【问题标题】:Is there any performance impact on declaring a variable inside critical section block compared to when declared outside?与在外部声明时相比,在临界区块内声明变量是否会对性能产生影响?
【发布时间】:2014-08-24 17:16:51
【问题描述】:

假设有如下代码

void func1()     // first way
{
    CRITICALSECTIONTYPE CS;
    ENTERCRITICALSECTION(CS);
    int x = getValue();
    LEAVECRITICALSECTION(CS);
}
void func2()    // second way
{
    int x;
    CRITICALSECTIONTYPE CS;
    ENTERCRITICALSECTION(CS);
    x = getValue();
    LEAVECRITICALSECTION(CS);
}

与第二种方式相比,第一种方式的性能开销是否有任何(甚至是最轻微的)可能性?有什么特定于编译器优化它的吗? 首选汇编代码回答。谢谢:)

【问题讨论】:

  • 你知道你可以让编译器为你自己输出汇编代码列表,对吧? /FAs 在 MSVC 上,-S 在 GCC 上。节省了每次都问一个问题,这不是很好的扩展。
  • @Codegray,这个技巧只有在你知道如何阅读汇编代码的情况下才有效。
  • 应该没有任何区别。您应该能够在一个简单的程序中对其进行测试。

标签: c++ multithreading visual-c++ gcc critical-section


【解决方案1】:

当然,答案可能取决于编译器。

但是,我已经编译了一个带有循环和在关键部分创建的块变量的程序。然后我通过在循环外创建变量来重新编译它。

  • 统一变量生成的汇编代码(MSVC13,没有优化的调试模式)完全相同。实际上,编译器会在函数入口处生成所需堆栈空间的预留,因此在进入临界区时无需进行任何操作。

我对您的问题进行了一些尝试:

  • 使用 初始化变量,编译器会生成额外的初始化指令,您将其放入代码中,可能位于临界区。

  • 在自动存储中使用未初始化的动态数组(例如:char y[n];)原理是一样的:临界区不会有额外的指令.为什么这样 ?因为标准仅在大小(此处为n)恒定时才接受这些动态数组。同样,在代码生成时,编译器知道在函数入口处需要在堆栈上分配多少空间。

  • 对于更复杂的对象,如果需要调用构造函数,那么相应的指令就必须在临界区执行。

在任何情况下,请记住,即使您在关键部分添加代码,优化器仍然可以找到优化它的方法(例如:常量传播、检测循环不变量等)。

编辑

根据您的要求,这里是第一种情况的 ASM 代码摘录。对不起,大屏幕截图,但这是轻松显示代码比较的唯一方法。差异以黄色和灰色突出显示。

您会注意到差异仅在于对应于 C++ 源代码的 cmets 以及使用 b 的行(唯一的原因是,堆栈偏移量被命名为 _b$1 用于块变量,_b$ 用于函数变量)。 (1) 访问变量的堆栈偏移量 (2) 函数中的入口点 (3) 局部变量初始化示例 (4) 临界区中的变量(左侧变量在该部分内部创建,右侧在外部创建)。

【讨论】:

  • 查看未优化的代码本质上是毫无价值的。您不关心调试版本的性能。
  • 能否同时添加优化和未优化版本的汇编代码?
  • @Abhinav 在这里未优化的代码向您展示它是如何工作的。但最好是您尝试生成文件,正如 Cody Gray 所解释的那样。即使您不熟悉汇编程序,使用 winmerge 之类的差异工具也可以轻松找到差异。
  • @CodyGray 是的,你完全正确!我在这里这样做是为了表明即使在未优化的代码中也没有性能开销,这意味着在优化版本中它只会更好。
  • 不确定“在优化版本中它只会更好”。有时优化会将变量初始化推到更接近实际使用变量的位置,因此在某些情况下甚至永远不会执行初始化(例如,如果函数在变量被使用之前返回)。在此示例中不会出现这种情况,因为变量在创建时并未初始化,但在另一个示例中可能是。在这种情况下,优化版本在这方面可能会更差......初始化可能在关键部分完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-26
  • 2017-12-02
  • 1970-01-01
  • 1970-01-01
  • 2023-02-08
  • 1970-01-01
相关资源
最近更新 更多