Почему локальная память потока является настолько медленной?

Вместо запуска цикла For по всему (!) Столбцу вы можете сначала проверить, есть ли что-то в столбце, а затем .Find искомое значение. Если вы нашли результат, вы можете использовать его номер строки.

Вместо копирования / вставки вы можете просто назначить Range.Value для получения значений без форматирования.

Этот код копирует значения всей строки четырьмя строками ниже.

Sub Test()
    Dim ws As Worksheet
    Dim c As Range

    Set ws = ActiveSheet
    If WorksheetFunction.CountA(ws.Columns(2)) > 0 Then
        Set c = ws.Columns(2).Find( _
            What:="Total WI Expenses", _
            After:=ws.Cells(1, 2), _
            SearchOrder:=xlByRows, _
            SearchDirection:=xlNext)
        If Not c Is Nothing Then
            ws.Rows(c.Row + 4).Value = ws.Rows(c.Row).Value
        End If
        Set c = Nothing
    End If
    Set ws = Nothing
End Sub
36
задан ks1322 18 October 2012 в 13:53
поделиться

4 ответа

Скорость зависит от реализации TLS.

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

Для поиска указателя Вы нуждаетесь в помощи от планировщика все же. Планировщик должен - на переключении задач - обновляют указатель на данные TLS.

Другой быстрый способ реализовать TLS через Блок управления памятью. Здесь TLS рассматривают как любые другие данные за исключением того, что переменные TLS выделяются в специальном сегменте. Планировщик будет - на переключении задач - отображают корректный блок памяти в адресное пространство задачи.

, Если планировщик не поддерживает ни одного из этих методов, компилятор/библиотека должен сделать следующее:

  • добираются, текущий ThreadId
  • Берут семафор
  • Поиск указатель на блок TLS ThreadId (может использовать приблизительно одну карту)
  • Выпуск семафор
  • Возврат тот указатель.

, Очевидно, выполнение все это для каждого доступа к данным TLS требует времени и, возможно, нуждается в трех вызовах ОС: Получение ThreadId, Возьмите и Выпуск семафор.

семафор является btw, требуемым не удостоверяться никакие чтения потока из списка указателя TLS, в то время как другой поток посреди порождения нового потока. (и как таковой выделяют новый блок TLS и изменяют datastructure).

, К сожалению, весьма распространено видеть медленную реализацию TLS на практике.

34
ответ дан Nils Pipenbrinck 27 November 2019 в 05:54
поделиться

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

Для наблюдения, какой код сгенерирован для tls скомпилируйте и obj2asm этот код:

__thread int x;
int foo() { return x; }

TLS реализован очень по-другому в Windows, чем на Linux и будет очень отличаться снова на OSX. Но во всех случаях это будет намного больше инструкций, чем простая загрузка местоположения статического ЗУ. TLS всегда будет медленным относительно простого доступа. Доступ к TLS globals в жестком цикле будет медленным, также. Попытайтесь кэшировать значение TLS во временном файле вместо этого.

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

8
ответ дан thkala 27 November 2019 в 05:54
поделиться

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

Я рад сообщить, что теперь у нас есть небольшой API, который предлагает более чем 50% сокращение Процессорное время для эквивалентной операции, когда вызывающий поток не «знает» свой идентификатор потока и сокращение> 65%, если вызывающий поток уже получил свой идентификатор потока (возможно, для какого-либо другого более раннего шага обработки).

function (get_thread_private_ptr ()) всегда возвращает указатель на структуру, которую мы используем внутри для хранения всех видов, поэтому нам нужен только один для каждого потока.

В общем, я думаю, что поддержка Win32 TLS действительно плохо продумана.

2
ответ дан 27 November 2019 в 05:54
поделиться

Если вы не можете использовать поддержку TLS компилятора, вы можете управлять TLS самостоятельно. Я создал шаблон оболочки для C ++, поэтому базовую реализацию легко заменить. В этом примере , я реализовал его для Win32. Примечание. Поскольку вы не можете получить неограниченное количество индексов TLS для каждого процесса (по крайней мере, в Win32), вы должны указать на блоки кучи, достаточно большие для хранения все данные, относящиеся к потоку. Таким образом, у вас будет минимальное количество индексов TLS и связанных запросов. В «лучшем случае» у вас будет всего 1 указатель TLS, указывающий на один частный блок кучи за поток.

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

Не забывайте освобождать память, если она больше не используется. Я делаю это, помещая поток в класс (как это делает Java) и обрабатываю TLS конструктором и деструктором. {{1} } Кроме того, я храню часто используемые данные, такие как дескрипторы потоков и идентификаторы, в качестве членов класса.

использование:

для типа *: tl_ptr

для константного типа *: tl_ptr

для типа * const: {{1 }} const tl_ptr

const type * const: const tl_ptr

template<typename T>
class tl_ptr {
protected:
    DWORD index;
public:
    tl_ptr(void) : index(TlsAlloc()){
        assert(index != TLS_OUT_OF_INDEXES);
        set(NULL);
    }
    void set(T* ptr){
        TlsSetValue(index,(LPVOID) ptr);
    }
    T* get(void)const {
        return (T*) TlsGetValue(index);
    }
    tl_ptr& operator=(T* ptr){
        set(ptr);
        return *this;
    }
    tl_ptr& operator=(const tl_ptr& other){
        set(other.get());
        return *this;
    }
    T& operator*(void)const{
        return *get();
    }
    T* operator->(void)const{
        return get();
    }
    ~tl_ptr(){
        TlsFree(index);
    }
};
4
ответ дан 27 November 2019 в 05:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: