2

I am currently working on a project that uses a lot of lambda functions, and I found that they use a lot of stack but I don't understand why. I am a quite new to c++, I am more used to C.

To try to reduce stack usage and to understand how lambda work, I wrote some very simple code reproducing our use of lambdas. I am compiling the following code with gcc and -Os option.

typedef struct structTest
{
    uint32_t var1;
    uint32_t var2;
} structTest;

// Test 1
int main()
{
    dostuff( [&]() -> structTest{ structTest $; $.var2 = 24; $.var1 = 48; return $; }() );
}

// Test 2
int main()
{
    dostuff( [&]() -> structTest{ structTest $; $.var2 = 24; $.var1 = 48; return $; }() );

    dostuff( [&]() -> structTest{ structTest $; $.var2 = 13; $.var1 = 42; return $; }() );
}

Test 1 uses 8 bytes of stack, which I didn't really expect but can understand, but Test 2 uses 16 bytes of stack and I don't understand why. I would have expected 8 bytes, as in the stack used for the first call of the function would be reused for the second call.

I would have guessed that the two structure would not be reserved on stack.

Stack was analyzed with Ozone and runs on a Nucleo L476RG. This project is an IoT project, where ROM and stack are precious.

Is there a way to keep this kind of structure/lambda usage but reduce stack usage ?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
BaptisteC
  • 95
  • 5
  • 1
    **Why** don’t you expect/understand this? I think you need to explain your expected/desired behaviour, and why the current behaviour is a problem for you. — Furthermore, how did you analyse the stack usage? – Konrad Rudolph May 23 '19 at 13:33
  • @KonradRudolph: The OP's expectation sounds reasonable to me (not an expert on C++ lambdas). – 500 - Internal Server Error May 23 '19 at 13:34
  • 2
    Most likely the lambdas are being inlined directly into `main`. This is one of the reasons we like functors, they get inlined a lot easier. Unfortunately I can't give you an example of how to refactor your code as the lambdas are not needed at all in this example. – NathanOliver May 23 '19 at 13:35
  • I would expect the two structTest structures to not be both reserved on the stack. I used Ozone to analyze stack usage, and the code runs on a Nucleo L476RG. I can update my post if it is not clear enough – BaptisteC May 23 '19 at 13:37
  • 2
    Please do. At any rate this heavily depends on your compiler/version and optimisation settings. On g++ 9.1 with `-O3` it doesn’t take up *any* stack space (depending on what `dostuff` does, and reuses stack space otherwise). – Konrad Rudolph May 23 '19 at 13:42
  • Since when dollar can be used in C++ identifiers? – Slava May 23 '19 at 13:57
  • I believe `-Os` optimize code size, not stack usage? – apple apple May 23 '19 at 14:29
  • @Slava It is an old gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Dollar-Signs.html#Dollar-Signs – Maxim Egorushkin May 23 '19 at 14:33
  • [More on dollars](https://stackoverflow.com/a/26302259/560648) – Lightness Races in Orbit May 23 '19 at 14:52
  • [the calls are inlined](https://godbolt.org/z/RL_gNr). So I think lambdas have nothing to do in here. But strangely i see r3 and r4 pushed onto the stack in the second one. – KamilCuk May 23 '19 at 15:03

1 Answers1

1

One of the primary benefits of lambdas like this is that they can be near-trivially inlined into the surrounding code. That's why we like them! There's no overhead of a fresh stack or a jump or anything like that.

Of course, with inlining comes the use of the surrounding function's stack to store all the data. That's what you've observed.

Do whatever you would do in any other "I didn't want this to be inlined" situation. In this particular instance, perhaps use actual functions rather than lambdas, and chuck a "noinline"-type attribute on them (see your toolchain's documentation for how).

If you wanted an example of why people don't often use C++ for embedded programming, this would be a good one. It's designed for more meaty processors where we don't really care in general whether we use up another 8 bytes of stack — we care whether our programs run quickly, and we trust the compiler to help us to do that by inlining where it makes sense to do so. Your environment & requirements don't quite match that social contract.

Surely reworking or overloading dostuff so you can call it like this:

dostuff(48, 24);

…would be better?

The implementation could be as simple as:

__attribute__((noinline))
void doStuff(const uint32_t var1, const uint32_t var2)
{
   const structTest sT = {var1, var2};
   doStuff(sT);
}

It's also a lot less code and, IMO, easier to read.

By the way, though it works on your platform, I don't really recommend the use of $ as an identifier. It's non-standard to a degree, and not very clear to the reader.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055