¿Por qué C es tan rápido y por qué otros lenguajes no son tan rápidos o rápidos? [cerrado]

209

Al escuchar el podcast StackOverflow, sigue apareciendo el jab 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 permite ser más rápido que otros lenguajes? O dicho de otra manera: ¿qué impide que otros lenguajes puedan compilarse en binarios que se ejecutan tan rápido como C?

Mike C.
fuente
66
¿Puedes enumerar qué programa en particular habló sobre esto? Me encantaría oírlo.
Giovanni Galbo el
2
Realmente sorprendido de lo mal que se responde esta pregunta (la mayoría de las respuestas ignoran las diferencias fundamentales entre los idiomas compilados e interpretados, etc., sé sobre JIT yada yada yada), y cuántas personas están tomando la posición de 'defender' su idioma favorito (el niño FORTRAN necesita tomar una pastilla).
Tim Ring el
No olvides el lenguaje ensamblador. Nada es más rápido ni más compacto que los ex ensamblados ensamblados. El ensamblado es binario casi puro, por lo que es sin sesgo el lenguaje más rápido.
KKZiomek
3
C es el más rápido porque es la velocidad de la luz y la relatividad.
Conoce a Taraviya el
Por supuesto, es incorrecto que C sea el lenguaje de programa más rápido. Ningún lenguaje de programa de ningún tipo se acerca a la velocidad de FORTH. FORTH se usa para disparar bombas nucleares, es el lenguaje del programa en la mayoría de los satélites, el lenguaje principal del programa en la Estación Espacial Internacional y también en el CERN y en el ITER. Estaba comparando la velocidad entre Microsoft C (diferentes versiones) y FORTH. YAWN to C ...
Scoobeedo Cool

Respuestas:

200

No hay mucho de especial sobre C. Esa es una de las razones por las que es rápido.

Los lenguajes más nuevos que admiten la recolección de basura , el tipeo dinámico y otras instalaciones que facilitan al programador escribir programas.

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 el tipeo estático de las variables.

Dicho esto, muchos lenguajes y plataformas, como Java (con su máquina virtual Java ) y .NET (con su Common Language Runtime) han mejorado el rendimiento a lo largo de los años con eventos como la compilación justo a tiempo que produce código de máquina nativo a partir de bytecode para lograr un mayor rendimiento.

Coobird
fuente
3
la recolección de basura puede ser más rápida que la administración manual de memoria (para programas de corta duración y / o mucha memoria). GC permite una asignación simple y rápida, y el programa no pierde el tiempo repartiendo cosas.
Kornel
2
Los programas C generalmente asignan y desasignan memoria según sea necesario. Esto es ineficiente. Una buena VM asignará y desasignará en grandes fragmentos, lo que generará grandes ganancias en el rendimiento en muchos casos.
skaffman
61
No hay nada que impida que un programa en C realice la misma asignación fragmentada y recolección de basura, aparte de ser "difícil".
ephemient
Muy bien dicho, pero como le gusta a Rob Allen, C también proporciona menos abstracción que Java o .NET, lo que resulta en una menor traducción (lo que es menos cierto hoy en día debido a la compilación justo a tiempo (JIT) como usted dijo)
Gab Royer
55
PorneL, la gestión manual y las asignaciones razonables siempre superarán a cualquier sistema GC cuando se usa correctamente y se le presta mucha atención, usted tiene un conocimiento absoluto sobre sus patrones de uso, el GC no, y los sistemas GC agregan gastos generales
Ion Todirel
89

Hay un intercambio entre los diseñadores de C que han hecho. Es decir, tomaron la decisión de poner la velocidad por encima de la seguridad. C no lo hará

  • Verificar los límites del índice de la matriz
  • Verifique los valores variables no inicializados
  • Verifique si hay pérdidas de memoria
  • Verifique si la referencia de puntero es nula

Cuando indexa en una matriz, en Java se requieren algunas llamadas a métodos en la máquina virtual, verificación de límites y otras verificaciones de sanidad. Eso es válido y absolutamente bueno , porque agrega seguridad donde es debido. Pero en C, incluso las cosas bastante triviales no se ponen a salvo. Por ejemplo, C no requiere memcpy para verificar si las regiones para copiar se superponen. Está no diseñado como un lenguaje para programar una aplicación de negocio grande.

Sin embargo, estas decisiones de diseño son sin errores en el lenguaje C . Son por diseño, ya que permite a los compiladores y escritores de bibliotecas obtener todo el rendimiento de la computadora. Aquí está el espíritu de C cómo lo explica el documento de Justificación de C :

El código C puede no ser portátil. Aunque se esforzó por dar a los programadores la oportunidad de escribir programas verdaderamente portátiles, el Comité no quería obligar a los programadores a escribir de manera portátil, para evitar 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.

Mantener el espíritu de C. El Comité se mantuvo como un objetivo principal para preservar el espíritu tradicional de C. Hay muchas facetas del espíritu de C, pero la esencia es un sentimiento comunitario 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ía en el programador.
  • No evite que el programador haga lo que debe hacerse.
  • Mantenga el lenguaje pequeño y simple.
  • Proporcione solo una forma 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 el hardware de la máquina de destino en lugar de 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 gobiernan el ensanchamiento de los objetos char para su uso en expresiones: si los valores de los objetos char se amplían a cantidades con o sin signo generalmente depende de qué operación de byte es más eficiente en la máquina de destino.

Johannes Schaub - litb
fuente
51
C comprueba si hay un puntero nulo a la derecha al estrellarse violentamente :-). También ocasionalmente verifica los índices de matriz fuera de rango y las variables no inicializadas al atornillar sus marcos de pila y datos. Desafortunadamente, verifica esto en tiempo de ejecución.
paxdiablo
18
No diría que C no es seguro, lo que suena como lo que estás insinuando. Se supone que no eres un idiota. Si apuntas con un arma y te disparas en el pie, C te complacerá y te permitirá hacerlo porque asume que eres más inteligente de lo que es. Eso no es necesariamente algo malo.
Bob Somers el
19
@Bob: Exactamente. Decir que C no es seguro porque te permite hacer cosas peligrosas es como decir que un automóvil no es seguro porque te permitirá conducir por un acantilado. C es tan seguro como la persona que conduce (pero hay muchos conductores inseguros por ahí).
Robert Gamble, el
55
Bob, hacer errores como desbordamientos del búfer no significa que seas un idiota. Simplemente significa que aún eres humano. Me doy cuenta de que C y C ++ no son malos (me gustan mucho).
Johannes Schaub - litb
44
@ JohannesSchaub-litb C es perfecto para la programación de aplicaciones a gran escala. Si a uno le resulta difícil hacer que los proyectos sean más grandes que un mundo hola, entonces el problema es con el programador, no con el lenguaje ...
75

