Tengo entendido que C / C ++ produce código nativo para ejecutarse en una arquitectura de máquina en particular. Por el contrario, lenguajes como Java y C # se ejecutan sobre una máquina virtual que abstrae la arquitectura nativa. Lógicamente, parecería imposible que Java o C # igualen la velocidad de C ++ debido a este paso intermedio, sin embargo, me han dicho que los últimos compiladores ("puntos calientes") pueden alcanzar esta velocidad o incluso superarla.
Quizás esto sea más una pregunta de compilador que una pregunta de idioma, pero ¿alguien puede explicar en inglés simple cómo es posible que uno de estos lenguajes de máquinas virtuales funcione mejor que un idioma nativo?
Respuestas:
En general, C # y Java pueden ser tan rápidos o más rápidos porque el compilador JIT, un compilador que compila su IL la primera vez que se ejecuta, puede realizar optimizaciones que un programa compilado en C ++ no puede hacer porque puede consultar la máquina. Puede determinar si la máquina es Intel o AMD; Pentium 4, Core Solo o Core Duo; o si es compatible con SSE4, etc.
Un programa C ++ tiene que compilarse de antemano normalmente con optimizaciones mixtas para que funcione decentemente bien en todas las máquinas, pero no está optimizado tanto como podría estarlo para una sola configuración (es decir, procesador, conjunto de instrucciones, otro hardware).
Además, ciertas características del lenguaje permiten que el compilador en C # y Java haga suposiciones sobre su código que le permite optimizar ciertas partes que simplemente no son seguras para el compilador de C / C ++. Cuando tienes acceso a los punteros, hay muchas optimizaciones que simplemente no son seguras.
Además, Java y C # pueden realizar asignaciones de montón de manera más eficiente que C ++ porque la capa de abstracción entre el recolector de basura y su código le permite hacer toda su compresión de montón a la vez (una operación bastante cara).
Ahora no puedo hablar por Java en el siguiente punto, pero sé que C #, por ejemplo, eliminará los métodos y las llamadas a métodos cuando sepa que el cuerpo del método está vacío. Y utilizará este tipo de lógica en todo su código.
Como puede ver, hay muchas razones por las que ciertas implementaciones de C # o Java serán más rápidas.
Dicho todo esto, se pueden hacer optimizaciones específicas en C ++ que harán volar cualquier cosa que pueda hacer con C #, especialmente en el ámbito de los gráficos y en cualquier momento que esté cerca del hardware. Los punteros hacen maravillas aquí.
Entonces, dependiendo de lo que estés escribiendo, iría con uno u otro. Pero si está escribiendo algo que no depende del hardware (controlador, videojuego, etc.), no me preocuparía por el rendimiento de C # (de nuevo, no puedo hablar de Java). Estará bien.
En el lado de Java, @Swati señala un buen artículo:
https://www.ibm.com/developerworks/library/j-jtp09275
fuente
JIT frente al compilador estático
Como ya se dijo en las publicaciones anteriores, JIT puede compilar IL / bytecode en código nativo en tiempo de ejecución. Se mencionó el costo de eso, pero no hasta su conclusión:
JIT tiene un problema masivo es que no puede compilar todo: la compilación JIT lleva tiempo, por lo que JIT compilará solo algunas partes del código, mientras que un compilador estático producirá un binario nativo completo: para algunos tipos de programas, el El compilador simplemente superará fácilmente al JIT.
Por supuesto, C # (o Java, o VB) suele ser más rápido para producir una solución viable y robusta que C ++ (aunque solo sea porque C ++ tiene semántica compleja, y la biblioteca estándar de C ++, aunque interesante y poderosa, es bastante pobre en comparación con la completa alcance de la biblioteca estándar de .NET o Java), por lo que, por lo general, la diferencia entre C ++ y .NET o Java JIT no será visible para la mayoría de los usuarios, y para aquellos binarios que son críticos, bueno, aún puede llamar al procesamiento de C ++ desde C # o Java (incluso si este tipo de llamadas nativas pueden ser bastante costosas en sí mismas) ...
Metaprogramación C ++
Tenga en cuenta que, por lo general, está comparando el código de tiempo de ejecución de C ++ con su equivalente en C # o Java. Pero C ++ tiene una característica que puede superar a Java / C # desde el primer momento, que es la metaprogramación de plantilla: el procesamiento del código se realizará en el momento de la compilación (por lo tanto, aumentará enormemente el tiempo de compilación), lo que dará como resultado un tiempo de ejecución cero (o casi cero).
Todavía he visto un efecto de la vida real en esto (jugué solo con conceptos, pero para entonces, la diferencia eran segundos de ejecución para JIT, y cero para C ++), pero vale la pena mencionarlo, junto con la plantilla de hechos, la metaprogramación no es trivial......
Uso de memoria nativa C ++
C ++ tiene un uso de memoria diferente al de Java / C # y, por lo tanto, tiene diferentes ventajas / defectos.
No importa la optimización JIT, nada funcionará tan rápido como el acceso directo del puntero a la memoria (ignoremos por un momento los cachés del procesador, etc.). Por lo tanto, si tiene datos contiguos en la memoria, acceder a ellos a través de punteros de C ++ (es decir, punteros de C ... Démosle a Caesar lo que le corresponde) será más rápido que en Java / C #. Y C ++ tiene RAII, lo que facilita mucho el procesamiento que en C # o incluso en Java. C ++ no necesita
using
analizar la existencia de sus objetos. Y C ++ no tienefinally
cláusula. Esto no es un error.:-)
Y a pesar de las estructuras de tipo primitivo de C #, los objetos "en la pila" de C ++ no costarán nada en la asignación y destrucción, y no necesitarán GC para trabajar en un hilo independiente para realizar la limpieza.
En cuanto a la fragmentación de memoria, los asignadores de memoria en 2008 no son los viejos asignadores de memoria de 1980 que generalmente se comparan con un GC: la asignación de C ++ no se puede mover en la memoria, es cierto, pero luego, como en un sistema de archivos Linux: ¿Quién necesita el disco duro? desfragmentar cuando la fragmentación no ocurre? El uso del asignador correcto para la tarea correcta debería ser parte del kit de herramientas para desarrolladores de C ++. Ahora, escribir asignadores no es fácil, y luego, la mayoría de nosotros tenemos mejores cosas que hacer, y para la mayor parte del uso, RAII o GC es más que suficiente.
Ahora, el modelo de memoria se está volviendo algo más complicado con el auge de la tecnología de múltiples núcleos y subprocesos. En este campo, supongo que .NET tiene la ventaja, y me dijeron que Java ocupaba el primer lugar. Es fácil para algunos hackers "en el metal desnudo" elogiar su código "cerca de la máquina". Pero ahora, es bastante más difícil producir un mejor ensamblaje a mano que dejar que el compilador haga su trabajo. Para C ++, el compilador se volvió generalmente mejor que el hacker desde hace una década. Para C # y Java, esto es aún más fácil.
Aún así, el nuevo estándar C ++ 0x impondrá un modelo de memoria simple a los compiladores de C ++, que estandarizará (y así simplificará) el código de multiprocesamiento / paralelo / subprocesamiento efectivo en C ++, y hará que las optimizaciones sean más fáciles y seguras para los compiladores. Pero luego, veremos en un par de años si sus promesas se cumplen.
C ++ / CLI frente a C # / VB.NET
Nota: En esta sección, estoy hablando de C ++ / CLI, es decir, el C ++ alojado por .NET, no el C ++ nativo.
La semana pasada, recibí una capacitación sobre optimización de .NET y descubrí que el compilador estático es muy importante de todos modos. Tan importante que JIT.
El mismo código compilado en C ++ / CLI (o su antecesor, Managed C ++) podría ser veces más rápido que el mismo código producido en C # (o VB.NET, cuyo compilador produce el mismo IL que C #).
Porque el compilador estático de C ++ era mucho mejor para producir código ya optimizado que el de C #.
Por ejemplo, la inserción de funciones en .NET está limitada a funciones cuyo código de bytes es menor o igual que 32 bytes de longitud. Entonces, algún código en C # producirá un descriptor de acceso de 40 bytes, que el JIT nunca incluirá. El mismo código en C ++ / CLI producirá un descriptor de acceso de 20 bytes, que será insertado por el JIT.
Otro ejemplo son las variables temporales, que simplemente son compiladas por el compilador de C ++ mientras aún se mencionan en el IL producido por el compilador de C #. La optimización de la compilación estática de C ++ dará como resultado menos código, por lo que autoriza una optimización JIT más agresiva, nuevamente.
Se especuló que la razón de esto era el hecho de que el compilador C ++ / CLI se benefició de las vastas técnicas de optimización del compilador nativo C ++.
Conclusión
Amo C ++.
Pero hasta donde yo lo veo, C # o Java son en general una mejor apuesta. No porque sean más rápidos que C ++, sino porque cuando sumas sus cualidades, terminan siendo más productivos, necesitan menos capacitación y tienen bibliotecas estándar más completas que C ++. Y como ocurre con la mayoría de programas, sus diferencias de velocidad (de una forma u otra) serán insignificantes ...
Editar (2011-06-06)
Mi experiencia en C # /. NET
Ahora tengo 5 meses de codificación C # profesional casi exclusiva (que se suma a mi CV ya lleno de C ++ y Java, y un toque de C ++ / CLI).
Jugué con WinForms (Ejem ...) y WCF (¡genial!), Y WPF (¡Genial! Tanto a través de XAML como de C # sin formato. WPF es tan fácil que creo que Swing simplemente no se puede comparar con él) y C # 4.0.
La conclusión es que, si bien es más fácil / rápido producir un código que funcione en C # / Java que en C ++, es mucho más difícil producir un código fuerte, seguro y robusto en C # (e incluso más difícil en Java) que en C ++. Las razones abundan, pero se pueden resumir en:
using
no es tan fácil y poderoso porque escribir una implementación correcta de Dispose es difícil )readonly
y Javafinal
no son tan útiles como C ++const
( no hay forma de que pueda exponer datos complejos de solo lectura (un árbol de nodos, por ejemplo) en C # sin un trabajo tremendo, mientras que es una función incorporada de C ++. Los datos inmutables son una solución interesante , pero no todo puede volverse inmutable, por lo que ni siquiera es suficiente, ni mucho menos ).Por lo tanto, C # sigue siendo un lenguaje agradable siempre que desee algo que funcione, pero un lenguaje frustrante en el momento en que desee algo que siempre y con seguridad funcione .
Java es aún más frustrante, ya que tiene los mismos problemas que C #, y más: al carecer del equivalente de la
using
palabra clave de C # , un colega mío muy hábil pasó demasiado tiempo asegurándose de que sus recursos se liberaran correctamente, mientras que el equivalente en C ++ habría ha sido fácil (usando destructores y punteros inteligentes).Así que supongo que la ganancia de productividad de C # / Java es visible para la mayoría del código ... hasta el día en que necesita que el código sea lo más perfecto posible. Ese día conocerás el dolor. (no creerá lo que le piden nuestro servidor y aplicaciones GUI ...).
Acerca de Java y C ++ del lado del servidor
Me mantuve en contacto con los equipos de servidores (trabajé 2 años entre ellos, antes de volver al equipo de GUI), al otro lado del edificio, y aprendí algo interesante.
En los últimos años, la tendencia era que las aplicaciones de servidor de Java estuvieran destinadas a reemplazar las antiguas aplicaciones de servidor de C ++, ya que Java tiene muchos marcos / herramientas y es fácil de mantener, implementar, etc., etc.
... Hasta que el problema de la baja latencia asomó su fea cabeza en los últimos meses. Luego, las aplicaciones del servidor de Java, sin importar la optimización intentada por nuestro experto equipo de Java, simplemente perdieron la carrera contra el viejo servidor C ++, no realmente optimizado.
Actualmente, la decisión es mantener los servidores Java para uso común donde el rendimiento, aunque sigue siendo importante, no se ve afectado por el objetivo de baja latencia, y optimizar agresivamente las aplicaciones de servidor C ++, que ya son más rápidas, para necesidades de latencia baja y latencia ultrabaja.
Conclusión
Nada es tan simple como se esperaba.
Java, e incluso más C #, son lenguajes geniales, con extensas bibliotecas y marcos estándar, donde se puede codificar rápidamente y obtener resultados muy pronto.
Pero cuando necesita potencia bruta, optimizaciones potentes y sistemáticas, soporte de compilador sólido, funciones de lenguaje potentes y seguridad absoluta, Java y C # dificultan la obtención de los últimos porcentajes de calidad que faltan pero críticos que necesita para mantenerse por encima de la competencia.
Es como si necesitara menos tiempo y desarrolladores con menos experiencia en C # / Java que en C ++ para producir código de calidad promedio, pero por otro lado, en el momento en que necesitaba un código de calidad excelente para perfeccionar, de repente fue más fácil y rápido obtener los resultados. justo en C ++.
Por supuesto, esta es mi propia percepción, quizás limitada a nuestras necesidades específicas.
Pero aún así, es lo que sucede hoy, tanto en los equipos de GUI como en los equipos del lado del servidor.
Por supuesto, actualizaré esta publicación si sucede algo nuevo.
Editar (2011-06-22)
Fuentes:
Editar (2011-09-20)
Fuentes:
fuente
Siempre que hablo de rendimiento administrado frente a rendimiento no administrado, me gusta señalar la serie que Rico (y Raymond) hicieron comparando las versiones C ++ y C # de un diccionario chino / inglés. Esta búsqueda en Google te permitirá leer por ti mismo, pero me gusta el resumen de Rico.
Para mí, la conclusión es que se necesitaron 6 revisiones para que la versión no administrada superara a la versión administrada que era un puerto simple del código no administrado original. Si necesita hasta el último bit de rendimiento (y tiene el tiempo y la experiencia para obtenerlo), tendrá que seguir sin administrarlo, pero para mí, tomaré el orden de magnitud de ventaja que tengo en las primeras versiones sobre las 33 % Gano si lo intento 6 veces.
fuente
La compilación para optimizaciones de CPU específicas generalmente está sobrevalorada. Simplemente tome un programa en C ++ y compile con optimización para pentium PRO y ejecútelo en un pentium 4. Luego recompile con optimizar para pentium 4. Pasé largas tardes haciéndolo con varios programas. Resultados generales Por lo general, menos del 2-3% de aumento de rendimiento. Entonces, las ventajas teóricas del JIT son casi nulas. La mayoría de las diferencias de rendimiento solo se pueden observar cuando se utilizan funciones de procesamiento de datos escalares, algo que eventualmente necesitará un ajuste fino manual para lograr el máximo rendimiento de todos modos. Las optimizaciones de ese tipo son lentas y costosas de realizar, lo que las hace a veces inadecuadas para JIT de todos modos.
En el mundo real y en aplicaciones reales, C ++ suele ser más rápido que java, principalmente debido a una menor huella de memoria que da como resultado un mejor rendimiento de la caché.
Pero para usar todas las capacidades de C ++, el desarrollador debe trabajar duro. Puede lograr resultados superiores, pero debe usar su cerebro para eso. C ++ es un lenguaje que decidió presentarte más herramientas, cobrando el precio de que debes aprenderlas para poder usar bien el lenguaje.
fuente
JIT (Just In Time Compiling) puede ser increíblemente rápido porque se optimiza para la plataforma de destino.
Esto significa que puede aprovechar cualquier truco del compilador que su CPU pueda admitir, independientemente de la CPU en la que el desarrollador haya escrito el código.
El concepto básico de .NET JIT funciona así (muy simplificado):
Llamar a un método por primera vez:
Llamar a un método por segunda vez:
Como puede ver, la segunda vez, es prácticamente el mismo proceso que C ++, excepto con la ventaja de las optimizaciones en tiempo real.
Dicho esto, todavía hay otros problemas generales que ralentizan un lenguaje administrado, pero el JIT ayuda mucho.
fuente
Me gusta la respuesta de Orion Adrian , pero tiene otro aspecto.
La misma pregunta se planteó hace décadas sobre el lenguaje ensamblador frente a los lenguajes "humanos" como FORTRAN. Y parte de la respuesta es similar.
Sí, un programa C ++ es capaz de ser más rápido que C # en cualquier algoritmo dado (¿no trivial?), Pero el programa en C # a menudo será tan rápido o más rápido que una implementación "ingenua" en C ++ y una versión optimizada en C ++ tardará más en desarrollarse y aún podría superar a la versión C # por un margen muy pequeño. Entonces, ¿realmente vale la pena?
Tendrás que responder a esa pregunta una por una.
Dicho esto, soy un fanático de C ++ desde hace mucho tiempo, y creo que es un lenguaje increíblemente expresivo y poderoso, a veces subestimado. Pero en muchos problemas de la "vida real" (para mí, personalmente, eso significa "del tipo que me pagan por resolver"), C # hará el trabajo antes y de forma más segura.
¿La mayor multa que pagas? Muchos programas .NET y Java acaparan la memoria. He visto que las aplicaciones .NET y Java consumen "cientos" de megabytes de memoria, cuando los programas C ++ de complejidad similar apenas alcanzan las "decenas" de MB.
fuente
No estoy seguro de con qué frecuencia encontrará que el código Java se ejecutará más rápido que C ++, incluso con Hotspot, pero intentaré explicar cómo podría suceder.
Piense en el código Java compilado como lenguaje de máquina interpretado para la JVM. Cuando el procesador de Hotspot nota que ciertas partes del código compilado se van a utilizar muchas veces, realiza una optimización en el código de la máquina. Dado que el ensamblaje de ajuste manual es casi siempre más rápido que el código compilado en C ++, está bien pensar que el código de máquina ajustado mediante programación no lo será demasiado. malo.
Entonces, para el código altamente repetitivo, pude ver dónde sería posible que Hotspot JVM ejecutara Java más rápido que C ++ ... hasta que la recolección de basura entre en juego. :)
fuente
Since hand-tuning Assembly is almost always faster than C++ compiled code
? ¿Qué quiere decir "ensamblador de ajuste manual" y "código compilado C ++"?Generalmente, el algoritmo de su programa será mucho más importante para la velocidad de su aplicación que el idioma . Puede implementar un algoritmo deficiente en cualquier idioma, incluido C ++. Con eso en mente, generalmente podrá escribir el código que se ejecuta más rápido en un lenguaje que lo ayude a implementar un algoritmo más eficiente.
Los lenguajes de nivel superior lo hacen muy bien al proporcionar un acceso más fácil a muchas estructuras de datos preconstruidas eficientes y fomentar prácticas que lo ayudarán a evitar código ineficiente. Por supuesto, a veces también pueden facilitar la escritura de un montón de código realmente lento, por lo que aún debe conocer su plataforma.
Además, C ++ se está poniendo al día con características "nuevas" (tenga en cuenta las comillas) como los contenedores STL, los punteros automáticos, etc., consulte la biblioteca boost, por ejemplo. Y ocasionalmente puede encontrar que la forma más rápida de realizar alguna tarea requiere una técnica como la aritmética de punteros que está prohibida en un lenguaje de nivel superior, aunque normalmente le permiten llamar a una biblioteca escrita en un lenguaje que puede implementarla como desee. .
Lo principal es saber el lenguaje que estás usando, su API asociada, lo que puede hacer y cuáles son sus limitaciones.
fuente
Tampoco lo sé ... mis programas Java siempre son lentos. :-) Sin embargo, nunca he notado que los programas C # sean particularmente lentos.
fuente
Aquí hay otro punto de referencia interesante, que puede probar usted mismo en su propia computadora.
Compara ASM, VC ++, C #, Silverlight, subprograma Java, Javascript, Flash (AS3)
Demostración de velocidad del complemento Roozz
Tenga en cuenta que la velocidad de JavaScript varía mucho según el navegador que lo esté ejecutando. Lo mismo es cierto para Flash y Silverlight porque estos complementos se ejecutan en el mismo proceso que el navegador de alojamiento. Pero el complemento Roozz ejecuta archivos .exe estándar, que se ejecutan en su propio proceso, por lo que la velocidad no se ve afectada por el navegador de alojamiento.
fuente
Debe definir "rendimiento mejor que ...". Bueno, lo sé, preguntaste sobre la velocidad, pero no es todo lo que cuenta.
Y así sucesivamente, es parcial, sí;)
Con C # y Java, paga un precio por lo que obtiene (codificación más rápida, administración automática de memoria, gran biblioteca, etc.). Pero no tienes mucho espacio para regatear los detalles: llévate el paquete completo o nada.
Incluso si esos lenguajes pueden optimizar algún código para ejecutarse más rápido que el código compilado, todo el enfoque es (en mi humilde opinión) ineficiente. ¡Imagínese conduciendo cada día 5 millas hasta su lugar de trabajo, con un camión! Es cómodo, se siente bien, estás a salvo (zona de deformación extrema) y después de pisar el acelerador durante algún tiempo, ¡incluso será tan rápido como un automóvil estándar! ¿Por qué no todos tenemos un camión para ir al trabajo? ;)
En C ++ obtienes lo que pagas, ni más ni menos.
Citando a Bjarne Stroustrup: "C ++ es mi lenguaje favorito de recolección de basura porque genera muy poca basura" .
fuente
times
un shell. Para que verifique todo el programa, no solo un aspecto. Entonces, ¿son similares los resultados?El código ejecutable producido a partir de un compilador Java o C # no se interpreta, se compila en código nativo "justo a tiempo" (JIT). Por lo tanto, la primera vez que se encuentra un código en un programa Java / C # durante la ejecución, hay algunos gastos generales ya que el "compilador de tiempo de ejecución" (también conocido como compilador JIT) convierte el código de bytes (Java) o el código IL (C #) en instrucciones nativas de la máquina. Sin embargo, la próxima vez que se encuentre ese código mientras la aplicación aún se está ejecutando, el código nativo se ejecutará inmediatamente. Esto explica cómo algunos programas Java / C # parecen ser lentos al principio, pero luego funcionan mejor cuanto más se ejecutan. Un buen ejemplo es un sitio web ASP.Net. La primera vez que se accede al sitio web, puede ser un poco más lento, ya que el compilador JIT compila el código C # en código nativo.
fuente
Algunas buenas respuestas aquí sobre la pregunta específica que hizo. Me gustaría dar un paso atrás y mirar el panorama general.
Tenga en cuenta que la percepción de su usuario sobre la velocidad del software que escribe se ve afectada por muchos otros factores además de qué tan bien optimiza el codegen. Aquí hay unos ejemplos:
La administración manual de la memoria es difícil de hacer correctamente (sin fugas) y aún más difícil de hacer de manera eficiente (memoria libre poco después de que haya terminado). En general, es más probable que el uso de un GC produzca un programa que gestione bien la memoria. ¿Está dispuesto a trabajar muy duro y retrasar la entrega de su software, en un intento de superar al GC?
Mi C # es más fácil de leer y comprender que mi C ++. También tengo más formas de convencerme de que mi código C # funciona correctamente. Eso significa que puedo optimizar mis algoritmos con menos riesgo de introducir errores (y a los usuarios no les gusta el software que falla, ¡incluso si lo hace rápidamente!)
Puedo crear mi software más rápido en C # que en C ++. Eso libera tiempo para trabajar en el rendimiento y aún así entregar mi software a tiempo.
Es más fácil escribir una buena interfaz de usuario en C # que en C ++, por lo que es más probable que pueda pasar el trabajo a un segundo plano mientras la interfaz de usuario se mantiene receptiva, o proporcionar progreso o escuchar la interfaz de usuario cuando el programa tiene que bloquearse por un tiempo. Esto no hace que nada sea más rápido, pero hace que los usuarios estén más contentos de esperar.
Todo lo que dije sobre C # probablemente sea cierto para Java, simplemente no tengo la experiencia para decirlo con seguridad.
fuente
Si es un programador de Java / C # que está aprendiendo C ++, tendrá la tentación de seguir pensando en términos de Java / C # y traducir literalmente a la sintaxis de C ++. En ese caso, solo obtiene los beneficios mencionados anteriormente del código nativo frente al interpretado / JIT. Para obtener la mayor ganancia de rendimiento en C ++ frente a Java / C #, debe aprender a pensar en C ++ y diseñar código específicamente para aprovechar las fortalezas de C ++.
Parafraseando a Edsger Dijkstra : [su primer idioma] mutila la mente más allá de la recuperación.
Parafraseando a Jeff Atwood : puedes escribir [tu primer idioma] en cualquier idioma nuevo.
fuente
Una de las optimizaciones JIT más importantes es la inserción de métodos. Java incluso puede incluir métodos virtuales en línea si puede garantizar la corrección del tiempo de ejecución. Este tipo de optimización generalmente no puede ser realizado por compiladores estáticos estándar porque necesita un análisis de todo el programa, lo cual es difícil debido a la compilación separada (en contraste, JIT tiene todo el programa disponible). La inserción de métodos mejora otras optimizaciones, proporcionando bloques de código más grandes para optimizar.
La asignación de memoria estándar en Java / C # también es más rápida y la desasignación (GC) no es mucho más lenta, sino menos determinista.
fuente
free
ydelete
tampoco son deterministas y GC se puede hacer determinista sin asignar.Es poco probable que los lenguajes de máquina virtual superen a los lenguajes compilados, pero pueden acercarse lo suficiente como para que no importe, por (al menos) las siguientes razones (estoy hablando de Java aquí ya que nunca he hecho C #).
1 / El Java Runtime Environment generalmente puede detectar fragmentos de código que se ejecutan con frecuencia y realizar la compilación Just-In-Time (JIT) de esas secciones para que, en el futuro, se ejecuten a la velocidad de compilación completa.
2 / Grandes porciones de las bibliotecas de Java se compilan para que, cuando llame a una función de biblioteca, esté ejecutando código compilado, no interpretado. Puede ver el código (en C) descargando OpenJDK.
3 / A menos que esté haciendo cálculos masivos, la mayor parte del tiempo que su programa se está ejecutando, está esperando la entrada de un humano muy lento (relativamente hablando).
4 / Dado que gran parte de la validación del código de bytes de Java se realiza en el momento de cargar la clase, la sobrecarga normal de las comprobaciones en tiempo de ejecución se reduce considerablemente.
5 / En el peor de los casos, el código de alto rendimiento se puede extraer a un módulo compilado y llamar desde Java (ver JNI) para que se ejecute a toda velocidad.
En resumen, el código de bytes de Java nunca superará al lenguaje de máquina nativo, pero hay formas de mitigar esto. La gran ventaja de Java (como yo lo veo) es la enorme biblioteca estándar y la naturaleza multiplataforma.
fuente
Orion Adrian , permíteme invertir tu publicación para ver cuán infundadas son tus comentarios, porque también se puede decir mucho sobre C ++. Y decir que el compilador Java / C # optimiza las funciones vacías realmente te hace sonar como si no lo estuvieras mi experto en optimización, porque a) ¿por qué un programa real debería contener funciones vacías, excepto por un código heredado realmente malo, b) que realmente no lo es optimización negra y de vanguardia.
Aparte de esa frase, despotricó descaradamente sobre punteros, pero ¿no funcionan los objetos en Java y C # básicamente como punteros de C ++? ¿No pueden superponerse? ¿No pueden ser nulos? C (y la mayoría de las implementaciones de C ++) tiene la palabra clave restrict, ambos tienen tipos de valor, C ++ tiene referencia a valor con garantía no nula. ¿Qué ofrecen Java y C #?
>>>>>>>>>>>
En general, C y C ++ pueden ser tan rápidos o más rápidos porque el compilador AOT, un compilador que compila su código antes de la implementación, de una vez por todas, en su servidor de compilación de muchos núcleos de alta memoria, puede hacer optimizaciones que un programa compilado en C # no puede porque tiene mucho tiempo para hacerlo. El compilador puede determinar si la máquina es Intel o AMD; Pentium 4, Core Solo o Core Duo; o si es compatible con SSE4, etc., y si su compilador no es compatible con el envío en tiempo de ejecución, puede resolverlo usted mismo implementando un puñado de binarios especializados.
El programa AC # se compila comúnmente al ejecutarlo para que funcione decentemente bien en todas las máquinas, pero no se optimiza tanto como podría estarlo para una sola configuración (es decir, procesador, conjunto de instrucciones, otro hardware), y debe pasar algo de tiempo primero. Las características como la fisión de bucle, la inversión de bucle, la vectorización automática, la optimización de todo el programa, la expansión de la plantilla, la salida a bolsa y muchas más, son muy difíciles de resolver todas y por completo de una manera que no moleste al usuario final.
Además, ciertas características del lenguaje permiten al compilador en C ++ o C hacer suposiciones sobre su código que le permiten optimizar ciertas partes que simplemente no son seguras para el compilador de Java / C #. Cuando no tiene acceso a la identificación de tipo completo de genéricos o un flujo de programa garantizado, hay muchas optimizaciones que simplemente no son seguras.
Además, C ++ y C realizan muchas asignaciones de pila a la vez con solo un incremento de registro, que seguramente es más eficiente que las asignaciones de Javas y C # en cuanto a la capa de abstracción entre el recolector de basura y su código.
Ahora no puedo hablar por Java en el siguiente punto, pero sé que los compiladores de C ++, por ejemplo, eliminarán métodos y llamadas a métodos cuando sepa que el cuerpo del método está vacío, eliminará subexpresiones comunes, puede intentarlo y volver a intentarlo. para encontrar el uso óptimo del registro, no impone la verificación de límites, autovectorizará los bucles y los bucles internos y se invertirá de adentro hacia afuera, mueve los condicionales fuera de los bucles, divide y deshace los bucles. Expandirá std :: vector en matrices nativas de cero sobrecarga como lo haría en C. Hará optimizaciones entre procedimientos. Construirá valores de retorno directamente en el sitio de la persona que llama. Doblará y propagará expresiones. Reordenará los datos de una manera amigable con la caché. Hará saltar hilos. Le permite escribir trazadores de rayos en tiempo de compilación sin sobrecarga de tiempo de ejecución. Hará optimizaciones basadas en gráficos muy costosas. Hará reducción de fuerza, si reemplaza ciertos códigos con código sintácticamente totalmente desigual pero semánticamente equivalente (el antiguo "xor foo, foo" es simplemente la optimización más simple, aunque obsoleta de este tipo). Si lo solicita amablemente, puede omitir los estándares de punto flotante IEEE y habilitar aún más optimizaciones, como el reordenamiento de operandos de punto flotante. Una vez que haya masajeado y masacrado su código, es posible que se repita todo el proceso, porque a menudo, ciertas optimizaciones sientan las bases para optimizaciones aún más seguras. También podría volver a intentarlo con parámetros mezclados y ver cómo puntúa la otra variante en su clasificación interna. Y utilizará este tipo de lógica en todo su código. donde reemplaza ciertos códigos con un código sintácticamente totalmente desigual pero semánticamente equivalente (el antiguo "xor foo, foo" es simplemente la optimización más simple, aunque obsoleta de este tipo). Si lo solicita amablemente, puede omitir los estándares de punto flotante IEEE y habilitar aún más optimizaciones, como el reordenamiento de operandos de punto flotante. Una vez que haya masajeado y masacrado su código, es posible que se repita todo el proceso, porque a menudo, ciertas optimizaciones sientan las bases para optimizaciones aún más seguras. También podría volver a intentarlo con parámetros mezclados y ver cómo puntúa la otra variante en su clasificación interna. Y utilizará este tipo de lógica en todo su código. donde reemplaza ciertos códigos con un código sintácticamente totalmente desigual pero semánticamente equivalente (el antiguo "xor foo, foo" es simplemente la optimización más simple, aunque obsoleta de este tipo). Si lo solicita amablemente, puede omitir los estándares de punto flotante IEEE y habilitar aún más optimizaciones, como el reordenamiento de operandos de punto flotante. Una vez que haya masajeado y masacrado su código, es posible que se repita todo el proceso, porque a menudo, ciertas optimizaciones sientan las bases para optimizaciones aún más seguras. También podría volver a intentarlo con parámetros mezclados y ver cómo puntúa la otra variante en su clasificación interna. Y utilizará este tipo de lógica en todo su código. Si lo solicita amablemente, puede omitir los estándares de punto flotante IEEE y habilitar aún más optimizaciones, como el reordenamiento de operandos de punto flotante. Una vez que haya masajeado y masacrado su código, es posible que se repita todo el proceso, porque a menudo, ciertas optimizaciones sientan las bases para optimizaciones aún más seguras. También podría volver a intentarlo con parámetros mezclados y ver cómo puntúa la otra variante en su clasificación interna. Y utilizará este tipo de lógica en todo su código. Si lo solicita amablemente, puede omitir los estándares de punto flotante IEEE y habilitar aún más optimizaciones, como el reordenamiento de operandos de punto flotante. Una vez que haya masajeado y masacrado su código, es posible que se repita todo el proceso, porque a menudo, ciertas optimizaciones sientan las bases para optimizaciones aún más seguras. También podría volver a intentarlo con parámetros mezclados y ver cómo la otra variante puntúa en su clasificación interna. Y utilizará este tipo de lógica en todo su código. También podría volver a intentarlo con parámetros mezclados y ver cómo la otra variante puntúa en su clasificación interna. Y utilizará este tipo de lógica en todo su código. También podría volver a intentarlo con parámetros mezclados y ver cómo la otra variante puntúa en su clasificación interna. Y utilizará este tipo de lógica en todo su código.
Entonces, como puede ver, hay muchas razones por las que ciertas implementaciones de C ++ o C serán más rápidas.
Dicho todo esto, se pueden hacer muchas optimizaciones en C ++ que eliminarán todo lo que pueda hacer con C #, especialmente en el ámbito de procesamiento numérico, en tiempo real y cercano al metal, pero no exclusivamente allí. Ni siquiera tiene que tocar un solo puntero para recorrer un largo camino.
Entonces, dependiendo de lo que estés escribiendo, iría con uno u otro. Pero si está escribiendo algo que no depende del hardware (controlador, videojuego, etc.), no me preocuparía por el rendimiento de C # (de nuevo, no puedo hablar de Java). Estará bien.
<<<<<<<<<<
Generalmente, ciertos argumentos generalizados pueden sonar bien en publicaciones específicas, pero generalmente no suenan ciertamente creíbles.
De todos modos, para hacer las paces: AOT es genial, al igual que JIT . La única respuesta correcta puede ser: depende. Y las personas realmente inteligentes saben que, de todos modos, puedes usar lo mejor de ambos mundos.
fuente
Solo sucedería si el intérprete de Java está produciendo un código de máquina que en realidad está mejor optimizado que el código de máquina que su compilador está generando para el código de C ++ que está escribiendo, hasta el punto en que el código de C ++ es más lento que el de Java y el costo de interpretación.
Sin embargo, las probabilidades de que eso suceda son bastante bajas, a menos que tal vez Java tenga una biblioteca muy bien escrita y usted tenga su propia biblioteca C ++ mal escrita.
fuente
En realidad, C # no se ejecuta realmente en una máquina virtual como lo hace Java. IL se compila en lenguaje ensamblador, que es código completamente nativo y se ejecuta a la misma velocidad que el código nativo. Puede pre-JIT una aplicación .NET que elimina por completo el costo de JIT y luego está ejecutando código completamente nativo.
La desaceleración con .NET vendrá no porque el código .NET sea más lento, sino porque hace mucho más detrás de escena para hacer cosas como recolectar basura, verificar referencias, almacenar marcos de pila completos, etc. Esto puede ser bastante poderoso y útil cuando aplicaciones de construcción, pero también tiene un costo. Tenga en cuenta que también puede hacer todas estas cosas en un programa C ++ (gran parte de la funcionalidad básica de .NET es en realidad código .NET que puede ver en ROTOR). Sin embargo, si escribiera manualmente la misma funcionalidad, probablemente terminaría con un programa mucho más lento, ya que el tiempo de ejecución de .NET se ha optimizado y ajustado con precisión.
Dicho esto, una de las fortalezas del código administrado es que puede ser completamente verificable, es decir. puede verificar que el código nunca accederá a la memoria de otros procesos o eliminará los mensajes antes de ejecutarlo. Microsoft tiene un prototipo de investigación de un sistema operativo totalmente administrado que sorprendentemente ha demostrado que un entorno 100% administrado puede funcionar significativamente más rápido que cualquier sistema operativo moderno al aprovechar esta verificación para desactivar las funciones de seguridad que ya no son necesarias para los programas administrados. (estamos hablando como 10x en algunos casos). SE Radio tiene un gran episodio hablando de este proyecto.
fuente
En algunos casos, el código administrado puede ser más rápido que el código nativo. Por ejemplo, los algoritmos de recolección de basura "marcar y barrer" permiten que entornos como JRE o CLR liberen una gran cantidad de objetos de corta duración (generalmente) en una sola pasada, donde la mayoría de los objetos del montón de C / C ++ se liberan de uno en uno. un momento.
De wikipedia :
Dicho esto, he escrito mucho C # y mucho C ++, y he ejecutado muchos puntos de referencia. En mi experiencia, C ++ es mucho más rápido que C #, de dos maneras: (1) si toma algún código que ha escrito en C #, lo transfiere a C ++, el código nativo tiende a ser más rápido. ¿Cuanto más rápido? Bueno, varía mucho, pero no es raro ver una mejora del 100% en la velocidad. (2) En algunos casos, la recolección de basura puede ralentizar enormemente una aplicación administrada. El .NET CLR hace un trabajo terrible con montones grandes (digamos,> 2GB) y puede terminar pasando mucho tiempo en GC, incluso en aplicaciones que tienen pocos, o incluso ningún, objetos de duración intermedia.
Por supuesto, en la mayoría de los casos con los que me he encontrado, los lenguajes administrados son lo suficientemente rápidos, por mucho, y la compensación de mantenimiento y codificación por el rendimiento adicional de C ++ simplemente no es buena.
fuente
Aquí hay un punto de referencia interesante http://zi.fi/shootout/
fuente
En realidad, HotSpot JVM de Sun utiliza la ejecución en "modo mixto". Interpreta el código de bytes del método hasta que determina (generalmente a través de un contador de algún tipo) que un bloque de código en particular (método, bucle, bloque try-catch, etc.) se ejecutará mucho, luego lo compila con JIT. El tiempo requerido para compilar un método JIT a menudo es más largo que si el método fuera a ser interpretado si es un método que rara vez se ejecuta. El rendimiento suele ser mayor para el "modo mixto" porque la JVM no pierde tiempo en el código JIT que rara vez, si es que alguna vez, se ejecuta. C # y .NET no hacen esto. .NET JIT todo lo que, a menudo, es una pérdida de tiempo.
fuente
Ir a leer sobre Dynamo de HP Labs , un intérprete para PA-8000 que se ejecuta en PA-8000 y, a menudo, ejecuta programas más rápido que de forma nativa. ¡Entonces no parecerá nada sorprendente!
No lo considere un "paso intermedio": ejecutar un programa ya implica muchos otros pasos, en cualquier idioma.
A menudo se reduce a:
Los programas tienen puntos calientes, por lo que incluso si ejecuta más lentamente el 95% del cuerpo del código que tiene que ejecutar, aún puede ser competitivo en rendimiento si es más rápido en el 5% caliente
un HLL sabe más sobre su intención que un LLL como C / C ++, por lo que puede generar un código más optimizado (OCaml tiene aún más y, en la práctica, a menudo es incluso más rápido)
un compilador JIT tiene mucha información que un compilador estático no tiene (como los datos reales que tiene esta vez)
un compilador JIT puede hacer optimizaciones en tiempo de ejecución que los enlazadores tradicionales no pueden hacer (como reordenar las ramas para que el caso común sea plano, o llamadas de biblioteca en línea)
En general, C / C ++ son lenguajes bastante pésimos para el rendimiento: hay relativamente poca información sobre sus tipos de datos, no hay información sobre sus datos y no hay tiempo de ejecución dinámico que permita mucho en el camino de la optimización del tiempo de ejecución.
fuente
Es posible que obtenga ráfagas breves cuando Java o CLR es más rápido que C ++, pero en general el rendimiento es peor durante la vida útil de la aplicación: consulte www.codeproject.com/KB/dotnet/RuntimePerformance.aspx para obtener algunos resultados al respecto.
fuente
Aquí está la respuesta de Cliff Click: http://www.azulsystems.com/blog/cliff/2009-09-06-java-vs-c-performanceagain
fuente
Eso es ilógico. El uso de una representación intermedia no degrada de forma inherente el rendimiento. Por ejemplo, llvm-gcc compila C y C ++ a través de LLVM IR (que es una máquina virtual de registro infinito) en código nativo y logra un rendimiento excelente (a menudo superando a GCC).
Aquí hay unos ejemplos:
Las máquinas virtuales con compilación JIT facilitan la generación de código en tiempo de ejecución (por ejemplo,
System.Reflection.Emit
en .NET) para que pueda compilar el código generado sobre la marcha en lenguajes como C # y F #, pero debe recurrir a escribir un intérprete relativamente lento en C o C ++. Por ejemplo, para implementar expresiones regulares.Partes de la máquina virtual (por ejemplo, la barrera de escritura y el asignador) a menudo se escriben en ensamblador codificado a mano porque C y C ++ no generan código lo suficientemente rápido. Si un programa hace hincapié en estas partes de un sistema, posiblemente podría superar cualquier cosa que se pueda escribir en C o C ++.
La vinculación dinámica del código nativo requiere la conformidad con una ABI que puede impedir el rendimiento y obvia la optimización de todo el programa, mientras que la vinculación generalmente se difiere en las VM y puede beneficiarse de las optimizaciones de todo el programa (como los genéricos reificados de .NET).
También me gustaría abordar algunos problemas con la respuesta altamente votada de paercebal anterior (porque alguien sigue eliminando mis comentarios sobre su respuesta) que presenta una vista polarizada contraproducente:
Por lo tanto, la metaprogramación de plantillas solo funciona si el programa está disponible en tiempo de compilación, lo que a menudo no es el caso, por ejemplo, es imposible escribir una biblioteca de expresiones regulares de rendimiento competitivo en C ++ básico porque es incapaz de generar código en tiempo de ejecución (un aspecto importante de metaprogramación).
En C #, eso solo es cierto para los tipos de referencia y no es cierto para los tipos de valor.
La gente ha observado que Java supera a C ++ en la prueba SOR del punto de referencia SciMark2 precisamente porque los punteros impiden las optimizaciones relacionadas con el aliasing.
También vale la pena señalar que .NET se especializa en tipos de genéricos en bibliotecas vinculadas dinámicamente después de la vinculación, mientras que C ++ no puede porque las plantillas deben resolverse antes de vincular. Y, obviamente, la gran ventaja que tienen los genéricos sobre las plantillas son los mensajes de error comprensibles.
fuente
Además de lo que han dicho otros, según tengo entendido .NET y Java son mejores en la asignación de memoria. Por ejemplo, pueden compactar la memoria a medida que se fragmenta, mientras que C ++ no puede (de forma nativa, pero puede hacerlo si está utilizando un recolector de basura inteligente).
fuente
Para cualquier cosa que necesite mucha velocidad, la JVM simplemente llama a una implementación de C ++, por lo que es más una cuestión de qué tan buenas son sus bibliotecas que de qué tan buena es la JVM para la mayoría de las cosas relacionadas con el sistema operativo. La recolección de basura reduce su memoria a la mitad, pero el uso de algunas de las características más sofisticadas de STL y Boost tendrá el mismo efecto pero con muchas veces el potencial de error.
Si solo está usando bibliotecas C ++ y muchas de sus características de alto nivel en un proyecto grande con muchas clases, probablemente terminará más lento que usar una JVM. Excepto mucho más propenso a errores.
Sin embargo, el beneficio de C ++ es que te permite optimizarte a ti mismo, de lo contrario, estás atascado con lo que hace el compilador / jvm. Si crea sus propios contenedores, escribe su propia administración de memoria que esté alineada, use SIMD y pase al ensamblaje aquí y allá, puede acelerar al menos 2 x 4 veces más de lo que la mayoría de los compiladores de C ++ harán por sí mismos. Para algunas operaciones, 16x-32x. Eso es usar los mismos algoritmos, si usa mejores algoritmos y paraleliza, los aumentos pueden ser dramáticos, a veces miles de veces más rápido que los métodos comúnmente usados.
fuente
Lo miro desde algunos puntos diferentes.
fuente
Una respuesta muy breve: con un presupuesto fijo, obtendrá una aplicación Java con un mejor rendimiento que una aplicación C ++ (consideraciones de ROI). Además, la plataforma Java tiene perfiladores más decentes, que lo ayudarán a identificar sus puntos de acceso más rápidamente.
fuente