
Деконструкция GO: Низкоуровневые концепции. Atomics. Часть 2.1
glibus 6 минут назад Деконструкция GO: Низкоуровневые концепции. Atomics. Часть 2.1 Простой 5 мин 0 Go * Программирование * Обзор Я самую малость обленился и как-то давно не делал новых разборов, поэтому следующим нашим...
Anthropic — What company has the best second artificial intelligence model at the end of June?
В сфере искусственного интеллекта произошло заметное событие. glibus 6 минут назад Деконструкция GO: Низкоуровневые концепции. 1 Простой 5 мин 0 Go * Программирование * Обзор Я самую малость обленился и как-то давно не делал новых разборов, поэтому следующим нашим этапом деконструкции будут низкоуровневые операции. Иногда здесь будет в отрыве от аллокаторов/планировщиков и прочего, но опять же, статьи для тех, кто знает и хочет разобраться поглубже, как тут всё устроено.
Поэтому, в этой части начнем с самого простого – пакета atomic. Концепции вокруг атомарных операций на уровне CPU я рассматривал здесь, поэтому советую почитать. Там мы даже пишем свой атомарный AND.!
Технические детали
Мы будем разбирать на примере src/internal/runtime/atomics, то есть внутреннего пакета, а не того, который представлен нам как пользователям(потому что в исходниках я не нашел реализации). Но по большей части операции одни и те же. Что из себя представляет пакетНа самом деле, каким бы маленьким этот пакет вам не казался, реализаций у него достаточно много!
Давайте заглянем в официальный репозиторий. Данный пакет реализован под большое количество архитектур, следовательно даже реализации самых простых низкоуровневых операций могут разниться. Например://arm64 TEXT ·Load(SB),NOSPLIT,$0-12 MOVD ptr+0(FP), R0 LDARW (R0), R0 MOVW R0, ret+8(FP) RET // 386 //go:nosplit //go:noinline func Load(ptr *uint32) Почему вообще так сделано?
Конкретно, в этом примере по очень простой причине – операции на 386/x86 достаточно строгие и не переупорядочивают load с другими load, store с другими store, и store с более старыми load, а так как на 386 размер машинослова 32 бит, а также load там сам по себе атомарен, то достаточно обычного разыменования. На arm такого нет, поэтому необходимо явно использовать LDARW(load-acquire w). О функции для 386 компилятор знает, как об атомарном чтении, но вот вопрос – а зачем нам go:nosplit и go:noinline?
Эти комменты означают, что функция не вызывает проверку/расширение стека перед вызовом, а noinline запрещает встраивать это как обычное выражение. Load, Store, SwapСамое примитивное, что мы можем в принципе сделать – хранить, загружать и менять местами значения переменных. Пример с Load был выше, поэтому вот Store:TEXT ·Store64(SB), NOSPLIT, $0-16 MOVQ ptr+0(FP), BX MOVQ val+8(FP), AX XCHGQ AX, 0(BX) RET И вот Swap:// uint64 Xchg64(ptr *uint64, new uint64) // Atomically: // old := *ptr; // *ptr = new; // return old; TEXT ·Xchg64(SB), NOSPLIT, $0-24 MOVQ ptr+0(FP), BX MOVQ new+8(FP), AX XCHGQ AX, 0(BX) MOVQ AX, ret+16(FP) RET Что интересно, используют они одну и ту же операцию – XCHGQ, что наводит на крамольную мысль о том, что по большей части разница семантическая.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





