Результат в общем-то получился предсказуемым - с помощью CAS быстрее...В среднем раза в 2. Хотя...не всегда. Периодически тестовые запуски показывали что код с использованием CRITICAL_SECTION так же быстр или даже чуть быстрее...
Я списал эти "неправильные" результаты на флюктуации и решил сравнить скорость блокировки потоков с помощью CRITICAL_SECTION и с помощью spinlock.
Код для реализации spinlock я взял отсюда и немного причесал. В принципе он совпадает с кодом приведенным в википедии.
(также я пробывал использовать код с codeguru но там результаты еще хуже)
Выяснилось...выяснилось что критические секции быстрее самопального спинлока минимум раза в 2. А то и больше - результат колебался от 2х до 4х раз. Хм...там же 4 инструкции...
Чтобы понять в чем фокус я попробывал посмотреть ассемблерный код реализации функций EnterCriticalSection и LeaveCriticalSection (если что - у меня Vista). Заодно бегло посмотрел пару статей об оптимизации ассемблерного кода под современные процессоры. А также о том что такое U-pipe, V-pipe и спаривание комманд...
Выводы:
а) код EnterCriticalSection очень похож на ручную оптимизированную правильную (и т.д.) реализацию спинлока...
б) инструкцию XCNG использовать вообще не рекомендуется.
в) чтобы написать блокировку быстрее CRITICAL_SECTION нужно очень постораться (мне это не грозит). да и то скорее всего будет работать только под конкретную архитектуру.
г) CAS все-таки быстрее. Если его не делать руками...а использовать InterlockedCompareExchangeXXX функции.
Крутил 5 потоков, вызывающих одну и ту же функцию. Считал суммарное время выполнения всех потоков. Разумеется в релизе и с оптимизацией. (там получалось что блокировка вообще не нужна, ну да это детали...)
Код с использованием CRITICAL_SECTION
void calc_synhronized_critical_section_impl()
{
for(int i=0;i<count;++i)
{
EnterCriticalSection(&g_critsec);
int temp = result;
temp = temp + 1;
result = temp;
LeaveCriticalSection(&g_critsec);
}
}
Код с "ручным" спинлоком :
void calc_synhronized_asm_spinlock_impl()
{
static int vcookie = 0;
LPVOID cookie = &vcookie;
for(int i=0;i<count;++i)
{
__asm
{
mov edx, dword ptr [cookie]
mov eax, 1
spinLoop:
lock xchg eax, dword ptr [edx]
test eax, eax
jnz spinLoop
}
int temp = result;
temp = temp + 1;
result = temp;
__asm
{
mov edx, dword ptr [cookie]
mov eax, 0
lock xchg eax, dword ptr [edx]
}
}
}
Из странного и необьяснимого - если поток был один то код с ручным спинлоком оказывался быстрее...
Комментариев нет:
Отправить комментарий