Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Микрокод конвеера процессора разбивает 64-битные операции на набор выполняющихся частично-параллельно 16-битных.
Что касается дискретных АЛУ — то это тяжелое наследие той мрачной эпохи на заре компьютеростроения
Поэтому физически никаких 64-битных АЛУ (так же как и отдельных блоков MMX/SSE) в современных x86/x64 процессорах просто нет
Конвеер разбивает 32/64 операции на последовательность 16-разрядных микроинструкций,
Именно благодаря этому факту в те же Атомы так просто и без пафоса была добавлена поддержка 64 бит.
x64 процессоры при выполнении 16- и 32-разрядных операций также
atomic<T>. Несмотря на то, что она определена в STL и, вроде бы, независима от самого языка, на самом деле на atomic<T> навешано довольно много семантики, то есть [очень грубо] можно считать, что atomic<T> является ключевым словом. И компиляторы, поддерживающие C++11 atomic, были существенно переработаны внутри, чтобы поддержать требования стандарта, новую модель памяти.atomic<T>.volatile какого-то статуса в C++11 был таким:struct VeryLargeStruct {
// 100500 полей
};
VeryLargeStruct volatile x;
x в данном примере) навесить не получится. volatile, как это случилось с auto в C++11. Используйте на здоровье! Просто надо помнить, что для атомарных переменных volatile избыточна.int мы получим segmentation fault. Это и называется в стандарте «неопределенным поведением» (UB).С использованием compare_exchange примитив fetch-and-add, показанный выше, может быть написан так:
int FAA( int * pAddr, int nIncr )
{
int ncur = *pAddr;
do {} while ( !compare_exchange( pAddr, ncur, ncur + nIncr );
return ncur;
}
int ncur = *pAddr; do {} ...Аргумент nExpected передается по ссылке и на входе содержит ожидаемое значение переменной по адресу pAddr, а на выходе – значение перед изменением. Возвращает же функция признак успеха: true, если по адресу находится значение nExpected (в этом случае оно меняется на nNew), false в случае неудачи (тогда nExpected будет содержать текущее значение переменной по адресу pAddr).
int ncur;
do { ncur = *pAddr; } while ( !CAS( pAddr, ncur, ncur + nIncr ) );
return ncur;
stack1.pop(), доходит до CAS и, к примеру, вытесняется. Поток 2, пока первый вытеснен, успевает сделать:T * a = stack1.pop() ;
T * anext = stack1.pop() ;
stack1.push( a ) ;
stack2.push( anext );
a на anext, который он запомнил. Но anext теперь включен в stack2! Нарушение stack1: он теперь будет указывать в stack2. ABA-проблема, без освобождения выделенной памяти.И это понятно: в Java они избавлены от необходимости следить за указателями, за правильным (в подходящий момент) удалением и пр. — за них все делает GC.
Подскажите, есть ли какой-нибудь "толстый" материал чтобы понимать внутренности процессора, битность, кэши и прочие детали упомянутые в этой статье и последующих, спасибо
Мне такой материал неизвестен.
Есть многотомные описания для каждой модели процессора (не архитектуры, а фактически каждой модели), там есть всё, но в 10-ти томах
Можно эту статью, например, почитать: Ulrich Drepper — What Every Programmer Should Know About Memory.
Она старая, но хороший старт в этих вопросах.
Lock-free структуры данных. Основы: Атомарность и атомарные примитивы