2009-03-11 9 views
8

¿Qué consecuencias inusuales e inesperadas han ocurrido en términos de rendimiento, memoria, etc. al cambiar de ejecutar sus aplicaciones .NET bajo el JIT de 64 bits frente al JIT de 32 bits? Estoy interesado en lo bueno, pero estoy más interesado en los problemas sorprendentemente malos que las personas se han topado.Mi dolor de cabeza de 32 bits ahora es una migraña de 64 bits?!? (o 64 bits .NET CLR Runtime issues)

Estoy en proceso de escribir una nueva aplicación .NET que se implementará tanto en 32 bits como en 64 bits. Ha habido muchas preguntas relacionadas con los problemas relacionados con la migración de la aplicación. No me preocupa el "gotchas" from a programming/porting standpoint. (es decir: manejar la interpolación nativa/COM correctamente, tipos de referencia incrustados en las estructuras que cambian el tamaño de la estructura, etc.)

Sin embargo, this question and it's answer me hizo pensar - ¿Qué otros problemas estoy pasando por alto?

Ha habido muchas preguntas y publicaciones en el blog que rodean este tema, o tocan un aspecto del mismo, pero no he visto nada que haya compilado una lista decente de problemas.

Particularmente - Mi aplicación está muy atada a la CPU y tiene grandes patrones de uso de memoria (de ahí la necesidad de 64 bits en primer lugar), además de ser de naturaleza gráfica. Me preocupa qué otros problemas ocultos pueden existir en el CLR o JIT que se ejecuta en Windows de 64 bits (usando .NET 3.5sp1).

Éstos son algunos problemas actualmente estoy al tanto de:

me gustaría saber lo que otros, específicos, problemas de las personas han descubierto en el JIT en Windows de 64 bits, y también si hay soluciones para el rendimiento.

¡Gracias a todos!

---- ----- EDITAR

sólo para aclarar -

Soy consciente de que se trata de optimizar temprana es a menudo mal. Soy consciente de que adivinar el sistema a menudo es malo. También sé que la portabilidad a 64 bits tiene sus propios problemas: corremos y probamos sistemas de 64 bits diariamente para ayudar con esto. etc.

Mi aplicación, sin embargo, no es la aplicación comercial típica. Es una aplicación de software científico. Tenemos muchos procesos que utilizan 100% de CPU en todos los núcleos (está altamente enhebrado) durante horas.

Pasé MUCHO tiempo perfilando la aplicación, y eso hace una gran diferencia. Sin embargo, la mayoría de los perfiladores desactivan muchas características del JIT, por lo que los pequeños detalles en cosas como asignación de memoria, alineación en el JIT, etc., pueden ser muy difíciles de precisar cuando se ejecuta bajo un perfilador. De ahí mi necesidad de la pregunta.

+0

Este hilo sería mucho más útil (fácil de encontrar con Google o Stacko-search, etc.) si el título se refería a .NET de 32 y 64 bits. –

Respuesta

3

Recuerdo haber escuchado un problema de un canal de IRC que frecuentaba. Optimiza distancia la copia temporal en este caso:

EventHandler temp = SomeEvent; 
if(temp != null) 
{ 
    temp(this, EventArgs.Empty); 
} 

Poner la condición de carrera de nuevo y causando posibles excepciones de referencia nula.

+0

Interesante .... ¿Es una optimización que solo ocurre en el JIT de 64 bits, o también ocurre en el JIT de 32 bits? –

+0

No ocurre en 32 bits.No fue mi conversación, así que no tengo forma de validar esto, pero la conversación continuó durante una hora más o menos, así que a menos que haya alguna otra fluctuación de 64 bits podría ser la que está trabajando en – Quibblesome

+0

IIRC la fluctuación de 32 bits en realidad no se ajusta a la especificación en este caso y debe optimizarse de esta manera de todos modos. Pero este es un truco utilizado para evitar condiciones de carrera cuando se activan eventos y se desconecta mediante diferentes subprocesos – Quibblesome

-1

No soy tan familiarizados con los problemas de 64 bits, pero tengo un comentario:

Debemos olvidarnos de pequeñas eficiencia, por ejemplo alrededor del 97% de las veces : la optimización prematura es la raíz de todo mal. - Donald Knuth

+4

Pero siempre está ese maldito 3% ... –

+0

Como dije, mi aplicación está muy atada a la CPU. Tengo procesos con tiempos de ejecución de 5 horas. La otra cara de tu comentario es que el 3% de las veces, no es la raíz de todo mal. Tomemos el comentario de Rico Mariani sobre esto: si solo importa el 3% del tiempo, eso significa que una línea en 33 del código es importante para la optimización. –

+0

Por curiosidad, ¿aún se exhiben estos problemas si se dirige a plataformas de 64 bits en VS en lugar de a la CPU por defecto? – Powerlord

1

