Как атомарная синхронизация работает в одном потоке, когда он переносится на другое ядро

Задавая этот вопрос как псевдокод, а также ориентируясь как на ржавчину, так и на С++, поскольку концепции модели памяти так же

SomeFunc(){
    x = counter.load(Ordering::Relaxed)   //#1
    counter.store(x+1, Ordering::Relaxed) //#2
    y = counter.load(Ordering::Relaxed)   //#3
}

Вопрос: Представьте, что SomeFunc выполняется потоком, и между #2 и #3 поток прерывается, и теперь #3 выполняется на другом ядре, в этом случае синхронизируется ли переменная счетчика с последним обновленным значением (ядро 1) при запуске на другом core2 (явного освобождения/приобретения нет). Я полагаю, вся кеш-линия + локальное хранилище потока откладывается и загружается, когда поток ненадолго переходит в спящий режим и снова работает на другом ядре?

Упреждение, переключение контекста и другие подобные механизмы ЦП прозрачны для C++, если вы соблюдаете требования синхронизации. В контексте однопоточного вызова функции нет требований к синхронизации, и переключение ядер не оказывает заметного эффекта.

François Andrieux 09.02.2023 17:11

Все это происходит в одном потоке, поэтому проблем с синхронизацией нет; он просто будет делать то, что, очевидно, делает. Даже если counter просто старая добрая int, а не атомарная. ЦП заботится об управлении контекстом для каждого потока. Только когда переменная используется более чем одним потоком, вам нужно беспокоиться о синхронизации.

Pete Becker 09.02.2023 17:56
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
106
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В первую очередь следует отметить, что атомарные инструкции добавляют синхронизацию, а не убирают ее.

Вы ожидаете:

unsigned func(unsigned* counter) {
    auto x = *counter;
    *counter = x + 1;
    auto y = *counter;
    return y;
}

Чтобы вернуть что-либо, кроме исходного значения *counter + 1?

Но точно так же поток может перемещаться между ядрами между двумя операторами!

Приведенный выше код выполняется нормально, даже когда ядро ​​перемещается, потому что во время переключения ОС заботится о надлежащей синхронизации между ядрами, чтобы сохранить порядок программ в пользовательском пространстве.

Итак, что происходит при использовании атомарных вычислений в одном потоке?

Ну, вы добавляете немного накладных расходов на обработку — больше синхронизации — и ОС по-прежнему заботится о правильной синхронизации во время переключения.

Следовательно, эффект будет точно таким же.

надлежащим образом управлять кэшем. Проблема не в кеше, а в буфере частного хранилища внутри каждого ядра. Кэш согласован между ядрами, на которых работает одна ОС, и может планировать потоки для них. Что на самом деле нужно ядру, так это «заботиться о синхронизации», возможно, с синхронизацией получения / освобождения, которая в любом случае нужна для его собственных данных ядра. (Или что-то более сильное для таких машин, как x86, которые имеют специальные слабо упорядоченные инструкции, такие как movntps, которые не упорядочены атомарным выпуском/приобретением.)

Peter Cordes 09.02.2023 18:35

Поэтому, чтобы избежать распространения распространенного заблуждения о том, что переупорядочивание памяти происходит из-за кешей, я бы рекомендовал «ОС позаботится ... о правильной синхронизации». Лучшей ментальной моделью является локальное переупорядочивание доступа к когерентному общему кешу, как в preshing.com/20120710/… . Вот почему переупорядочение IRIW происходит так редко; для этого требуется микроархитектура, которая может сделать хранилища видимыми между (логическими) ядрами, прежде чем они будут переданы в кэш. Я отредактирую, вы, конечно, можете отредактировать все, что хотите сказать.

Peter Cordes 09.02.2023 18:37

@PeterCordes: Это хороший момент, я специально думал о буфере хранилища во время записи и использовал кеш как общий термин для чтения/записи, не думая, что в контексте процессоров кеш будет пониматься как относящийся к L1/L2/ Л3. Спасибо за редактирование!

Matthieu M. 09.02.2023 19:37

Другие вопросы по теме