Pergunta

Quais são as características de C ++ deve ser evitado em sistemas embarcados?

Por favor classificar a resposta pela razão, tais como:

  • uso de memória
  • tamanho do código
  • velocidade
  • portabilidade

EDIT:. Vamos usar um ARM7TDMI com 64k carneiro como um alvo para controlar o escopo das respostas

Foi útil?

Solução

RTTI e Tratamento de Exceções:

  • Aumenta código-size
  • O desempenho diminui
  • muitas vezes pode ser substituída por mecanismos mais baratos ou melhor software de design.

Modelos:

  • ter cuidado com eles, se o código de tamanho é um problema. Se o seu CPU alvo tem nenhuma ou apenas um cache de instrução muito pequena pode reduzir o desempenho também. (Modelos tendem a código bloat se usado sem cuidados). Otoh inteligente meta-programação pode diminuir o código de tamanho também. Não há uma resposta clara sobre a sua.

funções virtuais e herança:

  • Estes são muito bem para mim. Escrevo quase todo o meu código incorporado em C. Isso não me impede de usar tabelas função-ponteiro para funções virtuais imitam. Eles nunca se tornou um problema peformance.

Outras dicas

A escolha para evitar certas características devem ser sempre conduzido por análise quantitativa do comportamento do o software, em o hardware, com o escolhido toolchain, sob as restrições seus implica domínio. Há muita sabedoria "nãos" convencionais em desenvolvimento C ++ que são baseados em superstição e história antiga, em vez de dados do disco. Infelizmente, isso muitas vezes resulta em um monte de código solução alternativa extra que está sendo escrito para evitar o uso de recursos de que alguém, em algum lugar, tinha um problema com era uma vez.

As excepções são provavelmente vai ser a resposta mais comum do que evitar. A maioria das implementações têm um custo de memória estática bastante grande, ou um custo de memória de tempo de execução. Eles também tendem a fazer garantias em tempo real mais difícil.

aqui para um bom exemplo bonito de um padrão de codificação escrito para c incorporado ++.

O documento " tecnologia da informação - Programação línguas, seus ambientes e interfaces de software do sistema - Técnicas Relatório sobre C ++ Desempenho " dá também algumas boas informações sobre a programação em C ++ para um dispositivo embutido.

É uma leitura interessante para o Fundamentação no início de incorporado C ++ standrard

Veja este artigo na CE ++ também.

O std Embutido C ++ era um subconjunto apropriado de C ++, isto é, não tem adições. Os seguintes recursos de linguagem foram removidos:

  • A herança múltipla
  • classes base virtuais
  • informações de tipo de tempo de execução (typeid)
  • moldes Novo estilo (static_cast, dynamic_cast, reinterpret_cast e const_cast)
  • O tipo mutável qualificador
  • Namespaces
  • Exceções
  • Modelos

É anotado no wiki página que Bjarne Stroustrup diz (do CE ++ std), "para o melhor de meu conhecimento CE ++ está morto (2004), e se não é que deveria ser." Stroustrup continua a recomendar a documento referenciado pela resposta de Prakash .

Usando um ARM7 e supondo que você não tem uma MMU externo, problemas de alocação dinâmica de memória pode ser mais difícil para depurar. Eu acrescentaria "uso criterioso de novo / delete / livre / malloc" à lista de diretrizes.

Se você estiver usando uma ARM7TDMI, memória unaligned acessos em todos os custos .

O núcleo básico ARM7TDMI não tem verificação de alinhamento, e retornará dados giradas quando você faz uma leitura não alinhado. Algumas implementações possuem um circuito adicional para levantar uma exceção ABORT, mas se você não tem uma dessas implementações, encontrar bugs devido a acessos não alinhados é muito doloroso.

Exemplo:

const char x[] = "ARM7TDMI";
unsigned int y = *reinterpret_cast<const unsigned int*>(&x[3]);
printf("%c%c%c%c\n", y, y>>8, y>>16, y>>24);
  • Em um x86 / x64 CPU, este imprime "7TDM".
  • Em um SPARC CPU, este despeja núcleo com um erro de bus.
  • Em uma CPU ARM7TDMI, isso pode imprimir algo como "7ARM" ou "ITDM", assumindo que a variável "x" está alinhado em um limite de 32-bit (que depende de onde "x" está localizado e quais as opções do compilador estão em uso, etc.) e você estiver usando o modo little-endian. É um comportamento indefinido, mas está praticamente garantido para não trabalhar da maneira que quiser.

