Mon mal de tête 32 bits est maintenant une migraine de 64 bits?!? (ou Problèmes d'exécution .NET CLR 64 bits)

StackOverflow https://stackoverflow.com/questions/635019

  •  10-07-2019
  •  | 
  •  

Question

Quelles conséquences inhabituelles et inattendues se sont produites en termes de performances, de mémoire, etc. lors du passage de l’exécution de vos applications .NET sous JIT 64 bits par rapport à JIT 32 bits? Je suis intéressé par les avantages, mais plus par les problèmes étonnamment graves rencontrés par les gens.

Je suis en train d'écrire une nouvelle application .NET qui sera déployée à la fois en 32 bits et en 64 bits. Il y a eu de nombreuses questions relatives aux problèmes de portage de l'application - je ne suis pas concerné par le " attrape " du point de vue de la programmation / portage . (ie: gérer correctement les interop natifs / COM, les types de référence incorporés dans les structures changeant la taille de la structure, etc.)

Cependant, cette question et sa réponse m'ont fait réfléchir - Quels sont les autres problèmes que je néglige?

De nombreuses questions et articles de blogs ont contourné ce problème ou en ont touché un aspect, mais je n’ai rien vu qui contienne une liste décente de problèmes.

En particulier - Mon application est très liée au processeur et présente d’importants modèles d’utilisation de la mémoire (d’où la nécessité de disposer de 64 bits en premier lieu), tout en étant de nature graphique. Je suis préoccupé par les autres problèmes cachés pouvant exister dans le CLR ou le JIT fonctionnant sous Windows 64 bits (à l'aide de .NET 3.5sp1).

Voici quelques problèmes dont je suis au courant:

Je voudrais savoir quels autres problèmes spécifiques les personnes ont découvertes dans le JIT sous Windows 64 bits, et également s’il existe des solutions de rechange pour améliorer les performances.

Merci à tous!

---- EDIT -----

Juste pour clarifier -

Je suis conscient qu'essayer d'optimiser tôt est souvent une mauvaise chose. Je suis conscient que le fait de deviner que le système est souvent mauvais. Je sais aussi que la portabilité vers 64 bits a ses propres problèmes - nous exécutons et testons quotidiennement des systèmes 64 bits pour vous aider. etc.

Cependant, mon application n'est pas votre application métier typique. C'est un logiciel d'application scientifique. De nombreux processus utilisent 100% de CPU sur tous les cœurs (il est hautement threadé) pendant des heures à la fois.

Je passe beaucoup de temps à profiler l'application, et cela fait une énorme différence. Cependant, la plupart des profileurs désactivent de nombreuses fonctionnalités du JIT, de sorte qu'il peut être très difficile de cerner les petits détails tels que l'allocation de mémoire, l'intégration dans le JIT, etc., lorsque vous utilisez un profileur. D'où mon besoin de la question.

Était-ce utile?

La solution

Je me souviens avoir entendu un problème d'un canal IRC que je fréquente. Il optimise la copie temporaire dans cet exemple:

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

Remettre la situation de concurrence à l'origine et générer des exceptions de référence nulles potentielles.

Autres conseils

Un problème de performances particulièrement gênant dans .NET concerne le JIT pauvre:

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

Fondamentalement, l'inligne et les structures ne fonctionnent pas bien ensemble sur x64 (bien que la page suggère que l'inligne fonctionne maintenant mais que les copies redondantes suivantes ne soient pas éliminées, cela semble suspect compte tenu de la petite différence de performances.) .

Dans tous les cas, après avoir lutté suffisamment longtemps avec .NET, ma solution consiste à utiliser le C ++ pour tout ce qui a une grande intensité numérique. Même dans & bon; bien " cas pour .NET, où vous ne traitez pas avec des structures et n'utilisez pas de tableaux où la vérification des limites est optimisée, C ++ bat .NET haut la main .

Si vous faites quelque chose de plus compliqué que les produits scalaires, la situation se dégrade très rapidement. le code .NET est à la fois plus long et moins lisible (parce que vous avez besoin d'inclure manuellement des éléments et / ou ne pouvez pas utiliser de génériques), et beaucoup plus lent.

Je suis passé à Eigen en C ++: c'est vraiment génial , résultant en un code lisible et de hautes performances; un wrapper C ++ / CLI mince fournit alors le lien entre le moteur de calcul et le monde .NET.

Eigen fonctionne par méta-programmation de modèles; compile les expressions vectorielles en instructions intrinsèques SSE et effectue un grand nombre des plus méchantes boucles de déroulage et de réorganisation de la mémoire cache; et bien que concentré sur l'algèbre linéaire, il fonctionnera aussi avec les expressions entières et les expressions non matricielles.

Donc, par exemple, si P est une matrice, ce genre de choses fonctionne simplement:

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

... qui n'alloue pas une variante de P temporairement transposée et ne calcule pas le produit de matrice entier mais uniquement les champs dont il a besoin.

Donc, si vous pouvez utiliser Full Trust, utilisez simplement C ++ via C ++ / CLI, cela fonctionnera beaucoup mieux.

La plupart du temps, Visual Studio et le compilateur cachent assez bien les problèmes. Cependant, je suis conscient d'un problème majeur qui peut survenir si vous configurez votre application pour qu'elle détecte automatiquement la plate-forme (x86 vs x64) et que ait également des dépendances sur des dll tierces tierces 32 bits. Dans ce cas, sur les plates-formes 64 bits, il tentera d’appeler les DLL avec des conventions et des structures 64 bits, mais cela ne fonctionnera tout simplement pas.

Vous avez mentionné les problèmes de portage, ce sont ceux-là qui doivent être concernés. Je (évidemment) ne connais pas votre candidature, mais essayer de deviner l’EEC est souvent une perte de temps totale. Les personnes qui écrivent le JIT ont une compréhension intime de l’architecture des puces x86 / x64 et savent très probablement ce qui fonctionne le mieux et ce qui est moins bon que quiconque sur la planète.

Oui, il est possible que votre cas soit différent et unique, mais si vous êtes en train d'écrire une nouvelle application " alors je ne m'inquiéterais pas du compilateur JIT. Il existe probablement une boucle idiote qui peut être évitée quelque part et qui vous rapportera 100 fois l’amélioration de la performance que vous obtiendrez en essayant de deviner l’avantage de l’EEC. Cela me rappelle des problèmes rencontrés lors de l'écriture de notre ORM; nous examinions le code et pensions pouvoir en extraire quelques instructions machine ... bien sûr, le code s'est ensuite éteint et s'est connecté à un serveur de base de données via un réseau. , nous avons donc réduit les microsecondes d’un processus limité par des millisecondes.

La règle universelle de l'optimisation des performances ... Si vous n'avez pas mesuré vos performances, vous ne savez pas où se trouvent vos goulets d'étranglement, vous pensez seulement que vous savez ... . et vous avez probablement tort.

À propos de la réponse de Quibblesome:

J'ai essayé d'exécuter le code suivant dans Windows 7 x64 en mode Release sans débogueur, et NullReferenceException n'a jamais été levée .

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);
        }
    }
}

