в 1.26 кроме green tea есть еще интересная оптимизация связанная со слайсами:
В чем суть этой оптимизации:
В некоторых случаях аллоцировать слайс во время append(а) на стеке, а не в куче. Это снижает нагрузку на GC.
К примеру, типичный код:
Во время итерирования, когда возрастает capacity (1, 2, 4, 8... итерация) создается память, которая не нужна. Эта памяти будет зачищаться при работе GC.
И в связи с этим стараемся не нагружать GC, а аллоцировать эти мусорные объекты на стеке.
И уже перед выходом из функции помещаем слайс на heap.
Немного деталей:
Во время append(а) вызывается не
Перечень того, где может использоваться эта оптимизация перечислина в тестовом файле, (где указано growsliceNoAlias, growsliceBuf).
Links:
CL: go-review.googlesource.com
Более подробная информация как это работат: cmd/compile/internal/slice/slice.go
stack-allocation optimization
for the backing store of slices.
В чем суть этой оптимизации:
В некоторых случаях аллоцировать слайс во время append(а) на стеке, а не в куче. Это снижает нагрузку на GC.
К примеру, типичный код:
func f(n int) []int {
var r []int
for i := range n {
r = append(r, i)
}
return r
}
Во время итерирования, когда возрастает capacity (1, 2, 4, 8... итерация) создается память, которая не нужна. Эта памяти будет зачищаться при работе GC.
И в связи с этим стараемся не нагружать GC, а аллоцировать эти мусорные объекты на стеке.
И уже перед выходом из функции помещаем слайс на heap.
func f(n int) []int {
var r []int
for i := range n {
r = append(r, i)
}
r = move2heap(r) // Подставляется во время комплияции
return r
}
Немного деталей:
Во время append(а) вызывается не
growslice, а growsliceBuf, где буффер это поинтер на стэк (link).Перечень того, где может использоваться эта оптимизация перечислина в тестовом файле, (где указано growsliceNoAlias, growsliceBuf).
Links:
CL: go-review.googlesource.com
Более подробная информация как это работат: cmd/compile/internal/slice/slice.go
👍5🔥3👏2
Крайне интересно исследуя Go находить наследия Plan 9.
Russ Cox, одна из ключевых фигур Go. До Go разрабатывал операционную системой Plan 9. Поэтому Go имеет некоторую наследственность Plan 9.
Одна простая строка:
Имеет почти что 22-ух летнюю историю.
Февраль 2004: в Plan 9 появился комит с емким названием various tweaks, в котором было установлено стандартное повдение для TCP сокета
Сентябрь 2008: В Go Russ Cox добавляет network, где стандартное поведение
Декабрь 2022: На Hacker News Russ Cox ответил зачем все это.
И вот краткий ответ:
И казалось бы, зачем отключать алгоритм, который что-то улчшает?
А везде есть trade-off.
С помощью этого алгоритма мы не отправляем сразу же все данные, а немного их копим, чтоб в одном TCP пакете данные отправить(у каждого пакета есть overhead в виде заголовка, чем меньше пакет, тем больше процент overhead ).
А для RPC важнее отрпавить как можно скорее, поэтому Nagle's algorithm для этой задачи абсолютно не подходит.
Таким образом
Одна простая строка:
setNoDelay(fd, true)
Имеет почти что 22-ух летнюю историю.
Февраль 2004: в Plan 9 появился комит с емким названием various tweaks, в котором было установлено стандартное повдение для TCP сокета
TCP_NODELAY.Сентябрь 2008: В Go Russ Cox добавляет network, где стандартное поведение
c.SetNoDelay(true). Это повдение сохраняется и до сих пор.Декабрь 2022: На Hacker News Russ Cox ответил зачем все это.
И вот краткий ответ:
TCP_NODELAY отключает Nagle's algorithm, который позволяет эффективнее использовать TCP соединение.И казалось бы, зачем отключать алгоритм, который что-то улчшает?
А везде есть trade-off.
С помощью этого алгоритма мы не отправляем сразу же все данные, а немного их копим, чтоб в одном TCP пакете данные отправить
А для RPC важнее отрпавить как можно скорее, поэтому Nagle's algorithm для этой задачи абсолютно не подходит.
Таким образом
TCP_NODELAY позволяет нам сразу же отрпавить пакет, не буфферизируя их.🔥8👍4❤1🥰1
Небольшой tip: как выйти из зависшего SSH при плохом соединении.
Бывает, что сеть просела/отвалилась и нельзя нормально отправить
TL;DR: нажми по очереди:
Важно: escape-символ распознаётся только в начале строки, сразу после перевода строки (поэтому и нужен Enter). Работает в интерактивной сессии.
Ещё пара полезных (вводятся так же:
Они могут отличаться от версии к версии.
Cамая полезная это
Бывает, что сеть просела/отвалилась и нельзя нормально отправить
exit или ^D. В этом случае можно разорвать соединение со стороны ssh-клиента, через escape-последовательности.TL;DR: нажми по очереди:
Enter, затем ~, затем . (то есть ~.)Важно: escape-символ распознаётся только в начале строки, сразу после перевода строки (поэтому и нужен Enter). Работает в интерактивной сессии.
Ещё пара полезных (вводятся так же:
Enter, потом команда):
~. - disconnect (разорвать соединение)
~? - показать список всех escape-команд
~^Z - отправить ssh в background
~# - показать активные форвардинги
~R - запросить rekey
~V/v - уменьшить/увеличить verbosity
Они могут отличаться от версии к версии.
Cамая полезная это
~.: когда соединение мёртвое, это часто единственный способ выйти, не убивая терминал целиком.1👍8🔥5❤1👏1👨💻1
Отхожу от новогодних праздников и пока еще свыкаюсь с рабочим режимом, поделюсь книжной рекомендацией.
Есть очень хорошая книга про внутрянку PostgreSQL (MVCC, WAL, блокировки, планирование/исполнение, индексы и т.д.) -- PostgreSQL 17 изнутри.
Что хочется отдельно отметить, что внутри много ссылок на исходный код, и если интересно, то можно открыть и изучать уже самостоятельно.
Книга свободно распространяется в элеткронном виде: https://postgrespro.ru/education/books/internals
Лично для меня эта книга сильно помогла, когда писал серию про B-tree.
https://t.me/LasiarAtWork/16
https://t.me/LasiarAtWork/17
https://t.me/LasiarAtWork/18
Есть очень хорошая книга про внутрянку PostgreSQL (MVCC, WAL, блокировки, планирование/исполнение, индексы и т.д.) -- PostgreSQL 17 изнутри.
Что хочется отдельно отметить, что внутри много ссылок на исходный код, и если интересно, то можно открыть и изучать уже самостоятельно.
Книга свободно распространяется в элеткронном виде: https://postgrespro.ru/education/books/internals
Лично для меня эта книга сильно помогла, когда писал серию про B-tree.
https://t.me/LasiarAtWork/16
https://t.me/LasiarAtWork/17
https://t.me/LasiarAtWork/18
❤4✍3👍3💅2🔥1
Временами не хватает generic methods:
В Go так нельзя, потому что такая структура может реализовывать бесконечное множество интерфейсов.
Для не generic компилятор заранее генерирует и линкует конечное число имплементаций. А для generic методов нужно: либо уметь генерировать бесконечное число реализаций, либо добавлять JIT, а Go этого избегает.
И вот 23.12.2025 Robert Griesemer, один из основателей языка создает proposal, в котором предлагает такие методы, но с с ограничением, такие методы не смогут реализовывать интерфейс:
К примеру есть интерфейс:
И generic метод не будет его реализовывать:
Важное замечание:
Proposal пока что еще active, он может очень сильно поменяться или вообще его могут реджектнут.
При важном обновлении этого issue, я обязательно напишу об этом здесь)
type S struct { … }
func (*S) m[P any](x P) { … }
В Go так нельзя, потому что такая структура может реализовывать бесконечное множество интерфейсов.
И вот 23.12.2025 Robert Griesemer, один из основателей языка создает proposal, в котором предлагает такие методы, но с с ограничением, такие методы не смогут реализовывать интерфейс:
К примеру есть интерфейс:
type I interface {
m(string)
}
И generic метод не будет его реализовывать:
type H struct{ … }
func (H) m[P any](P) { … }
var h H
var _ I = h // invalid because H.m has signature m[P any](P) which doesn't match m(string) of I
Важное замечание:
Proposal пока что еще active, он может очень сильно поменяться или вообще его могут реджектнут.
При важном обновлении этого issue, я обязательно напишу об этом здесь)
🎉4🔥3🫡2✍1❤1🥰1💅1
Кстати, про SIMD
Вроде все просто:
• У нас есть регистр который может хранить вектор (массив);
• У нас есть операции, которые могут работать с векторами;
• ... Profit!!!
Используя AVX-512 можно хранить в векторе аж 64 байта и всего лишь одним векторным сравнением (AVX-512BW) получить маску совпадений по всем 64 байтам (по-сути поиск по всему вектору).
НО, инструкции AVX-512 нагружают тепловой пакет ядра процессора, из-за чего turbo boost снижает его частоту.
Intel это снижение описывает через Power Levels:
И это еще не все:
Переходы между уровнями тоже не бесплатны, этот переход называется block time, когда ядро не исполняет инструкций.
На старых поколениях процессоров этот прееход осуществляется за 10-20us, а на новых (3rd Generation Intel) уменьшено почти до ~0us.
В связи с этим есть рекомендации использовать все AVX-512 за раз, чтоб не было оверхеда на block time.
Так что на всем известную картинку можно смело добавлять еще один слой:
Вроде все просто:
• У нас есть регистр который может хранить вектор (массив);
• У нас есть операции, которые могут работать с векторами;
• ... Profit!!!
Используя AVX-512 можно хранить в векторе аж 64 байта и всего лишь одним векторным сравнением (AVX-512BW) получить маску совпадений по всем 64 байтам (по-сути поиск по всему вектору).
НО, инструкции AVX-512 нагружают тепловой пакет ядра процессора, из-за чего turbo boost снижает его частоту.
Intel это снижение описывает через Power Levels:
Power Level 0 -- scalar/SSE и легкий AVX
максимальная частота
Power Level 1 -- тяжелый AVX2 и легкий AVX-512
частота зависит от поколения и модели
Power Level 2 -- тяжелый AVX-512
частота ниже, чем в Level 1
И это еще не все:
Переходы между уровнями тоже не бесплатны, этот переход называется block time, когда ядро не исполняет инструкций.
На старых поколениях процессоров этот прееход осуществляется за 10-20us, а на новых (3rd Generation Intel) уменьшено почти до ~0us.
В связи с этим есть рекомендации использовать все AVX-512 за раз, чтоб не было оверхеда на block time.
Так что на всем известную картинку можно смело добавлять еще один слой:
🔥7✍2👨💻2💘2❤🔥1👾1
Ранее я писал про предложение добавить generic методы: https://t.me/LasiarAtWork/69.
Этот proposal перешел в статус Proposal-Accepted. Есть шансы, что это появится в Go 1.27, но для этого нужно много изменений сделать в инструментарии языка, под завезли отдельный issue.
Итоговое решение можно посмотреть здесь:
https://go-review.googlesource.com/c/go/+/746820/4/src/internal/types/testdata/spec/methods.go
Этот proposal перешел в статус Proposal-Accepted. Есть шансы, что это появится в Go 1.27, но для этого нужно много изменений сделать в инструментарии языка, под завезли отдельный issue.
Итоговое решение можно посмотреть здесь:
https://go-review.googlesource.com/c/go/+/746820/4/src/internal/types/testdata/spec/methods.go
🔥8👏3🎉2👍1🥰1
https://t.me/LasiarAtWork/70
На x86, если у процесса есть /proc/<pid>/arch_status, там может быть поле:
Это миллисекунды с момента, когда ядро в последний раз записало факт использования AVX-512.
В теории это нужно для того чтоб процессы разложить по ядрам процессора, чтоб AVX-512 вызывались "рядом".
На практике я не нашел софт, который так бы делал (может плохо искал, в коментах можете подсказать).
———
Тут еще интересно то, как именно Linux отслеживает вызовы AVX-512.
Немного упрощено:
• При task switching вызывается инструкция
Не всегда смена контекста происходит через XSAVE, но это детали.
• И если в результате
• Сохраняем текущий timestamp, который потом отдаем в arch_status.
Сохраняем jiffies (счётчик тиков), чтобы время было монотонным, а при чтении arch_status получаем миллисекунды.
И этот способ может давать ошибки:
• Процесс может специально занулить регистры -- пропустим использование.
• Процесс изменил регистры, но не вызывал инструкцию -- ложное срабатывание.
Плюс время не совсем точное: оно обновляется в момент task switching, а не “на каждой инструкции”.
На x86, если у процесса есть /proc/<pid>/arch_status, там может быть поле:
AVX512_elapsed_ms: 8
Это миллисекунды с момента, когда ядро в последний раз записало факт использования AVX-512.
В теории это нужно для того чтоб процессы разложить по ядрам процессора, чтоб AVX-512 вызывались "рядом".
На практике я не нашел софт, который так бы делал (может плохо искал, в коментах можете подсказать).
———
Тут еще интересно то, как именно Linux отслеживает вызовы AVX-512.
Немного упрощено:
• При task switching вызывается инструкция
XSAVE, которая сохраняет состояние FPU регистров процессора.• И если в результате
XSAVE у ZMM_Hi256 или у Hi16_ZMM регистров значения не нули, то мы считаем что AVX-512 использовался.• Сохраняем текущий timestamp, который потом отдаем в arch_status.
static void update_avx_timestamp(struct fpu *fpu)
{
#define AVX512_TRACKING_MASK (XFEATURE_MASK_ZMM_Hi256 | XFEATURE_MASK_Hi16_ZMM)
if (fpu->fpstate->regs.xsave.header.xfeatures & AVX512_TRACKING_MASK)
fpu->avx512_timestamp = jiffies;
}
И этот способ может давать ошибки:
• Процесс может специально занулить регистры -- пропустим использование.
• Процесс изменил регистры, но не вызывал инструкцию -- ложное срабатывание.
Плюс время не совсем точное: оно обновляется в момент task switching, а не “на каждой инструкции”.
1✍4❤3🤯2👾2💯1
Из-за NTP-демона время может быть не только немонотонным, но и неравномерным.
О немонотонности обычно помнят:
Но есть ещё менее очевидная вещь: время может идти не с постоянной скоростью.
То есть проблема не только в том, что
В Linux это делается через системный вызов подстройки системных часов в ядре.
Условно говоря, чтобы избежать резкого скачка,
О немонотонности обычно помнят:
CLOCK_REALTIME может прыгнуть вперёд или назад.Но есть ещё менее очевидная вещь: время может идти не с постоянной скоростью.
То есть проблема не только в том, что
now() может перескочить. Проблема ещё и в том, что между двумя соседними чтениями часы могут идти чуть быстрее или чуть медленнее реального времени.В Linux это делается через системный вызов подстройки системных часов в ядре.
adjtimex.Условно говоря, чтобы избежать резкого скачка,
adjtimex как бы растягивает или ускоряет секунду для синхронизации времени.✍6❤4🔥3
О, в coreutils есть SIMD)
В тех самых GNU coreutils.
Вообще SIMD там появился достатчоно давно, ещё в
Cвежие релизы продолжают добавлять разные архитектуры, и докручивать текущее.
Вход обрабатывается крупными блоками, считается частичные результаты через
Сразу ищем
В 9.11 для
В тех самых GNU coreutils.
Вообще SIMD там появился достатчоно давно, ещё в
release 9.0 (2021-09-24).wc научился использовать AVX2 для подсчёта строк, а cksum -- PCLMUL для CRC.Cвежие релизы продолжают добавлять разные архитектуры, и докручивать текущее.
cksum -a crc -- вычисление CRC.Вход обрабатывается крупными блоками, считается частичные результаты через
SIMD, а потом склеиваются в тот же checksum. В 9.6 для AVX2/AVX512/ARMv8 SIMD заявлены ускорения до 40/60/80%.wc -l -- подсчёт строк.Сразу ищем
\n в пачке байтов: 16 байт на Neon, 32 на AVX2, 64 на AVX-512.В 9.11 для
Neon заявлено ускорение wc -l до 4.5×.🔥6✍4❤3😱1