mayoría de las veces Visual Studio y el compilador hacen un buen trabajo de ocultar los problemas de usted. Sin embargo, soy consciente de un problema importante que puede surgir si configuras tu aplicación para detectar automáticamente la plataforma (x86 vs x64) y también tienen alguna dependencia en dlls de terceros de 32 bits. En este caso, en las plataformas de 64 bits intentará llamar a los dlls utilizando convenciones y estructuras de 64 bits, y simplemente no funcionará.

+0

Sí, pero no me preocupan demasiado estos tipos de problemas. Me preocupan más los problemas de rendimiento/memoria/tiempo de ejecución que son los errores ocultos. –

+0

+1 - He encontrado este problema con una de mis bibliotecas de terceros. Debo incluir versiones de 32 y 64 bits en mi instalador e instalar la versión adecuada. –

1

Usted mencionó los problemas de portabilidad, esos son los que deben preocuparse. Yo (obviamente) no conozco su solicitud, pero tratar de adivinar el JIT es a menudo una completa pérdida de tiempo. Las personas que escriben el JIT tienen una comprensión íntima de la arquitectura de chip x86/x64, y es probable que sepan qué funciona mejor y qué funciona peor que probablemente cualquier otra persona en el planeta.

Sí, es posible que usted tiene un caso esquina que es diferente y único, pero si usted está "en el proceso de escribir una aplicación nueva " entonces yo no me preocuparía por el compilador JIT. Es probable que se evite un bucle tonto en algún lugar que le compre 100 veces la mejora en el rendimiento que obtendrá al tratar de adivinar el JIT. Me recuerda los problemas que tuvimos cuando escribimos nuestro ORM, miramos el código y pensamos que podríamos usar un par de instrucciones de la máquina ... por supuesto, el código se apagó y se conectó a un servidor de base de datos a través de una red , así que estábamos recortando microsegundos de un proceso que estaba limitado por milisegundos en otro lugar.

regla universal de ajustar el rendimiento ... Si no han medido el rendimiento no se sabe donde sus cuellos de botella, sólo piensa sabes ... y es muy probable mal.

+0

Walden: Estoy de acuerdo. Mi aplicación, sin embargo, está MUY LIMITADA EN LA CPU. Es altamente matemático y tiene muchos procesos de tiempo de ejecución de varias horas. Paso mucho tiempo perfilando y optimizando detalles finos, que pueden ser tremendamente útiles. Los profilers son difíciles, sin embargo, ya que deshabilitan los problemas de JIT. –

1

Sobre la respuesta de Quibblesome:

Me trataron de ejecutar el código siguiente en mi x64 de Windows 7 en modo de lanzamiento sin un depurador y NullReferenceException nunca se ha lanzado.

using System; 
using System.Threading; 

namespace EventsMultithreadingTest 
{ 
    public class Program 
    { 
     private static Action<object> _delegate = new Action<object>(Program_Event); 
     public static event Action<object> Event; 

     public static void Main(string[] args) 
     { 
      Thread thread = new Thread(delegate() 
       { 
        while (true) 
        { 
         Action<object> ev = Event; 

         if (ev != null) 
         { 
          ev.Invoke(null); 
         } 
        } 
       }); 
      thread.Start(); 

      while (true) 
      { 
       Event += _delegate; 
       Event -= _delegate; 
      } 
     } 

     static void Program_Event(object obj) 
     { 
      object.Equals(null, null); 
     } 
    } 
} 
+2

Este problema solo existía en .NET 1.x en x64; no ha sido un problema desde que se introdujo el .NET 2.0 Memory Model en 2005; ver http://code.logos.com/blog/2008/11/events_and_threads_part_4.html y http://msdn.microsoft.com/magazine/cc163715.aspx –

0

creo que el 64 JIT no está completamente desarrollado/portado a tomar ventaja de los dichos 64 CPU de arquitectura poco por lo que tiene problemas, usted podría estar recibiendo comportamiento 'emulado' de sus montajes que pueden causar problemas e inesperado comportamiento. Me gustaría investigar casos en los que esto pueda evitarse y/o ver si hay un buen compilador rápido de 64 C++ para escribir algoritmos y cálculos críticos.Pero incluso si tiene dificultades para encontrar información o no tiene tiempo para leer el código desensamblado, estoy bastante seguro de que si saca los cálculos pesados ​​fuera del código administrado se disminuirán los problemas que pueda tener & potenciar el rendimiento [algo seguro de que ya está haciendo esto pero solo para mencionar :)]

0

Un perfilador no debe influir significativamente en los resultados de tiempo. Si los gastos generales del perfilador realmente son "significativos", entonces probablemente no pueda exprimir mucha más velocidad de su código, y debería pensar en examinar los cuellos de botella de hardware (disco, RAM o CPU) y actualizar. (Parece que está vinculado a la CPU, por eso es por dónde empezar)

En general, .net y JIT lo liberan de la mayoría de los problemas de portabilidad de 64 bits. Como usted sabe, existen efectos relacionados con el tamaño del registro (cambios en el uso de la memoria, clasificación a código nativo, que requieren que todas las partes del programa sean compilaciones nativas de 64 bits) y algunas diferencias de rendimiento (mapa de memoria más grande, más registros, buses más amplios etc.), así que no puedo decirte nada más de lo que ya sabes en ese frente. Los otros problemas que he visto son el sistema operativo en lugar de los de C#: ahora hay diferentes sectores de registro para las aplicaciones de 64 bits y WOW64, por ejemplo, por lo que algunos accesos al registro deben escribirse con cuidado.

