c++ performance f# parallel-processing scientific-computing

c++ - Rendimiento F#en informática científica



performance parallel-processing (10)

Tengo curiosidad sobre cómo el rendimiento de F # se compara con el rendimiento de C ++.

Varía salvajemente dependiendo de la aplicación. Si está haciendo un uso extensivo de estructuras de datos sofisticadas en un programa de subprocesos múltiples, es probable que F # sea una gran ganancia. Si pasa la mayor parte de su tiempo en bucles numéricos ajustados, transformando matrices, entonces C ++ podría ser 2-3 veces más rápido.

Estudio de caso: Ray Tracer Mi punto de referencia here utiliza un árbol para el sacrificio jerárquico y el código de intersección de la esfera de rayos numéricos para generar una imagen de salida. Este punto de referencia tiene varios años y el código C ++ se ha mejorado decenas de veces a lo largo de los años y ha sido leído por cientos de miles de personas. Don Syme en Microsoft logró escribir una implementación F # que es ligeramente más rápida que el código C ++ más rápido cuando se compila con MSVC y se paraleliza con OpenMP.

He leído que se supone que F # es más escalable y más eficiente, pero ¿cómo se compara este rendimiento en el mundo real con C ++?

Desarrollar código es mucho más fácil y rápido con F # que con C ++, y esto se aplica tanto a la optimización como al mantenimiento. En consecuencia, cuando empiece a optimizar un programa, la misma cantidad de esfuerzo generará ganancias de rendimiento mucho mayores si usa F # en lugar de C ++. Sin embargo, F # es un lenguaje de nivel superior y, en consecuencia, limita el rendimiento. Entonces, si tiene tiempo infinito para optimizar, debería, en teoría, siempre ser capaz de producir código más rápido en C ++.

Este es exactamente el mismo beneficio que C ++ tenía sobre Fortran y Fortran tenía ensamblador escrito a mano, por supuesto.

Estudio de caso: descomposición QR Este es un método numérico básico del álgebra lineal proporcionado por bibliotecas como LAPACK. La implementación de LAPACK de referencia es 2.077 líneas de Fortran. Escribí una implementación F # en menos de 80 líneas de código que alcanza el mismo nivel de rendimiento. Pero la implementación de referencia no es rápida: las implementaciones adaptadas por los proveedores, como la Biblioteca de Kernel de Matriz (MKL) de Intel, a menudo son 10 veces más rápidas. Sorprendentemente, logré optimizar mi código F # mucho más allá del rendimiento de la implementación de Intel en hardware Intel, manteniendo mi código bajo 150 líneas de código y completamente genérico (¡puede manejar matrices simples y dobles, y matrices complejas e incluso simbólicas!): para matrices altas y delgadas, mi código F # es hasta 3 veces más rápido que Intel MKL.

Tenga en cuenta que la moraleja de este estudio de caso no es que deba esperar que su F # sea más rápido que las bibliotecas ajustadas por el proveedor, sino que incluso los expertos como Intel perderán optimizaciones productivas de alto nivel si utilizan solo idiomas de nivel inferior. Sospecho que los expertos en optimización numérica de Intel no aprovecharon completamente el paralelismo porque sus herramientas lo hacen extremadamente engorroso, mientras que F # lo hace sin esfuerzo.

¿Qué tan bien funciona el punto flotante?

El rendimiento es similar al ANSI C pero algunas funcionalidades (por ejemplo, modos de redondeo) no están disponibles en .NET.

¿Permite instrucciones vectoriales?

No.

¿Qué tan amigable es con la optimización de compiladores?

Esta pregunta no tiene sentido: F # es un lenguaje propietario de .NET de Microsoft con un solo compilador.

¿Qué tan grande tiene una huella de memoria?

Una aplicación vacía usa 1.3Mb aquí.

¿Permite un control detallado sobre la localidad de memoria?

