Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. For loop

For loop

Scheduled Pinned Locked Moved C / C++ / MFC
performance
34 Posts 9 Posters 183 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • L Lost User

    jschell wrote:

    Just curious - what exactly do you think the scope is when a CPU register is used

    All rules go out the window on the optimization pass. Could even optimize away the objects. But the initial code generation will follow the rules above if the compiler is ["C99 compliant"](https://www.google.com/search?q=list+of+c99+compliant+compilers) In fact if /LTCG is enabled entire functions could be removed/combined. Compiler optimization and link time code generation is an entirely different discussion.

    T Offline
    T Offline
    trønderen
    wrote on last edited by
    #25

    To me, that sounds like one of two: Either, the C/C++ scoping/life time rules apply to the source code only and how it 'conceptually' is executed, and does not relate to the compiled code and how it actually runs. Or, the C/C++ standard does not, strictly speaking, allow optimization of the kinds that we are talking about here, such as allocating a variable in a register, reuse of stack locations within a single scope, or removing variables entirely. I don't know which is correct. To me, it is the running code that matters, even if the correct interpretation of the standard is that it doesn't care about the generated code.

    L J 2 Replies Last reply
    0
    • T trønderen

      To me, that sounds like one of two: Either, the C/C++ scoping/life time rules apply to the source code only and how it 'conceptually' is executed, and does not relate to the compiled code and how it actually runs. Or, the C/C++ standard does not, strictly speaking, allow optimization of the kinds that we are talking about here, such as allocating a variable in a register, reuse of stack locations within a single scope, or removing variables entirely. I don't know which is correct. To me, it is the running code that matters, even if the correct interpretation of the standard is that it doesn't care about the generated code.

      L Offline
      L Offline
      Lost User
      wrote on last edited by
      #26

      Yeah, I guess the only way to answer a C/C++ language question is to point at the C/C++ standards that define the language. It reminds me of this old blog post: [This processor has no stack](https://devblogs.microsoft.com/oldnewthing/20161031-00/?p=94605)

      T 2 Replies Last reply
      0
      • L Lost User

        Yeah, I guess the only way to answer a C/C++ language question is to point at the C/C++ standards that define the language. It reminds me of this old blog post: [This processor has no stack](https://devblogs.microsoft.com/oldnewthing/20161031-00/?p=94605)

        T Offline
        T Offline
        trønderen
        wrote on last edited by
        #27

        The question is how the C/C++ standard defines its own scope. How far does the authority of the standard reach. How does it define which parts of the system must be compliant to the standard for the system to call itself standard compliant. Does it stop at the parser, or could an optimizer have any effect on whether the system is compliant. This is more a "meta-question", not something you will find in the syntax description pages. If you find it at all in the standard text about the authority and scope of the standard itself, and which parts of a system must comply to the standard, it is probably in some introductory or concluding chapters. I do not expect it to be in the standard itself, and if it is there, I expect the wording to be so formalistic that you have to have some experience from working in the standards committee to get it right. I did pick up a copy of a late-in-the-process draft documents, but the final document is too high priced that I am willing to pay that much for the spec of a language I use very rarely nowadays. I tried to read the drafts, but they were too huge for me to read every word - and in particular: to understand every word of it :-)

        L 1 Reply Last reply
        0
        • T trønderen

          The question is how the C/C++ standard defines its own scope. How far does the authority of the standard reach. How does it define which parts of the system must be compliant to the standard for the system to call itself standard compliant. Does it stop at the parser, or could an optimizer have any effect on whether the system is compliant. This is more a "meta-question", not something you will find in the syntax description pages. If you find it at all in the standard text about the authority and scope of the standard itself, and which parts of a system must comply to the standard, it is probably in some introductory or concluding chapters. I do not expect it to be in the standard itself, and if it is there, I expect the wording to be so formalistic that you have to have some experience from working in the standards committee to get it right. I did pick up a copy of a late-in-the-process draft documents, but the final document is too high priced that I am willing to pay that much for the spec of a language I use very rarely nowadays. I tried to read the drafts, but they were too huge for me to read every word - and in particular: to understand every word of it :-)

          L Offline
          L Offline
          Lost User
          wrote on last edited by
          #28

          trønderen wrote:

          they were too huge for me to read every word - and in particular: to understand every word of it :)

          Yeah, me too. The last language spec I read was C11 and C++11 and haven't looked at C++14 or higher. I've learned some the new language features but not reading the specs anymore unless I have to.

          1 Reply Last reply
          0
          • L Lost User

            Yeah, I guess the only way to answer a C/C++ language question is to point at the C/C++ standards that define the language. It reminds me of this old blog post: [This processor has no stack](https://devblogs.microsoft.com/oldnewthing/20161031-00/?p=94605)

            T Offline
            T Offline
            trønderen
            wrote on last edited by
            #29

            Randor wrote:

            It reminds me of this old blog post: This processor has no stack

            Slight sidetrack: Fortran did not allow recursion until the Fortran 90 version; the memory usage was fixed. One of the arguments brought up to keep Fortran alive was that you never risked a stack overflow in Fortran; it was safer than stack languages. Besides: Since there were no stack management, the call overhead was reduced. At least in its first standard version, Ada required recursive functions to be flagged as such in the program text, and that the maximum calling/recursion depth to be statically determinable at compile time, so that the maximum stack requirement can be calculated in advance. CHILL also required recursive functions to be marked as such, but I am not sure if you were allowed to code an 'uncontrolled' recursion. At least, to manually search for stack overflow problems, you could limit the search to functions marked as recursive.

            L 1 Reply Last reply
            0
            • T trønderen

              Randor wrote:

              It reminds me of this old blog post: This processor has no stack

              Slight sidetrack: Fortran did not allow recursion until the Fortran 90 version; the memory usage was fixed. One of the arguments brought up to keep Fortran alive was that you never risked a stack overflow in Fortran; it was safer than stack languages. Besides: Since there were no stack management, the call overhead was reduced. At least in its first standard version, Ada required recursive functions to be flagged as such in the program text, and that the maximum calling/recursion depth to be statically determinable at compile time, so that the maximum stack requirement can be calculated in advance. CHILL also required recursive functions to be marked as such, but I am not sure if you were allowed to code an 'uncontrolled' recursion. At least, to manually search for stack overflow problems, you could limit the search to functions marked as recursive.

              L Offline
              L Offline
              Lost User
              wrote on last edited by
              #30

              trønderen wrote:

              Slight sidetrack

              If you want to get further sidetracked you can watch Aaron Ballman go over all the [C23 changes](https://blog.aaronballman.com/2023/10/whats-new-in-c-in-2023/). He's on both the C and C++ committees. Video was posted yesterday. I'm not going to be using any C23 anytime soon, but I try to keep up with all the language changes.

              T 1 Reply Last reply
              0
              • T trønderen

                To me, that sounds like one of two: Either, the C/C++ scoping/life time rules apply to the source code only and how it 'conceptually' is executed, and does not relate to the compiled code and how it actually runs. Or, the C/C++ standard does not, strictly speaking, allow optimization of the kinds that we are talking about here, such as allocating a variable in a register, reuse of stack locations within a single scope, or removing variables entirely. I don't know which is correct. To me, it is the running code that matters, even if the correct interpretation of the standard is that it doesn't care about the generated code.

                J Offline
                J Offline
                jschell
                wrote on last edited by
                #31

                trønderen wrote:

                the C/C++ scoping/life time rules apply to the source code only and how it 'conceptually' is executed, and does not relate to the compiled code and how it actually runs.

                Yep. So as per the OP "will allocate memory for an int every frame." To me that is a question about how the compiler emitted code works and has nothing to do with the spec. It seems to me to be obviously referring to the stack frame (emitted code.) And for comparison both parts of that are meaningless if the compiler emitted code uses a CPU register. Compliance for the spec only applies to the 'scope' which means the visibility and the lifetime of the data. Thus no one can use a compliant compiler and then complain when they attempt to circumvent the scope, for example by using a pointer. Because a compliant compiler can (but is not required) to reuse either a stack frame slot or a CPU register. It is however possible to use a compliant compiler and deliberately circumvent the scope and it will work. At least for that compiler and that version of the compiler. Might even work in some cases in the same emitted binary and not others depending on how the compiler (not the spec) decides to optimize.

                1 Reply Last reply
                0
                • L Lost User

                  trønderen wrote:

                  Slight sidetrack

                  If you want to get further sidetracked you can watch Aaron Ballman go over all the [C23 changes](https://blog.aaronballman.com/2023/10/whats-new-in-c-in-2023/). He's on both the C and C++ committees. Video was posted yesterday. I'm not going to be using any C23 anytime soon, but I try to keep up with all the language changes.

                  T Offline
                  T Offline
                  trønderen
                  wrote on last edited by
                  #32

                  He is clear and concise, and he definitely knows what he is talking about. The seventy minutes were certainly not wasted time. Yet, for at least a third, maybe half of it, my immediate reaction as a C# developer is either, 'Do you still have to struggle with this?' Or, 'Have you really been without this until now?' ... I sort of knew; I was using C a few years back. This video certainly doesn't make me long back to C. Also, I am (not) surprised to see that features are adopted from C++, after C++ has adopted them from C# (and other languages). It is presented as if C++ was the inventor of several mechanisms that were only slowly adopted from from other languages. I have a strong feeling of NIH. It is interesting to see what 'they' - those in other camps - are doing. Even if I don't see a single thing that makes me exclaim 'Why don't they do it that way in C# as well?'

                  1 Reply Last reply
                  0
                  • L Lost User

                    It's been the same since C99 [C99 block scope](https://www.google.com/search?q=c99+block+scope) [C++ block scope](https://en.cppreference.com/w/cpp/language/scope)

                    honey the codewitchH Offline
                    honey the codewitchH Offline
                    honey the codewitch
                    wrote on last edited by
                    #33

                    I understand variable scope to work the way (I think?) jschell is describing. That is, of course the i itself is bounded by {} of the for loop (if I recall correctly) and yet I don't know that the spec is clear where the var has to be allocated/deallocated except perhaps limiting it to the method in question - I'm not even sure there to be honest. I don't have the head for reams of specifications so I tend to avoid them unless I need to resolve something specific. The above just comes from what I remember of using it, plus a bit of an educated guess - not all systems *could* potentially allocate variables at greater granularity than a method. Some assemblers require you to pre-reserve the entire stack frame you'll be using for that function. In that case, what's a C/C++ compiler to do? The variables effectively live for the entire life of the routine. Sure they're scoped more narrowly than that but the actual physical memory would be there. That's my understanding and I could be very wrong.

                    Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix

                    1 Reply Last reply
                    0
                    • C Calin Negru

                      Thanks for sharing

                      L Offline
                      L Offline
                      Lost User
                      wrote on last edited by
                      #34

                      By the way in general it is not right to think of variables as being allocated anywhere, neither in memory nor in registers. Variable are not the "thing" that is allocated, and any given variable may end being in zero or more places at the same time, if you insist on looking at it like that. It's not a completely useless mental model, which is probably why it persists, but that's as a [lie-to-children](https://en.wikipedia.org/wiki/Lie-to-children). If a variable is assigned to various times (in the static sense: not so much several times in a loop, but several times in straight line code), those different "versions" of the variable may well end up in different places. SSA considers those different "versions" of the variable to be different variables altogether. Furthermore, even one "version" of a variable can be split into multiple live ranges - that's not just theoretical, there can be multiple good reasons to split it and allocated the pieces to different places. For example, there are often restrictions on which set of register can be used for some instructions, such as on x64 divisions and "legacy" shift-by-variable instructions. For example, if we consider this code with a division and shift-by-variable:

                      int test(int x, int y)
                      {
                      x = x / y;
                      return y << x;
                      }

                      [MSVC compiles it like this, for x64](https://godbolt.org/z/T8dGWMzhc) (why doesn't this link linkify?)

                      0 x$ = 8
                      1 y$ = 16
                      2 int test(int,int) PROC ; test
                      3 mov r8d, edx
                      4 mov eax, ecx
                      5 cdq
                      6 idiv r8d
                      7 mov ecx, eax
                      8 shl r8d, cl
                      9 mov eax, r8d
                      10 ret 0
                      11 int test(int,int) ENDP ; test

                      On lines 0 and 1 MSVC helpfully defined stack offsets for x and y, which aren't used, they never end up being on the stack. `x` is passed in via `ecx`, and `y` via `edx`. `x` begins in `ecx`, then is copied to `eax` (line 4) because `idiv` takes the dividend in `edx:eax`, the division leaves it in `eax` (only because the code happens to assign the result of `x / y` back to `x` - to be clear, the output would be in `eax` either way, but `eax` could have represented some other variable otherwise), the original un-divided value of `x` is still in `ecx` at this point (after the division on line 6 but before the mov on line 7) but we need the new value to be in `ecx`, because `shl` needs the shift count to be in `cl` which is the

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups