Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.119.248.149] |
|
Страницы: (3) 1 [2] 3 все ( Перейти к последнему сообщению ) |
Сообщ.
#16
,
|
|
|
Synchronization Functions
Critical section Slim Reader/Writer (SRW) и возможно One-time initialization Synchronization barrier |
Сообщ.
#17
,
|
|
|
Реально...
Замерил скорость выполнения разного кода (в тактах), получилось вот что: Холостой ход - 30-60 cmpxchg (10 раз) - 60-120 lock cmpxchg (10 раз) - 400-550 SpinLoop 1024 циклов (cmp) - 26000-36000 SpinLoop 1024 циклов (locked cmpxchg) - 50000-70000 CreateEvent - 30000-50000 Set/ResetEvent - 2000-4000 CloseHandle - 4000-7000 SwitchToThread - 2500-4000 Sleep(0) - 3000-5000 Sleep(1) - 160 тыс-3 млн WaitForSingleObject (signaled) - 2000-5000 InitializeCriticalSectionAndSpinCount - 1800-2800 InitializeCriticalSection - 1400-2400 EnterCriticalSection (свободная) - 80-150 EnterCriticalSection (рекурсивно) - 100-300 DeleteCriticalSection - 500-1500 Добавлено Shaggy, гляну, thanx! |
Сообщ.
#18
,
|
|
|
Вопрос: есть ли гарантия, что WaitForSingleObject успеет сработать, если другой поток сделает SetEvent и CloseHandle подряд?
|
Сообщ.
#19
,
|
|
|
Цитата Jin X @ CreateEvent хоть и долгая тема Его нужно дернуть 1 раз при запуске и все. Цитата Jin X @ есть ли гарантия, что WaitForSingleObject успеет сработать, если другой поток сделает SetEvent и CloseHandle подряд? А зачем так делать? Нет, нету гарантий. Если уж так надо, то после WaitForSingleObject и закрывай. |
Сообщ.
#20
,
|
|
|
Цитата shm @ Если сделать инициализацию 1 раз, значит нужно делать и удаление (типа DeleteCriticalSection), а я хочу сделать только Lock (Enter) и Unlock (Leave). Есть прикольные функции: WaitOnAddress и WakeByAddressSingle/WakeByAddressAll, жаль только, что существуют они в Windows 8+ (посему придётся комбинировать их и Event'ы для поддержки работы в Windows 7/XP). Его нужно дернуть 1 раз при запуске и все. |
Сообщ.
#21
,
|
|
|
Jin X
А свою написать? Мне не понравилось то что для критической секции нужна глобальная переменная. И я сделал вой вариант. procedure TaskSwitch; var Cis:PCriticalSection; Status:Boolean; begin Status:=GetIntStatus; DisableInt; CisBegin(Cis); if TaskSetModeCmd.Lock=MUnLocked then ExecuteCMD; AlgTaskSwitch; CisEnd(Cis); if Status=False then EnableInt; end; Прикреплённый файлUSynchronization.pas (9,15 Кбайт, скачиваний: 321) Добавлено PS. Код сырой и не тестированный. Просто как пример. |
Сообщ.
#22
,
|
|
|
Цитата Jin X @ Если сделать инициализацию 1 раз, значит нужно делать и удаление (типа DeleteCriticalSection), а я хочу сделать только Lock (Enter) и Unlock (Leave) Это неэффективно. Неужели так лень пару лишних функций вызвать? Pavia, Я не претендую на правильность, но все же. class CriticalSection: private Event { static void EnterSingle(CriticalSection *cs) { //std::cout << "EnterSingle" << std::endl; } static void LeaveSingle(CriticalSection *cs) { //std::cout << "LeaveSingle" << std::endl; } static void EnterMultitasking(CriticalSection *cs); static void LeaveMultitasking(CriticalSection *cs); static void EnterSmp(CriticalSection *cs); static void LeaveSmp(CriticalSection *cs); static void (*EnterProc)(CriticalSection *cs); static void (*LeaveProc)(CriticalSection *cs); volatile atom State; // atom LockCounter; //locked bool OnWaitBegin(); //bool OnSetStateSingle(Thread *); public: class Lock { CriticalSection *section; public: Lock(CriticalSection &cs) { section = &cs; cs.enter(); } ~Lock() { section->leave(); } }; CriticalSection(); CriticalSection(bool Busy); static void SetMutlitaskingMode() { EnterProc = &EnterMultitasking; LeaveProc = &LeaveMultitasking; } static void SetSmpMode() { EnterProc = &EnterSmp; LeaveProc = &LeaveSmp; } void enter() { EnterProc(this); } void leave() { LeaveProc(this); } bool IsLock() const { return (State != 0); } bool enter(DWORD Timeout); }; //... void CriticalSection::EnterSmp(CriticalSection *cs) { static const int max_spin_it = 1024; unsigned int cpu_id, result; int i; cpu_id = GetCurrentCpuId() + 1; result = CpuCompareAndSwapLock<volatile atom>(&cs->State, 0, cpu_id); if(result == 0) return; else if(result != cpu_id) { for(i = 0; i < max_spin_it; i++) { if(cs->State == 0) { cpu_id = GetCurrentCpuId() + 1; result = CpuCompareAndSwapLock<volatile atom>(&cs->State, 0, cpu_id); if(result == 0) return; } CpuPause(); } } cs->Wait(); cs->State = GetCurrentCpuId() + 1; } void CriticalSection::LeaveSmp(CriticalSection *cs) { if(cs->HaveWaitingThreads()) cs->SetState(); else cs->State = 0; } |
Сообщ.
#23
,
|
|
|
Pavia, what's that?
uses SysSupport, UMemoryManager, UMemoryMap; Добавлено Цитата Pavia @ Что именно написать своё?А свою написать? shm, то ли неполный код, то ли... х/з. GetCurrentCpuId - ? CpuCompareAndSwapLock - ? Wait - ? SetState - ? HaveWaitingThreads - ? ... |
Сообщ.
#24
,
|
|
|
shm
Цитата shm @ Pavia, Я хотел показать метод с патчингом. В стеке страниц выделяем страницы. Страницы связаны в цепочку двойным списком. В этих страницах хранятся адреса критических секций и её данные как то мьютекс. При 1 заходе в критическую секцию адрес патчится на спинлок. Для задачи Jin X: Цитата Jin X @ Если сделать инициализацию 1 раз, значит нужно делать и удаление (типа DeleteCriticalSection), а я хочу сделать только Lock (Enter) и Unlock (Leave). Можно сделать по аналогии патч который накладывает 5-байтовый NOP. |
Сообщ.
#25
,
|
|
|
Цитата Jin X @ shm, то ли неполный код, то ли... х/з. Это для Pavia. Код не имеет никакого отношения к Win, как и код Pavia. Да простят нас админы за оффтоп. Добавлено Pavia, я у тебя не вижу усыпления потока. Без этого твои мьютексы это скорее спинлок, что крайне неэффективно в силу очевидных причин. Связанный список я у себя храню прямо в структуре потока, которую очень легко достать средствами TLS. |
Сообщ.
#26
,
|
|
|
Цитата Jin X @ Замерил скорость выполнения разного кода (в тактах), получилось вот что: lock cmpxchg (10 раз) - 400-550 pinLoop 1024 циклов (locked cmpxchg) - 50000-70000 Это для случая, когда только один поток крутит спин-луп. Если же его будут крутить два и более потока одновременно, то эти цифирьки могут увеличиться в несколько раз (где-то от 3 до 5). Дело в том, что lock cmpxchg в любом случае производит запись в переменную (записывается либо новое, либо старое значение), поэтому соответствующая линейка кэша оказывается модифицированной, со всеми вытекающими последствиями синхронизации кэшей 1-2 уровня при многопоточном доступе. В случае одного потока модифицированная линейка остается в кэше L1 того ядра, на котором работает поток. А в случае двух и более потоков, она постоянно перебрасывается из L1 одного ядра в L1 другого через общий L3. Поэтому, чтобы избежать этих перебросов, в приведенных тобой примерах от Intel перед lock-обменом используется простое сравнение переменной "только на чтение". В этом случае, при наличии нескольких ожидающих потоков, они до снятия лока только читают линейку, не изменяя ее, и соотв-но она находится в неизменном shared состоянии в L1 этих потоков\ядер |
Сообщ.
#27
,
|
|
|
Цитата leo @ Я даже скажу, что lock вообще сильно тормозит процесс. Особенно если это будут делать одновременно несколько потоков...Дело в том, что lock cmpxchg в любом случае производит запись в переменную (записывается либо новое, либо старое значение) Из Intel Instruction Set Reference (cmpxchg): TEMP ← DEST IF accumulator = TEMP THEN ZF ← 1; DEST ← SRC; ELSE ZF ← 0; accumulator ← TEMP; DEST ← TEMP; FI; |
Сообщ.
#28
,
|
|
|
Цитата Jin X @ Можно было бы обойтись и без неё. Как? |
Сообщ.
#29
,
|
|
|
Цитата shm @ А в чём её суть?Как? Temp := Dest Dest := Temp |
Сообщ.
#30
,
|
|
|
Быть может там прописана технология реализации. Т.е. TEMP - a'la кэш, а в конце команды обязательно надо что-то послать в память, ну а потому как совпадёт кэш с DEST, то не страшно его и послать обратно!??
|