Mejor que la mayoría de los lenguajes seguros para memoria, pero no tan bueno como C. Por ejemplo, puede desempaquetar estructuras de datos arbitrarias en F # representándolas como "estructuras".

¿tiene capacidad para procesadores de memoria distribuida, por ejemplo Cray?

Depende de lo que quiere decir con "capacidad para". Si puede ejecutar .NET en ese Cray, entonces podría usar el envío de mensajes en F # (al igual que en el siguiente idioma), pero F # está destinado principalmente para equipos x86 multinúcleo de escritorio.

¿Qué características tiene que pueden interesar a la ciencia computacional donde está involucrado el procesamiento de números pesados?

La seguridad de la memoria significa que no obtiene fallas de segmentación ni violaciones de acceso. El soporte para el paralelismo en .NET 4 es bueno. La capacidad de ejecutar código sobre la marcha a través de la sesión interactiva F # en Visual Studio 2010 es extremadamente útil para la informática técnica interactiva.

¿Hay implementaciones de computación científica que lo usan?

Nuestros productos comerciales para computación científica en F # ya tienen cientos de usuarios.

Sin embargo, su línea de preguntas indica que usted piensa en la informática científica como computación de alto rendimiento (por ejemplo, Cray) y no en la informática técnica interactiva (por ejemplo, MATLAB, Mathematica). F # es para este último.

Tengo curiosidad sobre cómo el rendimiento de F # se compara con el rendimiento de C ++. Hice una pregunta similar con respecto a Java, y la impresión que obtuve fue que Java no es adecuado para una gran cantidad de números.

He leído que se supone que F # es más escalable y más eficiente, pero ¿cómo se compara este rendimiento en el mundo real con C ++? preguntas específicas sobre la implementación actual son:

  • ¿Qué tan bien funciona el punto flotante?
  • ¿Permite instrucciones vectoriales?
  • ¿Qué tan amigable es con la optimización de compiladores?
  • ¿Qué tan grande tiene una huella de memoria? ¿Permite un control detallado sobre la localidad de memoria?
  • ¿tiene capacidad para procesadores de memoria distribuida, por ejemplo Cray?
  • ¿Qué características tiene que pueden interesar a la ciencia computacional donde está involucrado el procesamiento de números pesados?
  • ¿Hay implementaciones de computación científica que lo usan?

Gracias


Además de lo que otros dijeron, hay un punto importante sobre F # y eso es paralelismo . El rendimiento del código ordinario F # está determinado por CLR, aunque es posible que pueda usar LAPACK desde F # o que pueda realizar llamadas nativas utilizando C ++ / CLI como parte de su proyecto.

Sin embargo, los programas funcionales bien diseñados tienden a ser mucho más fáciles de paralelizar, lo que significa que puede obtener fácilmente rendimiento mediante el uso de CPU multinúcleo, que definitivamente están disponibles para usted si está haciendo algo de informática científica. Aquí hay un par de enlaces relevantes:

Con respecto a la informática distribuida, puede usar cualquier marco de computación distribuida que esté disponible para la plataforma .NET. Existe un proyecto de MPI.NET, que funciona bien con F #, pero también es posible utilizar DryadLINQ, que es un proyecto de MSR.


Al igual que con todas las comparaciones de idioma / rendimiento, su kilometraje depende en gran medida de qué tan bien puede codificar.

F # es un derivado de OCaml. Me sorprendió descubrir que OCaml se usa mucho en el mundo financiero, donde el rendimiento de la crítica de números es muy importante. Además, me sorprendió descubrir que OCaml es uno de los idiomas más rápidos, con un rendimiento a la par de los compiladores más rápidos de C y C ++.

F # se basa en el CLR . En el CLR, el código se expresa en una forma de bytecode llamado Common Intermediate Language. Como tal, se beneficia de las capacidades de optimización del JIT, y tiene un rendimiento comparable a C # (pero no necesariamente C ++), si el código está bien escrito.

