Começarei discordando de parte da resposta aceita (e bem votada) a esta pergunta, afirmando:
Na verdade, existem várias razões pelas quais o código JITted será executado mais lentamente que um programa C ++ (ou outro idioma sem sobrecarga de tempo de execução) otimizado corretamente, incluindo:
os ciclos de computação gastos no código JITting em tempo de execução são, por definição, indisponíveis para uso na execução do programa.
quaisquer caminhos ativos no JITter estarão competindo com seu código por instruções e cache de dados na CPU. Sabemos que o cache domina quando se trata de desempenho e idiomas nativos como C ++ não têm esse tipo de disputa, por definição.
o orçamento de tempo de um otimizador de tempo de execução é necessariamente muito mais restrito do que o de um otimizador de tempo de compilação (como outro comentarista apontou)
Bottom line: Finalmente, você irá quase certamente ser capaz de criar uma implementação mais rápida em C ++ do que você poderia, em C # .
Agora, com isso dito, o quão mais rápido realmente não é quantificável, pois há muitas variáveis: tarefa, domínio do problema, hardware, qualidade das implementações e muitos outros fatores. Você executará testes no seu cenário para determinar a diferença de desempenho e depois decidir se vale a pena o esforço e a complexidade adicionais.
Este é um tópico muito longo e complexo, mas acho que vale a pena mencionar por uma questão de integridade que o otimizador de tempo de execução do C # é excelente e é capaz de executar certas otimizações dinâmicas em tempo de execução que simplesmente não estão disponíveis para o C ++ com seu tempo de compilação ( estático) otimizador. Mesmo com isso, a vantagem ainda é tipicamente profunda no tribunal do aplicativo nativo, mas o otimizador dinâmico é a razão do " quase qualificador certamente" fornecido acima.
-
Em termos de desempenho relativo, também fiquei perturbado com os números e discussões que vi em outras respostas, por isso pensei em dar um toque e, ao mesmo tempo, fornecer algum suporte para as declarações que fiz acima.
Uma grande parte do problema com esses benchmarks é que você não pode escrever código C ++ como se estivesse escrevendo C # e espera obter resultados representativos (por exemplo, executar milhares de alocações de memória em C ++ dará números terríveis).
Em vez disso, escrevi um código C ++ um pouco mais idiomático e comparei com o código C # @Wiory fornecido. As duas principais alterações que fiz no código C ++ foram:
1) vetor usado :: reserve ()
2) achatou a matriz 2d para 1d para alcançar melhor localidade do cache (bloco contíguo)
C # (.NET 4.6.1)
private static void TestArray()
{
const int rows = 5000;
const int columns = 9000;
DateTime t1 = System.DateTime.Now;
double[][] arr = new double[rows][];
for (int i = 0; i < rows; i++)
arr[i] = new double[columns];
DateTime t2 = System.DateTime.Now;
Console.WriteLine(t2 - t1);
t1 = System.DateTime.Now;
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
arr[i][j] = i;
t2 = System.DateTime.Now;
Console.WriteLine(t2 - t1);
}
Tempo de execução (versão): Init: 124ms, Fill: 165ms
C ++ 14 (Clang v3.8 / C2)
#include <iostream>
#include <vector>
auto TestSuite::ColMajorArray()
{
constexpr size_t ROWS = 5000;
constexpr size_t COLS = 9000;
auto initStart = std::chrono::steady_clock::now();
auto arr = std::vector<double>();
arr.reserve(ROWS * COLS);
auto initFinish = std::chrono::steady_clock::now();
auto initTime = std::chrono::duration_cast<std::chrono::microseconds>(initFinish - initStart);
auto fillStart = std::chrono::steady_clock::now();
for(auto i = 0, r = 0; r < ROWS; ++r)
{
for (auto c = 0; c < COLS; ++c)
{
arr[i++] = static_cast<double>(r * c);
}
}
auto fillFinish = std::chrono::steady_clock::now();
auto fillTime = std::chrono::duration_cast<std::chrono::milliseconds>(fillFinish - fillStart);
return std::make_pair(initTime, fillTime);
}
Tempo de execução (versão): Init: 398µs (sim, isso é microssegundos), Fill: 152ms
Tempo total de execução: C #: 289ms, C ++ 152ms (aproximadamente 90% mais rápido)
Observações
Alterar a implementação do C # para a mesma implementação da matriz 1d resultou em Init: 40ms, Fill: 171ms, Total: 211ms (o C ++ ainda era quase 40% mais rápido ).
É muito mais difícil projetar e escrever código "rápido" em C ++ do que escrever código "regular" em qualquer idioma.
É (talvez) incrivelmente fácil obter um desempenho ruim em C ++; vimos isso com desempenho de vetores não reservados. E há muitas armadilhas como essa.
O desempenho do C # é bastante surpreendente quando você considera tudo o que está acontecendo no tempo de execução. E esse desempenho é comparativamente fácil de acessar.
Mais dados anedóticos comparando o desempenho de C ++ e C #: https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore
A conclusão é que o C ++ oferece muito mais controle sobre o desempenho. Deseja usar um ponteiro? Uma referência? Pilha de memória? Montão? Polimorfismo dinâmico ou eliminar a sobrecarga de tempo de execução de uma tabela com polimorfismo estático (via templates / CRTP)? Em C ++, você precisa ... er, chegar a fazer todas essas escolhas (e mais) você mesmo, idealmente, para que sua solução resolva melhor o problema que você está enfrentando.
Pergunte a si mesmo se você realmente deseja ou precisa desse controle, porque mesmo para o exemplo trivial acima, você pode ver que, embora haja uma melhoria significativa no desempenho, é necessário um investimento mais profundo para acessar.