Si pasas un mes para construir algo en C que se ejecuta en 0.05 segundos, y yo 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 lenguajes porque parte de escribir el código C "bien" incluye hacer optimizaciones manuales en un nivel cercano a la máquina.

Aunque los compiladores son muy inteligentes, aún no pueden crear creativamente un código que compita con algoritmos de masaje manual (suponiendo que las "manos" pertenezcan a un buen programador en 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 esta publicación :

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 escribe 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 uno a la vez. En Delphi, uso Pos.

Y este 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 y optimizado a mano.

JosephStyons
fuente
46
Sin embargo, para ser justos, no hay mucho que tomaría un mes en C que tomaría solo un día en Java que solo lleva 0.05 segundos para ejecutarse (es decir, un programa pequeño).
dreamlax
13
He programado en C durante años y casi nunca tengo que hacer ninguno de los tipos de optimizaciones que usted sugiere. He portado una serie de programas a C (principalmente de Perl) y generalmente veo un aumento de velocidad de más de 10x y una reducción significativa en el uso de memoria sin ninguna optimización codificada a mano.
Robert Gamble, el
1
Por supuesto, hay algunas cosas que pueden tomar mucho más tiempo para programar en C debido a la falta de instalaciones existentes, por lo que es una compensación entre el rendimiento de la computadora y el rendimiento del programador (entre otras cosas), por lo que no todos programamos todo en C.
Robert Gamble, el
44
He creado programas C ++ que procesan miles de líneas de datos en menos tiempo que los programas Java o .NET pueden iniciarse. Es una de las frustraciones que tengo con los idiomas más modernos. C es ideal para programas lean que necesitan requisitos mínimos de tiempo de ejecución. PowerBasic es excelente para eso también.
bruceatk
35
¿Estás diciendo que un programa que lleva un mes en C y es dos veces más rápido que un programa escrito en Java que lleva solo un día escribir no vale la pena? ¿Qué pasa si ese programa necesita ejecutarse más de 500,000,000 veces al día? Ser el doble de rápido es increíblemente significativo. Si se ejecuta en miles o millones de CPU, el ahorro de costos del mes adicional de desarrollo para lograr el doble de rendimiento será enorme. Básicamente, debe conocer / comprender la escala de su implementación antes de elegir la plataforma de desarrollo.
nicerobot
49

No he visto ya, así que lo voy a decir: C tiende a ser más rápido porque casi todo lo demás está escrito en C .

Java está construido en C, Python está construido en C (o Java, o .NET, etc.), Perl está, 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 aún más rápido. Cada vez se escriben más cosas en otra cosa, que está escrita en C.

Cada declaración que escribe en otros lenguajes (no en Ensamblaje) generalmente se implementa debajo de varias declaraciones en C, que se compilan en código máquina nativo. Dado que esos otros lenguajes tienden a existir para obtener un mayor nivel de abstracción que C, esas declaraciones adicionales requeridas en C tienden a centrarse en agregar seguridad, complejidad y proporcionar manejo de errores. 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 insinúas:

¿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 meollo 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 desplegarse a Ensamblaje desde Python (con alguna ayuda menor 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 lenguaje de nivel superior al nivel de rendimiento de la C. sin embargo, pero, para la mayoría de las aplicaciones, su programa pasa la mayor parte del tiempo esperando a personas o hardware, por lo que la diferencia realmente no importa.

Disfrutar.

Rob Williams
fuente
10
Esto realmente no se aplica a los lenguajes compilados JIT. No es que mi C # se esté compilando en IL, que se traduce en C, que se compila en código de máquina. No, el IL está compilado por JIT, y el lenguaje de implementación del JIT es irrelevante en ese momento. Solo está produciendo código de máquina.
Jon Skeet el
3
Dios no permita que cuestione al legendario Jon Skeet, pero parece completamente relevante que el código de máquina que se está produciendo sea para C # en lugar de C, por lo que es de "nivel superior", tiene más funcionalidad, tiene controles de seguridad, etc. y lo hará ser, por lo tanto, más lento que el "equivalente" C.
Rob Williams
3
@ Jon: Estaba a punto de decir algo similar, pero el punto en realidad es algo válido porque muchos de los componentes principales de la biblioteca .NET están realmente escritos en C y, por lo tanto, tienen limitaciones de velocidad de C. Será interesante ver cómo cambiará esto en el futuro.
Konrad Rudolph, el
1
Esto parece al revés, otros compiladores / intérpretes / vms de lenguaje se escriben con frecuencia pero no siempre en c (o al menos para la capa más baja) porque c es bastante rápido (y en muchos casos el más rápido).
Roman A. Taycher
2
Esta respuesta no es cierta. Como se señaló anteriormente, no se aplica a los lenguajes JIT, pero tampoco se aplica a los lenguajes con sus propios compiladores (que, si se aplicaran esfuerzos extraordinarios, podrían generar un código más rápido que los compiladores C modernos). La única otra clase de lenguajes restantes son los lenguajes interpretados, y estos no son más lentos que C solo porque están escritos en C, sino por la sobrecarga de la interpretación, no importa cómo se divida e incluso si el intérprete se escribió en ensamblador , es enorme.
Score_Under
38

Hay muchas preguntas allí, la mayoría de las cuales no estoy calificado para responder. Pero para este último:

¿Qué impide que otros lenguajes puedan compilarse en binarios que se ejecutan tan rápido como C?

En una palabra, abstracción.

C está a solo uno o dos niveles de abstracción del lenguaje máquina. Los lenguajes Java y .Net están a un mínimo de 3 niveles de abstracción del ensamblador. No estoy seguro acerca de Python y Ruby.

Por lo general, cuanto más juguetes de programador (tipos de datos complejos, etc.), más lejos esté del lenguaje de máquina y más traducción tendrá que hacerse.

Estoy aquí y allá, pero esa es la esencia básica.

Actualización ------- Hay algunos buenos comentarios en esta publicación con más detalles.

Rob Allen
fuente
3
Técnicamente, Java y .Net se abstraen infinitamente del lenguaje de la máquina en la que se ejecutan. Se ejecutan en máquinas virtuales. Incluso con JIT, el código original tiene que ser masajeado masivamente para obtener algo parecido al código de máquina nativo.
jmucchiello
1
El código .net no se ejecuta en una máquina virtual. Se ejecuta como instrucciones nativas en cualquier plataforma de procesador en la que se esté ejecutando (32 bits x86, 64 bits x86 o IA64).
Robert C. Barth
11
@ Robert: .net hace uso de una máquina virtual. El código .net se compila en bytecode que ejecuta la VM. La VM convierte el código de bytes en instrucciones nativas en tiempo de ejecución.
Robert Gamble, el
3
Es muy importante tener en cuenta que Java y otras abstracciones del lenguaje OO han afectado los conjuntos de instrucciones del procesador. Los procesadores más nuevos tienen instrucciones que hacen que Java se ejecute más rápido si Java VM conoce estas optimizaciones y las utiliza. No es enorme, pero es útil.
Adam Davis
1
Quizás ThumbEE en.wikipedia.org/wiki/…
Roman A. Taycher
35

No es tanto que C sea rápido, sino que el modelo de costos de C es transparente . Si un programa en 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 se compilan en binarios que son tan rápidos como C son Standard ML (utilizando 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 el esfuerzo que la gente ha puesto en ajustar el código.

Norman Ramsey
fuente
Es posible escribir código no obviamente costoso en cualquier idioma. Es solo que en algunos idiomas, primero tienes que escribir una variante interna de Lisp o Forth ...
Donal Fellows
También Rust coincide con C en los puntos de referencia.
rígido
18

C no siempre es más rápido.

C es más lento que, por ejemplo, Modern Fortran.

C es a menudo más lento que Java para algunas cosas. (Especialmente después de que el compilador JIT haya intentado su código)

C permite el alias de puntero, lo que significa que algunas buenas optimizaciones no son posibles. Particularmente cuando tienes múltiples unidades de ejecución, esto causa paradas de búsqueda de datos. Ay.

La suposición de que la aritmética de puntero funciona realmente causa un rendimiento lento e inflado en algunas familias de CPU (¡PIC en particular!) Solía ​​succionar el grande en x86 segmentado.

Básicamente, cuando obtienes una unidad vectorial, o un compilador paralelo, C apesta y el Fortran moderno se ejecuta más rápido.

Los trucos del programador C como thunking (modificar el ejecutable sobre la marcha) provocan paradas de captación previa de la CPU.

¿Entiendes la deriva?

Y nuestro buen amigo, el x86, ejecuta un conjunto de instrucciones que actualmente tiene poca relación con la arquitectura real de la CPU. Registros de sombra, optimizadores de carga y almacenamiento, todo en la CPU. Entonces C está cerca del metal virtual. El metal real, Intel no te deja ver. (Históricamente, las CPU VLIW fueron un poco fallidas, así que tal vez no sea tan malo)

Si programa en C en un DSP de alto rendimiento (¿tal vez un DSP TI?), El compilador tiene que hacer algunas cosas difíciles para desenrollar el C en las múltiples unidades de ejecución paralelas. Entonces, en ese caso, C no está cerca del metal, pero está cerca del compilador, que hará la optimización completa del programa. Extraño.

Y finalmente, algunas CPU (www.ajile.com) ejecutan códigos de bytes Java en hardware. C sería un PITA para usar en esa CPU.

Tim Williscroft
fuente
1
¿Cuándo fue la última vez que se escribió thunking, en C? Modern x86 es una interfaz para un diseño mayormente RISC, pero eso tiene poco que ver con VLIW ...
Calyth
77
Gran parte de su publicación ignora la existencia de C99. Además, muchos compiladores de C / C ++ ofrecen la palabra clave de restricción C99 (asegura que no haya alias de puntero) como una extensión.
Evan Teran el
Supongo que todos están siguiendo / haciendo la transición para seguir el CWE / SANS top 25 y evitando hacer nuevos diseños en C. Así que no hay campos verdes C, tan poco o ningún C99.
Tim Williscroft el
2
¿Podría mostrar un ejemplo cuando c es más lento que el Fortenberry moderno?
Adam
No estoy seguro de que haya habido un momento en que los compiladores de C compitieran muy bien con los mejores compiladores de Fortran. Por supuesto, hay mucho código que no querrá escribir en FORTRAN 77 (y mucho menos 66), pero los estándares más recientes de Fortran han sido cada vez más agradables.
tfb
11

¿Qué impide que otros lenguajes puedan compilarse en binarios que se ejecutan tan rápido como C?

Nada. Los lenguajes modernos como Java o .NET langs están más orientados a la productividad del programador que al rendimiento. El hardware es barato hoy en día. Además, la compilación para la representación intermedia ofrece muchas bonificaciones, como seguridad, portabilidad, etc. .NET CLR puede aprovechar diferentes hardware, por ejemplo, no necesita optimizar / recompilar manualmente el programa para usar el conjunto de instrucciones SSE.

aku
fuente
Yo diría que la portabilidad aquí. Si desea un código realmente portátil, lo escribirá en C y no en ningún otro lenguaje. Tenemos un código que se ejecuta en unos 25 sistemas operativos. A partir de dos y ThreadX y terminando en Linux / XP me muestran otro idioma que puede hacer eso :)
Ilya
1
@Ilya no estoy de acuerdo. Es igual de fácil escribir código no portátil en C. Mire lo doloroso que fue para algunos portar a 64 bits. Los idiomas de bytecode podrían funcionar en toda la plataforma si tienes el intérprete de bytecode correcto.
Calyth
1
@ III, el código C portátil es una excepción más que una regla, porté el código C entre diferentes plataformas de hardware / software y sé que es una pesadilla.
aku
Incluso para PC Word no es el caso. Mire a la realidad la mayoría de las aplicaciones multiplataforma escritas en c / c ++, un poco en java. Para el desarrollo integrado de bajo nivel simplemente no hay otros casos. C es el lenguaje más portátil de facto.
Ilya
@aku -> PORTAR código incorrecto podría ser un desastre, estoy de acuerdo. Escribir código portátil en ADVANCE - C es la mejor opción. Diría que C ++ es una opción, pero al ir a la plataforma integrada siempre encontrará un compilador decente de C, para c ++ podría encontrarse sin el compilador.
Ilya
8

Los factores principales son que es un lenguaje de tipo estático y que está compilado en código máquina. Además, dado que es un lenguaje de bajo nivel, generalmente no hace nada que no le digas.

Estos son algunos otros factores que vienen a la mente.

  • Las variables no se inicializan automáticamente
  • Sin límites de verificación en matrices
  • Manipulación de puntero sin marcar
  • Sin verificación de desbordamiento de enteros
  • Variables de tipo estático
  • Las llamadas a funciones son estáticas (a menos que use punteros de función)
  • Los escritores de compiladores han tenido mucho tiempo para mejorar el código de optimización. Además, las personas programan en C con el fin de obtener el mejor rendimiento, por lo que existe presión para optimizar el código.
  • Parte de la especificación del lenguaje está definida 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 se pueden compilar tan rápido o más rápido que C, especialmente si pueden hacer suposiciones que C no puede debido al alias de puntero, etc.

Matthew Crumley
fuente
C bajo nivel? Supongo que es un significado relativo ahora, en comparación con Java sí, pero en comparación con el ensamblaje no. Buena publicación, me hizo pensar.
Marcar el
Tienes razón, definitivamente es relativo. Lo que quiero decir es que está "cerca de la máquina" y no te ayuda a hacer cosas como la administración de memoria o el seguimiento de los tamaños de matriz.
Matthew Crumley el
2
C es un lenguaje de bajo nivel. C siempre ha sido un lenguaje de bajo nivel. Traduces manualmente el código C al ensamblador sin mucha dificultad.
Robert C. Barth, el
2
@Robert: C solía considerarse un lenguaje de alto nivel porque en comparación con el ensamblado (que era muy común), lo era. Se considera un idioma de bajo nivel en comparación con la mayoría de los idiomas que se usan actualmente.
Robert Gamble, el
Honestamente, esta es una respuesta muy parcial. Maldición, cerca de todos los programadores de C hacen la verificación de límites, etc. Sin embargo, C sigue siendo MUCHO más rápido que C ++.
MarcusJ
8

Supongo que olvidaste que el lenguaje ensamblador también es un idioma :)

Pero en serio, los programas 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 lenguajes que hacen el mismo trabajo.

La razón por la cual C es más rápido es porque está diseñado de esta manera. Le 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 cree comúnmente que el tiempo del programador es mucho más costoso 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 acelerar el programa porque muchas cosas están automatizadas.

PolyThinker
fuente
3
Aunque si escribió un programa una vez en C y nuevamente en Ensamblado, la versión en C probablemente sería más rápida porque el compilador es más inteligente que usted.
mk12
7

En su mayor parte, cada instrucción C corresponde a muy pocas instrucciones de 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 simples que pueden convertirse en mucho más código de lo que crees (funciones virtuales, constructores de copias, etc.) y los lenguajes interpretados como Java o Ruby tienen otra capa de instrucciones que nunca ve: la máquina virtual o el intérprete.

AShelly
fuente
Y algunos de estos idiomas de alto nivel se enorgullecen de ser capaces de eliminar la mayor parte de la basura que agregaron en primer lugar. Cosas como copiar elisión, optimización del valor de retorno, mover construcción / asignación, etc. en C ++.
cmaster - reinstalar a monica
Por lo tanto, todo se reduce a la cantidad de instrucciones de ensamblaje generadas a partir del código. ¿Hay más instrucciones de ensamblaje por línea de código de alto nivel, cuanto más sufre el rendimiento?
ENDEESA
Eso podría ser una simplificación excesiva, pero existe una relación directa entre el número de instrucciones de ensamblaje y la velocidad del programa. Hay un tiempo mínimo para que el procesador ejecute cada instrucción. En mi humilde opinión, un lenguaje que le permite comprender fácilmente cuántas instrucciones está escribiendo le ayuda a escribir código más eficiente. (Hay otros costos de tiempo de procesador, como fallas de ramificación y caché, pero incluso allí, una menor abstracción ayuda a dejar en claro lo que está haciendo la CPU).
AShelly
7

Sé que muchas personas lo han dicho de manera interminable, pero:

C es más rápido porque hace menos (para ti).

Daemin
fuente
Esta. Tan fiel al espíritu de C.
cmaster - reinstalar a monica
7

C ++ es más rápido en promedio (como lo fue inicialmente, en gran medida un superconjunto de C, aunque hay algunas diferencias). 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 más rápido en Scala

n-bodyy fastafueron más rápidos en Ada.

spectral-norm fue el más rápido en Fortran.

reverse-complement, mandelbroty pidigitsfueron más rápidos en ATS.

regex-dna fue 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 más rápidos en C o C ++.

Peter Lawrey
fuente
"ya que es un superconjunto de C" - No, C ++ no es un superconjunto de C.
PP
1
extern "C"no tiene nada que ver con que C ++ sea un superconjunto de C.
PP
2
Es como decir que system("bash script.sh");funciona para cualquier script bash y, por lo tanto, C es un superconjunto de bash. extern "C"proporciona el enlace C en C ++ debido a la alteración de nombre. Mientras que llamar a X como un superconjunto de Y significa que todo lo que se puede hacer en Y también se puede hacer en X, lo cual no es cierto para C ++. Hay bastantes construcciones de lenguaje que son válidas en C pero no en C ++.
PP
1
@PP No hay nada en el estándar C ++ que exija que bashsea ​​un programa de línea de comandos disponible. Si lo hiciera e incluyera qué versión / especificación de bash necesitaba ser soportada, consideraría que es un superconjunto.
Peter Lawrey
1
es trivialmente fácil escribir código C que no sea código C ++, por ejemplostruct foo { int: this; }; typedef float foo;
Jasen
6

Muchas de estas respuestas dan razones válidas de por qué C es, o no es, 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 gratuitas. Hay al menos algún costo asociado con estas características, que quizás no pensemos, ni siquiera comprendamos, al escribir código que use estas funciones.
  • El paso de la fuente a la máquina a menudo no es tan directo en otros idiomas como lo es en C.
  • OTOH, decir que el código C compilado se ejecuta más rápido que otro código escrito en otros lenguajes es una generalización que no siempre es cierta. Los contraejemplos son fáciles de encontrar (o inventar).

A pesar de todo esto, hay algo más que he notado que, creo, afecta el rendimiento comparativo de C frente a muchos otros lenguajes 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 realice operaciones innecesarias.

Como ejemplo, considere un programa simple de Windows en el que se crea una sola ventana principal. La versión AC llenaría una WNDCLASS[EX]estructura a la que se pasaría RegisterClass[Ex], luego llamaría CreateWindow[Ex]e ingresaría un bucle de mensajes. El código altamente simplificado y abreviado sigue:

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 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 usan en un proyecto típico de C) hizo mucho trabajo por nosotros, liberando nuestro tiempo para escribir muchos fragmentos más de código que nos parecen cortos pero implican muchos pasos detrás de escena.

