Devo pensar em código de máquina compilado quando escrevo meu código?

20

Por exemplo, eu tenho o seguinte código:

auto z = [](int x) -> int {
    if (x > 0) {
        switch (x) {
            case 2: return 5;
            case 3: return 6;
            default: return 1;
            }
        }
    return 0;
    };

E depois eu chamo isso várias vezes. No código asm, vejo chamadas externas com o lambda .... algo ... Não está sendo fácil ler e acho que também pode causar desempenho. Então, talvez eu ganhe na metaprogramação, mas perco na depuração e no desempenho do asm? Devo evitar recursos modernos de linguagem, macros e outros aspectos de meta programação para garantir a simplicidade de desempenho e depuração?

cnd
fonte
1
Dependendo da versão do compilador e de sua biblioteca padrão empacotada, o lambda pode realmente ser implementado ineficientemente. Veja esta pergunta no Stackoverflow. No entanto, a responsabilidade pelo aprimoramento deve recair sobre o fornecedor do compilador.
Rwong
15
Você não precisa depurar o código do assembly, a menos que esteja no caminho crítico do desempenho. Além disso, "código limpo"! = "Boas performances".
BЈовић
Corrija seu recuo, por favor. Tentei fazer isso, mas parece que você não pode editar apenas espaços em branco.
Christoffer Hammarström
3
@ Heather: Você parece estar usando o estilo Ratliff , que eu nunca vi antes e acho difícil de ler. É certamente um dos menos conhecidos. Minha impressão foi de que você não tinha recuado adequadamente. Deixa pra lá, se você acha legível, eu simplesmente discordo.
Christoffer Hammarström
1
O ifé completamente redundante no código de exemplo e, embora o compilador provavelmente entenda que não há razão para tentar uma previsão de ramificação incorreta.
dmckee

Respostas:

59

Devo pensar em código de máquina compilado quando escrevo meu código?

Não , não quando você escreve seu código pela primeira vez e não sofre de problemas reais e mensuráveis ​​de desempenho. Para a maioria das tarefas, esse é o caso padrão. Pensar muito cedo em otimização é chamado de "otimização prematura", e há boas razões pelas quais D. Knuth chamou isso de "a raiz de todo mal" .

Sim , quando você mede um gargalo de desempenho real e comprovável e identifica essa construção lambda específica como a causa raiz. Nesse caso, pode ser uma boa idéia lembrar a "lei das abstrações com vazamentos" de Joel Spolsky e pensar sobre o que pode acontecer no nível asm. Mas tenha cuidado, você pode se surpreender com o quão pequeno será o aumento de desempenho ao substituir uma construção lambda por uma construção de linguagem "não tão moderna" (pelo menos ao usar um compilador C ++ decente).

Doc Brown
fonte
2
+1 Conciso, preciso e fácil de seguir, por Doc habitual, feliz por termos você por aqui.
Jimmy Hoffa
Resposta concordada e muito clara.
CND
8

A escolha entre lambda e functor-class é uma troca.

O ganho do lambda é principalmente sintático, minimizando a quantidade de clichê e permitindo que o código conceitualmente relacionado seja escrito em linha, dentro da função que o utilizará (imediatamente ou mais tarde).

Em termos de desempenho, isso não é pior do que uma classe functor , que é uma estrutura ou classe C ++ que contém um único "método". De fato, os compiladores tratam o lambda de maneira diferente da classe de functor gerada por compilador nos bastidores.

// define the functor method somewhere
struct some_computer_generated_gibberish_0123456789
{
    int operator() (int x) const
    {
        if (x == 2) return 5;
        if (x == 3) return 6;
        return 0;
    }
};

// make a call
some_computer_generated_gibberish_0123456789 an_instance_of_0123456789;
int outputValue = an_instance_of_0123456789(inputValue);

No seu exemplo de código, em termos de desempenho, não é diferente de uma chamada de função, porque essa classe de functor não possui estado (porque possui uma cláusula de captura vazia), exigindo, portanto, nenhuma alocação, construtor ou destruição.

int some_computer_generated_gibberish_0123456789_method_more_gibberish(int x)
{
    if (...) return ...;
    return ...;
}

Depurar qualquer código C ++ não trivial usando um desmontador sempre foi uma tarefa difícil. Isso é verdade com ou sem o uso de lambda. Isso é causado pela sofisticada otimização de código do compilador C ++, que resultou na reordenação, intercalação e eliminação de código morto.

O aspecto de desconfiança de nomes é um pouco desagradável, e o suporte ao depurador para lambda ainda está em sua infância . Só podemos esperar que o suporte ao depurador melhore com o tempo.

Atualmente, a melhor maneira de depurar o código lambda é usar um depurador que suporte a configuração de pontos de interrupção no nível do código-fonte, ou seja, especificando o nome do arquivo de origem e o número da linha.

rwong
fonte
3

Para adicionar à resposta do @DocBrown, lembre-se de que hoje em dia as CPUs são baratas, mas a mão-de-obra é cara.

No custo geral de um programa, o hardware geralmente é trivial em comparação com o custo de manutenção, que é de longe a parte mais cara de um projeto típico (até mais do que seu desenvolvimento).

Portanto, seu código precisa otimizar a manutenção acima de tudo, exceto quando o desempenho é crítico (e mesmo assim a manutenção precisa ser considerada).

Paddy Landau
fonte
Apenas parcialmente verdade. Se o seu código executar O (n ^ 2) (quadrático) e você puder fazer algo melhor dizer O (log (n)) (logarítmico), o hardware nunca terá um aumento de desempenho muito superior à alteração do código. No caso especificado pelo pôster original, isso é muito improvável.
gnash117
@ gnash117 - sim, você está certo se o código deve ser executado várias vezes; Obrigado por apontar isto. Nesses casos, a documentação clara do código o manterá em manutenção, permitindo a melhoria do desempenho.
Paddy Landau
"trabalho é caro" - Correto. O tempo do seu cliente é muito importante e geralmente caro.
Cerad 03/08/13