En general, es una mala idea preocuparse por lo que el JIT hará con su código y tratar de ajustarlo para que funcione mejor, porque es probable que el JIT cambie con .net 4 o 5 o 6 y sus "optimizaciones" se convierten en ineficiencias, o peor, errores. También tenga en cuenta que el JIT compila el código específicamente para la CPU en la que se está ejecutando, por lo que una mejora potencial en su PC de desarrollo puede no ser una mejora en una PC diferente. Lo que consiga con el uso del JIT de hoy en día en la CPU de hoy puede morderlo en un año si actualiza algo.

Específicamente, usted cita "las propiedades no están en línea en x64". Para cuando haya ejecutado toda la base de código convirtiendo todas sus propiedades en campos, puede haber un nuevo JIT para 64 bits que tenga propiedades en línea. De hecho, puede funcionar mejor que su código de "solución temporal". Permita que Microsoft optimice eso para usted.

Usted correctamente señala que su perfil de memoria puede cambiar. Por lo tanto, es posible que necesite más RAM, discos más rápidos para la memoria virtual y cachés de CPU más grandes. Todos los problemas de hardware. Puede reducir el efecto utilizando (por ejemplo) Int32 en lugar de int, pero eso puede no hacer mucha diferencia y podría dañar el rendimiento (ya que su CPU puede manejar valores nativos de 64 bits más eficientemente que valores de 32 bits de la mitad del tamaño)

Usted dice que "los tiempos de inicio pueden ser más largos", pero eso parece bastante irrelevante en una aplicación que dice corre para horas a 100% CPU.

Entonces, ¿de qué estás realmente preocupado? Tal vez sea hora de codificar en una PC de 32 bits y luego hacerlo en la misma tarea en una PC de 64 bits. ¿Hay media hora de diferencia en una carrera de 4 horas? ¿O la diferencia es solo de 3 segundos? ¿O la PC de 64 bits es realmente más rápida? Tal vez estás buscando soluciones a problemas que no existen.

Volviendo al consejo habitual, más genérico. Perfil y tiempo para identificar los cuellos de botella. Observe los algoritmos y procesos matemáticos que está aplicando, e intente mejorarlos/reemplazarlos con otros más eficientes. Compruebe que su enfoque de subprocesos múltiples le ayuda en lugar de perjudicar su rendimiento (es decir, evitar esperas y bloqueos). Intente reducir la asignación/desasignación de memoria, p. reutilizar objetos en lugar de reemplazarlos por otros nuevos. Intente reducir el uso de llamadas a funciones frecuentes y funciones virtuales. Cambie a C++ y elimine los gastos indirectos inherentes a la recolección de basura, comprobación de límites, etc. que .net impone. Hmmm. Nada de eso tiene nada que ver con 64 bits, ¿o sí?

4

Un problema de rendimiento particularmente problemático en.NET se refiere a los pobres JIT:

https://connect.microsoft.com/VisualStudio/feedback/details/93858/struct-methods-should-be-inlined?wa=wsignin1.0

Básicamente, inline y estructuras no funcionan bien juntos en x64 (aunque esa página sugiere procesos en línea ahora funciona pero las copias redunant posteriores no se eliminan, que los sonidos sospechoso dada la pequeña diferencia de rendimiento).

En cualquier caso, después de luchar con .NET el tiempo suficiente para esto, mi solución es usar C++ para cualquier cosa numéricamente intensiva. Incluso en casos "buenos" para .NET, donde no se trata de estructuras y el uso de matrices en las que se optimiza la comprobación de límites, C++ supera a .NET hands down.

Si está haciendo algo más complicado que los productos dot, la imagen empeora rápidamente; el código .NET es más extenso + menos legible (porque necesita insertar material manualmente y/o no puede usar genéricos), y mucho más lento.

He cambiado al uso de Eigen en C++: es absolutamente genial, lo que resulta en código legible y alto rendimiento; una delgada capa C++/CLI proporciona el pegamento entre el motor de cómputo y el mundo .NET.

Eigen funciona mediante meta-programación de plantilla; compila expresiones vectoriales en instrucciones intrínsecas de SSE y hace muchos de los bucles más desagradables relacionados con la caché que se desenrollan y reorganizan; y aunque se centró en el álgebra lineal, también funcionará con enteros y expresiones de matriz que no sean matrices.

Así, por ejemplo, si P es una matriz, este tipo de cosas simplemente funciona:

1.0/(P.transpose() * P).diagonal().sum(); 

... que no se asigne una variante incorporaron temporalmente de P, y no calcula el producto de matriz completa, pero solo los campos que necesita.

Por lo tanto, si se puede ejecutar con plena confianza, solo use C++ a través de C++/CLI, funciona mucho mejor.

Cuestiones relacionadas