Pero una biblioteca rica que permita una expansión de código fácil y rápida no es realmente mi punto. Mi punto es más evidente cuando comienzas a examinar lo que realmente sucede cuando nuestro pequeño one-liner realmente se ejecuta. Para divertirse en algún momento, habilite el acceso a la fuente .NET en Visual Studio 2008 o superior, y pase a la línea simple de arriba. Una de las pequeñas gemas divertidas que encontrarás es este comentario en el captador de 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 más o menos equivalente a la suma de lo que está almacenado en una WNDCLASSEXestructura y lo que se pasa CreateWindowExse recupera de la Controlclase diez veces antes de almacenarse en una WNDCLASSEXestructura y se pasa a RegisterClassExy CreateWindowEx.

En general, 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 necesariamente está generalizada, en comparación con nuestro código C simple que hace exactamente lo que necesitamos y nada más. Pero parte de esto se debe al hecho de que la naturaleza modular y orientada a objetos de .NET framework se presta a una gran cantidad de repeticiones de ejecución que a menudo se evita mediante un enfoque de procedimiento.

No estoy tratando de elegir C # o el marco .NET. Tampoco estoy diciendo que la modularización, la generalización, las funciones de biblioteca / lenguaje, OOP, etc. son cosas malas . Solía ​​hacer la mayor parte de mi desarrollo en C, más tarde en C ++, y más recientemente en C #. Del mismo modo, antes de C, usé principalmente ensamblaje. Y con cada paso "más alto" mi lenguaje va, escribo mejores, más mantenibles, programas más robustos en menos tiempo. Sin embargo, tienden a ejecutarse un poco más lentamente.

