Когда использовать Test & Set или Test & Test & Set? - PullRequest
3 голосов
/ 29 ноября 2010

Параллельное программирование под x86 может быть тяжелой работой, особенно под многоядерным процессором. Допустим, у нас есть многоядерный процессор x86 и несколько разных многопоточных комбинаций связи.

  1. Один писатель и один читатель
  2. Один читатель, несколько писателей
  3. Несколько читателей и один писатель
  4. Несколько читателей и несколько писателей

Какая из моделей лучше (более эффективна) для блокировки области совместно используемой памяти: Test & Set или Test & Test & Set и когда ее использовать!

Здесь у меня есть две простые (без ограничений по времени) процедуры тестирования, написанные в Delphi IDE на ассемблере x86:

procedure TestAndSet(const oldValue, newValue: cardinal; var destination);
asm
//eax = oldValue
//edx = NewLockValue
//ecx = destination = 32 bit pointer on lock variable 4 byte aligned
@RepeatSpinLoop:
        push    eax                   //Save lock oldValue (compared)
        pause                         //CPU spin-loop hint
        lock    cmpxchg dword ptr [ecx], edx
        pop     eax                   //Restore eax as oldValue
        jnz     @RepeatSpinLoop       //Repeat if cmpxchg wasn't successful
end;

procedure TestAndTestAndSet(const oldValue, newValue: cardinal; var destination);
asm
//eax = oldValue
//edx = NewLockValue
//ecx = destination = 32 bit pointer on lock variable 4 byte aligned
@RepeatSpinLoop:
        push    eax                   //Save lock oldValue (compared)
@SpinLoop:
        pause                         //CPU spin-loop hint
        cmp     dword ptr [ecx], eax  //Test betfore test&set
        jnz     @SpinLoop
        lock    cmpxchg dword ptr [ecx], edx
        pop     eax                   //Restore eax as oldValue
        jnz     @RepeatSpinLoop       //Repeat if cmpxchg wasn't successful
end;

EDIT:

Intel в документации упоминает два подхода Test & Set или Test & Test & Set . Я не буду устанавливать, в каком случае кто-то лучше подходит, поэтому, когда его использовать. Проверить: Intel

Ответы [ 3 ]

3 голосов
/ 29 ноября 2010

Конечно, первый (testAndSet) лучше, потому что 2-й не добивается многого с повторением теста с использованием cmp & jnz - между ними.Пока вы делаете это, значение пункта назначения может измениться в любом случае, поскольку оно не заблокировано.

2 голосов
/ 29 ноября 2010

Я бы использовал 2-й подход, тест без блокировки, затем блокировку, если тест пройден успешно, с некоторыми предложениями:

  • использовать вызов SwitchToThread вместо паузы
  • положить вызов SwitchToThread в незапертый повторный цикл cmp
  • поставить вызов SwitchToThread только в случае сбоя cmp / lock

Во всех случаях, я думаю, вам лучше:

  • используйте Windows API для вашей синхронизации, если вы действительно хотите обрабатывать синхронизацию низкого уровня в вашем проекте, см. Функции синхронизации в MSDN - Microsoft сделала низкоуровневую иоптимизация работы для вас.Большинство из этих вызовов оптимизированы как ассемблерный код, выполняются в пользовательском режиме, поэтому они очень быстрые.
  • используют высокоуровневую многопоточную инфраструктуру, которая на практике решит все эти проблемы за вас и будет окончательно масштабироваться.хорошо - смотрите Delphi OmniThreadLibrary
  • используйте выделенный менеджер памяти, например NexusMM , TBBMM или ScaleMM / SynScaleMM
2 голосов
/ 29 ноября 2010

TTAS (# 2) - хорошая практика.«Скрываться» и ждать «возможности» перед выполнением CAS является обычной практикой в ​​классах Java и .NET.С учетом вышесказанного, cmpxchg получил довольно много оптимизаций за последние несколько лет, так что вполне возможно, что вы получите почти идентичные результаты на последнем поколении процессоров.

Что вы должны попробовать воднако в обоих случаях при вращении используется экспоненциальный откат .

Обновление

@ GJ: Вы должны найти ещеактуальная документация на сайте Intel .Обратите внимание на параграф о незапирании шины с 486 и сравнительную таблицу xchg и cmpxchg, которая показывает, что они практически идентичны.

Вращение по чтению против инструкции locked все еще будет хорошей идеей, чтобы избежать некоторого раздора при получении строки кэша в монопольном режиме.(Так что TTAS.)

Однако это даст полезный выигрыш только в том случае, если вы реализуете, например, экспоненциальный откат, даже через некоторое время уступая ЦП.

Различия между TTAS и TAS, или без отката, будут меньше, если вы используете один современный многоядерный процессор с общим кешем L3 между ядрами, и станут более заметными, если вы используетемногосекционный - например, сервер - компьютер или многоядерный процессор, который не имеет общего кэша между ядрами.Они также будут отличаться в зависимости от количества разногласий.(Т.е. при небольшой нагрузке разница между TTAS / TAS будет меньше.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...