Ложное совместное использование и переменные стека - PullRequest
7 голосов
/ 26 июля 2010

У меня есть небольшие, но часто используемые функциональные объекты.Каждый поток получает свою собственную копию.Все размещено статически.Копии не разделяют глобальные или статические данные.Нужно ли защищать эти объекты от ложного обмена?

Спасибо.РЕДАКТИРОВАТЬ: Вот игрушечная программа, которая использует Boost.Threads.Может ли произойти ложный обмен для поля data ?

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

Ответы [ 3 ]

6 голосов
/ 26 июля 2010

Ложное разделение между потоками происходит, когда 2 или более потоков используют одну и ту же строку кэша.

Например:

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

Потоки в первом блоке страдают от ложного совместного использования.Потоки во втором блоке не (спасибо CACHELINE_SIZE).

Данные в стеке всегда «далеко» от других потоков.(Например, под окнами, по крайней мере, несколько страниц).

С вашим определением функционального объекта может появиться ложное совместное использование, потому что экземпляры Work создаются в куче, и это пространство кучи используетсявнутри потока.

Это может привести к тому, что несколько экземпляров Work будут смежными, что может привести к совместному использованию строк кэша.

Но ... ваш пример не имеет смысла, поскольку данныеникогда не затрагивается снаружи, поэтому ложное совместное использование вызывается без необходимости.

Самый простой способ предотвратить подобные проблемы - это скопировать ваши «общие» данные локально в стек, а затем работать с копией стека.Когда работа закончена, скопируйте ее обратно в выходную переменную.

Например:

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

Это предотвращает все проблемы с совместным использованием.

2 голосов
/ 27 июля 2010

Я провел немало исследований, и, похоже, не существует решения «серебряной пули» для ложного обмена.Вот что я придумываю (благодаря Кристоферу): 1) Добавляйте свои данные с обеих сторон неиспользованным или менее часто используемым материалом.2) Скопируйте ваши данные в стек и скопируйте их обратно после выполнения всей тяжелой работы.3) Используйте выравнивание кеш-памяти.

0 голосов
/ 26 июля 2010

Я не чувствую себя в полной безопасности с деталями, но вот мое мнение:

(1) Ваш упрощенный пример не работает, так как boost create_thread ожидает ссылку, вы передаете временный.

(2) если бы вы использовали vector<Work> с одним элементом для каждого потока или в противном случае последовательно располагали их в памяти, произойдет ложное совместное использование.

...