Papi
fuente
2
Ese es un problema de API, no un problema de idioma.
Arafangion
1
@Arafangion: Entiendo lo que estás diciendo, pero de alguna manera se pierde el punto. La biblioteca rica en funciones está habilitada (y, en cierto modo, demandada) por el lenguaje rico en funciones. Y no es solo la biblioteca. La biblioteca es solo un ejemplo del uso común del lenguaje. El código de aplicación típico en cualquier idioma generalmente se parece a las bibliotecas que se usan típicamente en ese idioma. Realmente es más una mentalidad fomentada por el lenguaje. Por ejemplo, los lenguajes OO generalmente pasan más tiempo asignando, construyendo, destruyendo y desasignando objetos que los lenguajes con menos soporte OOP.
P Daddy
Admitiré que una elección de idioma dada generalmente implica una plataforma y una biblioteca en particular, por lo que hice ese comentario (para que el lector sea más consciente), pero dicho esto, usar (por ejemplo) C ++ en Windows es un bestia muy diferente a, por ejemplo, C ++ en Linux y diferente de nuevo con C ++ en Android. Otro ejemplo es Python: tenemos CPython, Jython, PyPy e IronPython, todos los cuales usan bibliotecas muy diferentes.
Arafangion
Pero usando cualquiera de estas pitones, los desarrolladores tenderán a escribir aplicaciones de cierta manera. Por ejemplo, pueden leer y escribir desde un archivo de texto, creando nuevos objetos con los datos que leen. En C, por otro lado, es más probable que un desarrollador haga una asignación única de una matriz de estructuras, y lea y escriba esas estructuras desde un archivo binario. Esto es, por supuesto, simplemente un ejemplo artificial que intenta ilustrar el punto que estoy tratando de hacer sobre la mentalidad .
P Daddy
6

