8
votes

Migrer une seule application filetée sur une exécution parallèle multi-threadé, la simulation de Monte Carlo

J'ai été chargé de prendre une simulation de Monte Carlo de Monte Carlo de Monte Carlo Thread Carlo et Optimiser IT. Ceci est une application C # Console, Aucun accès DB Il charge des données une fois à partir d'un fichier CSV et l'écrit à la fin, il est donc à peu près de la CPU liée , n'utilise également qu'en 50 Mo de mémoire. < P> Je l'ai exécuté à travers Jetbrains DotTrace Profiler. Du temps d'exécution total d'environ 30% génère des nombres aléatoires uniformes, à 24% de traduire des nombres aléatoires uniformes à des nombres aléatoires normalement distribués.

L'algorithme de base est un grand nombre de boucles , avec des appels de nombres aléatoires et une multiplication de matrice au centre, chaque itération renvoie un double qui est ajouté à une liste de résultats, cette liste est triée et testée périodiquement pour certains critères de convergence (à des points de contrôle tous les 5% du nombre total d'itérations) si acceptable Le programme sort des boucles et écrit les résultats, sinon cela procède à la fin.

J'aimerais que les développeurs pèsent sur:

  • Devrais-je utiliser nouveau threadpolf threadpool
  • dois-je regarder la bibliothèque d'extension Microsoft Parallels
  • devrais-je regarder Aforge.net parallel.for , http: //code.google.com/p/aforge/ Toutes autres bibliothèques?

    Certains Liens vers des tutoriels sur ce qui précède seraient les bienvenus comme je n'ai jamais écrit de code parallèle ou multi-threadé . .

    • meilleures stratégies pour générer des nombres aléatoires normalement distribués, puis les consommer. Les numéros aléatoires uniformes ne sont jamais utilisés dans cet état par l'application, ils sont toujours traduits à normalement distribués et ensuite consommés.
    • bonnes bibliothèques rapides (parallèle?) Pour génération de nombres aléatoires
    • Considérations de mémoire lorsque je prends ce parallèle , combien d'extra aurai-je besoin?

      L'application actuelle prend 2 heures pour 500 000 itérations, l'entreprise a besoin de cela pour atteindre 3 000 000 itérations et être appelée fois par jour par jour, alors besoin d'une optimisation lourde.

      particulièrement d'entendre des personnes qui ont utilisé Microsoft Parallels Extension ou Aforge.net parallèle

      Ceci doit être réalisé assez rapidement si .NET 4 bêta est sorti même si je sais que cela a des bibliothèques de simultanément cuites, nous pouvons envisager de migrer vers .NET 4 plus tard dans la piste une fois que c'est libéré. Pour le moment, le serveur a .NET 2, j'ai été soumis à votre revue une mise à niveau vers .NET 3.5 SP1 que ma boîte de devise a.

      merci

      Mise à jour

      Je viens d'essayer le parallèle. Pour la mise en œuvre, mais il s'agit de des résultats étranges. Fileté unique: xxx

      à: xxx

      intérieur simulant Il y a beaucoup d'appels à rnd.nexiforme (), Je pense que je reçois de nombreuses valeurs qui sont les mêmes , est-ce que cela est susceptible de se produire, car cela est maintenant parallèle?

      Peut-être aussi des problèmes avec la liste addrange appeler n'étant pas le fil de sécurité? Je vois ce

      System.Threading.Collections.BlockingCollection peut être utile, mais il n'a qu'une méthode d'addition, aucun addrange, je devrais donc regarder là-bas des résultats et ajouter un fil de sécurité. Tout aperçu de quelqu'un qui a utilisé parallèle.Pour très apprécié. J'ai passé temporairement à la System.Random pour mes appels, car je recevais une exception lors de l'appel à Nexiform avec ma mise en œuvre de Mersenne Twister, peut-être que ce n'était peut-être pas le fil de sécurité un certain tableau obtenait une index hors limites ....


2 commentaires

Sur quelle machine utilisez-vous? Pourrait recevoir une partie de l'augmentation de la vitesse requise du matériel mis à niveau.


Ceci est sur une AMD OPTERON 275, 4 CPU, je pense, pas sûr combien de cœurs. Windows Server 2003 SP2 32 bits


3 Réponses :


13
votes