Na maioria dos sistemas você não quiser usar new / Excluir a menos que tenha substituído-los com sua própria implementação que puxa a partir do seu próprio heap gerenciado. Sim, vai ser trabalho, mas você está lidando com um sistema de memória restrita.

Eu não teria dito há uma regra dura e rápida a este; ele depende muito de sua aplicação. Sistemas embarcados são tipicamente:

  • Mais constrangido na quantidade de memória que têm disponível
  • Muitas vezes rodar em hardware mais lento
  • Tendem a ser mais perto de hardware ou seja, conduzi-lo de alguma forma, como mexer com configurações de registro.

Assim como qualquer outro desenvolvimento, porém, você deve equilibrar todos os pontos que você mencionou contra os requisitos que foram dadas / derivados.

Em relação inchaço código, acho que o culpado é muito mais provável que seja em linha do que modelos.

Por exemplo:

// foo.h
template <typename T> void foo () { /* some relatively large definition */ }

// b1.cc
#include "foo.h"
void b1 () { foo<int> (); }

// b2.cc
#include "foo.h"
void b2 () { foo<int> (); }

// b3.cc
#include "foo.h"
void b3 () { foo<int> (); }

O vinculador provavelmente irá mesclar todas as definições de 'foo' em uma única unidade de tradução. Portanto, o tamanho do 'foo' não é diferente à de qualquer outra função namespace.

Se o seu vinculador não fizer isso, então você pode usar uma instanciação explícita para fazer isso por você:

// foo.h
template <typename T> void foo ();

// foo.cc
#include "foo.h"
template <typename T> void foo () { /* some relatively large definition */ }
template void foo<int> ();        // Definition of 'foo<int>' only in this TU

// b1.cc
#include "foo.h"
void b1 () { foo<int> (); }

// b2.cc
#include "foo.h"
void b2 () { foo<int> (); }

// b3.cc
#include "foo.h"
void b3 () { foo<int> (); }

Agora, considere o seguinte:

// foo.h
inline void foo () { /* some relatively large definition */ }

// b1.cc
#include "foo.h"
void b1 () { foo (); }

// b2.cc
#include "foo.h"
void b2 () { foo (); }

// b3.cc
#include "foo.h"
void b3 () { foo (); }

Se o compilador decide inline 'foo' para você, então você vai acabar com 3 cópias diferentes de 'foo'. Não há modelos à vista!

EDIT: From um comentário acima InSciTek Jeff

Usando instantiations explícitas para as funções que você sabe que vai ser utilizado, você também pode garantir que todas as funções não utilizadas são removidas (que pode realmente reduzir o tamanho do código em comparação com o caso não template):

// a.h
template <typename T>
class A
{
public:
  void f1(); // will be called 
  void f2(); // will be called 
  void f3(); // is never called
}


// a.cc
#include "a.h"

template <typename T>
void A<T>::f1 () { /* ... */ }

template <typename T>
void A<T>::f2 () { /* ... */ }

template <typename T>
void A<T>::f3 () { /* ... */ }

template void A<int>::f1 ();
template void A<int>::f2 ();

A menos que sua cadeia de ferramenta é completamente quebrado, o acima irá gerar o código apenas para 'F1' e 'F2'.

funções de tempo são geralmente OS dependente (a menos que você reescrevê-los). Use suas próprias funções (especialmente se você tiver um RTC)

modelos estão ok para usar, contanto que você tem espaço suficiente para o código - othwerise não usá-los

exceções não são muito portátil também

funções printf que não gravação a um buffer não são portáteis (você precisa estar de alguma forma ligado ao sistema de arquivos para gravar em um arquivo * com printf). Use apenas sprintf, snprintf e str * funções (strcat, STRLEN) e, claro, seus corespondents de char de largura (wcslen ...).

