2015-10-04 20 views
6

Ho cercato di utilizzare le nuove funzionalità del linguaggio C++ come gli iteratori su sistemi embedded (16 KB di SRAM e 64 KB di flash, Cortex M4) e ho raggiunto un sorprendente roadblock. Perché sulla terra esistono iteratori così mostruosamente grandi? Avevo l'impressione che fossero fondamentalmente dei puntatori aritmetici o indicizzati. STL sta inserendo un codice inaspettato?Perché l'utilizzo di iteratori C++ aumenta drasticamente le dimensioni del codice rispetto a at() o all'indicizzazione?

Questi stanno utilizzando Kinetis Design Studio su Windows con la toolchain gcc-arm-none-eabi-4_9 da here utilizzando i seguenti flag.

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 prende solo un singolo carattere e lo inserisce in un registro.

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 
+2

Si sta compilando in alcune configurazioni di debug? Non conosco il tuo ambiente di compilazione, ma, ad esempio, nella configurazione di debug di Visual C++ vengono eseguiti tutti i tipi di controllo. – JohnB

+1

Per prima cosa dovresti confrontare le mele con le mele e non fare copie nel ciclo basato su intervalli. Cosa succede se usi 'auto & some_char: input'? – NathanOliver

+0

Grazie a @NathanOliver per il suggerimento, ma nessun cambiamento. Ho aggiunto i risultati alla mia domanda. E JohnB c'è il flag -g3 per il debug ma rimuoverlo non fa alcuna differenza. Il KDS, per quanto ne so, non include nulla in più durante il debug. – hak8or

risposta

1

uno (o due) standard C++ fa era legale per iteratori da attuare con i puntatori. (Puoi google "rimossa la formulazione di donnola" nello standard per saperne di più.) Gli standard più recenti richiedono di più dagli iteratori, ad esempio se hai due iteratori corrispondenti in due contenitori dello stesso tipo, quindi scambia anche questi due contenitori richiede di scambiare quei due iteratori (vedi N4527 23.2.1 nota 9 per leggerlo da solo, se preferisci). Tutto ciò significa che l'indicizzazione in un contenitore con indici anziché iteratori può certamente essere più efficiente. È solo che non è supportato per tutti i tipi di container standard ... Ed è anche per questo che l'uso degli iteratori aumenta la dimensione del codice.

3

La differenza principale tra l'operatore [] e .at() è che .at() fa il controllo dei limiti, e un'eccezione se l'indice è fuori dai limiti.

Sembra probabile che l'implementazione della libreria standard utilizzata stia collegando un codice extra per qualcosa quando si utilizza un iteratore. L'unico modo per trovare la causa è esaminare il file della mappa del linker per entrambe le versioni e osservare da vicino il codice sorgente per le funzioni che stai utilizzando, e forse anche l'assembly generato.

In generale, se si desidera che il proprio codice sia molto piccolo, si desidera evitare l'uso di una qualsiasi libreria standard, poiché le funzioni in esso contenute possono includere molti codice e dati. Anche il codice che analizza la riga di comando nel formato che main() si aspetta può essere abbastanza grande.

Per fare un confronto, provate questo:

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

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

Ciò che hai appena mostrato è anche del tutto adeguato in termini di dimensioni del codice, grazie. Speravo solo di entrare nella magia di iteratore per embedded perché sembrava sia molto utile e abbia un impatto minimo sulle dimensioni del codice grazie alla mia comprensione di come funzionano gli iteratori, ma purtroppo sembra che non ci sia. Guarderò in seguito il file di mappa del linker per vedere se riesco a scoprire cosa succede e se ci sono soluzioni alternative in modo da poter ancora usare gli iteratori senza che tutti i miei flash vadano come il dodo. – hak8or

+0

@ hak8or - * "Ciò che hai appena mostrato è anche totalmente adeguato in termini di dimensioni del codice ..." * - usi spesso 'CXXFLAGS + = -ffunction-section -fdata-sections' e' LDFLAGS + = -Wl, - -gc-section' per ridurre le dimensioni del codice. Ma questo è un problema/domanda diverso da * "Perché usare gli iteratori C++ aumenta drasticamente la dimensione del codice rispetto a a() o all'indicizzazione?" * E * "Perché sulla terra ci sono iteratori così mostruosamente grandi?" * – jww