Pergunta

Existe uma penalidade de desempenho de tempo de execução ao usar interfaces (classes base abstratas) em C ++?

Foi útil?

Solução

Resposta curta: Não.

Long Resposta: Não é a classe base ou o número de ancestrais uma classe tem em sua hierarquia que afeta a velocidade. A única coisa é o custo de uma chamada de método.

Uma chamada de método não virtual tem um custo (mas pode ser embutido)
Uma chamada de método virtual tem um custo ligeiramente superior como você precisa olhar para o método a chamada antes de chamá-lo (mas isso é uma tabela simples olhar para cima não a pesquisa). Uma vez que todos os métodos em uma interface são virtuais, por definição, não existe este custo.

A menos que você está escrevendo alguma aplicação sensível hiper velocidade isso não deve ser um problema. A clareza extra que você vai receber de usar uma interface geralmente compensa qualquer diminuição de velocidade percebida.

Outras dicas

Funções chamado usando expedição virtual não são inline

Há um tipo de penalidade para as funções virtuais que é fácil de esquecer: as chamadas virtuais não são indexados num determinado (comum) situação em que o tipo do objeto não é conhecido em tempo de compilação. Se a sua função é pequeno e adequado para inlining, esta penalidade pode ser muito significativo, já que você não está apenas adicionando uma sobrecarga de chamada, mas o compilador também é limitado em como ele pode otimizar a função de chamada (que tem de assumir a função virtual pode mudaram alguns registros ou posições de memória, não pode propagar valores constantes entre o chamador e o receptor).

custo da chamada Virtual depende de plataforma

Como para a pena de chamada sobrecarga em comparação com uma chamada de função normal, a resposta depende de sua plataforma de destino. Se você está alvejando um PC com x86 / x64 CPU, a pena para chamar uma função virtual é muito pequeno, tão moderno CPU x86 / x64 pode executar previsão de desvios de chamadas indiretas. No entanto, se você está direcionando um PowerPC ou alguma outra plataforma RISC, a pena chamada virtual pode ser bastante significativo, porque as chamadas indiretas não são previstos em algumas plataformas (Cf. PC / Xbox 360 multi-plataforma de desenvolvimento Melhores Práticas ).

Há uma pequena penalização por chamada de função virtual em comparação com uma chamada regular. É improvável que você observar uma diferença, a menos que você está fazendo centenas de milhares de chamadas por segundo, eo preço é muitas vezes vale a pena pagar para maior clareza código adicionado de qualquer maneira.

Quando você chamar uma função virtual (dizer através de uma interface), o programa tem que fazer um olhar-se da função de uma tabela para ver qual função a ser chamada para esse objeto. Isto dá uma pena pequena em comparação com uma chamada direta para a função.

Além disso, quando você usa uma função virtual, o compilador pode não inline chamada de função. Portanto pode haver uma penalidade para usar uma função virtual para algumas pequenas funções. Este é geralmente o maior "hit" desempenho que são susceptíveis de ver. Isso realmente um problema apenas se a função é pequeno e chamado muitas vezes, digamos, de dentro de um loop.

Outra alternativa que é aplicável em alguns casos, é tempo de compilação polimorfismo com modelos. É útil, por exemplo, quando quiser para fazer uma escolha de implementação no início do programa, e em seguida, usá-lo para a duração da execução. Um exemplo com polimorfismo em tempo de execução

class AbstractAlgo
{
    virtual int func();
};

class Algo1 : public AbstractAlgo
{
    virtual int func();
};

class Algo2 : public AbstractAlgo
{
    virtual int func();
};

void compute(AbstractAlgo* algo)
{
      // Use algo many times, paying virtual function cost each time

}   

int main()
{
    int which;
     AbstractAlgo* algo;

    // read which from config file
    if (which == 1)
       algo = new Algo1();
    else
       algo = new Algo2();
    compute(algo);
}

O mesmo usando o polimorfismo em tempo de compilação

class Algo1
{
    int func();
};

class Algo2
{
    int func();
};


template<class ALGO>  void compute()
{
    ALGO algo;
      // Use algo many times.  No virtual function cost, and func() may be inlined.
}   

