c - rapidamente - los 10 idiomas mas rapidos del mundo
¿Por qué C es tan rápido y por qué otros idiomas no son tan rápidos o más rápidos? (30)
¿Qué es lo que impide que otros idiomas puedan compilarse en binarios que se ejecutan tan rápido como C?
Nada. Los lenguajes modernos como Java o .NET están orientados más a la productividad del programador que al rendimiento. El hardware es barato hoy en día. Además, la compilación a la representación intermedia ofrece muchos beneficios como seguridad, portabilidad, etc. CLR .NET puede aprovechar diferentes hardware; por ejemplo, no necesita optimizar / recompilar manualmente el programa para usar el conjunto de instrucciones SSE.
Al escuchar el podcast StackOverflow, el jab sigue apareciendo que los "programadores reales" escriben en C, y que C es mucho más rápido porque está "cerca de la máquina". Dejando la afirmación anterior para otra publicación, ¿qué tiene de especial C que le permita ser más rápido que otros idiomas? O dicho de otra manera: ¿qué es lo que impide que otros idiomas puedan compilarse en binarios que se ejecutan tan rápido como C?
C ++ es más rápido en promedio (ya que inicialmente era un super conjunto de C). Sin embargo, para puntos de referencia específicos, a menudo hay otro idioma que es más rápido.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/
fannjuch-redux
fue el más rápido en Scala
n-body
y fasta
fueron más rápidos en Ada.
spectral-norm
fue la más rápida en Fortran.
reverse-complement
, mandelbrot
y pidigits
fueron los más rápidos en ATS.
regex-dna
fue el más rápido en JavaScript.
chameneou-redux
fue el más rápido es Java 7.
thread-ring
fue el más rápido en Haskell.
El resto de los puntos de referencia fueron los más rápidos en C o C ++.
C no siempre es más rápido.
C es más lento que, por ejemplo, el moderno Fortran.
C es a menudo más lento que Java para algunas cosas. (Especialmente después de que el compilador JIT haya probado su código)
C permite que el aliasing de punteros ocurra, lo que significa que no son posibles algunas buenas optimizaciones. En particular, cuando tiene varias unidades de ejecución, esto provoca que los datos se detengan. Ay.
La suposición de que la aritmética de punteros funciona realmente causa un rendimiento lento e inflado en algunas familias de CPU (¡PIC en particular!) Solía chupar la grande en x86 segmentado.
Básicamente, cuando obtienes una unidad vectorial o un compilador de paralelización, C apesta y Fortran moderno corre más rápido.
Los trucos del programador de C como thunk (modificación del ejecutable sobre la marcha) causan paradas de captación previa de la CPU.
Tienes la deriva?
Y nuestro buen amigo, el x86, ejecuta un conjunto de instrucciones que en estos días guarda poca relación con la arquitectura de la CPU real. Registros ocultos, optimizadores de almacenamiento de carga, todo en la CPU. Entonces C está cerca del metal virtual. El verdadero metal, Intel no te deja ver. (Históricamente, las CPU de VLIW fueron un poco revueltas, quizás no sea tan malo).
Si programa en C en un DSP de alto rendimiento (¿quizás un DSP de TI?), El compilador tiene que hacer algunas cosas difíciles para desenrollar la C a través de las múltiples unidades de ejecución paralelas. Entonces, en ese caso, C no está cerca del metal, pero está cerca del compilador, lo que hará la optimización completa del programa. Extraño.
Y, finalmente, algunas CPU (www.ajile.com) ejecutan códigos de byte de Java en hardware. C sería un PITA para usar en esa CPU.
En su mayor parte, cada instrucción C corresponde a muy pocas instrucciones del ensamblador. Básicamente, está escribiendo código de máquina de nivel superior, por lo que tiene control sobre casi todo lo que hace el procesador. Muchos otros lenguajes compilados, como C ++, tienen muchas instrucciones de apariencia simple que pueden convertirse en mucho más código del que crees (funciones virtuales, constructores de copias, etc.) y los lenguajes interpretados como Java o Ruby tienen otra capa de Instrucciones que nunca ves: la máquina virtual o el intérprete.
Esto es en realidad un poco de una falsedad perpetuada. Si bien es cierto que los programas de C con frecuencia son más rápidos, esto no siempre es así, especialmente si el programador de C no es muy bueno.
Un gran agujero deslumbrante que la gente tiende a olvidar es cuando el programa tiene que bloquear para algún tipo de IO, como la entrada del usuario en cualquier programa GUI. En estos casos, no importa el idioma que use, ya que está limitado por la velocidad a la que pueden ingresar los datos en lugar de qué tan rápido puede procesarlos. En este caso, no importa mucho si está utilizando C, Java, C # o incluso Perl; Usted simplemente no puede ir más rápido de lo que los datos pueden entrar.
La otra cosa importante es que usar la recolección de basura y no usar los punteros adecuados permite que la máquina virtual realice una serie de optimizaciones que no están disponibles en otros idiomas. Por ejemplo, la JVM es capaz de mover objetos alrededor del montón para desfragmentarlo. Esto hace que las asignaciones futuras sean mucho más rápidas, ya que el siguiente índice puede usarse simplemente en lugar de buscarlo en una tabla. Las JVM modernas tampoco tienen que desasignar la memoria; en su lugar, simplemente mueven los objetos vivos cuando se recuperan esencialmente de forma gratuita.
Esto también muestra un punto interesante sobre C y más aún en C ++. Hay algo así como una filosofía de diseño de "Si no la necesitas, no la pagas". El problema es que si lo quieres, terminas pagando por ello. Por ejemplo, la implementación de vtable en Java tiende a ser mucho mejor que las implementaciones de C ++, por lo que las llamadas a funciones virtuales son mucho más rápidas. Por otro lado, no tiene más remedio que usar funciones virtuales en Java y aún cuestan algo, pero en los programas que usan muchas funciones virtuales, el costo reducido se suma.
Hay muchas preguntas ahí, la mayoría de las cuales no estoy calificado para responder. Pero para este último:
¿Qué es lo que impide que otros idiomas puedan compilarse en binarios que se ejecutan tan rápido como C?
En una palabra, abstracción.
C es solo uno o dos niveles de abstracción fuera del lenguaje de máquina. Java y los lenguajes .Net están a un mínimo de 3 niveles de abstracción lejos del ensamblador. No estoy seguro de Python y Ruby.
Por lo general, cuantos más juguetes de programador (tipos de datos complejos, etc.), más lejos se encuentre del lenguaje de máquina y más haya que hacer la traducción.
Me voy de aquí para allá, pero esa es la esencia básica.
Actualización ------- Hay algunos buenos comentarios en esta publicación con más detalles.
Hay un comercio que los diseñadores de C han hecho. Es decir, tomaron la decisión de poner la velocidad por encima de la seguridad. C no
- Compruebe los límites del índice de matriz
- Compruebe si hay valores de variable no inicializados
- Compruebe si hay fugas de memoria
- Compruebe si no hay referencia de puntero nulo
Cuando se indexa en una matriz, en Java toma algún método de llamada en la máquina virtual, comprobación de límites y otras comprobaciones de validez. Eso es válido y absolutamente correcto , porque agrega seguridad donde es debido. Pero en C, incluso las cosas bastante triviales no se ponen a salvo. Por ejemplo, C no requiere que memcpy compruebe si las regiones para copiar se superponen. No está diseñado como un lenguaje para programar una gran aplicación empresarial.
Pero estas decisiones de diseño no son errores en el lenguaje C. Son de diseño, ya que permite que los compiladores y escritores de bibliotecas obtengan todo el rendimiento de la computadora. Aquí está el espíritu de C como lo explica el documento de C :
El código C puede ser no portátil. Aunque se esforzó por darles a los programadores la oportunidad de escribir programas verdaderamente portátiles, el Comité no quiso obligar a los programadores a escribir de manera portátil, para impedir el uso de C como un "ensamblador de alto nivel": la capacidad de escribir en máquinas específicas El código es uno de los puntos fuertes de C.
Mantenga el espíritu de C. El Comité se mantuvo como un objetivo importante para preservar el espíritu tradicional de C. Hay muchas facetas del espíritu de C, pero la esencia es un sentimiento de la comunidad de los principios subyacentes en los que se basa el lenguaje C. Algunas de las facetas del espíritu de C se pueden resumir en frases como
- Confíe en el programador.
- No impida que el programador haga lo que se necesita hacer.
- Mantener el lenguaje pequeño y sencillo.
- Proporcionar una sola manera de hacer una operación.
- Hazlo rápido, incluso si no se garantiza que sea portátil.
El último proverbio necesita una pequeña explicación. El potencial para la generación eficiente de código es una de las fortalezas más importantes de C. Para ayudar a garantizar que no se produzca una explosión de código para lo que parece ser una operación muy simple, muchas operaciones se definen como la forma en que lo hace el hardware de la máquina objetivo en lugar de hacerlo. Una regla abstracta general. Un ejemplo de esta disposición a vivir con lo que hace la máquina se puede ver en las reglas que rigen la ampliación de los objetos char para su uso en expresiones: si los valores de los objetos char se amplían a cantidades firmadas o no firmadas, generalmente depende de qué operación de byte sea más eficiente en la máquina de destino.
Los principales factores son que es un lenguaje de tipo estático y que está compilado a código de máquina. Además, como es un lenguaje de bajo nivel, generalmente no hace nada que no se le diga.
Estos son algunos otros factores que vienen a la mente.
- Las variables no se inicializan automáticamente
- Sin límites de comprobación en matrices
- Manipulación del puntero sin marcar.
- Sin comprobación de desbordamiento de enteros
- Variables de tipo estático
- Las llamadas a funciones son estáticas (a menos que uses punteros a funciones)
- Los escritores de compiladores han tenido mucho tiempo para mejorar el código de optimización. Además, la gente programa en C con el fin de obtener el mejor rendimiento, por lo que hay presión para optimizar el código.
- Las partes de la especificación del lenguaje están definidas por la implementación, por lo que los compiladores son libres de hacer las cosas de la manera más óptima
Sin embargo, la mayoría de los lenguajes de tipo estático podrían compilarse tan rápido o más rápido que C, especialmente si pueden hacer suposiciones de que C no puede debido al aliasing de punteros, etc.
Muchas de estas respuestas dan razones válidas por las cuales C es, o no, más rápido (ya sea en general o en escenarios específicos). Es innegable que:
- Muchos otros idiomas proporcionan funciones automáticas que damos por sentado. La verificación de límites, la verificación de tipos en tiempo de ejecución y la administración automática de memoria, por ejemplo, no son gratis. Hay al menos un costo asociado con estas características, que tal vez no pensemos, ni nos demos cuenta, mientras escribimos un código que usa estas características.
- El paso de la fuente a la máquina a menudo no es tan directo en otros idiomas como en C.
- OTOH, decir que el código C compilado se ejecuta más rápido que otro código escrito en otros idiomas es una generalización que no siempre es cierta. Los contra-ejemplos son fáciles de encontrar (o idear).
A pesar de todo esto, hay algo más que he notado que, creo, afecta el rendimiento comparativo de C frente a muchos otros idiomas más que cualquier otro factor. Esto es:
Otros idiomas a menudo facilitan la escritura de código que se ejecuta más lentamente. A menudo, incluso es alentado por las filosofías de diseño del lenguaje. Corolario: es más probable que un programador en C escriba código que no realiza operaciones innecesarias.
Como ejemplo, considere un programa simple de Windows en el que se crea una única ventana principal. La versión de AC WNDCLASS[EX]
una estructura WNDCLASS[EX]
que se pasaría a RegisterClass[Ex]
, luego llamará a CreateWindow[Ex]
e ingresará un bucle de mensajes. Código altamente simplificado y abreviado a continuación:
WNDCLASS wc;
MSG msg;
wc.style = 0;
wc.lpfnWndProc = &WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "MainWndCls";
RegisterClass(&wc);
CreateWindow("MainWndCls", "", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Un programa equivalente en C # podría ser solo una línea de código:
Application.Run(new Form());
Esta única línea de código proporciona toda la funcionalidad que hicieron casi 20 líneas de código C, y agrega algunas cosas que omitimos, como la verificación de errores. La biblioteca más rica y completa (en comparación con las que se utilizan en un proyecto típico de C) hizo mucho trabajo para nosotros, liberando nuestro tiempo para escribir muchos más fragmentos de código que nos parecen cortos pero que implican muchos pasos detrás de la escena.
Pero una biblioteca rica que permite un rápido y rápido aumento de código no es realmente mi punto. Mi punto es más evidente cuando empiezas a examinar lo que realmente sucede cuando realmente se ejecuta nuestro pequeño one-liner. Para divertirse en algún momento, habilite el acceso a la fuente .NET en Visual Studio 2008 o superior, y entre en la línea de una línea simple arriba. Una de las pequeñas gemas divertidas que encontrarás es este comentario en el captador para Control.CreateParams
:
// In a typical control this is accessed ten times to create and show a control.
// It is a net memory savings, then, to maintain a copy on control.
//
if (createParams == null) {
createParams = new CreateParams();
}
Diez veces La información aproximadamente equivalente a la suma de lo que está almacenado en una estructura WNDCLASSEX
y lo que se pasa a CreateWindowEx
se recupera de la clase de Control
diez veces antes de almacenarse en una estructura WNDCLASSEX
y se pasa a RegisterClassEx
y CreateWindowEx
.
En total, el número de instrucciones ejecutadas para realizar esta tarea tan básica es de 2 a 3 órdenes de magnitud más en C # que en C. Parte de esto se debe al uso de una biblioteca rica en características, que es necesariamente generalizada, en comparación con Nuestro código C simple que hace exactamente lo que necesitamos y nada más. Pero, en parte, se debe al hecho de que la estructura modular y orientada a objetos del marco .NET se presta a muchas repeticiones de ejecución que a menudo se evitan mediante un enfoque de procedimiento.
No estoy tratando de elegir en C # o .NET Framework. Tampoco estoy diciendo que la modularización, la generalización, las características de biblioteca / lenguaje, OOP, etc. sean cosas malas . Solía hacer la mayor parte de mi desarrollo en C, más adelante en C ++, y más recientemente en C #. De manera similar, antes de C, usé mayormente ensamblaje. Y con cada paso "más alto" mi lenguaje va, escribo programas mejores, más fáciles de mantener y más sólidos en menos tiempo. Sin embargo, tienden a ejecutarse un poco más lentamente.
No creo que nadie haya mencionado el hecho de que se ha puesto mucho más esfuerzo en los compiladores de C que en cualquier otro compilador, quizás con la excepción de Java.
C es extremadamente optimizable por muchas de las razones ya mencionadas, más que casi cualquier otro idioma. Por lo tanto, si se pone la misma cantidad de esfuerzo en otros compiladores de lenguaje, es probable que C aún salga a la cabeza.
Creo que hay al menos un lenguaje candidato que con esfuerzo podría optimizarse mejor que C y, por lo tanto, podríamos ver implementaciones que produzcan binarios más rápidos. Estoy pensando en Digital Mars D porque el creador se encargó de construir un lenguaje que potencialmente podría ser mejor optimizado que C. Puede haber otros idiomas que tengan esta posibilidad. Sin embargo, no puedo imaginar que cualquier lenguaje tenga compiladores más que un poco más rápido que los mejores compiladores de C. Me encantaría estar equivocado.
Creo que la verdadera "fruta baja" estará en lenguajes diseñados para que sean fáciles de optimizar para los humanos. Un programador experto puede hacer que cualquier lenguaje vaya más rápido, pero a veces tienes que hacer cosas ridículas o usar construcciones antinaturales para que esto suceda. Aunque siempre requerirá esfuerzo, un buen lenguaje debe producir un código relativamente rápido sin tener que obsesionarse con la forma exacta en que se escribe el programa.
También es importante (al menos para mí) que el código del peor de los casos tiende a ser rápido. Existen numerosas "pruebas" en la web de que Java es tan rápido o más rápido que C, pero que se basa en ejemplos de selección de cerezas. No soy un gran fan de C, pero sé que CUALQUIER COSA que escribo en C va a funcionar bien. Con Java "probablemente" se ejecutará dentro del 15% de la velocidad, generalmente dentro del 25%, pero en algunos casos puede ser mucho peor. Cualquier caso en el que sea tan rápido o dentro de un par de porcentajes generalmente se debe a que la mayor parte del tiempo se invierte en el código de la biblioteca, que de todas formas está muy optimizado.
No es tanto que C sea rápido como el modelo de costos de C es transparente . Si un programa de C es lento, es lento de una manera obvia: ejecutando muchas declaraciones. En comparación con el costo de las operaciones en C, las operaciones de alto nivel en objetos (especialmente la reflexión) o cadenas pueden tener costos que no son obvios.
Dos lenguajes que generalmente compilan a binarios que son tan rápidos como C son Standard ML (usando el compilador MLton ) y Objective Caml . Si revisa el juego de puntos de referencia , encontrará que para algunos puntos de referencia, como los árboles binarios, la versión OCaml es más rápida que C. (No encontré ninguna entrada de MLton). Pero no se tome el tiroteo demasiado en serio; es, como dice, un juego, los resultados a menudo reflejan cuánto esfuerzo han puesto las personas en afinar el código.
No lo vi ya, así que lo diré: C tiende a ser más rápido porque casi todo lo demás está escrito en C.
Java se basa en C, Python se basa en C (o Java, .NET, etc.), Perl es, etc. El sistema operativo está escrito en C, las máquinas virtuales están escritas en C, los compiladores están escritos en C, los intérpretes están escritos en C. Algunas cosas todavía están escritas en lenguaje ensamblador, que tiende a ser incluso más rápido. Cada vez se escriben más cosas en otra cosa, que a su vez está escrita en C.
Cada declaración que escribe en otros idiomas (no ensamblado) se implementa generalmente debajo de varias declaraciones en C, que se compilan en código de máquina nativo. Dado que esos otros lenguajes tienden a existir para obtener un nivel de abstracción más alto que C, esas afirmaciones adicionales requeridas en C tienden a centrarse en agregar seguridad, agregar complejidad y proporcionar manejo de errores. Esas a menudo son cosas buenas, pero tienen un costo , y sus nombres son velocidad y tamaño .
Personalmente, he escrito en literalmente docenas de idiomas que abarcan la mayor parte del espectro disponible, y personalmente he buscado la magia que usted insinúa:
¿Cómo puedo tener mi pastel y comerlo, también? ¿Cómo puedo jugar con abstracciones de alto nivel en mi idioma favorito, y luego bajar al grano nítido de C para la velocidad?
Después de un par de años de investigación, mi respuesta es Python (en C). Es posible que desee echarle un vistazo. Por cierto, también puede bajar a Asamblea desde Python (con un poco de ayuda de una biblioteca especial).
Por otro lado, el código incorrecto se puede escribir en cualquier idioma . Por lo tanto, el código C (o ensamblado) no es automáticamente más rápido. Del mismo modo, algunos trucos de optimización pueden acercar partes del código de idioma de nivel superior al nivel de rendimiento de la C cruda. Pero, para la mayoría de las aplicaciones, su programa pasa la mayor parte del tiempo esperando en personas o hardware, por lo que la diferencia realmente no importa.
Disfrutar.
Si pasas un mes para construir algo en C que se ejecuta en 0.05 segundos, y yo me paso un día escribiendo lo mismo en Java, y se ejecuta en 0.10 segundos, ¿entonces C es realmente más rápido?
Pero para responder a su pregunta, el código C bien escrito generalmente se ejecutará más rápido que el código bien escrito en otros idiomas porque parte de la escritura del código C "bien" incluye hacer optimizaciones manuales a un nivel cercano a la máquina.
A pesar de que los compiladores son muy inteligentes, todavía no son capaces de crear de manera creativa un código que compita con los algoritmos de masaje manual (asumiendo que las "manos" pertenecen a un buen programador de C).
Editar:
Muchos comentarios están en la línea de "Escribo en C y no pienso en optimizaciones".
Pero para tomar un ejemplo específico de este post :
En Delphi podría escribir esto:
function RemoveAllAFromB(a, b: string): string;
var
before, after :string;
begin
Result := b;
if 0 < Pos(a,b) then begin
before := Copy(b,1,Pos(a,b)-Length(a));
after := Copy(b,Pos(a,b)+Length(a),Length(b));
Result := before + after;
Result := RemoveAllAFromB(a,Result); //recursive
end;
end;
y en CI escribo esto:
char *s1, *s2, *result; /* original strings and the result string */
int len1, len2; /* lengths of the strings */
for (i = 0; i < len1; i++) {
for (j = 0; j < len2; j++) {
if (s1[i] == s2[j]) {
break;
}
}
if (j == len2) { /* s1[i] is not found in s2 */
*result = s1[i];
result++; /* assuming your result array is long enough */
}
}
Pero, ¿cuántas optimizaciones hay en la versión C? Tomamos muchas decisiones sobre la implementación que no pienso en la versión de Delphi. ¿Cómo se implementa una cadena? En Delphi no lo veo. En C, he decidido que será un puntero a una matriz de enteros ASCII, que llamamos caracteres. En C, probamos la existencia del personaje de uno en uno. En Delphi, uso Pos.
Y esto es solo un pequeño ejemplo. En un programa grande, un programador en C tiene que tomar este tipo de decisiones de bajo nivel con cada pocas líneas de código. Se suma a un ejecutable hecho a mano, optimizado a mano.
Supongo que olvidaste que el lenguaje ensamblador también es un lenguaje :)
Pero en serio, los programas de C son más rápidos solo cuando el programador sabe lo que está haciendo. Puede escribir fácilmente un programa en C que se ejecute más lentamente que los programas escritos en otros idiomas que hacen el mismo trabajo.
La razón por la que C es más rápido es porque está diseñado de esta manera. Te permite hacer muchas cosas de "nivel inferior" que ayudan al compilador a optimizar el código. O, digamos, usted el programador es responsable de optimizar el código. Pero a menudo es bastante complicado y propenso a errores.
Otros lenguajes, como otros ya mencionados, se centran más en la productividad del programador. Se suele creer que el tiempo del programador es mucho más caro que el tiempo de la máquina (incluso en los viejos tiempos). Por lo tanto, tiene mucho sentido minimizar el tiempo que los programadores dedican a escribir y depurar programas en lugar del tiempo de ejecución de los programas. Para hacer eso, sacrificará un poco lo que puede hacer para que el programa sea más rápido porque muchas cosas están automatizadas.
No tome la palabra de alguien, mire el desensamblaje de C y su idioma de elección en cualquier parte crítica del rendimiento de su código. Creo que solo puede mirar en la ventana de desmontaje en tiempo de ejecución en Visual Studio para ver .Net desensamblado. Debería ser posible si es difícil para Java usar windbg, aunque si lo haces con .Net, muchos de los problemas serían los mismos.
No me gusta escribir en C si no lo necesito, pero creo que muchas de las afirmaciones que se hacen en estas respuestas, que muestran la velocidad de otros idiomas además de C, se pueden dejar de lado simplemente desmontando la misma rutina en C y en el idioma de su nivel superior de elección, especialmente si se trata de una gran cantidad de datos, como es común en las aplicaciones de rendimiento crítico. Fortran puede ser una excepción en su área de especialización, no sé. ¿Es el nivel más alto que C?
La primera vez que comparé el código JITed con el código nativo resolví todas y cada una de las preguntas sobre si el código .Net podría ejecutarse de manera comparable al código C El nivel adicional de abstracción y todos los controles de seguridad tienen un costo significativo. Probablemente se apliquen los mismos costos a Java, pero no tome mi palabra, pruébelo en algo donde el rendimiento sea crítico. (¿Alguien sabe lo suficiente sobre JITed Java para localizar un procedimiento compilado en la memoria? Ciertamente debería ser posible)
No hay mucho que sea especial acerca de C. Esa es una de las razones por las que es rápido.
Lenguajes más nuevos que admiten la recolección de basura , la escritura dinámica y otras instalaciones que facilitan la programación de programas por parte del programador.
El problema es que hay una sobrecarga de procesamiento adicional que degradará el rendimiento de la aplicación. C no tiene nada de eso, lo que significa que no hay sobrecarga, pero eso significa que el programador debe poder asignar memoria y liberarla para evitar pérdidas de memoria , y debe lidiar con la escritura estática de variables.
Dicho esto, muchos lenguajes y plataformas, como Java (con su Máquina Virtual de Java ) y .NET (con su Common Language Runtime) han mejorado el desempeño a lo largo de los años con avances tales como compilación justo a tiempo que produce código de máquina nativo de Bytecode para lograr un mayor rendimiento.
Algunos algoritmos de C ++ son más rápidos que C, y algunas implementaciones de algoritmos o patrones de diseño en otros lenguajes pueden ser más rápidos que C.
Cuando la gente dice que C es rápido y luego pasa a hablar sobre otro idioma, generalmente están usando el desempeño de C como punto de referencia.
En los buenos tiempos, había solo dos tipos de lenguajes: compilados e interpretados.
Los lenguajes compilados utilizaron un "compilador" para leer la sintaxis del lenguaje y convertirlo en un código de lenguaje ensamblador idéntico, lo que podría hacerlo directamente en la CPU. Los idiomas interpretados utilizaron un par de esquemas diferentes, pero esencialmente la sintaxis del lenguaje se convirtió en una forma intermedia, y luego se ejecutó en un "intérprete", un entorno para ejecutar el código.
Por lo tanto, en cierto sentido, había otra "capa", el intérprete, entre el código y la máquina. Y, como siempre es el caso en una computadora, más significa que se utilizan más recursos. Los intérpretes fueron más lentos, porque tuvieron que realizar más operaciones.
Más recientemente, hemos visto más lenguajes híbridos como Java, que emplean tanto un compilador como un intérprete para hacer que funcionen. Es complicado, pero un JVM es más rápido, más sofisticado y está mucho más optimizado que los intérpretes antiguos, por lo que es mucho mejor el cambio de rendimiento (a lo largo del tiempo) más cercano al código compilado directamente. Por supuesto, los compiladores más nuevos también tienen trucos de optimización más sofisticados, por lo que tienden a generar código mucho mejor de lo que solían hacerlo. Pero la mayoría de las optimizaciones, la mayoría de las veces (aunque no siempre) hacen algún tipo de compensación, de modo que no siempre son más rápidas en todas las circunstancias. Como todo lo demás, nada viene gratis, por lo que los optimizadores deben obtener su alarde de algún lugar (aunque a menudo se usa una CPU de tiempo de compilación para ahorrar CPU de tiempo de ejecución).
Volviendo a C, es un lenguaje simple, que puede compilarse en un ensamblaje bastante optimizado y luego ejecutarse directamente en la máquina de destino. En C, si incrementa un número entero, es más que probable que sea solo un paso del ensamblador en la CPU; en Java, sin embargo, podría ser mucho más que eso (y también podría incluir un poco de recolección de basura: -) C le ofrece una abstracción que está mucho más cerca de la máquina (el ensamblador es el más cercano), pero termina teniendo que trabajar mucho más para que funcione, y no es tan protegido, fácil de usar o amigable con los errores. La mayoría de los otros idiomas le ofrecen una mayor abstracción y se encargan de más detalles subyacentes, pero a cambio de su funcionalidad avanzada, requieren más recursos para ejecutarse. A medida que generaliza algunas soluciones, debe manejar una gama más amplia de computación,que a menudo requiere más recursos.
Pablo.
Incluso la diferencia entre C y C ++ a veces puede ser grande.
Cuando está asignando memoria para un objeto, invocando constructores, alineando la memoria en los límites de las palabras, etc., el programa termina con una gran cantidad de sobrecarga que se extrae del programador.
C te obliga a echar un vistazo a cada cosa que hace tu programa, generalmente con un nivel de detalle muy fino. Esto hace que sea más difícil (aunque de ninguna manera es imposible) escribir código que realice muchas tareas que no son necesarias para el objetivo inmediato en cuestión.
Entonces, donde, por ejemplo, un programa BÁSICO, usaría la palabra clave INPUT para leer una cadena STDIN y asignar automáticamente la memoria para su variable, en C el programador normalmente ya habrá asignado memoria y podrá controlar cosas como si el programa se bloquea para I / O o no, y si deja de leer la entrada después de tener la información que necesita o continúa leyendo los caracteres hasta el final de la línea.
C también realiza una comprobación de errores mucho menor que otros lenguajes, suponiendo que el programador sepa lo que está haciendo. Por lo tanto, mientras que en PHP, si declara una cadena $myStr = getInput();
y continúa con la referencia $myStr[20]
, pero la entrada solo tenía 10 caracteres, PHP detectará esto y le devolverá con seguridad una cadena en blanco. C supone que ya ha asignado suficiente memoria para retener los datos más allá del final de la cadena o que sabe qué información viene después de la cadena y está intentando hacer referencia a esa en su lugar. Estos pequeños factores tienen un gran impacto en los gastos generales en conjunto.
Sé que mucha gente lo ha dicho de una manera muy larga, pero:
C es más rápido porque hace menos (para ti).
1) Como han dicho otros, C hace menos por ti. Sin variables de inicialización, sin verificación de límites de matriz, sin gestión de memoria, etc. Esas funciones en otros idiomas cuestan la memoria y los ciclos de CPU que C no gasta.
2) Las respuestas que dicen que C es menos abstracta y, por lo tanto, más rápidas, son solo la mitad correctas, creo. Técnicamente hablando, si tenía un "compilador suficientemente avanzado" para el lenguaje X, entonces el lenguaje X podría aproximarse o ser igual a la velocidad de C. La diferencia con C es que, dado que se mapea tan obviamente (si ha tomado un curso de arquitectura) directamente al lenguaje ensamblador que incluso un compilador ingenuo puede hacer un trabajo decente. Para algo como Python, necesita un compilador muy avanzado para predecir los tipos probables de objetos y generar código de máquina al vuelo: la semántica de C es lo suficientemente simple como para que un compilador simple pueda hacerlo bien.
Con los compiladores de optimización modernos, es altamente improbable que un programa de C puro sea mucho más rápido que el código .net compilado, en todo caso. Con el aumento de la productividad que los marcos como .NET proporciona el desarrollador, puede hacer las cosas de un día que solía tomar semanas o meses en C. regulares Junto con el bajo costo de hardware en comparación con el salario de un desarrollador, es sólo MANERA más barato para escribir las cosas en un lenguaje de alto nivel y lanzar hardware a cualquier lentitud.
La razón por la que Jeff y Joel hablan de que C es el lenguaje de "programador real" es porque no hay control en C. Debes asignar tu propia memoria, desasignar esa memoria, hacer tus propios controles de límites, etc. como nuevo objeto (); No hay recolección de basura, clases, OOP, marcos de entidades, LINQ, propiedades, atributos, campos ni nada de eso. Tienes que saber cosas como aritmética de punteros y cómo desreferenciar un puntero. Y, para el caso, saber y entender qué es un puntero. Debe saber qué es un marco de pila y cuál es el puntero de instrucción. Debe conocer el modelo de memoria de la arquitectura de la CPU en la que está trabajando. Existe una gran cantidad de comprensión implícita de la arquitectura de un microordenador (generalmente elmicrocomputador en el que está trabajando) al programar en C que simplemente no está presente ni es necesario al programar en algo como C # o Java. Toda esa información se ha descargado al programador del compilador (o VM).
Dejando a un lado las técnicas avanzadas de optimización, como la optimización de los puntos calientes , los meta-algoritmos precompilados y varias formas de parallelism , la velocidad fundamental de un lenguaje se correlaciona fuertemente con la complejidad implícita detrás de las escenas requerida para soportar las operaciones Se especificará dentro de los bucles internos .
Quizás lo más obvio sea la verificación de la validez en las referencias de memoria indirectas, como la comprobación de los punteros null
y la comparación de los índices con los límites de la matriz. La mayoría de los lenguajes de alto nivel realizan estas comprobaciones de forma implícita, pero C no. Sin embargo, esto no es necesariamente una limitación fundamental de estos otros lenguajes : un compilador lo suficientemente inteligente puede ser capaz de eliminar estos controles de los bucles internos de un algoritmo a través de alguna forma de movimiento de código invariante de bucle .
La ventaja más fundamental de C (y, en una medida similar, el C ++ estrechamente relacionado) es una gran dependencia de la asignación de memoria basada en pila , que es inherentemente rápida para la asignación, desasignación y acceso. En C (y C ++), la pila de llamadas primaria se puede usar para la asignación de primitivas, matrices y agregados ( struct
/ class
).
Mientras que C ofrece la capacidad de asignar dinámicamente memoria de tamaño y duración arbitrarios (utilizando el denominado ''montón''), se evita hacerlo de forma predeterminada (en su lugar se usa la pila).
De manera tentadora, a veces es posible replicar la estrategia de asignación de memoria C en los entornos de ejecución de otros lenguajes de programación. Esto ha sido demostrado por asm.js , que permite que el código escrito en C o C ++ se traduzca en un subconjunto de JavaScript y se ejecute de forma segura en un entorno de navegador web, con una velocidad casi nativa.
Como algo aparte, otra área en la que C y C ++ eclipsan a la mayoría de los otros idiomas para la velocidad es la capacidad de integrarse perfectamente con los conjuntos de instrucciones de la máquina nativa. Un ejemplo notable de esto es la disponibilidad (compilada y dependiente de la plataforma) de los intrínsecos de SIMD que soportan la construcción de algoritmos personalizados que aprovechan el hardware de procesamiento paralelo ahora casi omnipresente, mientras se siguen utilizando las abstracciones de asignación de datos proporcionadas por el lenguaje -la asignación de registros de nivel es gestionada por el compilador).
El código de ejecución más rápido sería el código de máquina cuidadosamente elaborado a mano. El ensamblador será casi tan bueno. Ambos son de muy bajo nivel y se necesita mucha escritura de código para hacer cosas. C es un poco por encima del ensamblador. Aún tiene la capacidad de controlar las cosas a un nivel muy bajo en la máquina real, pero hay suficiente abstracción para que escribirlas sea más rápido y más fácil que el ensamblador. Otros lenguajes como C # y JAVA son aún más abstractos. Mientras que el ensamblador y el código de máquina se llaman lenguajes de bajo nivel, C # y JAVA (y muchos otros) se llaman lenguajes de alto nivel. C se llama a veces un lenguaje de nivel medio.
Es la diferencia entre automático y manual, los lenguajes de nivel superior son abstracciones así automatizadas. C / C ++ se controlan y manejan manualmente, incluso el código de verificación de errores es a veces un trabajo manual.
C y C ++ también son lenguajes compilados, lo que significa que ninguno de los que se ejecutan en todos los negocios, estos lenguajes deben ajustarse al hardware con el que trabaja, por lo que se agrega una capa adicional de gotcha. Aunque esto se está desmoronando ahora, ya que los compiladores de C / C ++ se están volviendo más comunes en todas las plataformas. Puedes hacer compilaciones cruzadas entre plataformas. Todavía no es una situación de ejecución en todas partes, básicamente le indica al compilador A que compile contra el compilador B con el mismo código de arquitectura diferente.
Los idiomas de la línea C no están destinados a ser fáciles de entender o razonar, por eso también se los denomina idiomas de sistemas. Salieron antes de toda esta tontería de abstracción de alto nivel. Esta es también la razón por la que no se utilizan para la programación web front-end. Simplemente no son adecuados para la tarea, son medios para resolver problemas complejos que no pueden resolverse con herramientas de lenguaje convencional.
Esta es la razón por la que obtienes cosas locas como (microarquitecturas, controladores, física cuántica, juegos AAA, sistemas operativos), hay cosas en las que C y C ++ están bien adaptados. La velocidad y el número crujido son las áreas principales.
He encontrado una respuesta en el enlace sobre por qué algunos idiomas son más rápidos y otros más lentos. Espero que esto aclare más sobre por qué C o C ++ es más rápido que otros. Hay otros idiomas también que son más rápidos que C, pero no podemos utilizar todos ellos Algunas explicaciones -
Una de las grandes razones por las que Fortran sigue siendo importante es porque es rápido: las rutinas de procesamiento de números escritas en Fortran tienden a ser más rápidas que las equivalentes escritas en la mayoría de los otros idiomas. Los idiomas que compiten con Fortran en este espacio, C y C ++, se utilizan porque son competitivos con este rendimiento.
Esto plantea la pregunta: ¿por qué? ¿Qué es lo que los hace rápidos a C ++ y Fortran, y por qué superan a otros lenguajes populares, como Java o Python?
Interpretación versus compilación Hay muchas formas de categorizar y definir los lenguajes de programación, de acuerdo con el estilo de programación que fomentan y las características que ofrecen. Cuando se observa el rendimiento, la mayor distinción es entre los idiomas interpretados y los compilados.
La división no es dura; más bien, hay un espectro. En un extremo, tenemos lenguajes compilados tradicionales, un grupo que incluye Fortran, C y C ++. En estos idiomas, hay una etapa de compilación discreta que traduce el código fuente de un programa a una forma ejecutable que el procesador puede usar.
Este proceso de compilación tiene varios pasos. El código fuente es analizado y analizado. Errores de codificación básicos, como errores tipográficos y errores ortográficos, pueden detectarse en este punto. El código analizado se usa para generar una representación en memoria, que también puede usarse para detectar errores; esta vez, errores semánticos, como llamar a funciones que no existen, o intentar realizar operaciones aritméticas en cadenas de texto.
Esta representación en memoria se utiliza para controlar un generador de código, la parte que produce el código ejecutable. La optimización del código, para mejorar el rendimiento del código generado, se realiza en varios momentos dentro de este proceso: las optimizaciones de alto nivel se pueden realizar en la representación del código, y las optimizaciones de nivel inferior se utilizan en la salida del generador de código.
En realidad la ejecución del código pasa más tarde. Todo el proceso de compilación se utiliza simplemente para crear algo que se puede ejecutar.
En el extremo opuesto, tenemos intérpretes. Los intérpretes incluirán una etapa de análisis similar a la del compilador, pero luego se utilizará para impulsar la ejecución directa, y el programa se ejecutará de inmediato.
El intérprete más simple tiene dentro un código ejecutable correspondiente a las diversas funciones que admite el idioma, por lo que tendrá funciones para agregar números, unir cadenas, cualquier otra cosa que tenga un idioma determinado. A medida que analiza el código, buscará la función correspondiente y la ejecutará. Las variables creadas en el programa se mantendrán en algún tipo de tabla de búsqueda que asigne sus nombres a sus datos.
El ejemplo más extremo del estilo del intérprete es algo así como un archivo por lotes o un script de shell. En estos idiomas, el código ejecutable a menudo ni siquiera está integrado en el propio intérprete, sino programas independientes y separados.
Entonces, ¿por qué esto hace una diferencia en el rendimiento? En general, cada capa de indirección reduce el rendimiento. Por ejemplo, la forma más rápida de agregar dos números es tener ambos de esos registros en el procesador y usar la instrucción de adición del procesador. Eso es lo que pueden hacer los programas compilados; pueden poner variables en registros y aprovechar las instrucciones del procesador. Pero en los programas interpretados, esa misma adición puede requerir dos búsquedas en una tabla de variables para obtener los valores para agregar, luego llamar a una función para realizar la adición. Esa función puede muy bien usar la misma instrucción del procesador que utiliza el programa compilado para realizar la adición real, pero todo el trabajo adicional antes de que se pueda usar la instrucción hace que las cosas sean más lentas.
Si quieres saber más por favor revisa la Source
Increíble ver el viejo "C / C ++ debe ser más rápido que Java porque Java se interpreta" el mito sigue vivo y pateando. Hay artículos que se remontan a algunos años , así como otros más recientes , que explican con conceptos o mediciones por qué esto simplemente no siempre es el caso .
Las implementaciones de máquinas virtuales actuales (y no solo la JVM, por cierto) pueden aprovechar la información recopilada durante la ejecución del programa para ajustar dinámicamente el código mientras se ejecuta, utilizando una variedad de técnicas:
- Procesamiento de métodos frecuentes a código de máquina,
- alineando pequeños métodos,
- ajuste de bloqueo
y una variedad de otros ajustes basados en saber lo que realmente está haciendo el código y en las características reales del entorno en el que se está ejecutando.
La falta de abstracción es lo que hace que C sea más rápido. Si escribe una declaración de salida, sabe exactamente lo que está sucediendo. Si escribe una declaración de salida en java, se compila en un archivo de clase que luego se ejecuta en una máquina virtual introduciendo una capa de abstracción. La falta de características orientadas a objetos como parte del lenguaje también aumenta su velocidad para generar menos código. Si usa C como lenguaje orientado a objetos, entonces está haciendo toda la codificación para cosas tales como clases, falta de estilo, etc. Esto significa que en lugar de hacer algo lo suficientemente generalizado para todos con la cantidad de código y el rendimiento que requiere que solo escriba lo que necesitas para hacer el trabajo
No se trata tanto del lenguaje como de las herramientas y bibliotecas. Las bibliotecas y los compiladores disponibles para C son mucho más antiguos que para los idiomas más nuevos. Se podría pensar que esto los haría más lentos, pero au contraire.
Estas bibliotecas se escribieron en un momento en que la capacidad de procesamiento y la memoria eran muy importantes. Tenían que ser escritos muy eficientemente para poder funcionar. Los desarrolladores de compiladores de C también han tenido mucho tiempo para trabajar en todo tipo de optimizaciones inteligentes para diferentes procesadores. La madurez de C y su amplia adopción constituyen una ventaja significativa sobre otros idiomas de la misma edad. También le da a C una ventaja de velocidad sobre las nuevas herramientas que no enfatizan el rendimiento en bruto tanto como C tenía que hacerlo.
Solo paso a través del código de la máquina en su IDE, y verá por qué es más rápido (si es más rápido). Se deja fuera de muchas manos. Lo más probable es que también se le pueda decir a su Cxx que lo omita, en cuyo caso debería ser el mismo.
Las optimizaciones del compilador están sobrevaloradas, al igual que casi todas las percepciones sobre la velocidad del lenguaje.
La optimización del código generado solo hace una diferencia en el código de punto de acceso, es decir, algoritmos ajustados sin llamadas a funciones (explícitas o implícitas). En cualquier otro lugar, logra muy poco.