Je pense que le JIT 64 n’est pas entièrement développé / porté pour tirer parti de ces processeurs à architecture 64 bits. Il a donc des problèmes. Vous pouvez obtenir un comportement «émulé» de vos assemblys, ce qui peut causer des problèmes et un comportement inattendu. Je voudrais examiner les cas où cela peut être évité et / ou peut-être voir s'il existe un bon compilateur rapide 64 c ++ pour écrire des calculs et des algorithmes critiques. Mais même si vous rencontrez des difficultés pour trouver des informations ou n'avez pas le temps de lire le code dissemblé, je suis tout à fait sûr que prendre des calculs lourds en dehors du code géré réduirait tous les problèmes que vous pourriez avoir & amp;; améliorer les performances [un peu sûr que vous faites déjà cela, mais juste pour mentionner :)]

Un profileur ne devrait pas influencer de manière significative vos résultats de chronométrage. Si les frais généraux du profileur sont vraiment "significatifs" dans ce cas, vous ne pourrez probablement pas utiliser votre code avec plus de rapidité et devriez penser à vos goulots d'étranglement matériels (disque, RAM ou CPU?) et à la mise à niveau. (On dirait que vous êtes lié au processeur, c'est donc par où commencer)

En général, .net et JIT vous libèrent de la plupart des problèmes de portage de 64 bits. Comme vous le savez, il existe des effets liés à la taille des registres (modifications de l'utilisation de la mémoire, mise en séquence en code natif, obligeant toutes les parties du programme à être des versions 64 bits natives) et à certaines différences de performances (carte mémoire plus grande, davantage de registres, bus plus larges etc), donc je ne peux rien vous dire de plus que ce que vous savez déjà sur ce point. Les autres problèmes que j'ai vus sont ceux du système d'exploitation plutôt que ceux de C #. Il existe désormais différentes ruches de registre pour les applications 64 bits et WOW64, par exemple, de sorte que certains accès au registre doivent être écrits avec soin.

C’est généralement une mauvaise idée de s’inquiéter de ce que l’ECM fera avec votre code et de tenter de l’ajuster pour qu’il fonctionne mieux, car l’ECI est susceptible de changer avec .net 4 ou 5 ou 6 et vos "optimisations". peut se transformer en inefficacité, ou pire, en bogues. N'oubliez pas non plus que JIT compile le code spécifiquement pour le processeur sur lequel il s'exécute. Une amélioration potentielle de votre PC de développement peut donc ne pas être une amélioration sur un autre PC. Ce que vous pouvez faire avec l’utilisation de JIT d’aujourd’hui sur les processeurs d’aujourd’hui risque de vous faire craquer dans quelques années lorsque vous améliorerez quelque chose.

Plus précisément, vous citez "les propriétés ne sont pas alignées sur x64". Au moment où vous avez parcouru l'intégralité de votre base de code en transformant toutes vos propriétés en champs, il pourrait bien y avoir un nouveau JIT pour 64 bits qui gère les propriétés en ligne. En effet, il se peut que sa performance soit meilleure que celle de votre "solution de contournement". code. Laissez Microsoft optimiser cela pour vous.

Vous soulignez à juste titre que votre profil de mémoire peut changer. Vous aurez donc peut-être besoin de plus de RAM, de disques plus rapides pour la mémoire virtuelle et de caches plus volumineux. Tous les problèmes matériels. Vous pourrez peut-être réduire l'effet en utilisant (par exemple) Int32 plutôt qu'int mais cela ne changera pas grand chose et pourrait nuire aux performances (votre CPU pouvant gérer les valeurs natives 64 bits plus efficacement que les valeurs 32 bits demi-taille ).

Vous dites que "les temps de démarrage peuvent être plus longs", mais cela semble plutôt inutile dans une application qui s'exécute pendant heures à 100% de CPU.

Alors, de quoi t'inquiètes-tu vraiment? Peut-être que votre code de temps sur un PC 32 bits, puis le temps de faire la même tâche sur un PC 64 bits. Y a-t-il une demi-heure de différence en 4 heures? Ou est-ce que la différence est seulement 3 secondes? Ou le PC 64 bits est-il réellement plus rapide? Peut-être cherchez-vous des solutions à des problèmes qui n'existent pas.

Revenons donc aux conseils habituels, plus génériques. Profil et temps pour identifier les goulots d'étranglement. Examinez les algorithmes et les processus mathématiques que vous appliquez et essayez de les améliorer / les remplacer par des plus efficaces. Vérifiez que votre approche multithreading aide plutôt que de nuire à votre performance (c'est-à-dire que les attentes et les verrous sont évités). Essayez de réduire l’allocation / désallocation de mémoire - par exemple, réutilisez les objets plutôt que de les remplacer par de nouveaux. Essayez de réduire l'utilisation des appels de fonction fréquents et des fonctions virtuelles. Passez en C ++ et éliminez les frais généraux inhérents à la collecte des ordures ménagères, à la vérification des limites, etc. imposés par .net. Hmmm. Rien de tout cela n'a rien à voir avec 64 bits, n'est-ce pas?

Je ne connais pas très bien les problèmes 64 bits, mais j'ai un commentaire à faire:

  

Nous devrions oublier les petites   efficacité, disons environ 97% des   temps: l'optimisation prématurée est le   racine de tout mal.   - Donald Knuth

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top