El código CIL se puede compilar en código nativo en un paso separado antes del tiempo de ejecución utilizando Native Image Generator (NGEN). Esto acelera todas las ejecuciones posteriores del software ya que la compilación CIL-a-nativo ya no es necesaria.

Una cosa a considerar es que los lenguajes funcionales como F # se benefician de un estilo de programación más declarativo. En cierto sentido, está sobre-especificando la solución en lenguajes imperativos como C ++, y esto limita la capacidad del compilador para optimizar. Un estilo de programación más declarativo teóricamente puede dar al compilador oportunidades adicionales para la optimización algorítmica.


Aquí hay dos ejemplos que puedo compartir:

  1. Multiplicación de matrices: tengo una publicación de blog que compara diferentes implementaciones de multiplicación de matrices .

  2. LBFGS

Tengo un solucionador de regresión logística a gran escala que utiliza la optimización LBFGS, que está codificada en C ++. La implementación está bien ajustada. Modifiqué código en código en C ++ / CLI, es decir, compilé el código en .Net. La versión de .Net es de 3 a 5 veces más lenta que la compilada ingenua en diferentes conjuntos de datos. Si codifica LBFGS en F #, el rendimiento no puede ser mejor que C ++ / CLI o C #, (pero estaría muy cerca).

Tengo otra publicación sobre por qué F # es el lenguaje para la minería de datos , aunque no está del todo relacionado con el problema de rendimiento que le concierne aquí, está bastante relacionado con la informática científica en F #.


Depende del tipo de informática científica que esté haciendo.

Si está haciendo traditional heavy computing , por ejemplo, álgebra lineal, varias optimizaciones, entonces no debe poner su código en .Net Framework, al menos no es adecuado en F #. Debido a que esto es a nivel de algoritmo, la mayoría de los algoritmos deben codificarse en un lenguaje imperativo para tener un buen rendimiento en tiempo de ejecución y uso de la memoria. Otros mencionaron paralelos, debo decir que probablemente sea inútil cuando haces cosas de bajo nivel como una implementación SVD paralela. Porque cuando sabes cómo hacer paralelo a una SVD, simplemente no usas un lenguaje de alto nivel, Fortran, C o C modificado (por ejemplo, cilk ) son tus amigos.

Sin embargo, gran parte de la informática científica actual no es de este tipo, que es algún tipo de aplicaciones de alto nivel, por ejemplo, informática estadística y minería de datos. En estas tareas, aparte de algunos álgebra lineal u optimización, también hay una gran cantidad de flujos de datos, IO, preposesión, gráficos, etc. Para estas tareas, F # es realmente potente, por su brevedad, funcionalidad, seguridad, facilidad para paralelo, etc.

Como otros han mencionado, .Net es compatible con Platform Invoke, en realidad bastantes proyectos dentro de MS usan .Net y P / Invoke para mejorar el desempeño en el cuello de la botella.


En primer lugar, C es significativamente más rápido que C ++. Por lo tanto, si necesita tanta velocidad, debe hacer la lib, etc. en c.

Con respecto a F #, la mayoría de los benchmarks usan Mono que es hasta 2 * más lento que MS CLR debido en parte a su uso del boehm GC (tienen un nuevo GC y LVVM pero aún son inmaduros, no admiten genéricos, etc.).

.NEt idiomas se compilan a un IR (el CIL) que compilan al código nativo tan eficientemente como C ++. Hay un problema establecido que la mayoría de los lenguajes de GC sufren y que es una gran cantidad de escrituras mutables (esto incluye C ++ .NET como se mencionó anteriormente). Y existe un cierto conjunto de problemas científicos que lo requiere, estos, cuando sea necesario, probablemente deberían usar una biblioteca nativa o usar el patrón Flyweight para reutilizar objetos de un grupo (lo que reduce las escrituras). La razón es que hay una barrera de escritura en .NET CLR donde al actualizar un campo de referencia (incluyendo un cuadro) establecerá un bit en una tabla que dice que esta tabla está modificada. Si su código consiste en muchas de esas escrituras, sufrirá.

