Pregunta

Tengo objetos de función pequeños pero de uso frecuente.Cada hilo obtiene su propia copia.Todo se asigna estáticamente.Las copias no comparten ningún dato global o estático.¿Necesito proteger estos objetos contra el intercambio falso?

Gracias.EDITAR:Aquí hay un programa de juguete que usa Boost.Threads.¿Puede ocurrir un intercambio falso para el campo? datos?

#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();
}
¿Fue útil?

Solución

El intercambio falso entre subprocesos se produce cuando 2 o más subprocesos utilizan la misma línea de caché.

P.ej.:

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();
}

Los hilos del primer bloque sufren de intercambio falso.Los hilos del segundo bloque no (gracias a CACHELINE_SIZE).

Los datos de la pila siempre están "lejos" de otros subprocesos.(P.ej.debajo de ventanas, al menos un par de páginas).

Con su definición de un objeto de función, puede aparecer un intercambio falso, porque las instancias de Work se crea en el montón y este espacio del montón se utiliza dentro del hilo.

Esto puede llevar a varios Work las instancias son adyacentes y, por lo tanto, pueden incurrir en el uso compartido de líneas de caché.

Pero ...su muestra no tiene sentido, porque los datos nunca se tocan externamente y, por lo tanto, se induce innecesariamente un intercambio falso.

La forma más sencilla de evitar problemas como este es copiar los datos "compartidos" localmente en la pila y luego trabajar en la copia de la pila.Cuando termine su trabajo, cópielo nuevamente a la var de salida.

P.ej:

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;
};

Esto evita todos los problemas al compartir.

Otros consejos

Hice un poco de investigación y parece que no hay una solución de bala de plata para compartir falsos.Aquí está lo que yo soy (gracias a Christopher): 1) Padice sus datos de ambos lados con cosas no utilizadas o con menos frecuencia. 2) Copie sus datos en la pila y copígela después de que se realice todo el trabajo duro. 3) Utilice la asignación de memoria alineada por caché.

No me siento del todo seguro con los detalles, pero aquí está mi opinión:

(1) Su ejemplo simplificado no funciona desde el impulso create_thread espera una referencia, le pasas una temporal.

(2) si usarías vector<Work> con un elemento de cada hilo, o de lo contrario tenerlos en la memoria de forma secuencial, se producirá un intercambio falso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top