0

J'utilise OpenCV dans une application commerciale et je n'ai pas d'autorisation de gestion pour acheter des licences TBB. J'ai donc construit OpenCV avec OpenMP comme framework de parallélisme. Toutes les caméras de vision industrielle que nous utilisons comme sources de trames que nous traitons en temps réel ont des SDK qui remplissent les mémoires tampons dans une file circulaire avec des données et appellent des rappels fournis par l'utilisateur pour les traiter simultanément dans les threads des SDKs 'propres pools de threads. Cela fonctionne très bien si je ne considère pas OpenMP, car je fais un tas de traitement (sans mémoire) sur des images individuelles avant de les sérialiser par des tampons inter-threads pour alimenter l'état de traitement où les trames doivent être traitées dans l'ordre. Si c'était juste le traitement d'image simultané, alors je n'aurais pas besoin d'OpenMP du tout; cependant, je dois le laisser activé dans OpenCV afin que le traitement de trame en ordre soit également accéléré. Ce qui m'inquiète, c'est comment je peux m'attendre à ce que l'OpenMP fonctionne quand il est utilisé dans la première phase, les callbacks exécutés simultanément dans les threads créés explicitement par les SDK de la caméra. Puis-je supposer que le runtime OpenMP est suffisamment intelligent pour utiliser efficacement son pool de threads lorsque des régions parallèles sont déclenchées dans plusieurs threads créés de l'extérieur?OpenMP et interopérabilité explicite des threads lors de l'utilisation d'OpenCV

La plate-forme est garantie x86-64 (VC 15 ou GCC).

+0

Si vous espérez que le pool de threads openmp prenne en compte vos threads d'appel, vous devez utiliser la même implémentation de threads que openmp. Ms thread pour ms ou Intel libiomp pthread pour libgomp – tim18

+0

Peut nécessiter _OMP_NESTED – tim18

+0

Ou, remplacez ces threads explicites par omp tâche, et utilisez omp imbri – tim18

Répondre

1

Situation

Si je l'ai bien compris la question, la bibliothèque de l'appareil que vous utilisez va engendrer un certain nombre de threads, et chacun d'entre eux appellera votre fonction de rappel. Dans votre callback, vous voulez utiliser OpenMP pour accélérer ce traitement. Les résultats de cela sont envoyés via un canal inter-thread à un pipeline de threads faisant plus de traitement.

Si cela ne va pas, veuillez ignorer le reste de cette réponse!

Reste de réponse