No creo que nadie haya mencionado el hecho de que se ha puesto mucho más esfuerzo en los compiladores de C que cualquier otro compilador, tal vez con la excepción de Java.

C es extremadamente optimizable por muchas de las razones ya mencionadas, más que casi cualquier otro lenguaje. Entonces, si se pone la misma cantidad de esfuerzo en otros compiladores de lenguaje, C probablemente seguirá siendo el primero.

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 producen binarios más rápidos. Estoy pensando en Marte digital D porque el creador se encargó de crear un lenguaje que podría optimizarse mejor que C. Es posible que haya otros lenguajes que tengan esta posibilidad. Sin embargo, no puedo imaginar que ningún lenguaje tenga compiladores más que solo 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 idiomas diseñados para ser FÁCILES para que los humanos los optimicen. Un programador experto puede hacer que cualquier lenguaje sea 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 debería producir un código relativamente rápido sin tener que obsesionarse exactamente sobre cómo está escrito el programa.

También es importante (al menos para mí) que el 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 eso se basa en ejemplos de selección de cerezas. No soy un gran admirador de C, pero sé que CUALQUIER COSA que escriba en C 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 por ciento generalmente se debe a la mayor parte del tiempo que se pasa en el código de la biblioteca, que de todos modos está muy optimizado.

Don
fuente
5

Esto es en realidad un poco de una falsedad perpetuada. Si bien es cierto que los programas C son con frecuencia más rápidos, este no es siempre el caso, especialmente si el programador C no es muy bueno en eso.

Un gran agujero evidente que la gente tiende a olvidar es cuando el programa tiene que bloquear para algún tipo de E / S, como la entrada del usuario en cualquier programa GUI. En estos casos, realmente no importa qué idioma use, ya que está limitado por la velocidad a la que pueden ingresar los datos en lugar de la rapidez con la que puede procesarlos. En este caso, no importa mucho si está utilizando C, Java, C # o incluso Perl; simplemente no puede ir más rápido de lo que pueden ingresar los datos.

La otra cosa importante es que usar la recolección de basura y no usar los punteros adecuados le permite a la máquina virtual hacer 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 simplemente se puede usar en lugar de buscarlo en una tabla. Las JVM modernas tampoco tienen que desasignar la memoria; en cambio, simplemente mueven los objetos vivos cuando GC y la memoria gastada de los objetos muertos se recupera esencialmente de forma gratuita.

Esto también trae un punto interesante sobre C y aún más en C ++. Hay algo de una filosofía de diseño de "Si no lo necesita, no lo paga". El problema es que si lo quieres, terminas pagando por la nariz. 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 todavía cuestan algo, pero en los programas que usan muchas funciones virtuales, el costo reducido se suma.

James
fuente
1
"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". ¿Cómo puedes ir más rápido que MOV EAX, [ECX]; LLAMADA [EAX + algún índice]; ? A menos que pueda llamar a una función sin buscarla, esto se ve bastante óptimo.
Frans-Willem
@Frans: un compilador JIT (como Java HotSpot) puede alinear la búsqueda de vtable si determina que un objeto dado es siempre de un tipo determinado. C ++ también hará esto si conoce la misma información en tiempo de compilación, pero es más fácil hacer esta optimización con código de bytes Java que con las instrucciones de máquina x86.
Tom
66
@James - Argumentar "E / S hace que el rendimiento sea menos importante" no invalida la afirmación "C es más rápido que otros lenguajes". Eso no es un agujero evidente, es un argumento de paja.
Tom
Hubiera sido mejor haber utilizado el manejo de cadenas de C (y también la biblioteca estándar de C) como ejemplo, ya que esa es un área donde C es pobre. La mayoría de los otros idiomas funcionan mejor, incluso con un código de inicio simplista.
Donal Fellows
@DonalFellows las funciones mem * pueden ser más rápidas que las funciones str * en algunas tareas, pero el manejo de cadenas es eficiente si se tiene cuidado. ¿Tienes un punto de referencia específico en mente?
Jasen
4

