Ложное совместное использование и переменные стека

StackOverflow https://stackoverflow.com/questions/3332803

  •  29-09-2020
  •  | 
  •  

Вопрос

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

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

#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();
}
Это было полезно?

Решение

Ложный обмен между потоками - это когда 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).

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

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

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

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

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

Например,:

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

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

Другие советы

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top