Question

J'ai des objets fonction petits mais fréquemment utilisés.Chaque fil de discussion obtient sa propre copie.Tout est alloué de manière statique.Les copies ne partagent aucune donnée globale ou statique.Dois-je protéger ces objets contre les faux partages ?

Merci.MODIFIER:Voici un programme de jouets qui utilise Boost.Threads.Un faux partage peut-il se produire pour le champ données?

#include <boost/thread/thread.hpp>

struct Work {
    void operator()() {
        ++data;
    }

    int data;
};

int main() {
    boost::thread_group threads;
    for (int i = 0; i < 10; ++i)
        threads.create_thread(Work());
    threads.join_all();
}
Était-ce utile?

La solution

Le faux partage entre les threads se produit lorsque 2 threads ou plus utilisent la même ligne de cache.

Par exemple.:

struct Work {
    Work( int& d) : data( d ) {}
    void operator()() {
        ++data;
    }

    int& data;
};

int main() {
    int false_sharing[10] = { 0 };
    boost::thread_group threads;
    for (int i = 0; i < 10; ++i)
        threads.create_thread(Work(false_sharing[i]));
    threads.join_all();

    int no_false_sharing[10 * CACHELINE_SIZE_INTS] = { 0 };
    for (int i = 0; i < 10; ++i)
        threads.create_thread(Work(no_false_sharing[i * CACHELINE_SIZE_INTS]));
    threads.join_all();
}

Les fils de discussion du premier bloc souffrent de faux partages.Les threads du deuxième bloc ne le font pas (grâce à CACHELINE_SIZE).

Les données sur la pile sont toujours « loin » des autres threads.(Par exemple.sous Windows, au moins quelques pages).

Avec votre définition d'un objet fonction, un faux partage peut apparaître, car les instances de Work est créé sur le tas et cet espace de tas est utilisé à l'intérieur du thread.

Cela peut conduire à plusieurs Work les instances doivent être adjacentes et peuvent donc entraîner le partage des lignes de cache.

Mais ...votre échantillon n'a pas de sens, car les données ne sont jamais touchées à l'extérieur et un faux partage est donc induit inutilement.

Le moyen le plus simple, pour éviter de tels problèmes, est de copier vos données « partagées » localement sur la pile, puis de travailler sur la copie de la pile.Lorsque votre travail est terminé, copiez-le dans la variable de sortie.

Par exemple:

struct Work {
    Work( int& d) : data( d ) {}
    void operator()()
    {
        int tmp = data;
        for( int i = 0; i < lengthy_op; ++i )
           ++tmp;
        data = tmp;
    }

    int& data;
};

Cela évite tous les problèmes de partage.

Autres conseils

J'ai fait un peu de recherche et il semble qu'il n'y ait aucune solution de balle d'argent à un faux partage.Voici ce que je propose (merci à Christopher): 1) Tamponnez vos données des deux côtés avec des trucs inutilisés ou moins fréquemment utilisés. 2) Copiez vos données en pile et copiez-la après tout le travail acharné. 3) Utilisez une allocation de mémoire alignée sur le cache.

Je ne me sens pas entièrement en sécurité avec les détails, mais voici mon avis :

(1) Votre exemple simplifié est cassé depuis boost create_thread attend une référence, vous passez un temporaire.

(2) si vous utilisiez vector<Work> avec un élément pour chaque thread, ou autrement les avoir en mémoire de manière séquentielle, un faux partage se produira.

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