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?
c
performance
Mike C.
fuente
fuente
Respuestas:
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.
fuente
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á
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 :
fuente
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:
y en CI escribe esto:
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.
fuente
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:
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.
fuente
Hay muchas preguntas allí, la mayoría de las cuales no estoy calificado para responder. Pero para este último:
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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.
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.
fuente
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.
fuente
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.
fuente
Sé que muchas personas lo han dicho de manera interminable, pero:
fuente
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 Scalan-body
yfasta
fueron más rápidos en Ada.spectral-norm
fue el más rápido en Fortran.reverse-complement
,mandelbrot
ypidigits
fueron 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 ++.
fuente
extern "C"
no tiene nada que ver con que C ++ sea un superconjunto de C.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 ++.bash
sea 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.struct foo { int: this; }; typedef float foo;
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:
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íaRegisterClass[Ex]
, luego llamaríaCreateWindow[Ex]
e ingresaría un bucle de mensajes. El código altamente simplificado y abreviado sigue:Un programa equivalente en C # podría ser solo una línea de código:
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
:Diez veces . La información más o menos equivalente a la suma de lo que está almacenado en una
WNDCLASSEX
estructura y lo que se pasaCreateWindowEx
se recupera de laControl
clase diez veces antes de almacenarse en unaWNDCLASSEX
estructura y se pasa aRegisterClassEx
yCreateWindowEx
.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.
fuente
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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:
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.
fuente
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.
fuente
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
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.
fuente
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.
fuente
++i
podrí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.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
null
y 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).
fuente
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
fuente
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.
fuente
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).
fuente
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.
fuente
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.
fuente
Hay muchas razones, que incluyen:
fuente