Dicho esto, una aplicación .NET como C # que usa muchos códigos estáticos, estructuras y ref / out en las estructuras puede producir un rendimiento tipo C, pero es muy difícil codificar así o mantener el código (como C).

Sin embargo, donde F # brilla es parralelismo sobre datos inmutables que van de la mano con problemas más leídos. Vale la pena señalar que la mayoría de los puntos de referencia son mucho más altos en las escrituras mutables que en las aplicaciones de la vida real.

Con respecto al punto flotante, debe usar una lib alternativa (es decir, la .Net) a la oCaml debido a que es lenta. C / C ++ permite una precisión más baja que oCaml no lo hace de forma predeterminada.

Por último, argumentaré que un lenguaje de alto nivel como C #, F # y un perfil adecuado le otorgarán un mejor comportamiento que C y C ++ para el mismo tiempo de desarrollador. Si cambia el cuello de una botella para realizar una llamada rápida, también obtendrá un rendimiento similar al de C en áreas críticas. Dicho eso, si tiene un presupuesto ilimitado y se preocupa más por la velocidad y el mantenimiento que C, es el camino a seguir (no C ++).


Lo último que supe es que la mayoría de los cálculos científicos aún se realizaban en FORTRAN. Todavía es más rápido que cualquier otra cosa para los problemas de álgebra lineal, no Java, no C, no C ++, no C #, no F #. LINPACK está muy bien optimizado.

Pero el comentario sobre "su kilometraje puede variar" es cierto para todos los puntos de referencia. Las declaraciones generales (excepto la mía) raramente son verdaderas.


No creo que encuentres mucha información confiable, desafortunadamente. F # sigue siendo un lenguaje muy nuevo, por lo que incluso si fuera ideal para cargas de trabajo pesadas de rendimiento, todavía no habría tanta gente con experiencia significativa sobre la que informar. Además, el rendimiento es muy difícil de medir con precisión y microbenchmarks son difíciles de generalizar. Incluso dentro de C ++, puede ver diferencias dramáticas entre los compiladores: ¿se está preguntando si F # es competitivo con cualquier compilador de C ++ o con el hipotético "mejor posible" ejecutable de C ++?

En cuanto a los puntos de referencia específicos contra C ++, aquí hay algunos enlaces posiblemente relevantes: O''Caml vs. F #: descomposición QR ; here . Tenga en cuenta que como autor de material relacionado con F # y como proveedor de herramientas F #, el escritor tiene un gran interés en el éxito de F #, por lo que tome estas afirmaciones con un grano de sal.

Creo que es seguro decir que habrá algunas aplicaciones en las que F # sea competitivo en el tiempo de ejecución y probablemente en otras no. F # probablemente requiera más memoria en la mayoría de los casos. Por supuesto, el rendimiento final también dependerá en gran medida de la habilidad del programador. Creo que F # será casi con certeza un lenguaje más productivo para programar para un programador moderadamente competente. Además, creo que, en este momento, el CLR en Windows tiene un mejor rendimiento que Mono en la mayoría de los sistemas operativos para la mayoría de las tareas, lo que también puede afectar sus decisiones. Por supuesto, dado que F # es probablemente más fácil de paralelizar que C ++, también dependerá del tipo de hardware en el que planea ejecutar.

En definitiva, creo que la única manera de responder realmente a esta pregunta es escribir códigos F # y C ++ representativos del tipo de cálculos que desea realizar y compararlos.


Si digo "pregunta nuevamente en 2-3 años", creo que responderá a tu pregunta por completo :-)

En primer lugar, no espere que F # sea diferente de C # perf-wise, a menos que esté haciendo algunas recursiones intrincadas a propósito y supongo que no lo es ya que ha preguntado acerca de los números.

En términos de punto flotante, es mejor que Java, ya que CLR no apunta a la uniformidad multiplataforma, lo que significa que JIT irá a 80 bits siempre que sea posible. Por otro lado, no tiene control sobre eso más allá de observar el número de variables para asegurarse de que haya suficientes registros de FP.

