Стоимость thread_local - PullRequest
       32

Стоимость thread_local

16 голосов
/ 13 декабря 2011

Теперь, когда C ++ добавляет thread_local хранилище в качестве функции языка, мне интересно несколько вещей:

  1. Какова будет стоимость thead_local?
    • В памяти?
    • Для операций чтения и записи?
  2. Связано с этим: как операционные системы обычно реализуют это? Казалось бы, все, что было объявлено thread_local, должно было бы иметь место для каждого потока, созданного для конкретного потока.

Ответы [ 2 ]

11 голосов
/ 13 декабря 2011

Место для хранения: размер переменной * количество потоков или, возможно, (sizeof (var) + sizeof (var *)) * количество потоков.

Существует два основных способа реализации локального хранилища потоков:

  1. Использование какого-либо системного вызова, который получает информацию о текущем потоке ядра. Sloooow.

  2. Использование некоторого указателя, возможно, в регистре процессора, который устанавливается правильно при каждом переключении контекста потока ядром - одновременно со всеми другими регистрами. Дешево.

На платформах Intel вариант 2 обычно реализуется через некоторый сегментный регистр (FS или GS, я не помню). И GCC, и MSVC поддерживают это. Поэтому время доступа примерно такое же быстрое, как и для глобальных переменных.

Это также возможно, но я еще не видел это на практике, чтобы это было реализовано с помощью существующих библиотечных функций, таких как pthread_getspecific. В этом случае производительность будет равна 1. или 2. плюс накладные расходы на вызовы библиотеки. Имейте в виду, что вариант 2. + издержки на вызов библиотеки по-прежнему намного быстрее, чем вызов ядра.

9 голосов
/ 13 декабря 2011

Описание работы Linux в Uli Drepper (сопровождающий glibc) можно найти здесь: www.akkadia.org / drepper / tls.pdf

Требование обработки динамически загружаемых модулей и т. Д. Делает весь механизм немного запутанным, что, возможно, частично объясняет, почему вес документа составляет 79 страниц (!).

С точки зрения использования памяти, каждая переменная для каждого потока, очевидно, нуждается в собственной памяти для каждого потока (хотя в некоторых случаях это может быть выполнено лениво, так что пространство выделяется только после первого доступа к переменной), а затем некоторые дополнительные структуры данных, необходимые для таблиц смещения и т. д.

С точки зрения производительности дополнительные затраты на доступ к переменной TLS в основном связаны с получением адреса переменной. В x86 Linux регистр GS используется в качестве начала для получения идентификатора потока, в x86-64 FS. Обычно существует несколько разыменований указателя и вызов функции (__tls_get_addr) для динамически загружаемого кода. Кроме того, стоит отметить, что создание нового потока медленнее, поскольку реализация должна выделять пространство и, возможно, инициализировать все переменные TLS (если не выполняется лениво).

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

...