No se trata tanto del lenguaje como de las herramientas y bibliotecas. Las bibliotecas y compiladores disponibles para C son mucho más antiguas que para los lenguajes más nuevos. Se podría pensar que esto los haría más lentos, pero al contrario.

Estas bibliotecas se escribieron en un momento en que la potencia de procesamiento y la memoria eran muy importantes. Tenían que ser escritos de manera muy eficiente para poder trabajar en absoluto. 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 y la amplia adopción de C lo convierten en una ventaja significativa sobre otros idiomas de la misma edad. También le da a C una ventaja de velocidad sobre las herramientas más nuevas que no enfatizan el rendimiento bruto tanto como C tenía que hacerlo.

Dave Swersky
fuente
4

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 que presenta un diseño 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 un lenguaje orientado a objetos, entonces está haciendo toda la codificación de cosas como clases, inhabilidades, etc. Esto significa, en lugar de hacer algo lo suficientemente generalizado para todos con la cantidad de código y la cantidad de rendimiento que requiere que solo escriba lo que necesitas para hacer el trabajo.

Jared
fuente
4

Es sorprendente ver que el viejo mito "C / C ++ debe ser más rápido que Java porque se interpreta Java" sigue vivo y coleando. Hay artículos que se remontan unos años atrás , así como otros más recientes , que explican con conceptos o medidas por qué esto simplemente no siempre es el caso .

Las implementaciones actuales de máquinas virtuales (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 a medida que se ejecuta, utilizando una variedad de técnicas:

  • renderizando métodos frecuentes para el código de máquina,
  • en línea pequeños métodos,
  • ajuste de bloqueo

y una variedad de otros ajustes basados ​​en saber qué está haciendo realmente el código y en las características reales del entorno en el que se está ejecutando.

joel.neely
fuente
Estoy de acuerdo en que Java ha realizado mejoras significativas en el rendimiento en los últimos años que lo acercan mucho más a C en términos de rendimiento bruto, pero llevará un tiempo vivir el hecho de que fue tan lento durante tanto tiempo. ¿Pero quién hablaba de Java de todos modos?
Robert Gamble, el
Java es un "otro lenguaje" al que hace referencia el OP, ¿no es así?
Robert C. Barth, el
@Robert: "otros idiomas", plural, no se menciona ningún lenguaje específico que no sea C. ¿Cómo se lee "Java"?
Robert Gamble, el
@Roberd: Varias de las respuestas que se habían publicado antes de encontrarme con la pregunta hablaban de Java (u otros lenguajes cuya implementación a menudo es a través de un intérprete o VM).
joel.neely
3
@Joel: si conoce el hardware de destino, la mayoría de las optimizaciones que puede realizar la JVM en tiempo de ejecución también se pueden realizar mediante la optimización guiada por perfil con C o C ++. Eso hace una gran diferencia, y generalmente empuja a C y C ++ a la cabeza, ya que no tienen que "aprender" durante la ejecución.
Tom
4

El código de ejecución más rápido sería un código de máquina cuidadosamente elaborado a mano. El ensamblador será casi tan bueno. Ambos son de muy bajo nivel y se necesita mucho código de escritura para hacer las cosas. C está un poco por encima del ensamblador. Todavía tiene la capacidad de controlar las cosas a un nivel muy bajo en la máquina real, pero hay suficiente abstracción que hace que escribirlo sea más rápido y fácil que el ensamblador. Otros lenguajes como C # y JAVA son aún más abstractos. Mientras el ensamblador y el código de máquina se denominan lenguajes de bajo nivel, C # y JAVA (y muchos otros) se denominan lenguajes de alto nivel. C a veces se llama lenguaje de nivel medio.

Jim C
fuente
Mientras leía su respuesta, solo dos palabras en todo el párrafo atraían mis ojos hacia ellas, como un imán que tira de metales. La palabra es JAVA, dos veces en el párrafo. Nunca lo había visto antes escrito en mayúsculas, y se ve bien :-)
Sнаđошƒаӽ
3

No tome la palabra de alguien, mire el desensamblaje tanto para C como para el idioma de su elección en cualquier parte crítica del rendimiento de su código. Creo que puede mirar en la ventana de desmontaje en tiempo de ejecución en Visual Studio para ver .Net desmontado. Debería ser posible si es complicado para Java con windbg, aunque si lo hace 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 hechas en estas respuestas que afirman la velocidad de los lenguajes distintos de C se pueden dejar de lado simplemente desarmando la misma rutina en C y en el idioma de su elección de nivel superior, especialmente si hay muchos datos involucrados, como es común en las aplicaciones críticas de rendimiento. Fortran puede ser una excepción en su área de especialización, no lo sé. ¿Es más alto que C?

La primera vez que comparé el código JITed con el código nativo resolvió todas y cada una de las preguntas sobre si el código .Net podí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. Los mismos costos probablemente se aplicarían a Java, pero no confíe en 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? Sin duda debería ser posible)


fuente
2

1) Como otros han dicho, C hace menos por ti. Sin variables de inicialización, sin comprobación de límites de matriz, sin gestión de memoria, etc. Esas características en otros idiomas cuestan memoria y ciclos de CPU que C no gasta.

2) Las respuestas que dicen que C es menos abstracto y, por lo tanto, más rápido son solo la mitad correcta, creo. Técnicamente hablando, si tuvieras un "compilador suficientemente avanzado" para el lenguaje X, entonces el lenguaje X podría acercarse o igualarse a la velocidad de C. La diferencia con C es que ya que se mapea muy obviamente (si has tomado un curso de arquitectura) y 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 sobre la marcha: la semántica de C es lo suficientemente simple como para que un compilador simple pueda funcionar bien.

Joseph Garvin
fuente
2

En los buenos tiempos, solo había dos tipos de idiomas: 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, que podría hacerlo directamente en la CPU. Los lenguajes 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.

