Должен ли std :: atomic & lt; int * & gt; :: load выполнять цикл сравнения и замены?

Резюме: Я ожидал, что std::atomic<int*>::load с std::memory_order_relaxed было бы близко к производительности простой загрузки указателя напрямую, по крайней мере, когда загруженное значение редко изменяется. Я видел гораздо худшую производительность для атомарной нагрузки, чем обычная загрузка в Visual Studio C ++ 2012, поэтому я решил исследовать. Оказывается, атомная нагрузка реализована в виде сравнения и замены цикл, который я подозреваю, не самая быстрая реализация.

Вопрос: Есть ли какая-то причина, по которой std::atomic<int*>::load нужно сделать цикл сравнения и обмена?

ФонЯ полагаю, что MSVC ++ 2012 выполняет цикл сравнения и замены при атомной загрузке указателя на основе этой тестовой программы:

#include <atomic>
#include <iostream>

template<class T>
__declspec(noinline) T loadRelaxed(const std::atomic<T>& t) {
return t.load(std::memory_order_relaxed);
}

int main() {
int i = 42;
char c = 42;
std::atomic<int*> ptr(&i);
std::atomic<int> integer;
std::atomic<char> character;
std::cout
<< *loadRelaxed(ptr) << ' '
<< loadRelaxed(integer) << ' '
<< loadRelaxed(character) << std::endl;
return 0;
}

Я использую __declspec(noinline) функция для того, чтобы изолировать инструкции по сборке, связанные с атомной нагрузкой. Я сделал новый проект MSVC ++ 2012, добавил платформу x64, выбрал конфигурацию выпуска, запустил программу в отладчике и посмотрел на разборку. Оказывается, что оба std::atomic<char> а также std::atomic<int> параметры в конечном итоге дают один и тот же вызов loadRelaxed<int> — это должно быть что-то, что сделал оптимизатор. Вот разборка двух экземпляров loadRelaxed, которые вызываются:

loadRelaxed<int * __ptr64>

000000013F4B1790  prefetchw   [rcx]
000000013F4B1793  mov         rax,qword ptr [rcx]
000000013F4B1796  mov         rdx,rax
000000013F4B1799  lock cmpxchg qword ptr [rcx],rdx
000000013F4B179E  jne         loadRelaxed<int * __ptr64>+6h (013F4B1796h)

loadRelaxed<int>

000000013F3F1940  prefetchw   [rcx]
000000013F3F1943  mov         eax,dword ptr [rcx]
000000013F3F1945  mov         edx,eax
000000013F3F1947  lock cmpxchg dword ptr [rcx],edx
000000013F3F194B  jne         loadRelaxed<int>+5h (013F3F1945h)

Инструкция lock cmpxchg атомно сравнения и замены и мы видим здесь, что код для атомной загрузки char, int или int* это цикл сравнения и обмена Я также создал этот код для 32-битной x86, и эта реализация все еще основана на lock cmpxchg,

Вопрос: Есть ли какая-то причина, по которой std::atomic<int*>::load нужно сделать цикл сравнения и обмена?

5

Решение

Я не верю, что расслабленные атомные нагрузки требуют сравнения и обмена. В конце концов, эта реализация std :: atomic была непригодна для моих целей, но я все же хотел иметь интерфейс, поэтому я создал собственный std :: atomic, используя барьерные свойства MSVC. Это имеет лучшую производительность, чем по умолчанию std::atomic для моего случая использования. Вы можете увидеть код Вот. Предполагается, что он будет реализован в спецификации C ++ 11 для всех заказов на загрузку и хранение. Кстати GCC 4.6 не лучше в этом отношении. Я не знаю о GCC 4.7.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]