En utilisant OpenMP dans les callbacks semble être couper la charge de calcul de cette partie de votre demande en petits morceaux pour bénéficier pas grand-chose. La bibliothèque de caméras chevauche déjà le traitement des images. Utiliser OpenMP ici signifie que le traitement des images ne se chevauche pas (mais la bibliothèque de la caméra utilise encore plusieurs threads comme si c'était le cas).

Si elle ne encore se chevauchent, alors vous parler logiquement pas assez noyaux obtenu dans votre système pour maintenir la charge de travail globale de toute façon (en supposant que votre utilisation de OpenMP a donné lieu à tous les noyaux étant maxed le traitement d'une seule image) ... Je suppose que votre système suit avec succès le flux de trames, et qu'il doit avoir assez de grognement pour pouvoir le faire. Donc, je pense que ce ne sera pas vraiment une question de savoir si OpenMP sera intelligent dans son utilisation de son pool de threads; le pool de threads sera dédié au traitement d'une seule trame, et il se terminera avant l'arrivée de l'image suivante. Le non-overalapping signifie que la latence est plus faible, ce qui pourrait être ce que vous voulez. Cependant, vous pourriez obtenir la même chose si la bibliothèque de caméra utilisait un seul thread avec votre callback en utilisant OpenMP (et en assumant la responsabilité de terminer avant l'arrivée de la prochaine trame). Avec moins de changement de contexte de thread, il serait même un peu plus rapide.Donc, si vous pouvez arrêter la bibliothèque générant tous ces threads (peut-être il y a un paramètre de configuration, ou une variable d'environnement, ou une autre partie de son API), cela pourrait en valoir la peine.

+0

Par intelligent, je veux dire éviter de sursouscrire les threads du pool de threads aux threads matériels. Lorsque la région openmp parallel est atteinte, elle génère un certain nombre de threads N en fonction des paramètres openmp, par défaut le niveau de concurrence matérielle. Si des régions parallèles openmp sont imbriquées l'une dans l'autre, aucun thread supplémentaire n'est normalement généré, sauf si omp_set_nested() est activé. Mais si une région parallèle openmp est dans M threads créés de l'extérieur, on se retrouve avec un total de N * M threads. Donc, le problème est de savoir comment désactiver cela. J'ai peut-être trouvé une solution de contournement, que je vais publier. –

+0

@DisplayName, oui je pense que de gauche à ses propres dispositifs, vous finiriez avec N x M threads. Mais il y a une différence entre le nombre de threads existants et le nombre de threads essayant de les exécuter tous en même temps. Parmi ces threads N x M, je pense que vous constaterez que seulement N d'entre eux tournent à un moment donné. Logiquement, si N threads ne peuvent pas traiter une seule trame dans le temps d'une seule trame, alors vous n'avez pas assez de cœurs en premier lieu. En supposant que ce n'est pas le cas, il n'y aura plus que N threads en cours d'exécution. Donc vous ne comptez pas sur OpenMP étant "intelligent" - ce n'est pas nécessaire. – bazza

+0

Oui, mais il me semble que le manque de garanties quant à l'ordonnancement de la programmation de l'exécution de ces threads signifie qu'il y aura plus de changements de contexte, ce qui est l'un des problèmes de plus de threads. Cela pourrait également diminuer la localisation du cache par cœur. Je vais tester les différences de performances en utilisant la solution de contournement des sections omp pour que OpenMP utilisé par la bibliothèque dans le rappel utilise uniquement un thread. –

0

Voici un exemple de code qui explique une solution de contournement que j'ai trouvée. LibraryFunction() représente une fonction que je ne peux pas modifier qui utilise déjà la parallélisation OpenMP, comme quelque chose d'OpenCV.

void LibraryFunction(int phase, mutex &mtx) 
{ 
    #pragma omp parallel num_threads(3) 
    { 
     lock_guard<mutex> l{mtx}; 
     cerr << "Phase: " << phase << "\tTID: " << this_thread::get_id() << "\tOMP: " << omp_get_thread_num() << endl; 
    } 
} 

Le problème de sursouscription avec un filetage extérieur est visible à la présente:

int main(void) 
{ 
    omp_set_dynamic(thread::hardware_concurrency()); 
    omp_set_nested(0); 
    vector<std::thread> threads; 
    threads.reserve(3); 
    mutex mtx; 
    for (int i = 0; i < 3; ++i) 
    { 
     threads.emplace_back([&] 
     { 
      this_thread::sleep_for(chrono::milliseconds(200)); 
      LibraryFunction(1, mtx); 
     }); 
    } 
    for (auto &t : threads) t.join(); 
    cerr << endl; 
    LibraryFunction(2, mtx); 
    return EXIT_SUCCESS; 
} 

La sortie est la suivante:

Phase: 1  TID: 7812  OMP: 0 
Phase: 1  TID: 3928  OMP: 0 
Phase: 1  TID: 2984  OMP: 0 
Phase: 1  TID: 9924  OMP: 1 
Phase: 1  TID: 9560  OMP: 2 
Phase: 1  TID: 2576  OMP: 1 
Phase: 1  TID: 5380  OMP: 2 
Phase: 1  TID: 3428  OMP: 1 
Phase: 1  TID: 10096  OMP: 2 

Phase: 2  TID: 9948  OMP: 0 
Phase: 2  TID: 10096  OMP: 1 
Phase: 2  TID: 3428  OMP: 2 

Phase 1 représente l'exécution du code de bibliothèque OpenMPed dans le SDK caméra threads, alors que Phase 2 est le code de bibliothèque OpenMPed utilisé pour les étapes de pipeline de traitement ultérieures déclenchées à partir d'un seul thread. Le problème est évident: le nombre de threads est multiplié par l'imbrication de threads OpenMP-in-external, ce qui entraîne une surabonnement durant la phase 1. Le développement d'OpenCV avec OpenMP désactivé, tout en corrigeant la sursouscription en Phase 1, en l'absence d'accélération de la phase 2.

La solution de contournement que j'ai trouvée est que l'appel de LibraryFunction() pendant la phase 1 dans #pragma omp parallel sections {} supprime la génération de threads dans cet appel particulier. Maintenant le résultat est:

Phase: 1  TID: 3168  OMP: 0 
Phase: 1  TID: 8888  OMP: 0 
Phase: 1  TID: 5712  OMP: 0 

Phase: 2  TID: 10232  OMP: 0 
Phase: 2  TID: 5012  OMP: 1 
Phase: 2  TID: 4224  OMP: 2 

Je n'ai pas encore testé cela avec OpenCV, mais je m'attends à ce que cela fonctionne.