11
votes

Strange problème de concurrence avec STL / OpenMP dans des constructions 64 bits

J'ai un problème étrange lorsque je construis un de nos projets dans une configuration de débogage 64 bits. Il semble produire un comportement étrange qui ressemble un peu à un seul itérateur est incrémenté plusieurs fois. Je l'ai réduit au code de test suivant: XXX

Cependant, le test de taille échoue fréquemment qui implique d'en quelque sorte l'insertion de tout le contenu du vecteur - et beaucoup de poking À cela, il semble que les itérateurs de vecteur sont incrémentés plus d'une étape à la fois. Il est tout à fait difficile de dire que cela n'a pas tendance à se produire si l'on se brise avec le débogueur.

La conclusion évidente au tirage serait que ce n'est pas threadsafe, mais ma compréhension est que cela devrait être parce que cela devrait être parce que La seule variable modifiée est s qui a une portée locale.

Il y a beaucoup de choses qui résoudront le problème immédiat:

  • retirer le parallèle pour
  • lancer une section critique autour de l'insertion () appel
  • #define has_iterator_debugging 1
  • Remplacez l'appel unique d'insertion () avec une boucle manuelle et insérez chaque élément individuellement (il s'agit essentiellement de ce que cette fonction fait en interne, mais le problème disparaît définitivement lorsque je le fais moi-même)
  • Construisez une version 32 bits du même code
  • Construire la version de version du même code

    Ceci est compilé sous MSVC ++ 2008 SP1, avec la mise en œuvre STL fournie par compilateur.

    Peut-on perdre une lumière sur ce qui se passe ici? Merci d'avance pour toutes les astuces - je suis assez soulevée :)

    Edit: Au cas où il n'est pas clair, je ne cherche pas une "solution rapide" pour faire fonctionner ce code; Comme indiqué ci-dessus, je connais depuis pas quelques-uns de ceux-ci. Ce que je veux comprendre, c'est pourquoi ce problème se produit en premier lieu.

    Editer 2: code fonctionne correctement lors de la compilation avec GCC.


12 commentaires

arrive sans optimisation allumé?


Pourrait-il être dû au fait que votre S , commence et fin (code> variables sont partagés sur toutes les discussions? Essayez d'ajouter privé (s), privé (début), privé (fin) après oomp parallèle pour .


Darhuuk: L'ajout de ces clauses privées ne compile pas ("identifiant non déclaré"). Ma compréhension est que toute variable déclarée à l'intérieur de la boucle est implicitement privée de toute façon?


FAZO: n'arrive que sans optimisation (/ OD). AT / O1 Le problème disparaît.


Pour le #pragma omp parallel pour, dois-je avoir quelque chose sur mon système pour que cela fonctionne réellement? J'ai essayé de compiler votre solution pour X64 dans le VS10 dans le débogage, le dirigé et n'avait aucun problème; Donc je ne suis pas sûr.


Leetightshade: vous devez activer OpenMP dans les propriétés du projet pour que le pragma signifie n'importe quoi (Propriétés de configuration> C / C ++> Langue> Support OpenMP ou Ajout / OpenMP à la ligne de commande)


Juste une supposition sauvage, mais il se peut que OpenMP détecte que s.insert (début, fin); est une boucle également, tente de le parallémenter mais échoue car elle utilise le ! = < / Code> La syntaxe que AFAIK n'est pas prise en charge comme une condition de terminaison pour un pour boucle ...


@Eugen: OpenMP ne fait pas ou tente de faire une parallélisation automatique; Il doit y avoir un pragme qui marque la boucle comme parallèle. Les compilateurs peuvent parallèler des boucles automatiquement - par ex. Le compilateur d'Intel a une option pour cela; Mais je ne suis pas au courant d'une option similaire dans le compilateur MSVC.


Je soupçonne fortement un bogue dans la sécurité du fil de la mise en œuvre de MS2008. Essayez-le sans OpenMP et un filetage simple?


SEHE: Mes pensées sont similaires, mais je marche un peu soigneusement avant de les blâmer. Merci pour l'idée - je vais l'essayer quand de retour au travail demain.


@Peter: bonne attitude. Fwiw j'ai testé cela avec g ++ - {4.5 | 4.6} {-d_glibcxx_parallel} {-g} -fopenmp -lgomp ( {} désignant les options) fonctionnant sous la valeur de Valgrind - Outil = Memcheck et Valgrind --Tool = Hélicite sans résultats anormaux (sauf que fonctionnant sous valgrind Memcheck semblait empêcher le multitraitement, verrouillant efficacement le travail à 1 noyau, mais c'est probablement le verrouillage des emballages Malloc / Gratuits).


SEHE: ont essayé d'utiliser le threading Windows qui le corrige, il semblerait que quelque chose ne va pas avec leur mise en œuvre OpenMP. Semble fonctionner si je déplace l'intérieur de la boucle en une fonction distincte - peut-être que le code OpenMP généré des champs de variables incorrectement?


3 Réponses :


2
votes

ressemble à un bogue dans la mise en œuvre de VS 2008 de OpenMP et / ou de STL. Avec vs 2010, il n'est pas reproduit; De plus, il n'est pas reproduit avec les en-têtes de compilateur d'Intel et de VS 2008, ce qui me fait penser que le bogue est plus probable dans la prise en charge OpenMP dans le compilateur VC ++.

EDIT: Le code avec PremierPrivate I Publié précédemment ne fonctionne pas en effet, même avec _secure_scl = 1. Semble que je viens d'avoir une seule chance chanceux.


1 commentaires

Ne semble pas le réparer pour moi ...? _Secure_scl n'a pas semblé avoir beaucoup d'impact ici, sauf que cela prend parfois que les choses ne sont pas fausses juste avant que l'hilarité s'ensuit.



0
votes

Je suis d'accord avec Darhuk, je suggérerais de marquer s privé xxx


2 commentaires

Comme je l'ai noté dans les commentaires, cela ne fonctionne pas; s est défini à l'intérieur de la boucle afin que le code ci-dessus ne compile même pas. Il devrait y avoir une copie de s par fil de toute façon.


Les variables déclarées à l'intérieur de la portée de la boucle sont automatiquement privées. Tout comme le compteur de boucle I dans ce cas.



0
votes

Je n'ai jamais écrit de code OpenMP, mais j'ai fait beaucoup de programmation multi-filetées, mon instinct peut donc être correct ou non.

La méthode insert () sur l'ensemble n'est pas du fil de sécurité, et il échoue car plusieurs threads l'invoquent simultanément. Vous devez verrouiller (ou certains équivalents OpenMP) pour garantir les inserts sont effectués de manière séquentielle.

EDIT: Essayez ceci xxx


3 commentaires

Ce n'est pas le même ensemble - il devrait y avoir un ensemble par fil, car il est créé à l'intérieur de la région parallèle. Afin tant que la méthode insert () est réentrante, cela devrait suffire.


J'ai essayé un équivalent à cela en utilisant les directives OMM Critiques qui le résoudent, mais n'explique pas ce qui se passe mal. Je ne pense pas que je devrais avoir à ajouter une serrure à la version de production de cela puisqu'elle fonctionne bien sans entrées 32 bits.


Oh bien sûr. J'aurais dû comprendre que cela ne pouvait pas être aussi simple que cela.