int main()
{
    int which;
    // read which from config file
    if (which == 1)
       compute<Algo1>();
    else
       compute<Algo2>();
}

Eu não acho que a comparação de custos entre chamada de função virtual e uma chamada de função em linha reta. Se você estiver pensando em usar uma classe base abstrata (interface), então você tem uma situação onde você deseja executar uma das várias ações baseadas do tipo dinâmico de um objeto. Você tem que fazer essa escolha de alguma forma. Uma opção é usar funções virtuais. Outro é um interruptor do tipo de objecto, quer através de RTTI (potencialmente caro), ou a adição de um método de tipo () para a classe de base (aumentando potencialmente o uso de memória de cada objecto). Assim, o custo da chamada de função virtual deve ser comparado com o custo da alternativa, não para o custo de não fazer nada.

A maioria das pessoas nota a pena de tempo de execução, e com razão.

No entanto, na minha experiência trabalhando em grandes projetos, os benefícios de interfaces claras e encapsulamento adequado compensar rapidamente o ganho em velocidade. código modular pode ser trocado por uma implementação melhorada, de modo que o resultado líquido é um grande ganho.

Sua milhagem pode variar, e depende claramente do aplicativo que você está desenvolvendo.

Uma coisa que deve ser observado é que o custo da chamada de função virtual pode variar de uma plataforma para outra. Em consoles eles podem ser mais visíveis, como meio de chamada normalmente vtable um cache miss e pode estragar previsão de desvios.

Note que a herança múltipla bloats a instância do objeto com vários ponteiros vtable. Com G ++ em x86, se sua classe tem um método virtual e nenhuma classe base, você tem um ponteiro para vtable. Se você tem uma classe base com métodos virtuais, você ainda tem um ponteiro para vtable. Se você tem duas classes de base com métodos virtuais, você tem dois ponteiros vtable em cada instância .

Assim, com herança múltipla (que é o que as interfaces de execução em C ++ é), você paga classes base vezes o tamanho ponteiro no tamanho do objeto instância. O aumento no consumo de memória pode ter implicações indiretas de desempenho.

Usando classes base abstratas em C ++ geralmente exige o uso de uma tabela de função virtual, todas as chamadas de interface vão ser olhou para cima por aquela mesa. O custo é minúsculo em comparação com uma chamada de função cru, para ter certeza que você precisa estar indo mais rápido do que antes de se preocupar com isso.

A única diferença principal que eu conheço é que, desde que você não está usando uma classe concreta, inlining é (muito?) Mais difícil de fazer.

A única coisa que posso pensar é que os métodos virtuais são um pouco mais lento para chamar do que os métodos não-virtuais, porque a chamada tem que passar pelo virtual tabela de métodos .

No entanto, esta é uma razão ruim para estragar seu design. Se precisar de mais desempenho, use um servidor mais rápido.

Como para qualquer classe que contém uma função virtual, uma vtable é usado. Obviamente, invocando um método através de um mecanismo de envio como uma vtable é mais lento do que uma chamada direta, mas na maioria dos casos, você pode viver com isso.

Sim, mas nada digno de nota ao meu conhecimento. O acerto de desempenho é por causa de 'engano' você tem em cada chamada de método.

No entanto, ele realmente depende do compilador que você está usando uma vez que alguns compiladores não são capazes para inline as chamadas de método, dentro das classes que herdam a classe base abstrata.

Se você quer ter certeza de que você deve executar seus próprios testes.

Sim, há uma penalidade. Algo que pode melhorar o desempenho da sua plataforma é a utilização de uma classe não-abstrata sem funções virtuais. Em seguida, use um ponteiro função de membro para a sua função não-virtual.

Eu sei que é um ponto de vista incomum, mas mesmo mencionar este assunto me faz suspeitar que você está colocando demasiada pensamento na estrutura de classes. Já vi muitos sistemas que eu tive muitos "níveis de abstração", e que só fez propenso a problemas de desempenho graves, não devido o custo de chamadas de método, mas devido à tendência para fazer chamadas desnecessárias. Se isso acontecer ao longo de vários níveis, é um assassino. uma olhada

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