Así, 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 que funcionen. Es complicado, pero una JVM es más rápida, más sofisticada y mucho más optimizada que los antiguos intérpretes, por lo que representa un cambio de rendimiento mucho mejor (con el 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 también tienden a generar un código mucho mejor que antes. Pero la mayoría de las optimizaciones, con mayor frecuencia (aunque no siempre) hacen algún tipo de compensación de tal manera que no siempre son más rápidas en todas las circunstancias. Al igual que todo lo demás, nada es gratis, por lo que los optimizadores deben presumir de algún lugar (aunque a menudo usan CPU en tiempo de compilación para ahorrar 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 de ensamblador en la CPU, sin embargo, en Java, podría terminar siendo mucho más que eso (y podría incluir también 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 hacer mucho más trabajo para que funcione y no es tan protegido, fácil de usar o amigable con los errores. La mayoría de los otros lenguajes le brindan una mayor abstracción y se ocupan 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 informática,

Pablo.

Paul W Homer
fuente
"En C, si incrementa un número entero, es más que probable que sea solo un paso de ensamblador en la CPU" No es exactamente cierto. Si este número entero no está en el registro de la CPU, debe tener el código de la máquina para recuperarlo de la memoria, incrementarlo y volver a escribirlo en la memoria. Casi lo mismo esperaría que suceda al ejecutar código Java. Y no entiendo por qué "++ i" desencadenaría por sí mismo un ciclo de GC.
quant_dev
@quant_dev: "usted ... tiene que ... recuperarlo de la memoria, incrementarlo, escribirlo de nuevo ...". Tal vez tal vez no. El x86, por ejemplo, tiene instrucciones que funcionan con datos en la memoria. ++ipodría compilarse para "agregar [ebp - 8], 1". No quiere decir que buscar, incrementar, almacenar todavía no está sucediendo, pero esto lo soluciona la CPU y es solo una instrucción, como dijo Paul.
P Daddy
... que sigue siendo irrelevante del POV del rendimiento, porque podría ser solo una instrucción, pero aún implica esperar a que los datos lleguen a la CPU. La memoria caché falla y todo eso.
quant_dev
No, no diría que es irrelevante. Una instrucción de CPU generalmente ocupa menos bytes de código que varias instrucciones de CPU, produciendo un mejor rendimiento de caché en el segmento de código. Una instrucción de CPU también ocupa menos espacio en la tubería de la CPU, y los pasos múltiples (buscar, incrementar, almacenar) se pueden manejar, quizás en paralelo, por las etapas de tubería separadas. De hecho, algunas partes de una operación pueden incluso omitirse si se pueden combinar con otras operaciones en la tubería. Por ejemplo, el almacenamiento de un valor se puede combinar con una carga posterior del mismo valor.
P Daddy
2

Dejando a un lado las técnicas avanzadas de optimización, como la optimización de puntos calientes , meta-algoritmos precompilados y varias formas de paralelismo , la velocidad fundamental de un lenguaje se correlaciona fuertemente con la complejidad implícita detrás de escena requerida para soportar las operaciones que comúnmente ser especificado dentro de los bucles internos .

Quizás lo más obvio es la comprobación de validez de referencias indirectas de memoria, como la comprobación de punteros nully la comprobación de índices contra los límites de la matriz. La mayoría de los lenguajes de alto nivel realizan estas comprobaciones implícitamente, pero C no. Sin embargo, esto no es necesariamente una limitación fundamental de estos otros lenguajes : un compilador suficientemente inteligente puede ser capaz de eliminar estas comprobaciones 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 un grado similar el C ++ estrechamente relacionado) es una gran dependencia de la asignación de memoria basada en la pila , que es inherentemente rápida para la asignación, desasignación y acceso. En C (y C ++), la pila de llamadas principal se puede utilizar para la asignación de primitivas, matrices y agregados ( struct/ class).

Si bien C ofrece la capacidad de asignar dinámicamente memoria de tamaño arbitrario y de por vida (utilizando el llamado 'montón'), se evita de forma predeterminada (en su lugar, se utiliza la pila).

Tentadoramente, a veces es posible replicar la estrategia de asignación de memoria C dentro de los entornos de tiempo 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 a 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 donde C y C ++ eclipsan a la mayoría de los otros lenguajes para la velocidad es la capacidad de integrarse perfectamente con los conjuntos de instrucciones de máquina nativas. Un ejemplo notable de esto es la disponibilidad (dependiente del compilador y la plataforma) de intrínsecos SIMD que soportan la construcción de algoritmos personalizados que aprovechan el hardware de procesamiento paralelo ahora casi omnipresente, mientras siguen utilizando las abstracciones de asignación de datos proporcionadas por el lenguaje (más bajo -la asignación de registro de nivel es administrada por el compilador).

sin bar
fuente
1
Parte de esta ventaja de asignación de memoria de C probablemente también puede ser replicada en otros lenguajes por compiladores inteligentes (ver aquí ). Sin embargo, tengo la impresión de que esto es estructuralmente muy difícil de hacer, especialmente para los tipos de datos no primitivos. Esta publicación menciona la idea de un objeto sin escape que puede asignarse a la pila como una optimización en Java.
nobar
2

Encontré una respuesta en el enlace sobre por qué algunos lenguajes son más rápidos y otros son más lentos, espero que esto aclare más sobre por qué C o C ++ es más rápido que otros. También hay otros lenguajes que son más rápidos que C, pero no podemos úsalos todos. Alguna explicación

Una de las principales razones por las que Fortran sigue siendo importante es porque es rápido: las rutinas de cálculo numérico escritas en Fortran tienden a ser más rápidas que las rutinas equivalentes escritas en la mayoría de los otros idiomas. Los lenguajes que compiten con Fortran en este espacio, C y C ++, se usan porque son competitivos con este rendimiento.

Esto plantea la pregunta: ¿por qué? ¿Qué tienen C ++ y Fortran que los hacen rápidos y por qué superan a otros lenguajes populares, como Java o Python?

Interpretación versus compilación Hay muchas formas de categorizar y definir 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 difícil; 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 en una forma ejecutable que el procesador puede usar.

Este proceso de compilación tiene varios pasos. El código fuente se analiza y analiza. En este punto, se pueden detectar errores de codificación básicos como errores tipográficos y ortográficos. El código analizado se usa para generar una representación en memoria, que también se puede usar 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 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: se pueden realizar optimizaciones de alto nivel en la representación del código, y se utilizan optimizaciones de nivel inferior en la salida del generador de código.

En realidad, la ejecución del código ocurre más tarde. Todo el proceso de compilación se usa simplemente para crear algo que se pueda 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 utiliza para impulsar la ejecución directa, y el programa se ejecuta de inmediato.

El intérprete más simple tiene dentro de sí un código ejecutable correspondiente a las diversas características 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 de 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 más bien en 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 sumar dos números es tener ambos números en los registros del procesador y usar la instrucción de agregar 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 que se agregarán, y luego llamar a una función para realizar la adición. Esa función puede muy bien usar la misma instrucción de procesador que el programa compilado para realizar la adición real, pero todo el trabajo adicional antes de que la instrucción pueda usarse realmente hace las cosas más lentas.

Si desea saber más, consulte la Fuente

uniqueNt
fuente
1

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 las personas dicen que C es rápido, y luego continúan hablando sobre algún otro lenguaje, generalmente usan el desempeño de C como punto de referencia.

Arafangion
fuente
2
"Algunos algoritmos de C ++ son más rápidos que C" no tiene sentido. Cualquier "algoritmo C ++" se puede escribir en C y viceversa. Los algoritmos son independientes del lenguaje. C ++ esencialmente agrega características a C, y ninguna de estas nuevas características conduce a algoritmos más rápidos (aunque tal vez sean más fáciles de escribir).
Joseph Garvin el
1
La reprimenda clásica es el algoritmo std :: sort .. std :: sort es más rápido que cualquier algoritmo de clasificación en C: la única forma de obtener el mismo rendimiento en C es codificarlo en cualquier lugar que desee o usar macros, e incluso entonces El compilador tiene menos información para optimizar.
Arafangion
1

Con los compiladores de optimización modernos, es muy poco probable que un programa C puro sea mucho más rápido que el código compilado de .NET, si es que lo hace. Con la mejora de la productividad que los marcos como .net proporcionan al desarrollador, puede hacer cosas en un día que solía llevar semanas o meses en C. normal. Junto con el bajo costo del hardware en comparación con el salario de un desarrollador, es MUCHO más barato escribir las cosas en un lenguaje de alto nivel y tirar 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 agarre manual en C. Debe asignar su propia memoria, desasignar esa memoria, hacer su propia verificación de límites, etc. No existe tal cosa como nuevo objeto (); No hay recolección de basura, clases, OOP, marcos de entidades, LINQ, propiedades, atributos, campos ni nada de eso. Debe saber cosas como la aritmética de punteros y cómo desreferenciar un puntero. Y, para el caso, saber y comprender 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 comprensión implícita de la arquitectura de una microcomputadora (generalmente elmicroordenador en el que está trabajando) cuando programa en C que simplemente no está presente ni es necesario cuando programa en algo como C # o Java. Toda esa información se ha descargado al programador del compilador (o VM).

Robert C. Barth
fuente
"Cuantos más hardware en el problema" solo funciona en entornos donde eso es realmente posible. El mercado integrado es un contraejemplo perfecto (y ese es un mercado masivo ).
Bob Somers
Jeff y Joel bloguean únicamente sobre sistemas empresariales, no sistemas integrados, por lo que es razonable suponer que ese es el contexto en el que se formuló esta pregunta.
Robert C. Barth
1) ¿el código .net se ejecuta tan rápido como el código C? ¿Alguna vez has escrito un programa en C? 2) La mentalidad de "arrojar más hardware al problema" es la razón por la cual mi máquina de 2 GB de doble núcleo a 1,3 GHz apenas puede seguir el ritmo de Windows XP, mientras que mi máquina de 512 MB a 800 MHz funciona con la última versión de Ubuntu.
Robert Gamble, el
Sí, he escrito C. No es tan glorioso como la gente cree que es. Y los proyectos cuestan demasiado. Es un caso simple de economía. Ejecuté Win2k en un Pentium Pro 180MHz con 768MB de RAM durante años como servidor de correo y web. Funcionó bien. La evidencia anecdótica no significa nada.
Robert C. Barth
C no es "glorioso" pero es rápido, he escrito suficientes códigos C y C # para saber que C casi siempre es mucho más rápido que C # mientras realizo la misma tarea. Para algunas tareas, lleva más tiempo desarrollarlas que los lenguajes de nivel superior, pero se trata de usar la herramienta adecuada para el trabajo y, a veces, C lo es.
Robert Gamble, el
1

