1

According to this statement :

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

And this implementation of 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;

I have a software which can manage several levels of logs, and I would like to know what is the behavior in case of Logger.Enabled equals False.

I have a lot of logs calls, with sometimes complex strings to evaluate and I'm on a real time system, so I don't want to lost time to evaluate a string which will not printed.

I would like to know if compiler optimize the code in order to not evaluate the string in parameter of Trace.Debug while Logger.enabled is False, knowing that this boolean is set at the begging of runtime by reading a value in a file.

I am using gnat 7.3.2.

Chris
  • 26,361
  • 5
  • 21
  • 42
LnlB
  • 180
  • 2
  • 11
  • 1
    If it can inline everywhere, the it might skip even constructing the arg; you could check the asm. If it can't inline then it definitely won't skip because the optimizer won't know what the Debug() function is going to do with the arg you pass. I don't know Ada, but you'd need this function to be inline*able* at least in the no-logging case. Perhaps via link-time optimization, like `-flto` if it uses gcc-style options. The compiler can still choose not to inline it for the log-enabled case, but actually I guess it would just inline a call to Put_Line since that's all that happens. – Peter Cordes Nov 16 '21 at 14:33
  • Any optimization must have the same result as no optimization. Since evaluating an expression can have side effects, it is not possible in general for optimization to remove the evaluation of an expression. – Jeffrey R. Carter Nov 17 '21 at 10:00
  • The question title is misleading. A runtime branch to maybe use the arg isn't an "empty body". I was assuming your Logger.Enabled was a compile-time constant, because `if(false)` counts as empty function body, but `if(variable) print()` certainly isn't. As discussed in comments on flyx's answer, even after inlining, the compiler would still have to emit conditional branches, although the work of concatenating strings could potentially be optimized into the conditional block if the compiler is smart enough. – Peter Cordes Nov 17 '21 at 13:20

1 Answers1

1

You can ensure that evaluation doesn't happen by providing a callback:

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

Then to call it, do

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;

In your original code, the only way that the compiler could skip the evaluation is when it inlines the Debug procedure and re-arranges the code so that the Message object is only assigned inside the if-block. You cannot force this; even pragma Inline is only a hint for the compiler.

Shark8
  • 4,095
  • 1
  • 17
  • 31
flyx
  • 35,506
  • 7
  • 89
  • 126
  • *re-arranges the code so that the Message object is only assigned inside the if-block* - I guess that's one way to look at what optimizers do, but really it would just be a matter of noticing that the value is unused. They optimize away all work that has no visible side effects and whose results aren't used. – Peter Cordes Nov 17 '21 at 12:21
  • @PeterCordes Inlining the `Debug` procedure would still place the construction of the `Message` object in front of the `if` statement. That means that a liveness analysis will show that the variable is live (since it is accessed inside `if`) and therefore its value must be constructed. The compiler can, however, see that it is only used inside the `if` statement and therefore move its construction there. A pure unused values optimization cannot get rid of the evaluation. – flyx Nov 17 '21 at 12:28
  • Ada is fully ahead-of-time compiled, right? GNAT is a front-end for GCC's optimizing back-end. "Evaluating" in the abstract machine isn't meaningful. For example, https://godbolt.org/z/zs64seGTP demonstrates a C function where an expression is evaluated but unused: you can see no sign of it in the optimized asm. There's no `if()` for the compiler to hide it in, it just doesn't waste asm instructions computing results that nothing will ever see or use. It's a "let's not and say we did" situation, i.e. the as-if rule. Does that not apply to this Ada situation? (I don't know Ada). – Peter Cordes Nov 17 '21 at 12:36
  • @PeterCordes The code you show is a different situation where a variable is indeed not live at its definition (i.e. it is never used) and can therefore be optimised away. As I explained, our variable is live because the following branch, which may use it, depends on `Logger.Enabled`, which is runtime information and therefore cannot be optimized away precisely because Ada is compiled ahead-of-time. – flyx Nov 17 '21 at 12:42
  • Oh, I was assuming the branch condition was a compile-time constant! If it's not defined that way, then yeah, everything you were saying applies. And the asm would need conditional branches over blocks of code everywhere, even if it did inline instead of having to call every time. – Peter Cordes Nov 17 '21 at 12:48
  • (I think I made that assumption because if efficiency is critical, you'd want to design your debug logging so it can fully optimize away.) – Peter Cordes Nov 17 '21 at 12:50
  • 1
    @PeterCordes OP did say *this boolean is set at the begging of runtime by reading a value in a file.* so that's what I went with. I agree that this would better be a compile-time constant in which case a typical Ada project would just do compile-time code selection to replace the whole `Debug` definition with `procedure Debug (Message : in String) is null;`, that will most certainly be inlined and cause the evaluation not to occur. – flyx Nov 17 '21 at 12:53
  • Yeah, I saw that too when I went back the question in more detail, instead of just a quick skim :P – Peter Cordes Nov 17 '21 at 13:04