Se a velocidade é o problema, talvez você deve usar seus próprios recipientes em vez de STL (por exemplo o recipiente std :: mapa para certificar-se de uma chave é igual faz 2 (sim 2) comparações com o 'menos' operador (a [menos de] b == falso && b [inferior] == uma falsa um significativo == b). 'menos' é o único parâmetro de comparação recebidos pela classe std :: mapa (e não apenas). Isso pode levar a alguma perda de desempenho em rotinas críticas.

templates, exceções estão aumentando o tamanho do código (você pode ter certeza disso). às vezes até mesmo o desempenho é afetado quando se tem um código maior.

funções de alocação de memória provavelmente precisará ser reescrita também porque eles são dependentes de muitas maneiras (especialmente quando se lida com a alocação de memória de segurança do thread) OS.

malloc usa a variável _END (declarados normalmente no script linker) para alocar memória, mas isso não é thread-safe em ambientes "desconhecidos".

Às vezes você deve usar Thumb em vez do modo Arm. Ele pode melhorar o desempenho.

Assim, para 64k de memória, eu diria que C ++ com algumas de suas características agradáveis ??(STL, exceções etc) pode ser um exagero. Eu definitivamente escolher C.

Tendo usado o compilador GCC ARM e própria SDT da ARM eu teria os seguintes comentários:

  • A ARM SDT produz mais apertado, mais rápido código, mas é muito caro (> Eur5k por assento!). No meu trabalho anterior que utilizado este compilador e foi ok.

  • As ferramentas GCC ARM funciona muito bem embora e é o que eu uso no meu próprio projetos (GBA / DS).

  • Use o modo 'polegar' como isso reduz o tamanho do código de forma significativa. Em 16 de bus pouco variantes do braço (tal como o GBA), há também uma vantagem de velocidade.

  • 64k é sério pequeno para C ++ desenvolvimento. Eu usaria C & Assembler nesse ambiente.

Em uma pequena plataforma como você vai ter que ter cuidado com o uso de pilha. Evite recursão, grandes automáticas (locais) estruturas de dados etc. uso Heap também será um problema (novo, malloc etc). C vai lhe dar mais controle sobre estas questões.

Se você estiver usando um ambiente de desenvolvimento orientada para o desenvolvimento incorporado ou um sistema embarcado particular, que deveria ter limitado algumas das opções para você já. Dependendo da capacidade de recursos de seu alvo, ele irá desligar alguns dos itens acima mencionados (RTTI, exceções, etc.). Este é o caminho mais fácil para ir, em vez de manter em mente o que vai aumentar de tamanho ou de memória requisitos (embora, você deve ficar a saber que mentalmente de qualquer maneira).

Para sistemas embarcados, você vai predominantemente quer evitar coisas que têm um custo de tempo de execução anormal definitiva. Alguns exemplos: Exceções e RTTI (para incluir dynamic_cast e typeid ).

Certifique-se de saber quais recursos são suportados pelo compilador para a sua plataforma integrado e também certifique-se de conhecer as peculiaridades de sua plataforma. Por exemplo compilador CodeComposer da TI não faz instantiations modelo automáticas. Como resultado, se você quiser usar tipo de STL, você precisa instanciar cinco coisas diferentes manualmente. Ele também não suporta fluxos.

Outro exemplo é que você pode estar usando um chip DSP, que não tem suporte de hardware para operações de ponto flutuante. Isso significa que cada vez que você usar uma bóia ou um duplo você paga o custo de uma chamada de função.

Para resumir, sabe tudo o que há para saber sobre a sua plataforma embarcada e seu compilador, e então você vai saber que características para evitar.

Um problema particular que me surpreendeu com ATmega GCC 3.algo: quando eu adicionei uma função brasa virtual para uma das minhas aulas, eu tive que adicionar um destrutor virtual. Nesse ponto, o ligador pediu operador delete (void *). Eu não tenho idéia por que isso acontece, e adicionando uma definição vazia para esse operador slolved o problema.

Note que o custo de exceções depende do seu código. Em uma aplicação eu perfilado (um relativamente pequeno na ARM968), suporte excepção adicionado 2% para o tempo de execução, e o tamanho do código foi aumentada em 9,5 KB. Nesta aplicação, as exceções foram lançados apenas no caso de algo sério ruim aconteceu - ou seja, não na prática -. Que manteve o tempo de execução sobrecarga muito baixa

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top