Локальный указатель потока, указывающий на локальные данные вне потока - PullRequest
1 голос
/ 28 марта 2012

У меня есть массив, который не является локальным для потока, например, следующий:

long array[NTHREADS];

Здесь массив [0] управляется потоком 0, массив [1] - потоком 1 и так далее.Мы не использовали локальную переменную потока, потому что в какой-то момент потоки также должны читать части других потоков.Однако большую часть времени они модифицируют свою собственную часть.Конечно, мы можем изменить данные, используя array[thread_id], но для ускорения выполнения я хочу использовать указатель.

Теперь, поскольку каждый поток управляет своими собственными данными, указатель должен быть локальным и назначаться в начале.Поэтому мне нужно что-то вроде этого (в синтаксисе gcc).

  __thread long* tl_ptr;

  tl_ptr = &array[threadid];

Таким образом, я могу изменить специфичные для потока данные, используя *tl_ptr.Теперь мой вопрос, является ли этот подход правильным?Есть ли проблемы в этом подходе?

Ответы [ 2 ]

1 голос
/ 28 марта 2012

C ++ 11 имеет модель памяти, которая определяет поведение в этих условиях. C и C ++ 03 и более ранние версии в основном являются однопоточными - никаких нативных атомизаторов / заборов.

Это означает, что если вы не используете компилятор C ++ 11 (который реализует модель памяти), вы можете получить странные эффекты из-за проблем с когерентностью кэша и т. Д. Это зависит от процессора.

Если вы знаете, на каком процессоре вы будете работать, что у него достаточно «сильная» модель памяти, вы можете установить, что ваш подход безопасен, но он не будет переносимым.

0 голосов
/ 29 марта 2012

Если вы пишете программу на диалекте до C ++ 11, то, строго говоря, результат не будет переносимым - старые диалекты C ++ не имеют никакого понятия о потоках.

Но если вы используете C ++ 11 (чего можно достичь, сказав «Это теперь C ++ 11» без изменения одного байта и использования флага компилятора) и придерживайтесь потоковых возможностейязык (thread_local вместо __thread), переносимость гарантируется языком.

В реальной жизни большинство реализаций до C ++ 11 достаточно хорошо работают с потоками.Например, gcc и x86_64 будут работать - gcc имеет встроенные механизмы для многопоточности, а x86_64 - это архитектура с сильной моделью памяти (подробнее об этом здесь в главе, посвященной упорядочению памяти).

Тем не менее, вы используете локальный поток странным образом.Обычная семантика -

__thread long array_data;
long* array[NTHREADS];  // non thread local pointer to thread local data

Теперь, если вы измените array_data в любом потоке, будет изменена только локальная копия потока array_data.Если вам нужен доступ к array_data из другого потока, скажем, из супервизорного потока, который суммирует все array_data, вам нужен массив, который содержит указатели на все локальные потоки и какие элементы инициализируются при запуске каждого потока с

array[iThread] = &array_data;  // where iThread is an index for each thread

Доступ из потока супервизора будет выглядеть следующим образом:

long sum=0;
for (int i=0; i<NTHREADS; ++i)
    sum += *array[i];

Необходимо убедиться, что ваш доступ к локальным потокам потоков из других потоков сериализован или, при необходимости, защищен мьютексами.В вашем случае (x86 и gcc) цикл суммирования будет просто работать - выровненные длинные строки гарантированно будут атомарными на вашем оборудовании, а gcc увидит указатель как неограниченный - но будьте осторожны.

...