¿Cuánto más rápido es C++ que C#?
¿O ahora es al revés?
Por lo que he oído, hay algunas áreas en las que C# demuestra ser más rápido que C++, pero nunca he tenido el valor de probarlo por mí mismo.
Pensé que alguno de ustedes podría explicar estas diferencias en detalle o indicarme el lugar correcto para obtener información al respecto.
No existe una razón estricta por la cual un lenguaje basado en código de bytes como C# o Java que tiene un JIT no pueda ser tan rápido como el código C++. Sin embargo, el código C++ ha sido mucho más rápido durante mucho tiempo y hoy en día sigue siéndolo en muchos casos. Esto se debe principalmente a que las optimizaciones JIT más avanzadas son complicadas de implementar, y las realmente interesantes recién están llegando.
Entonces C++ es más rápido, en muchos casos. Pero esto es sólo una parte de la respuesta. Los casos en los que C++ es realmente más rápido son programas altamente optimizados, donde los programadores expertos optimizaron minuciosamente el código. Esto no sólo lleva mucho tiempo (y por lo tanto es caro), sino que también suele generar errores debido a optimizaciones excesivas.
Por otro lado, el código en lenguajes interpretados se vuelve más rápido en versiones posteriores del tiempo de ejecución (.NET CLR o Java VM), sin que usted haga nada. Y hay muchas optimizaciones útiles que los compiladores JIT pueden hacer y que son simplemente imposibles en lenguajes con punteros. Además, algunos argumentan que la recolección de basura generalmente debería ser tan rápida o más rápida que la administración manual de la memoria, y en muchos casos lo es. Generalmente puedes implementar y lograr todo esto en C++ o C, pero será mucho más complicado y propenso a errores.
Como dijo Donald Knuth, "la optimización prematura es la raíz de todos los males". Si realmente está seguro de que su aplicación consistirá principalmente en aritmética muy crítica para el rendimiento, y que será el cuello de botella, y ciertamente será más rápida en C++, y está seguro de que C++ no entrará en conflicto con sus otras requisitos, opte por C++. En cualquier otro caso, concéntrese primero en implementar su aplicación correctamente en el idioma que más le convenga, luego encuentre cuellos de botella en el rendimiento si funciona demasiado lento y luego piense en cómo optimizar el código. En el peor de los casos, es posible que necesite llamar al código C a través de una interfaz de función externa, por lo que aún tendrá la capacidad de escribir partes críticas en un lenguaje de nivel inferior.
Tenga en cuenta que es relativamente fácil optimizar un programa correcto, pero mucho más difícil corregir un programa optimizado.
Es imposible dar porcentajes reales de ventajas de velocidad, depende en gran medida de su código. En muchos casos, la implementación del lenguaje de programación ni siquiera es el cuello de botella. Tome los puntos de referencia en http://benchmarksgame.alioth.debian.org/ con mucho escepticismo, ya que prueban en gran medida el código aritmético, que probablemente no se parece en nada a su código.
Voy a comenzar por no estar de acuerdo con parte de la respuesta aceptada (y bien votada) a esta pregunta al afirmar:
En realidad, existen muchas razones por las que el código JITted se ejecutará más lento que un programa C++ (u otro lenguaje sin sobrecarga de tiempo de ejecución) correctamente optimizado, entre las que se incluyen:
Los ciclos de cómputo dedicados al código JITting en tiempo de ejecución no están, por definición, disponibles para su uso en la ejecución del programa.
cualquier ruta activa en JITter competirá con su código por el caché de instrucciones y datos en la CPU. Sabemos que el caché domina cuando se trata de rendimiento y los lenguajes nativos como C++ no tienen este tipo de competencia, por diseño.
El presupuesto de tiempo de un optimizador en tiempo de ejecución es necesariamente mucho más limitado que el de un optimizador en tiempo de compilación (como señaló otro comentarista)
En pocas palabras: en última instancia, es casi seguro que podrá crear una implementación más rápida en C++ que en C# .
Ahora bien, dicho esto, no se puede cuantificar realmente cuánto más rápido , ya que hay demasiadas variables: la tarea, el dominio del problema, el hardware, la calidad de las implementaciones y muchos otros factores. Habrá realizado pruebas en su escenario para determinar la diferencia en el rendimiento y luego decidirá si vale la pena el esfuerzo y la complejidad adicionales.
Este es un tema muy largo y complejo, pero creo que vale la pena mencionar para completar que el optimizador de tiempo de ejecución de C# es excelente y es capaz de realizar ciertas optimizaciones dinámicas en tiempo de ejecución que simplemente no están disponibles para C++ con su tiempo de compilación ( estático) optimizador. Incluso con esto, la ventaja suele estar profundamente en el tribunal de la aplicación nativa, pero el optimizador dinámico es la razón del calificativo " casi con certeza" dado anteriormente.
--
En términos de desempeño relativo, también me molestaron las cifras y discusiones que vi en algunas otras respuestas, por lo que pensé en intervenir y, al mismo tiempo, brindar algo de apoyo a las declaraciones que hice anteriormente.
Una gran parte del problema con esos puntos de referencia es que no se puede escribir código C++ como si estuviera escribiendo C# y esperar obtener resultados representativos (por ejemplo, realizar miles de asignaciones de memoria en C++ le dará números terribles).
En cambio, escribí un código C++ un poco más idiomático y lo comparé con el código C# que proporcionó @Wiory. Los dos cambios principales que hice en el código C++ fueron:
vector usado::reserva()
aplanó la matriz 2d a 1d para lograr una mejor localidad de caché (bloque contiguo)
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);
}
Tiempo de ejecución (lanzamiento): Inicio: 124 ms, Llenado: 165 ms
C ++ 14 (sonido metálico 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);
}
Tiempo de ejecución (liberación): Inicio: 398 µs (sí, son microsegundos), Llenado: 152 ms
Tiempos totales de ejecución: C#: 289 ms, C++ 152 ms (aproximadamente un 90 % más rápido)
Observaciones
Cambiar la implementación de C# a la misma implementación de matriz 1d produjo Init: 40ms, Fill: 171ms, Total: 211ms ( C++ todavía era casi un 40% más rápido ).
Es mucho más difícil diseñar y escribir código "rápido" en C++ que escribir código "normal" en cualquiera de los idiomas.
Es (quizás) sorprendentemente fácil obtener un rendimiento deficiente en C++; Lo vimos con el rendimiento de los vectores sin reservas. Y hay muchos obstáculos como este.
El rendimiento de C# es bastante sorprendente si se considera todo lo que sucede en tiempo de ejecución. Y ese rendimiento es comparativamente de fácil acceso.
Más datos anecdóticos que comparan el rendimiento de C++ y C#: https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore
La conclusión es que C++ le brinda mucho más control sobre el rendimiento. ¿Quieres utilizar un puntero? ¿Una referencia? ¿Memoria de pila? ¿Montón? ¿Polimorfismo dinámico o eliminar la sobrecarga de tiempo de ejecución de una vtable con polimorfismo estático (a través de plantillas/CRTP)? En C++ tienes que... es decir, tomar todas estas decisiones (y más) tú mismo, idealmente para que tu solución aborde mejor el problema que estás abordando.
Pregúntese si realmente quiere o necesita ese control, porque incluso en el ejemplo trivial anterior, puede ver que, aunque hay una mejora significativa en el rendimiento, se requiere una inversión más profunda para acceder.
Son cinco naranjas más rápido. O mejor dicho: no puede haber una respuesta general (correcta). C++ es un lenguaje compilado estáticamente (pero también hay optimización guiada por perfiles), C# se ejecuta con la ayuda de un compilador JIT. Hay tantas diferencias que preguntas como “cuánto más rápido” no pueden responderse, ni siquiera dando órdenes de magnitud.
En mi experiencia (y he trabajado mucho con ambos lenguajes), el principal problema de C# comparado con C++ es el alto consumo de memoria, y no he encontrado una buena manera de controlarlo. Fue el consumo de memoria lo que eventualmente ralentizaría el software .NET.
Otro factor es que el compilador JIT no puede permitirse demasiado tiempo para realizar optimizaciones avanzadas, porque se ejecuta en tiempo de ejecución y el usuario final lo notaría si lleva demasiado tiempo. Por otro lado, un compilador de C++ tiene todo el tiempo que necesita para realizar optimizaciones en tiempo de compilación. Este factor es mucho menos significativo que el consumo de memoria, en mi humilde opinión.
Un escenario particular en el que C++ todavía tiene la ventaja (y lo tendrá en los años venideros) se produce cuando las decisiones polimórficas se pueden predeterminar en el momento de la compilación.
Generalmente, la encapsulación y la toma de decisiones diferida son algo bueno porque hace que el código sea más dinámico, más fácil de adaptar a los requisitos cambiantes y más fácil de usar como marco. Es por eso que la programación orientada a objetos en C# es muy productiva y puede generalizarse bajo el término "generalización". Desafortunadamente, este tipo particular de generalización tiene un costo en tiempo de ejecución.
Por lo general, este costo no es sustancial, pero hay aplicaciones en las que la sobrecarga de las llamadas a métodos virtuales y la creación de objetos puede marcar la diferencia (especialmente porque los métodos virtuales impiden otras optimizaciones, como la inserción de llamadas a métodos). Aquí es donde C++ tiene una gran ventaja porque puedes usar plantillas para lograr un tipo diferente de generalización que no tiene impacto en el tiempo de ejecución pero que no es necesariamente menos polimórfico que la programación orientada a objetos. De hecho, todos los mecanismos que constituyen la programación orientada a objetos se pueden modelar utilizando únicamente técnicas de plantilla y resolución en tiempo de compilación.
En tales casos (y es cierto que a menudo están restringidos a dominios problemáticos especiales), C++ gana frente a C# y lenguajes comparables.