Aléatoire Complète Système Une absence de réponse Fonctions mathématiques en cours d’exécution

J’ai un programme qui charge un fichier (entre 10 Mo et 5 Go) un morceau à la fois (ReadFile), et pour chaque bloc effectue un ensemble d’opérations mathématiques (calcule essentiellement le hachage).

Après avoir calculé le hachage, il stocke des informations sur le bloc dans une carte STL (essentiellement ), puis écrit le bloc lui-même dans un autre fichier (WriteFile).

C’est tout ce qu’il fait. Ce programme amènera certains PC à s’étouffer et à mourir. La souris commence à bégayer, le gestionnaire de tâches prend plus de 2 min pour afficher, ctrl + alt + del ne répond plus, les programmes en cours d’exécution sont lents …. les travaux.

J’ai fait littéralement tout ce que je pouvais imaginer pour optimiser le programme, et j’ai vérifié trois fois tous les objects.

Ce que j’ai fait:

  • Essayé différents algorithmes de hachage (moins intensifs).
  • Changement de toutes les allocations à nedmalloc au lieu du nouvel opérateur par défaut
  • Passé de stl :: map à unordered_set, nous avons trouvé que les performances étaient toujours catastrophiques, alors je suis passé à nouveau à dense_hash_map de Google.
  • Convertit tous les objects pour stocker des pointeurs sur des objects au lieu des objects eux-mêmes.
  • Mettre en cache toutes les opérations de lecture et d’écriture. Au lieu de lire un morceau de 16 Ko du fichier et d’effectuer les calculs, j’ai lu 4 Mo dans un tampon et j’ai lu des blocs de 16 Ko à partir de . Idem pour toutes les opérations d’écriture – elles sont regroupées en blocs de 4 Mo avant d’être écrites sur le disque.
  • Exécutez des profils étendus avec Visual Studio 2010, Analyste de code AMD et perfmon.
  • Définissez la priorité du thread sur THREAD_MODE_BACKGROUND_BEGIN
  • Définissez la priorité du thread sur THREAD_PRIORITY_IDLE
  • Ajout d’un appel Sleep (100) après chaque boucle.

Même après tout cela , l’application entraîne toujours un blocage du système sur certaines machines dans certaines circonstances.

Perfmon et Process Explorer montrent une utilisation minimale du processeur (avec le sumil), pas de lectures / écritures constantes sur le disque, peu de défauts de pages physiques (et seulement environ 30k pages par page pendant la durée de vie de l’application), peu de mémoire virtuelle de 150 Mo), aucune fuite de poignées, aucune fuite de mémoire.

Les machines que j’ai testées sous Windows XP – Windows 7, x86 et x64 inclus. Aucun ne possède moins de 2 Go de RAM, bien que le problème soit toujours exacerbé dans des conditions de mémoire plus faibles.

Je ne sais plus quoi faire ensuite. Je ne sais pas ce qui la cause – je suis déchiré entre CPU ou Memory comme coupable. CPU car sans le sumil et sous différentes priorités de threads, les performances du système changent sensiblement. Mémoire, car il y a une grande différence dans la fréquence à laquelle le problème survient lors de l’utilisation de unordered_set vs dense_hash_map de Google.

Qu’est-ce qui est vraiment bizarre? De toute évidence, la conception du kernel NT est supposée empêcher ce type de comportement de se produire (une application en mode utilisateur conduisant le système à ce type de performances extrêmes!?) ….. mais quand je comstack le code et l’exécute sous OS X ou Linux (il est assez standard en C ++), il fonctionne très bien même sur des machines pauvres avec peu de mémoire vive et des processeurs plus faibles.

Qu’est-ce que je suis censé faire ensuite? Comment puis-je savoir ce que fait Windows dans les coulisses qui détruisent les performances du système, alors que tous les indicateurs indiquent que l’application elle-même ne fait rien d’extrême?

Tout conseil serait le bienvenu.

Je sais que vous avez dit que vous aviez surveillé l’utilisation de la mémoire et que celle-ci semblait minime ici, mais les symptômes ressemblent beaucoup à ceux du système d’exploitation, ce qui entraînerait une perte générale de la réactivité du système d’exploitation.

Lorsque vous exécutez l’application sur un fichier, dites 1/4 à 1/2 de la taille de la mémoire physique disponible, cela semble-t-il mieux fonctionner?

Ce que je soupçonne peut-être, c’est que Windows met “utilement” en mémoire cache vos lectures de disque et ne cède pas cette mémoire cache à votre application pour la forcer à passer à l’échange. Ainsi, même si l’utilisation des swaps est minimale (150 Mo), elle entre et sort constamment lorsque vous calculez le hachage. Cela met alors le système à genoux.

Quelques points à vérifier:

  • Logiciel antivirus. Celles-ci parsingnt souvent les fichiers à mesure qu’ils sont ouverts pour détecter les virus. Votre délai se produit-il avant que des données ne soient lues par l’application?
  • Performances générales du système Est-ce que copier le fichier avec Explorer montre aussi ce problème?
  • Votre code. Décomposez les différentes étapes. Écrivez un programme qui lit simplement le fichier, puis un autre qui lit et écrit les fichiers, puis un programme qui ne fait que hacher des blocs aléatoires de ram (par exemple, supprimer la partie IO du disque) et voir si une étape particulière est problématique. Si vous pouvez obtenir un profileur, utilisez-le aussi pour voir s’il y a des taches lentes dans votre code.

MODIFIER

