简体   繁体   中英

Why does using C++ iterators increase code size drastically compared to at() or indexing?

I have been looking at using the newer C++ language features such as iterators on embedded systems (16KB of SRAM and 64 KB of flash, Cortex M4) and hit a surprising roadblock. Why on earth are iterators so monstrously large? I was under the impression they are basically some pointer arithmetic or indexing. Is the STL pulling in some unexpected code?

These are using Kinetis Design Studio on windows with the gcc-arm-none-eabi-4_9 toolchain from here using the following flags.

arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Os -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -fsingle-precision-constant -flto  -g3 -I"../Sources" -I"../Includes" -std=gnu++11 -fabi-version=0 -std=c++11 -MMD -MP -MF"Sources/System.d" -MT"Sources/System.o" -c -o "Sources/System.o" "../Sources/System.cpp"

ITM_SendChar just takes a single char and puts it in a register.

std::string input = "Oh hai there! :D\n";

#ifdef char_array
    // .text              7352
    // .data               376
    // .bss                236
    for(int i = 0; i < input.size(); i++)
            ITM_SendChar(input[i]);
#endif

#ifdef char_at
    // .text              7392
    // .data               376
    // .bss                236
    for(int i = 0; i < input.size(); i++)
        ITM_SendChar(input.at(i));
#endif

#ifdef char_itterator
    // .text             39744        
    // .data               384   
    // .bss                252   
    for(char some_char : input)
        ITM_SendChar(some_char);
#endif

#ifdef char_itterator_auto
    // .text             39744        
    // .data               384   
    // .bss                252   
    for(auto some_char : input)
        ITM_SendChar(some_char);
#endif

#ifdef char_itterator_auto_no_copy
    // .text             39744        
    // .data               384   
    // .bss                252   
    for(auto& some_char : input)
        ITM_SendChar(some_char);
#endif

The main difference between the [] operator and .at() is that .at() does bounds checking, and will throw an exception if the index is out of bounds.

It seems likely that the standard library implementation you're using is linking in extra code for something when using an iterator. The only way to find the cause is to examine the linker map file for both versions, and look closely at source code for the functions you're using, and maybe the generated assembly too.

In general, if you need your code to be very small you want to avoid using any of the standard library, because the functions in there can pull in lots of code and data with them. Even the code that parses the command line into the format that main() expects can be quite big.

For comparison, try this:

const char *input = "Oh hai there! :D\n";

while (*input)
        ITM_SendChar(*input++);

One (or two) C++ standards ago it was legal for iterators to be implemented with pointers. (You can google "removed weasel wording" in the standard to find out more about this.) Newer standards require more from iterators, for example that if you have two corresponding iterators in two containers of the same type, then swapping those two containers also requires swapping those two iterators (see N4527 23.2.1 footnote 9 to read it for yourself, if you like). All of this means that indexing into a container with indexes instead of iterators can certainly be more efficient. It's just that that isn't supported for all standard container types... And that's also why using iterators increases code size.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM