Depuração, Testes e Otimização de Desempenho
Descreva técnicas comuns de depuração em C++. Como você aborda um bug difícil de reproduzir?
Resposta:
Técnicas comuns incluem o uso de um depurador (breakpoints, step-through), logging e verificações de asserção (assert). Para bugs difíceis de reproduzir, eu tentaria reduzir o escopo, adicionar logging extensivo, usar breakpoints condicionais e considerar técnicas como bisseção ou sanitizadores de memória (ASan, MSan).
Qual é o propósito de assert() em C++? Quando você deve usá-lo em vez de lançar uma exceção?
Resposta:
assert() é usado para depuração para verificar condições que devem ser sempre verdadeiras. Se a condição for falsa, ele termina o programa. Use assert() para erros de lógica interna que indicam um bug, e exceções para erros de tempo de execução recuperáveis que o código externo pode tratar.
Explique o conceito de teste unitário (unit testing). Quais são alguns frameworks populares de teste unitário em C++?
Resposta:
O teste unitário envolve testar componentes ou funções individuais de um programa isoladamente para garantir que funcionem como esperado. Ele ajuda a capturar bugs precocemente e facilita a refatoração. Frameworks populares de C++ incluem Google Test (GTest), Catch2 e Boost.Test.
Como você identifica gargalos de desempenho em uma aplicação C++?
Resposta:
Eu usaria um profiler (por exemplo, Callgrind do Valgrind, perf, Google Perftools) para identificar "hot spots" no código, como funções que consomem mais tempo de CPU ou memória. Analisar grafos de chamadas e cache misses também ajuda a identificar gargalos.
Qual é a diferença entre um build de release e um build de debug em C++? Por que essa distinção é importante para o desempenho?
Resposta:
Um build de debug inclui símbolos de depuração e desabilita otimizações, tornando a depuração mais fácil, mas mais lenta. Um build de release habilita otimizações do compilador e omite símbolos de depuração, resultando em executáveis mais rápidos e menores. Essa distinção é crucial porque as medições de desempenho devem ser sempre feitas em builds de release.
Cite algumas técnicas comuns de otimização de desempenho em C++ no nível do código.
Resposta:
Técnicas incluem minimizar alocações de memória, usar std::move para transferência eficiente de recursos, otimizar estruturas de dados para localidade de cache, evitar cópias desnecessárias, usar correção const e alavancar otimizações do compilador (por exemplo, loop unrolling, inlining).
O que é a 'Regra do Zero/Três/Cinco' em C++? Como ela se relaciona com o gerenciamento de recursos e potenciais implicações de desempenho?
Resposta:
Ela dita como gerenciar recursos. Regra do Zero: se não houver ponteiros brutos/recursos, os membros especiais padrão são suficientes. Regra do Três/Cinco: se você definir um destrutor, construtor de cópia ou operador de atribuição de cópia, você provavelmente precisará definir todos os três (ou cinco, incluindo construtor/atribuição de movimentação). Isso evita vazamentos de recursos e garante cópias profundas corretas, o que pode impactar o desempenho se não for tratado eficientemente (por exemplo, cópias excessivas).
Como a correção const pode contribuir para uma melhor qualidade de código e potencialmente para o desempenho?
Resposta:
A correção const ajuda a impor a imutabilidade, tornando o código mais seguro e fácil de raciocinar, prevenindo modificações acidentais. Ela também permite que o compilador realize otimizações mais agressivas, pois sabe que certos dados não mudarão, potencialmente levando a um melhor desempenho.
Explique o conceito de 'localidade de cache' (cache locality) e por que é importante para o desempenho em C++.
Resposta:
Localidade de cache refere-se à organização de padrões de acesso a dados para maximizar acertos de cache (cache hits). CPUs modernas são muito mais rápidas que a memória principal, então acessar dados que já estão no cache da CPU é significativamente mais rápido. Boa localidade de cache (temporal e espacial) reduz a latência de acesso à memória, levando a melhorias substanciais de desempenho.
Quando você usaria um analisador estático (static analyzer) no desenvolvimento C++, e quais benefícios ele oferece?
Resposta:
Eu usaria um analisador estático (por exemplo, Clang-Tidy, Cppcheck) cedo e regularmente no ciclo de desenvolvimento. Ele ajuda a identificar bugs potenciais, violações de padrões de codificação e problemas de design sem executar o código, melhorando a qualidade do código, a manutenibilidade e prevenindo erros em tempo de execução.