【问题标题】:Are Ada function arguments evaluated if the body is empty?如果主体为空,是否评估 Ada 函数参数?
【发布时间】:2021-12-27 14:04:42
【问题描述】:

根据这个说法:

Trace.Debug("My String" & Integer'Image(x) & "is evaluated" & "or not" & "if my logger is disabled ?" & Boolean'Image(YesOrNo) );

还有Trace.Debug的这个实现:

procedure Debug (Message : in String) is 
begin 
    if Logger.Enabled then  -- This boolean is defined during runtime by reading a value in a file
        Put_Line(Message);
    else
        null; -- Do nothing
    end if;
end Debug;

我有一个可以管理多个级别日志的软件,我想知道Logger.Enabled等于False时的行为。

我有很多日志调用,有时需要评估复杂的字符串,而且我在实时系统上,所以我不想浪费时间来评估不会打印的字符串。

我想知道编译器是否优化代码以便在Logger.enabledFalse 时不评估Trace.Debug 的参数中的字符串,知道这个布尔值是在运行时通过读取一个值来设置的在一个文件中。

我正在使用 gnat 7.3.2。

【问题讨论】:

  • 如果它可以在任何地方内联,它甚至可能会跳过构造 arg;你可以检查asm。如果它不能内联,那么它肯定不会跳过,因为优化器不会知道 Debug() 函数将如何处理您传递的参数。我不认识 Ada,但至少在无日志记录的情况下,您需要此函数是 inlineable 的。也许通过链接时优化,例如-flto,如果它使用 gcc 样式选项。对于启用日志的情况,编译器仍然可以选择不内联它,但实际上我猜它只会内联对 Put_Line 的调用,因为这就是发生的一切。
  • 任何优化都必须与不优化具有相同的结果。由于对表达式求值可能会产生副作用,因此优化通常不可能移除对表达式的求值。
  • 问题标题具有误导性。可能使用 arg 的运行时分支不是“空体”。我假设您的 Logger.Enabled 是编译时常量,因为 if(false) 算作空函数体,但 if(variable) print() 肯定不是。正如 cmets 在 flyx 的回答中所讨论的,即使在内联之后,编译器仍然必须发出条件分支,尽管如果编译器足够聪明,连接字符串的工作可能会优化到条件块中。

标签: compiler-optimization ada branch-prediction


【解决方案1】:

您可以通过提供回调来确保不会发生评估:

procedure Debug (Message : access function return String) is 
begin 
    if Logger.Enabled then
        Put_Line(Message.all);
    end if;
end Debug;

然后调用它,做

declare
   function Msg return String is
     ("My String" & Integer'Image(x) & "is evaluated" & "or not" & "if my logger is disabled ?" & Boolean'Image(YesOrNo));
begin
   Debug (Msg'Access);
end;

在您的原始代码中,编译器可以跳过评估的唯一方法是当它内联 Debug 过程并重新排列代码以便 Message 对象仅分配在内部 if-块。你不能强迫这个;甚至pragma Inline 也只是对编译器的提示。

【讨论】:

  • 重新排列代码以便 Message 对象仅在 if 块内分配 - 我想这是查看优化器所做工作的一种方式,但实际上它会只需注意该值未使用。它们会优化掉所有没有明显副作用且结果未被使用的工作。
  • @PeterCordes 内联Debug 过程仍会将Message 对象的构造放在if 语句的前面。这意味着活性分析将显示变量是活动的(因为它是在if 内部访问的),因此必须构造它的值。但是,编译器可以看到它仅在 if 语句中使用,因此将其构造移到那里。纯粹的未使用值优化无法摆脱评估。
  • Ada 是完全提前编译的,对吧? GNAT 是 GCC 优化后端的前端。抽象机器中的“评估”没有意义。例如,godbolt.org/z/zs64seGTP 演示了一个 C 函数,其中计算了一个表达式但未使用:您在优化的 asm 中看不到它的迹象。编译器没有 if() 可以将其隐藏,它只是不会浪费 asm 指令计算结果,而这些结果将永远不会看到或使用。这是一种“让我们不要说我们做了”的情况,即假设规则。这不适用于这种 Ada 情况吗? (我不认识艾达)。
  • @PeterCordes 您显示的代码是另一种情况,其中变量确实不在其定义中(即从未使用过),因此可以优化掉。正如我所解释的,我们的变量是实时的,因为可能使用它的以下分支依赖于 Logger.Enabled,这是运行时信息,因此无法精确优化掉,因为 Ada 是提前编译的。
  • 哦,我假设分支条件是编译时常量!如果不是这样定义的,那么是的,你所说的一切都适用。而且 asm 需要在任何地方的代码块上进行条件分支,即使它确实是内联的,而不是每次都必须调用。
猜你喜欢
  • 1970-01-01
  • 2013-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-20
  • 1970-01-01
  • 1970-01-01
  • 2015-12-01
相关资源
最近更新 更多