Tout d'abord, vous devez comprendre pourquoi vous pensez que l'utilisation d'utiliser plusieurs threads est une optimisation - quand elle est, en fait, pas. L'utilisation de plusieurs threads rendra votre charge de travail complète plus rapidement uniquement si vous avez plusieurs processeurs, puis au plus rapidement que vous avez des processeurs disponibles (ceci s'appelle le Speed-up ). Le travail n'est pas "optimisé" dans le sens traditionnel du mot (c'est-à-dire que la quantité de travail n'est pas réduite - en fait, avec multithreading, la quantité totale de travail augmente généralement en raison de la surcharge de filetage).

Donc, dans la conception de votre application, vous devez trouver des morceaux de travail pouvant être effectués de manière parallèle ou se chevauchant. Il peut être possible de générer des nombres aléatoires en parallèle (en utilisant plusieurs RNG exécutés sur différents processeurs), mais cela modifierait également les résultats, car vous obtenez différents nombres aléatoires. Une autre option a une génération de nombres aléatoires sur un processeur et tout le reste sur différents processeurs. Cela peut vous donner une vitesse maximale de 3, car la RNG fonctionnera toujours séquentiellement et prendra toujours 30% de la charge.

Donc, si vous optez pour cette parallélisation, vous vous retrouvez avec 3 threads: le fil 1 exécute le rng, le fil 2 produit une distribution normale et le fil 3 fait le reste de la simulation.

Pour cette architecture, un Architecture producteur-consommateur est le plus approprié. Chaque thread va lire son entrée à partir d'une file d'attente et produira sa sortie dans une autre file d'attente. Chaque file d'attente doit être bloquée, de sorte que si le fil RNG tombe derrière, le fil de normalisation bloquera automatiquement jusqu'à ce que de nouveaux nombres aléatoires soient disponibles. Pour une efficacité, je transmettrais les nombres aléatoires dans la matrice de, disons, 100 (ou plus grand) sur les threads, pour éviter les synchronisations de chaque nombre aléatoire.

Pour cette approche, vous n'avez pas besoin d'un filetage avancé. Il suffit d'utiliser une classe de fil régulière, pas de piscine, pas de bibliothèque. La seule chose dont vous avez besoin est (malheureusement) non dans la bibliothèque standard est une classe de file d'attente de blocage (la classe de file d'attente dans System.Collections n'est pas bonne). CodeProject fournit une implémentation raisonnablement recherchée d'un; Il y a probablement d'autres.


7 commentaires

L'autre question à considérer est la commutation de contexte. Si vous n'avez pas choisi l'architecture ci-dessus (probablement une erreur imo de ce que vous avez dit), vous essayez ensuite de courir beaucoup de calculs en parallèle, ce qui dépasserait de loin votre nombre de transformateurs. Cela serait désastreux que beaucoup de temps de processeur qui calculait les réponses précédemment est désormais consacré à la commutation entre les threads. Si vous avez eu un fichier IO après chaque calcul, cela pourrait peut-être être fait ASYNC (mais vous utiliseriez ensuite une file d'attente et passez des éléments à stocker dans un composant dédié).


Le calcul de Monte Carlo est entièrement lié à CPU, vous dites donc que je devrais toujours mapper 1 fil à 1 CPU sur la boîte il n'y a jamais un avantage pour aller> 1 threads par cpu? Sauf si un thread n'attend que quelque chose, cela permettrait aux édifices des commutateurs contextuels, mais dans mon cas, il n'y a aucun avantage en fait, ce serait pire performance.


Correct. S'il n'y a vraiment pas d'IO dans ces fils, vous utilisez plusieurs threads par processeur ralentissez-le, non pas accéléré.


OK merci. Quelle est la différence entre Core et CPU? Le filetage hyper fait également une différence? Je développe et profilez-moi sur ma propre machine A: Intel Core 2 Duo E6550 @ 2.33 GHz (dans le gestionnaire de périphériques, il présente 2 processeurs). Le serveur est un: AMD Opteron 275 (dans le gestionnaire de périphériques, il affiche 4 processeurs). En C # Si je fais un environnement.Processorcount et commencez ce nombre de threads, c'est que la voie à suivre? De plus, si je devais proposer de nouveaux matériels pour cette application critique de mission au travail, à quoi devrais-je envisager. Merci


De plus, mon PC est Windows XP, le serveur est Windows Server 2003 32 bit. Je viens de lire sur le système d'exploitation Windows HPC et je me demande si cela en vaut la peine pour cette migration. De plus, si cette application est entièrement liée à la CPU, la mémoire n'est-elle probablement pas accélérée à X64, fonctionne comme Math.SQRT, multiplications matricielles, la division soit plus rapide sur x64?