Plus d’idées Votre programme conserve peut-être trop le verrou GDI. Cela expliquerait que tout le rest soit lent sans utilisation élevée du processeur. Une seule application à la fois peut avoir le verrou GDI. Est-ce une application graphique ou une simple application console?

Vous avez également mentionné RtlEnterCriticalSection. Cette opération est coûteuse et peut facilement bloquer le système, c’est-à-dire les entrées et les feuilles incompatibles. Êtes-vous multi-threading du tout? Le ralentissement est-il dû aux conditions de course entre les threads?

XPerf est votre guide ici – regardez la vidéo PDC à ce sujet, puis prenez une trace de l’application qui se comporte mal. Cela vous dira exactement ce qui se passe dans tout le système, il est extrêmement puissant.

J’aime les suggestions de mise en cache et de mise en cache du disque, mais si ce n’est pas le cas, voici quelques suggestions éclairées:

À quelles bibliothèques non-MSVC, le cas échéant, vous connectez-vous?

Votre programme peut-il être modifié (# ifdef’d) pour fonctionner sans interface graphique? Le problème se produit-il?

Vous avez ajouté :: Sleep (100) après chaque boucle dans chaque thread, non? De combien de fils parlez-vous? Une poignée ou des centaines? Combien de temps dure chaque boucle, grosso modo? Que se passe-t-il si vous faites ça :: Sleep (10000)?

Est-ce que votre programme est peut-être en train de faire autre chose qui verrouille des ressources limitées (ProcExp peut vous montrer quels sont les descripteurs en cours d’acquisition… bien sûr, vous pourriez avoir des difficultés avec ProcExp qui ne répond pas: – [)

Êtes-vous sûr que les sections critiques sont réservées aux utilisateurs? Je me souviens que c’était tellement le cas quand je travaillais sur Windows (ou du moins je le croyais), mais Microsoft aurait pu le modifier. Je ne vois aucune garantie dans l’article MSDN Critical Section Objects ( http://msdn.microsoft.com/en-us/library/ms682530%28VS.85%29.aspx ) … et cela m’amène à me demander : Anti-convoi se verrouille dans Windows Server 2003 SP1 et Windows Vista

Hmmm … on peut supposer que nous sums tous multiprocesseurs maintenant, alors est-ce que vous définissez le nombre de tours sur le CS?

Que diriez-vous d’exécuter une version de débogage de l’un de ces systèmes d’exploitation et de surveiller la sortie de débogage du kernel (en utilisant DbgView) … en utilisant éventuellement le débogueur du kernel du Platform SDK … si MS l’appelle encore?

Je me demande si VMMap (un autre utilitaire SysInternal / MS) pourrait aider avec l’hypothèse de mise en cache de disque.

Il se trouve que c’est un bogue dans le compilateur Visual Studio. L’utilisation d’un autre compilateur résout complètement le problème.

Dans mon cas, j’ai installé et utilisé le compilateur Intel C ++ et même avec toutes les optimisations désactivées, je n’ai pas vu le blocage complet du système que je rencontrais avec les compilateurs Visual Studio 2005 – 2010 sur cette bibliothèque.

Je ne suis pas sûr de ce qui cause le compilateur à générer un tel code cassé, mais il semblerait que nous achèterons une copie du compilateur Intel.

On dirait que vous êtes en train de fouiller les choses sans savoir quel est le problème. Prenez des photos de stacks. Ils vous indiqueront ce que fait votre programme lorsque le problème survient. Il n’est peut-être pas facile d’obtenir les stacks de stockage si le problème se produit sur d’autres machines sur lesquelles vous ne pouvez pas utiliser un IDE ou un échantillonneur de stack. Une possibilité consiste à tuer l’application et à obtenir un vidage de la stack lorsqu’elle fonctionne. Vous devez reproduire le problème dans un environnement où vous pouvez obtenir un vidage de stack.


Ajouté: Vous dites qu’il fonctionne bien sur OSX et Linux, et mal sur Windows. Je suppose que le rapport entre le temps d’achèvement et un nombre assez important, comme 10 ou 100, si vous avez même eu la patience de l’attendre. Je l’ai dit dans le commentaire, mais c’est un point clé. Le programme attend quelque chose et vous devez savoir quoi. Cela peut être l’une des choses que les gens ont mentionnées, mais ce n’est pas un hasard .

Chaque programme, tout le temps pendant qu’il fonctionne, a une stack d’appels consistant en une hiérarchie d’instructions d’appel à des adresses spécifiques. Si, à un moment donné, il calcule, la dernière instruction de la stack est une instruction sans appel. Si elle se trouve dans les E / S, la stack peut atteindre quelques niveaux d’appels de bibliothèque que vous ne pouvez pas voir. C’est bon. Chaque instruction d’appel sur la stack est en attente. Il attend le travail demandé pour terminer. Si vous regardez la stack d’appels et que vous regardez où se trouvent les instructions d’appel dans votre code , vous saurez ce que votre programme attend.

Votre programme, qui prend beaucoup de temps , passe presque tout son temps à attendre que quelque chose se termine et, comme je l’ai dit, c’est ce que vous devez savoir. Obtenez un vidage de la stack pendant qu’il est lent, et cela vous donnera la réponse. La chance qu’il manque, c’est 1 / le ratio de lenteur.

Désolé d’être si élémentaire à ce sujet, mais beaucoup de gens ( et les créateurs de profils ) ne le comprennent pas . Ils pensent qu’ils doivent mesurer.