Es la diferencia entre automático y manual, los lenguajes de nivel superior son abstracciones, por lo tanto, automatizadas. C / C ++ se controlan y manejan manualmente, incluso el código de verificación de errores es a veces una labor manual.

C y C ++ también son lenguajes compilados, lo que significa que nada de eso funciona en todas partes, estos lenguajes deben ajustarse para el hardware con el que trabaja, agregando así una capa adicional de gotcha. Aunque esto se está debilitando ahora, ya que los compiladores 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 compila al compilador A para que compile contra el compilador B del mismo código de arquitectura diferente.

En resumen, los lenguajes C no están destinados a ser fáciles de entender o razonar, por eso también se los conoce como lenguajes de sistemas. Salieron ante toda esta abstracción de alto nivel sin sentido. 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, su medio para resolver problemas complejos que no se pueden resolver con herramientas de lenguaje convencionales.

Es por eso que obtienes cosas locas como (microarquitecturas, controladores, física cuántica, juegos AAA, sistemas operativos) hay cosas para las que C y C ++ son muy adecuadas. La velocidad y el número son las áreas principales.

Val
fuente
1

C es rápido porque es un lenguaje de bajo nivel compilado de forma nativa. Pero C no es el más rápido. El punto de referencia recursivo de Fibonacci muestra que Rust, Crystal y Nim pueden ser más rápidos.

Bill Zelenko
fuente
1

Hay muchas razones, que incluyen:

  • Se compila en lenguaje ensamblador.
  • Está estáticamente escrito.
  • Sin recolección de basura.
  • Sin manejo de errores.
  • Muchos lenguajes de programación agregan nuevas características. Parte de la filosofía de C es mantener las cosas simples en lugar de agregar más funciones.
Zafiro_Brick
fuente
¿Por qué debería ser más rápido porque no está orientado a objetos?
sb27
A) la programación orientada a objetos crea una necesidad de recolección de basura, B) las grandes características como la programación orientada a objetos agregan complejidad al compilador,
Sapphire_Brick
A) No. Ver C ++ u Óxido. B) Sí, pero también le da al Compilador oportunidades para nuevas optimizaciones.
sb27
A) Rust tiene recolección de basura en tiempo de compilación, y c ++ tiene recolección de basura para clases, es por eso que tiene destructores, B) la complejidad optimizada sigue siendo complejidad
Sapphire_Brick
A) No es recolección de basura, la administración de memoria debe hacerse incluso si realiza su programa en el ensamblado B) No. Más abstracción permite al optimizador hacer mejores suposiciones.
sb27