En cuanto al vector, si gritas lo suficientemente fuerte, puede pasar algo en 2-3 años ya que Direct3D ingresa .NET como API general de todos modos y el código C # hecho en XNA se ejecuta en Xbox, que es lo más parecido al metal puro que puedes obtener con CLR . Eso aún significa que necesitarías hacer un código intermediario por tu cuenta.

Así que no esperes CUDA o incluso la capacidad de simplemente vincular libs NVIDIA y ponerse en marcha. Tendrás mucha más suerte probando ese enfoque con Haskell si por alguna razón realmente, realmente necesitas un lenguaje "funcional" ya que Haskell fue diseñado para ser amigable por pura necesidad.

Ya se ha mencionado Mono.Simd y aunque debería ser back-portable para CLR, podría ser bastante trabajo realmente hacerlo.

Hay bastante código en una publicación de social.msdn sobre el uso de SSE3 en .NET, con C ++ / CLI y C #, viene blitting de matriz, inyectando código SSE3 para perf, etc.

Se habló de ejecutar CECIL en C # compilado para extraer partes en HLSL, compilar sombreadores y vincular un código de pegamento para programarlo (CUDA está haciendo el equivalente de todos modos), pero no creo que haya nada ejecutable en eso.

Una cosa que puede valer más para ti si quieres probar algo pronto es PhysX.Net en codeplex . No esperes que solo desempaquete y haga la magia. Sin embargo, ih tiene actualmente un autor activo y el código es tanto normal C ++ como C ++ / CLI y yopu probablemente pueda obtener alguna ayuda del autor si desea entrar en detalles y tal vez usar un enfoque similar para CUDA. Para CUDA a toda velocidad, igual tendrás que compilar tus propios kernels y luego simplemente interactuar con .NET para que cuanto más fácil sea esa parte, más feliz serás.

Hay una lib de CUDA.NET que se supone que es gratuita, pero la página da solo la dirección de correo electrónico, así que espere algunas condiciones, y mientras el autor escribe un blog , no es particularmente comunicativo sobre lo que hay dentro de la lib.

Ah, y si tienes el presupuesto, podrías darle una mirada a Psi Lambda (KappaCUDAnet es la parte de .NET). Aparentemente subirán los precios en noviembre (si no es un truco de ventas :-)


  • F # realiza el cálculo en coma flotante tan rápido como lo permita .NET CLR. No hay mucha diferencia con C # u otros lenguajes .NET.
  • F # no permite instrucciones de vectores por sí mismo, pero si su CLR tiene una API para estos, F # no debería tener problemas para usarlo. Ver por ejemplo Mono .
  • Hasta donde yo sé, solo hay un compilador de F # por el momento, así que tal vez la pregunta debería ser "¿qué tan bueno es el compilador de F # cuando se trata de la optimización?". La respuesta es en cualquier caso "potencialmente tan buena como el compilador de C #, probablemente un poco peor en este momento". Tenga en cuenta que F # difiere de, por ejemplo, C # en su soporte para alinear en tiempo de compilación, lo que potencialmente permite un código más eficiente que se basa en los genéricos.
  • Las huellas de memoria de los programas F # son similares a las de otros lenguajes .NET. La cantidad de control que tiene sobre la asignación y la recolección de basura es la misma que en otros lenguajes .NET.
  • No sé sobre el soporte para la memoria distribuida.
  • F # tiene primitivas muy buenas para tratar con estructuras de datos planas, por ejemplo, matrices y listas. Busque, por ejemplo, el contenido del módulo Array: map, map2, mapi, iter, fold, zip ... Las matrices son populares en la informática científica, supongo que debido a sus inherentemente buenas propiedades de memoria local.
  • Para los paquetes de cálculo científico que usan F #, es posible que desee ver lo que está haciendo Jon Harrop.