Vous devriez considérer le processeur, le noyau et le processeur comme synonymes et ne compter que le total des cœurs (c'est-à-dire deux croustilles de la CPU avec chaque cœurs de deux cœurs sont identiques à un processeur avec quatre cœurs, à peu près). Windows affiche chaque noyau (de manière appropriée) en tant que CPU. Avec hyperthreading, chaque noyau peut apparaître comme deux processeurs; Désactiver HT si cela se produit. Environnement.Processorcount doit vous donner le même numéro que le chef de la tâche. Ignorer Windows HPC: il est utile uniquement pour les grappes de plusieurs ordinateurs serveurs. X64: Cela dépend vraiment de l'application. Pour les opérations à virgule flottante, il ne devrait y avoir aucune différence.


Une autre remarque: n'essayez pas de surgirer cette application. Cela ressemble vraiment à un étui à usage multi-threading standard avec quelques travaux séparables, de sorte que les approches vraiment anciennes et établies vont très bien fonctionner. Obtenez cela pour travailler et une vitesse de trois (sur quatre processeurs) serait excellente.



0
votes

Le threading va être compliqué. Vous devrez casser votre programme en unités logiques pouvant être exécutées sur leurs propres threads et vous devrez faire face à des problèmes de concurrence qui émergent.

La bibliothèque d'extension parallèle devrait vous permettre de paralléler votre programme en modifiant certaines de vos boucles pour parallèle.for boucles. Si vous voulez voir comment cela fonctionne, Anders Hejlsberg et Joe Duffy offrent une bonne introduction dans leur vidéo de 30 minutes ici:

http://channel9.msdn.com/shows/arning+deep/programming-in-the-age-of-concurency-anders-hejlsberg-and-joe-duffy-concurrent- Programmation - avec /

filetage contre threadpool

Le threadpool, comme son nom l'indique, est un bassin de fils. L'utilisation de la threadpool pour obtenir vos threads présente des avantages. La mise en commun du fil vous permet d'utiliser des threads plus efficacement en fournissant votre application avec un pool de threads de travailleur gérés par le système.


1 commentaires

Hmm, je ne pense pas que l'utilisation de la threadpool soit plus compliquée que le filetage manuel - ce que je pense est quelque chose que vous vouliez dire, mais laissé de côté? En comparant les threads de threadpool et de manutention manuellement, le threadpool est plus efficace (car il recycle des threads remplis, la création de fil est coûteuse) et est plus facile à utiliser - surtout si vous obtenez des délégués. Cela dit, je ne peux pas parler de le comparer aux bibliothèques parallèles - je ne voulais tout simplement pas que le threadpool ait un mauvais nom :-)



1
votes

Liste code> n'est certainement pas thread-coffre-fort. Voir la section "Sécurité du fil" dans le System.Collections.Generic.List Documentation . La raison est la performance: l'ajout de la sécurité du thread n'est pas libre.

Votre implémentation de nombre aléatoire n'est pas non plus thread-coffre-fort; Obtenir les mêmes numéros Plusieurs fois, c'est exactement ce que vous attendez dans ce cas. Utilisons le modèle simplifié suivant de rnd.nextUniforme () code> pour comprendre ce qui se passe: p>

  1. calculer le nombre pseudo-aléatoire de l'état actuel de l'objet li>
  2. mettre à jour l'état de l'objet afin que le Le prochain appel donne un numéro différent Li>
  3. retourner le nombre pseudo-aléatoire li> ol>

    Maintenant, si deux threads exécutent cette méthode en parallèle, quelque chose comme cela peut arriver: p>

    • Le fil a calcule un nombre aléatoire comme à l'étape 1. li>
    • thread b calcule un nombre aléatoire comme à l'étape 1. Le fil d'un filetage A n'a pas encore mis à jour l'état de l'objet, donc Le résultat est le même. li>
    • thread une mise à jour de l'état du objet comme à l'étape 2. li>
    • thread b met à jour l'état du objet comme à l'étape 2, piétinant sur l'état de A changements ou peut-être donner la même chose résultat. li> ul>

      Comme vous pouvez le constater, tout raisonnement que vous pouvez faire pour prouver que rnd.nexiformiforme () code> n'est plus valide car deux threads interfèrent les uns avec les autres. Pire, des insectes comme celui-ci dépendent du chronométrage et peuvent ne figurer que rarement comme des "problèmes" sous certaines charges de travail ou sur certains systèmes. Nightmare de débogage! P>

      Une solution possible consiste à éliminer le partage d'Etat: Donnez chaque tâche son propre générateur de nombres aléatoires strong> initialisé avec une autre graine (en supposant que les instances ne partageaient pas l'état des champs statiques. D'une manière ou d'une autre). p>

      Une autre solution (inférieure) consiste à créer un champ Holding Objet de verrouillage fort> dans votre MERSENNETWISTER CODE> Classe comme ceci: P > xxx pré>

      puis utilisez ce verrou dans votre MERSENNETWISTER.NexiformIForm () code> Mise en œuvre: P>

      public double NextUniform()
      {
         lock(lockObject)
         {
            // original code here
         }
      }
      


3 commentaires

réponse fantastique merci! Cela fait un sens parfait. Maintenant, la question est de devrais-je utiliser ajouter une plage ou de trouver une collection threadsafe qui me permet d'accumuler la liste des nombres aléatoires (doubles), l'ordre est sans importance, mais je dois trier périodiquement les résultats et accroître un résultat à un certain centile et Recherchez les critères de convergence Pour tester une résiliation anticipée de la simulation, j'ai besoin de le faire pour chaque parallèle .Pour Path fonctionnant, puis annulez toutes les exécutions parallèles immédiatement si aucun traitement supplémentaire n'est requis, aucune idée de quoi faire?


Je n'ai pas immédiatement de réponse pour cela. Les chèques d'état périodiques et l'annulation des tâches parallèles en attente / en cours d'exécution constituent un gros sujet. Je vous recommande de poster une nouvelle question.


Bien que, découvrez blogs.msdn.com/pfxxteam/archive/ 2009/05/22/9635790.aspx