накладные расходы локального хранилища - PullRequest
6 голосов
/ 27 марта 2011

Предположим, что есть не-реентерабельная функция, использующая глобальные переменные:


int i;
void foo(void){
/* modify i */
}

И затем я хочу использовать эту функцию в многопоточном коде, чтобы я мог изменить код следующим образом:


void foo(int i){
/* modify i */
}

или, с помощью спецификатора gcc __thread, проще:


__thread int i;
void foo(void){
/* modify i */
}

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

Мои вопросы: сколько накладных расходов на локальное хранилище потока?Есть ли некоторые неочевидные проблемы с TLS?

Существуют ли дополнительные издержки, если я изменю переменную TLS с помощью отдельного указателя, например:


__thread int i;
void foo(void){
int *p = &i;
/* modify i using p pointer */
}

Спасибо.

Ответы [ 2 ]

10 голосов
/ 27 марта 2011

И затем, я хочу использовать эту функцию в многопоточном коде, чтобы я мог изменить код следующим образом:

void foo(int i){
    /* modify i */
}

Это, безусловно, не работает, так как вы будете изменять только копию i.Вместо этого вам нужно будет передать int* или int&, если вы хотите, чтобы изменения не изменились.

Использование TLS, безусловно, не вызовет значительных накладных расходов (в пространстве или времени) по сравнению с любым пользовательским подходомможет следовать, чтобы реализовать ту же функциональность.Компиляторы на практике реализуют TLS, динамически выделяя «слот» хранилища в глобальной структуре данных, содержащей вашу локальную переменную потока.

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

Если вы собираетесь сделать это:

__thread int i;
void foo(void){
    int *p = &i;
    /* modify i using p pointer */
}

, тогда нет необходимости обращаться кi с помощью указателя.Думайте о i как о глобальной переменной, которая имеет разные значения для каждого работающего потока.Вам не нужно было бы обращаться к обычному глобальному объекту через указатель, чтобы вносить изменения, поэтому также нет необходимости использовать указатель с локальной переменной потока.

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

1 голос
/ 27 марта 2011

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

...