<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:yandex="http://news.yandex.ru" xmlns:turbo="http://turbo.yandex.ru" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>Бесплатные материалы</title>
    <link>https://igoroutine.courses</link>
    <description/>
    <language>ru</language>
    <lastBuildDate>Tue, 21 Apr 2026 13:13:07 +0300</lastBuildDate>
    <item turbo="true">
      <title>Как совмещать несовместимое и ускорять неускоряемое с помощью ассемблера Go</title>
      <link>https://igoroutine.courses/free-materials/novg7ca2s1-kak-sovmeschat-nesovmestimoe-i-uskoryat</link>
      <amplink>https://igoroutine.courses/free-materials/novg7ca2s1-kak-sovmeschat-nesovmestimoe-i-uskoryat?amp=true</amplink>
      <pubDate>Fri, 20 Mar 2026 17:50:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <category>Computer Science</category>
      <enclosure url="https://static.tildacdn.com/tild3138-6637-4533-a333-393532316631/image.png" type="image/png"/>
      <turbo:content><![CDATA[<header><h1>Как совмещать несовместимое и ускорять неускоряемое с помощью ассемблера Go</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3138-6637-4533-a333-393532316631/image.png"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="5gwv0I6tmp8"
    data-vk=""
    data-rutube=""
    data-dzen=""> 
  <amp-youtube
    data-videoid="5gwv0I6tmp8"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">На этом открытом уроке за 50 минут с небольшим разберем использование ассемблера в Go от «А» до «Я» — зачем он вообще нужен, как устроены векторные инструкции процессора и как с их помощью ускорить узкие места в коде в десятки раз.<br /><br />В этом уроке:<br /><br /><ul><li data-list="bullet"><strong>Пайплайн компиляции Go:</strong> как код превращается в машинный и почему стандартный компилятор не всегда может применять самые эффективные инструкции (например, SIMD).</li><li data-list="bullet"><strong>Векторные вычисления (SIMD):</strong> что это такое, как работать с расширенными регистрами процессора и обрабатывать целые массивы данных за одну операцию.</li><li data-list="bullet"><strong>Практика написания ассемблера:</strong> разберем специфичный синтаксис (Plan 9) в Go, напишем быстрое сложение слайсов и реализуем сверхбыстрый поиск элементов (аналог bytes.Contains).</li><li data-list="bullet"><strong>Альтернативы и поддержка:</strong> когда можно обойтись CGO, как правильно тестировать платформозависимый код и писать безопасные фоллбэки на чистом Go для других архитектур.</li></ul><br />В итоге у тебя будет четкое понимание того, когда реально стоит спускаться на уровень железа: разберем на практических примерах весь процесс от работы с сырыми инструкциями процессора до интеграции оптимизированного кода в свои рабочие highload-проекты.</div><h4  class="t-redactor__h4">Дополнительные материалы</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">The Design of the Go Assembler</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=KINIAgRpkDA</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Архитектура ассемблера Go</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://habr.com/ru/companies/badoo/articles/317864/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Руководство по ассемблеру Go</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://habr.com/ru/companies/vk/articles/358088/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Quick Guide to Go's Assembler</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://go.dev/doc/asm</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Продвинутая база GO для работы и собеседований</title>
      <link>https://igoroutine.courses/free-materials/mlj92jl7b1-prodvinutaya-baza-go-dlya-raboti-i-sobes</link>
      <amplink>https://igoroutine.courses/free-materials/mlj92jl7b1-prodvinutaya-baza-go-dlya-raboti-i-sobes?amp=true</amplink>
      <pubDate>Thu, 02 Oct 2025 11:38:00 +0300</pubDate>
      <enclosure url="https://static.tildacdn.com/tild3630-3835-4431-b030-663666613830/image.png" type="image/png"/>
      <turbo:content><![CDATA[<header><h1>Продвинутая база GO для работы и собеседований</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3630-3835-4431-b030-663666613830/image.png"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
     data-yt="MhLECoS55II" 
     data-vk="oid=-230033131&id=456239037&hd=4"
     data-rutube="8fae41fb027e493c41c8c329e509eb3b"
     data-dzen="">
  <amp-youtube
    data-videoid="MhLECoS55II"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">На этом открытом уроке за 1,5 часа мы разберем язык Go от «А» до «Я» — от установки окружения до продвинутых нюансов синтаксиса и работы с памятью, которые необходимо знать для работы и уверенного прохождения собеседований.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Настройка окружения:</strong> как правильно установить Go, что такое GOROOT/GOPATH и как устроена структура проекта (Go modules).</li><li data-list="bullet"><strong>Типизация и указатели:</strong> разбор системы типов, работа с указателями и концепция Zero Value.</li><li data-list="bullet"><strong>Массивы и слайсы:</strong> глубокое погружение в механику работы слайсов, использование функций append и copy, а также нюансы выделения памяти.</li><li data-list="bullet"><strong>Строки под капотом:</strong> в чем разница между байтами и рунами (runes), и как корректно работать с кодировкой UTF-8.</li><li data-list="bullet"><strong>Структуры и композиция:</strong> использование тегов, модификаторы доступа (Public/Private) и особенности пустых структур.</li><li data-list="bullet"><strong>Функции и замыкания:</strong> множественные возвращаемые значения, вариативные функции и работа с контекстом.</li></ul><br />В итоге у тебя будет мощная база для написания корректных программ на Go и понимание внутренних механизмов языка, которое поможет тебе отвечать на каверзные вопросы на интервью в любой BigTech-компании.</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0010_go_base_syntax</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Статья "Как называть пакеты в GO"</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://go.dev/blog/package-names</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Соглашение по импортам</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://google.github.io/styleguide/go/best-practices.html#import-ordering</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Гайд по структуре проекта</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://github.com/golang-standards/project-layout</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Видео про типы</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=321</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Видео про типизированный LRU кэш</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=345</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="6" data-column="0"><div class="t-table__cell-content">Видео про память</div></td><td class="t-table__cell" data-row="6" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine_chat/295</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="7" data-column="0"><div class="t-table__cell-content">Видео про SIMD</div></td><td class="t-table__cell" data-row="7" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/72</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="8" data-column="0"><div class="t-table__cell-content">Как в ассемблере представляются возвращаемые значения</div></td><td class="t-table__cell" data-row="8" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=R121Xpb28og&pp=ygUVZ29mdW5jINC_0LDQvdCw0YHRjtC6</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="9" data-column="0"><div class="t-table__cell-content">Полезный гайд по Go (Effective Go)</div></td><td class="t-table__cell" data-row="9" data-column="1"><div class="t-table__cell-content">https://go.dev/doc/effective_go</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="10" data-column="0"><div class="t-table__cell-content">Книга The Go Programming Language Alan A.A. Donovan, Brian W.Kernighan</div></td><td class="t-table__cell" data-row="10" data-column="1"><div class="t-table__cell-content">https://edu.anarcho-copy.org/Programming%20Languages/Go/The%20Go%20Programming%20Language%20-%20Donovan,%20Alan%20A.%20A.%20_%20Kernigha_6127.pdf</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Устройство операционных систем: от ядра и системных вызовов до Go | Полный разбор</title>
      <link>https://igoroutine.courses/free-materials/rjovjjnlr1-ustroistvo-operatsionnih-sistem-ot-yadra</link>
      <amplink>https://igoroutine.courses/free-materials/rjovjjnlr1-ustroistvo-operatsionnih-sistem-ot-yadra?amp=true</amplink>
      <pubDate>Thu, 02 Oct 2025 13:04:00 +0300</pubDate>
      <enclosure url="https://static.tildacdn.com/tild6434-3061-4536-b831-616666346162/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Устройство операционных систем: от ядра и системных вызовов до Go | Полный разбор</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild6434-3061-4536-b831-616666346162/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
     data-yt="1uNill38z1I"
    data-vk="oid=-230033131&id=456239040&hd=4"
    data-rutube="82184805845090941736082ec77a1fb5"
    data-dzen="oaUalYwAJAAA"> 
  <amp-youtube
    data-videoid="1uNill38z1I"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">На этом открытом уроке мы разберем устройство операционных систем от «А» до «Я» — как ядро управляет железом, как программы общаются с ОС через системные вызовы и как все это реализовано внутри языка Go.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Архитектура ядра:</strong> разница между монолитными и микроядерными системами, а также устройство ядра Linux.</li><li data-list="bullet"><strong>Процессы и потоки:</strong> глубокий разбор абстракций ОС, структуры адресного пространства (Stack, Heap, Text) и контекст-свитчинга.</li><li data-list="bullet"><strong>Системные вызовы (Syscalls):</strong> как происходит переход из User Space в Kernel Space на уровне регистров и прерываний процессора.</li><li data-list="bullet"><strong>Планировщик Go vs ОС:</strong> как рантайм Go экономит ресурсы системы, управляя тысячами горутин на ограниченном количестве потоков (модель GMP).</li><li data-list="bullet"><strong>Сигналы и прерывания:</strong> как ОС общается с вашим приложением и как Go обрабатывает сигналы для Graceful Shutdown или работы GC.</li><li data-list="bullet"><strong>Низкоуровневые примеры:</strong> пишем системные вызовы на ассемблере и разбираем, как Go обращается к ядру напрямую.</li></ul><br />В итоге у тебя будет четкое понимание фундаментальных принципов работы ОС: ты узнаешь, что происходит под капотом обычного fmt.Println или открытия файла, и научишься писать более производительный код, понимая ограничения и возможности системы</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/igoroutine-courses/the_nature_of_computer_science/blob/master/lectures/lecture_5/syscall_perfomance_test.go</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Comparing Linux and Minix</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://lwn.net/Articles/220255/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Видео про профилирование</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://youtu.be/3R6Lke2aGE0</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Планировщик в Go
</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://habr.com/ru/articles/891426/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Видео про ассемблер</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://youtu.be/iflItE2PUAU</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Видео про сборщик мусора</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=492</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="6" data-column="0"><div class="t-table__cell-content">Видео про память</div></td><td class="t-table__cell" data-row="6" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine_chat/295</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Продвинутые дженерики в новых версиях Go</title>
      <link>https://igoroutine.courses/free-materials/zyi9uuexh1-prodvinutie-dzheneriki-v-novih-versiyah</link>
      <amplink>https://igoroutine.courses/free-materials/zyi9uuexh1-prodvinutie-dzheneriki-v-novih-versiyah?amp=true</amplink>
      <pubDate>Sun, 12 Oct 2025 13:10:00 +0300</pubDate>
      <category>Собеседования</category>
      <enclosure url="https://static.tildacdn.com/tild3466-6535-4233-a163-386233656562/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Продвинутые дженерики в новых версиях Go</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3466-6535-4233-a163-386233656562/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="HGCug6pDyhs"
    data-vk="oid=-230033131&id=456239038&hd=4"
    data-rutube="fee81c93e813b244f078296b112a4168"
    data-dzen="oaUYVZdwIAAA"> 
  <amp-youtube
    data-videoid="HGCug6pDyhs"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">На этом открытом уроке за 1,5 часа мы разберем интерфейсы и дженерики в Go от «А» до «Я» — как они устроены под капотом, зачем нужны итераторы и как писать гибкий код без потери производительности.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Методы и ресиверы:</strong> в чем разница между Value и Pointer ресиверами, и как это влияет на копирование данных и память.</li><li data-list="bullet"><strong>Интерфейсы под капотом:</strong> детальный разбор структур iface и eface, виртуальные таблицы методов и накладные расходы на динамический полиморфизм.</li><li data-list="bullet"><strong>Дженерики (Type Parameters):</strong> как работают type sets, что такое comparable и почему Go генерирует отдельный код для каждого типа (мономорфизация).</li><li data-list="bullet"><strong>Работа с коллекциями:</strong> нюансы работы с хэш-мапами, создание сетов и использование пакетов slices, maps и cmp.</li><li data-list="bullet"><strong>Итераторы в Go 1.23:</strong> глубокое погружение в iter.Seq, разница между Push и Pull итераторами и как они упрощают обход сложных структур данных.</li><li data-list="bullet"><strong>Анализ бинарников:</strong> смотрим, как дженерики влияют на размер исполняемого файла и скорость компиляции.</li></ul><br />В итоге у тебя будет полное понимание того, как эффективно использовать систему типов Go: ты научишься проектировать чистые API с помощью интерфейсов и писать универсальные алгоритмы на дженериках, не допуская типичных ошибок новичков.</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0011_generics</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Типы значений (lvalue, xvalue, rvalue)</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/cpp-value-categories</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Мой курс по многопоточному программированию</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://igoroutine.courses/courses/concurrency_nature/?utm_source=site&utm_medium=post&utm_campaign=igoroutine&utm_content=tnogp_adv_generics</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Доклад Highload про оптимизацию с помощью инлайна интерфейсов (и другого)
</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=QMC9Kg4Ogxg</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Предыдущая часть про основы Go</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=296</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Интересная статья как дизайнить библиотеки в Go</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://abhinavg.net/2022/12/06/designing-go-libraries/</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Оперативная, виртуальная и внешняя память: от железа, дисков и ядра ОС до Go</title>
      <link>https://igoroutine.courses/free-materials/eh84a8sxm1-operativnaya-virtualnaya-i-vneshnyaya-pa</link>
      <amplink>https://igoroutine.courses/free-materials/eh84a8sxm1-operativnaya-virtualnaya-i-vneshnyaya-pa?amp=true</amplink>
      <pubDate>Wed, 24 Sep 2025 13:17:00 +0300</pubDate>
      <category>System Design</category>
      <enclosure url="https://static.tildacdn.com/tild6463-3133-4465-a130-646137633936/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Оперативная, виртуальная и внешняя память: от железа, дисков и ядра ОС до Go</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild6463-3133-4465-a130-646137633936/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
     data-yt="NDN87z3co6U" 
     data-vk="oid=-230033131&id=456239036&hd=4" 
     data-rutube="d91a8da3492f71463313ac15df873123"
     data-dzen="oaUZqUdUIAAA">
  <amp-youtube
    data-videoid="NDN87z3co6U"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">В этом видео мы разберем устройство памяти от «А» до «Я» — от физических транзисторов в оперативной памяти и устройства магнитных дисков до механизмов виртуальной памяти в ядре ОС и их использования в языке Go.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Физический уровень:</strong> разница между DRAM и SRAM, устройство ячеек памяти и почему оперативную память нужно постоянно «подзаряжать».</li><li data-list="bullet"><strong>Виртуальная память:</strong> зачем она нужна, как обеспечивается изоляция процессов и почему ваша программа никогда не видит реальные физические адреса.</li><li data-list="bullet"><strong>Таблицы страниц (Page Tables):</strong> глубокий разбор перехода от одноуровневых к иерархическим таблицам трансляции адресов.</li><li data-list="bullet"><strong>Аппаратное ускорение:</strong> как работают MMU и TLB-кэши, ускоряя доступ к данным на уровне железа.</li><li data-list="bullet"><strong>Механизмы ОС:</strong> что такое Lazy Allocation (ленивое выделение памяти), свопинг и магия Memory-mapped файлов (mmap).</li><li data-list="bullet"><strong>Продвинутые техники:</strong> как Huge Pages помогают базам данных работать быстрее и что такое Tagged Pointers.</li><li data-list="bullet"><strong>Внешняя память:</strong> сравнение HDD и SSD под капотом — от магнитных головок до транзисторов с плавающим затвором.</li></ul><br />В итоге у тебя будет четкое понимание того, как данные путешествуют между диском и процессором: ты узнаешь, что на самом деле происходит при вызове make в Go и как понимание этих основ помогает в оптимизации высоконагруженных систем.</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/igoroutine-courses/the_nature_of_computer_science/tree/master/lectures</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Latency Numbers Every Programmer Should Know</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://gist.github.com/jboner/2841832</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Визуализация IO devices and latency</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://planetscale.com/blog/io-devices-and-latency</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Собеседование на Senior Go | Разбор задачи из BigTech | LRU Cache</title>
      <link>https://igoroutine.courses/free-materials/hdlptye3f1-sobesedovanie-na-senior-go-razbor-zadach</link>
      <amplink>https://igoroutine.courses/free-materials/hdlptye3f1-sobesedovanie-na-senior-go-razbor-zadach?amp=true</amplink>
      <pubDate>Sun, 19 Oct 2025 13:24:00 +0300</pubDate>
      <category>Собеседования</category>
      <category>Продвинутый Go</category>
      <enclosure url="https://static.tildacdn.com/tild6563-3933-4430-b933-306130666631/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Собеседование на Senior Go | Разбор задачи из BigTech | LRU Cache</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild6563-3933-4430-b933-306130666631/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="hylkOZEI5TI"
    data-vk="oid=-230033131&id=456239039&hd=4"
    data-rutube="aedde55b0486c8239c406f38c77cb54e"
    data-dzen="oaUZxuu4IAAA"> 
  <amp-youtube
    data-videoid="hylkOZEI5TI"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">В этом видео мы за 30 минут разберем одну из самых популярных задач на Senior Go собеседованиях в BigTech — проектирование и реализацию LRU Cache (Least Recently Used) с использованием самых современных возможностей языка.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Проектирование интерфейса:</strong> как создать гибкий кэш на дженериках, поддерживающий любые сравниваемые ключи (comparable) и значения (any).</li><li data-list="bullet"><strong>Структуры данных:</strong> почему для эффективного LRU необходимо сочетание Hash-map и двусвязного списка (Linked List), и как добиться асимптотики $O(1)$ для основных операций.</li><li data-list="bullet"><strong>Использование итераторов:</strong> реализуем современный способ обхода кэша через for range, появившийся в Go 1.23.</li><li data-list="bullet"><strong>Работа с Legacy:</strong> как правильно писать типобезопасные обертки (wrappers) над стандартными коллекциями из пакета container/list.</li><li data-list="bullet"><strong>Нюансы реализации:</strong> обработка Zero Value, перемещение элементов в «голову» списка и стратегия удаления наиболее старых записей.</li><li data-list="bullet"><strong>Life-coding:</strong> пошаговое написание кода, решение проблемы доступа к ключам в нодах и финальная проверка решения на LeetCode.</li></ul><br />В итоге ты научишься не просто решать алгоритмическую задачу, а применять на практике дженерики, итераторы и интерфейсы для создания производительных и расширяемых систем, которые ожидают увидеть интервьюеры в топовых IT-компаниях</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0012_lru_cache</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Задача LRU LeetCode</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://leetcode.com/problems/lru-cache/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Задача LFU LeetCode</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://leetcode.com/problems/lfu-cache/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Псевдо-рандомная политика вытеснения в телефонах
</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://developer.arm.com/documentation/den0042/0100/Caches/Cache-policies/Replacement-policy</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Видео про итераторы и дженерики</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine_chat/321</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Профилирование на Go | Полное руководство</title>
      <link>https://igoroutine.courses/free-materials/ehbhj0gfe1-profilirovanie-na-go-polnoe-rukovodstvo</link>
      <amplink>https://igoroutine.courses/free-materials/ehbhj0gfe1-profilirovanie-na-go-polnoe-rukovodstvo?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 13:32:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <category>Computer Science</category>
      <enclosure url="https://static.tildacdn.com/tild3031-6631-4063-b161-636538663064/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Профилирование на Go | Полное руководство</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3031-6631-4063-b161-636538663064/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="3R6Lke2aGE0"
    data-vk="oid=-230033131&id=456239022&hd=4"
    data-rutube="699bb99e04ec8e87eb14e4bc0b7e284a"
    data-dzen="oaUbOUnAIAAA"> 
  <amp-youtube
    data-videoid="3R6Lke2aGE0"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">В этом видео мы разберем профилирование в Go от «А» до «Я» — как находить узкие места в коде, оптимизировать потребление памяти и использовать данные о работе приложения для автоматического ускорения компиляции.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Теория профилирования:</strong> зачем это нужно, что такое баттлнеки и в чем разница между инструментацией и самплированием.</li><li data-list="bullet"><strong>Работа с pprof:</strong> как собирать профили CPU и памяти локально и через HTTP-эндпоинты прямо на лету.</li><li data-list="bullet"><strong>Визуализация данных:</strong> учимся читать графы вызовов и Flame Graphs, разбираемся в метриках Flat и Cumulative.</li><li data-list="bullet"><strong>Профилирование блокировок:</strong> как находить задержки в мьютексах и каналах, ограничивающие пропускную способность системы.</li><li data-list="bullet"><strong>Трассировка (Execution Trace):</strong> глубокий анализ работы планировщика и сборщика мусора (GC) для борьбы со спайками нагрузки.</li><li data-list="bullet"><strong>Continuous Profiling:</strong> как организовать постоянный сбор и сравнение профилей в реальных тех-компаниях с помощью инструментов вроде Pyroscope.</li><li data-list="bullet"><strong>Оптимизация PGO:</strong> ускоряем приложение на 2-14%, скармливая профили компилятору Go.</li></ul><br />В итоге у тебя будет полное руководство по поиску и устранению проблем производительности: от понимания сигналов операционной системы до практических кейсов использования sync.Pool и тонкой настройки компилятора</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0004_profiling</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Презентация</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://docs.google.com/presentation/d/1TUJ3VPXPat8jpWxJ3V8Ej9p7oY-FSoTrZPaWcb9gfGs/edit?usp=sharing</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Всё про ошибки в Go | Полное руководство для работы и собеседований</title>
      <link>https://igoroutine.courses/free-materials/uzh2adlrs1-vsyo-pro-oshibki-v-go-polnoe-rukovodstvo</link>
      <amplink>https://igoroutine.courses/free-materials/uzh2adlrs1-vsyo-pro-oshibki-v-go-polnoe-rukovodstvo?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 13:35:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <enclosure url="https://static.tildacdn.com/tild3130-3938-4161-b037-306362333564/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Всё про ошибки в Go | Полное руководство для работы и собеседований</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3130-3938-4161-b037-306362333564/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="VujFXe4e2VY"
    data-vk="oid=-230033131&id=456239044&hd=4"
    data-rutube="8106da7bb71caa474a976c9e040b174d"
    data-dzen="oaUZye88JAAA">  
  <amp-youtube
    data-videoid="VujFXe4e2VY"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">В этом видео мы разберем обработку ошибок в Go от «А» до «Я» — как устроены ошибки под капотом, почему важно понимать разницу между errors.Is и errors.As, и как правильно использовать панику и дефёр в реальных приложениях.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Природа ошибок:</strong> почему ошибки в Go — это значения, и как работает встроенный интерфейс error.</li><li data-list="bullet"><strong>Ловушка с NIL:</strong> разбираем популярный баг с интерфейсами, когда ошибка кажется пустой, но проверка err != nil возвращает true.</li><li data-list="bullet"><strong>Цепочки ошибок (Error Wrapping):</strong> учимся чейнить ошибки через fmt.Errorf с глаголом %w, использовать errors.Join и распаковывать их для анализа.</li><li data-list="bullet"><strong>Механизм Defer:</strong> как отложенные вызовы помогают управлять ресурсами, в каком порядке они выполняются и как могут менять именованные возвращаемые значения.</li><li data-list="bullet"><strong>Паника и восстановление:</strong> в каких случаях допустимо использовать panic, как работает recover и как за кулисами происходит раскрутка стека (unwinding).</li><li data-list="bullet"><strong>Практика:</strong> пишем безопасный сервер, который не падает при внутренних ошибках, и даже пробуем реализовать конструкцию try-catch на Go (и узнаем, почему так делать не стоит).</li></ul><br />В итоге у тебя будет полное руководство по работе с исключительными ситуациями: от написания кастомных типов ошибок до понимания нюансов рантайма, что позволит тебе писать надежный код и уверенно отвечать на сложные вопросы на собеседованиях.</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0016_errors</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Видео про интерфейсы (дженерики)</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=321</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Код Go касательно defer в цикле</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://github.com/golang/go/blob/2b62144069a130cc469f33009c0c392cc6de8810/src/cmd/compile/internal/walk/stmt.go#L109</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Тайны компилятора и истории Go | Полное руководство для собеседований</title>
      <link>https://igoroutine.courses/free-materials/071iuthvb1-taini-kompilyatora-i-istorii-go-polnoe-r</link>
      <amplink>https://igoroutine.courses/free-materials/071iuthvb1-taini-kompilyatora-i-istorii-go-polnoe-r?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 13:50:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <category>Computer Science</category>
      <category>Собеседования</category>
      <category>System Design</category>
      <enclosure url="https://static.tildacdn.com/tild6263-3065-4834-a466-656666636131/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Тайны компилятора и истории Go | Полное руководство для собеседований</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild6263-3065-4834-a466-656666636131/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="3CPtbrCXhC8"
    data-vk="oid=-230033131&id=456239043&hd=4"
    data-rutube="285f0ccd6ce4fe8c04c8d355c4d144f1"
    data-dzen="oaUbOOokJAAA">  
  <amp-youtube
    data-videoid="3CPtbrCXhC8"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">На этом открытом уроке за 1 час мы разберем тайны компилятора Go и историю создания языка от «А» до «Я» — как идеи из операционной системы Plan 9 повлияли на современный Go, какие оптимизации применяет компилятор и как управлять процессом сборки с помощью директив.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>История и корни:</strong> путь от Bell Labs и Plan 9 до Google. Разбираем, как концепции каналов и кросс-компиляции появились задолго до официального релиза Go.</li><li data-list="bullet"><strong>Пайплайн компиляции:</strong> пошаговый разбор стадий от парсинга лексем и построения AST-дерева до генерации SSA (Static Single Assignment) и машинного кода.</li><li data-list="bullet"><strong>Оптимизации компилятора:</strong> что такое инлайнинг (Inlining), девиртуализация интерфейсов и удаление неиспользуемого кода (Dead code elimination).</li><li data-list="bullet"><strong>Escape Analysis:</strong> глубокое погружение в механизм, который решает, где выделить память — на стеке или в куче, и как это влияет на производительность.</li><li data-list="bullet"><strong>Директивы компилятора:</strong> учимся использовать //go:noinline, //go:noescape и //go:linkname для тонкой настройки поведения программ.</li><li data-list="bullet"><strong>Практика:</strong> пробуем скомпилировать приложение вручную по стадиям, разбираем билд-теги для управления сборкой и анализируем бинарные файлы.</li></ul><br />В итоге у тебя будет полное понимание того, что происходит с твоим кодом после команды go build: ты узнаешь, почему Go компилируется так быстро, как работают «внутренности» тулчейна и как эти знания помогают писать максимально эффективный код для высоконагруженных систем.</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0015_compilation</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Доклад про сборку и запуск Aleft/Newsqueak </div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=ql-uncsqoAU</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Видео про интерфейсы</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=321</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Доказательная база про IR SSA</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://ru.wikipedia.org/wiki/SSA</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Видео про процессор</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=294</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Видео про Escape Analysis</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/82</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Полное руководство про GC</title>
      <link>https://igoroutine.courses/free-materials/xlk4j18811-polnoe-rukovodstvo-pro-gc</link>
      <amplink>https://igoroutine.courses/free-materials/xlk4j18811-polnoe-rukovodstvo-pro-gc?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 13:55:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <category>Computer Science</category>
      <enclosure url="https://static.tildacdn.com/tild3735-3037-4334-b766-656165663963/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Полное руководство про GC</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3735-3037-4334-b766-656165663963/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="yChVguSiZw0"
    data-vk="oid=-230033131&id=456239042&hd=4"
    data-rutube="77dc240170796f8e5a4fb5c996cfe964"
    data-dzen="oaUZYm1kJAAA">  
  <amp-youtube
    data-videoid="yChVguSiZw0"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">В видео мы разберем устройство сборщика мусора (GC) в Go от «А» до «Я» — от классических алгоритмов до нового экспериментального GC в версии 1.26, который уже активно внедряет Google.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Основы и мотивация:</strong> зачем нужен GC, какие боли он снимает (Dangling pointers, Double free) и сколько мы на самом деле платим ресурсами за автоматическое управление памятью.</li><li data-list="bullet"><strong>Подходы к сборке мусора:</strong> разница между подсчетом ссылок (Reference Counting) и трейсингом (Tracing), плюсы и минусы каждого метода.</li><li data-list="bullet"><strong>Алгоритм Go под капотом:</strong> детальный разбор Concurrent Mark-and-Sweep, стадии Stop the World и как рантайм Go останавливает тысячи горутин для разметки объектов.</li><li data-list="bullet"><strong>Управление памятью:</strong> что такое арены, страницы по 8 Кб и классы объектов, и как Go заранее запрашивает память у ОС для ускорения аллокаций.</li><li data-list="bullet"><strong>Настройка и оптимизация:</strong> учимся использовать переменные GOGC и GOMEMLIMIT, разбираем кейсы применения sync.Pool для снижения нагрузки на GC.</li><li data-list="bullet"><strong>Новинки Go 1.26:</strong> почему новый сборщик мусора быстрее, как работает последовательное сканирование страниц вместо прыжков по памяти и как используются векторные инструкции (AVX-512) для ускорения разметки.</li></ul><br />В итоге у тебя будет полное руководство по работе GC: ты поймешь, как писать код, который «дружит» с памятью и кэшем процессора, и научишься диагностировать проблемы производительности с помощью трейсинг-профилировщика</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0014_gc</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Видео про операционные системы</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=360</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Исследование про GC: Quantifying the Performance of Garbage Collection vs. Explicit Memory Management</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://people.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Видео про базу concurrency</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=417</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Видео про виртуальную память</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=295</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Видео про профилирование</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/23</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="6" data-column="0"><div class="t-table__cell-content">Видео про продвинутые дженерики</div></td><td class="t-table__cell" data-row="6" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=321</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="7" data-column="0"><div class="t-table__cell-content">Dijkstra Insert Write Barrier and Yuasa Delete Write Barrier</div></td><td class="t-table__cell" data-row="7" data-column="1"><div class="t-table__cell-content">https://www.dingyuqi.com/en/article/go-garbage-collection/#tri-color-mark-%E2%80%8B%E2%80%8Bsweep</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="8" data-column="0"><div class="t-table__cell-content">Видео про SIMD</div></td><td class="t-table__cell" data-row="8" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/72</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Понимание ассемблера Go</title>
      <link>https://igoroutine.courses/free-materials/eavtdu5p91-ponimanie-assemblera-go</link>
      <amplink>https://igoroutine.courses/free-materials/eavtdu5p91-ponimanie-assemblera-go?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 14:00:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <enclosure url="https://static.tildacdn.com/tild3737-6165-4838-b536-333162663233/image.png" type="image/png"/>
      <turbo:content><![CDATA[<header><h1>Понимание ассемблера Go</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3737-6165-4838-b536-333162663233/image.png"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="R121Xpb28og"
    data-vk=""
    data-rutube=""
    data-dzen="">   
  <amp-youtube
    data-videoid="R121Xpb28og"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">В этом докладе за 45 минут мы разберем ассемблер Go от внутреннего устройства до практической оптимизации — как понять "железо", на котором работает ваш код, и почему функции из стандартной библиотеки можно ускорить в 14 раз.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>История и корни:</strong> почему ассемблер Go базируется на архитектуре Plan 9 и как он связан с кросс-компиляцией.</li><li data-list="bullet"><strong>Терминология и примитивы:</strong> что такое мнемоники, как устроена быстрая память процессора (регистры) и зачем нужен регистр флагов для реализации циклов.</li><li data-list="bullet"><strong>Синтаксис и виртуальные регистры:</strong> разбор работы с FP (Frame Pointer), SB (Static Base Pointer) и PC (Program Counter) для управления аргументами и глобальными данными.</li><li data-list="bullet"><strong>Соглашения о вызовах:</strong> как Go передает данные в функции и как правильно возвращать результат согласно ABI.</li><li data-list="bullet"><strong>SIMD-оптимизации:</strong> как использовать векторные инструкции процессора для обработки данных пачками по 16 байт и почему это радикально быстрее обычного for range.</li><li data-list="bullet"><strong>Примеры на практике:</strong> пошаговый разбор реализации суммы слайса и поиска элементов (slices.Contains) на ассемблере.</li></ul><br />В итоге у тебя будет четкое понимание того, что происходит на самом низком уровне исполнения Go-программ: ты узнаешь, когда стоит жертвовать портативностью ради скорости и как писать по-настоящему эффективный инфраструктурный код</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Презентация</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://squidex.jugru.team/api/assets/srm/bf40fd1e-aab5-4383-8a0a-f7abdb82939e/gofunc-final.pdf</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>gRPC на Go: от внутреннего устройства до архитектуры</title>
      <link>https://igoroutine.courses/free-materials/3z6o3yuut1-grpc-na-go-ot-vnutrennego-ustroistva-do</link>
      <amplink>https://igoroutine.courses/free-materials/3z6o3yuut1-grpc-na-go-ot-vnutrennego-ustroistva-do?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 14:57:00 +0300</pubDate>
      <enclosure url="https://static.tildacdn.com/tild3665-3533-4335-b432-373565623862/image.png" type="image/png"/>
      <turbo:content><![CDATA[<header><h1>gRPC на Go: от внутреннего устройства до архитектуры</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3665-3533-4335-b432-373565623862/image.png"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="6ABApS2FDIQ"
    data-vk="oid=-230033131&id=456239048&hd=4"
    data-rutube="bc0a177bc4ae4624001f037a0655bbdf"
    data-dzen="oaUbESdsKAAA">   
  <amp-youtube
    data-videoid="6ABApS2FDIQ"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0019_grpc/02_orders</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div><div class="t-redactor__embedcode"><div id="custom-article-toc"></div>

<style>
/* --- Базовые стили основного оглавления --- */
#custom-article-toc:empty { display: none; }

.custom-toc-container {
    background: rgba(26, 28, 40, 0.6); 
    border-radius: 12px;
    padding: 24px;
    font-family: 'ShareTechMono', sans-serif;
    margin-bottom: 24px;
}

.custom-toc-title {
    color: #ffffff;
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 16px;
    font-family: 'ShareTechMono', sans-serif;
}

.custom-toc-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* --- Стили для ссылок --- */
.custom-toc-link {
    color: rgba(255, 255, 255, 0.85) !important;
    text-decoration: none !important;
    font-size: 15px;
    transition: color 0.2s ease, transform 0.2s ease;
    display: flex; 
    align-items: center; 
    line-height: 1.4;
    cursor: pointer;
    font-family: 'ShareTechMono', sans-serif;
}

.custom-toc-link:hover {
    color: #F4721E !important; 
    transform: translateX(2px); 
}

/* --- Иерархия --- */
.custom-toc-level-h1 { margin-left: 0; font-weight: 600; color: #ffffff !important; }
.custom-toc-level-h2 { margin-left: 0; }
.custom-toc-level-h3 { margin-left: 8px; font-size: 14px; color: rgba(255, 255, 255, 0.6) !important; }

/* --- Скрытый блок с h4 --- */
.custom-toc-h4-wrapper {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 0; 
    overflow: hidden;
    transition: max-height 0.4s ease-in-out;
}

.custom-toc-h4-wrapper.is-open {
    max-height: 1000px; 
    margin-top: 6px; 
}

.custom-toc-level-h4 { 
    margin-left: 16px; 
    font-size: 13px; 
    color: rgba(255, 255, 255, 0.4) !important; 
}

/* --- Иконка стрелочки для h3 --- */
.custom-toc-toggle-icon {
    width: 14px;
    height: 14px;
    margin-right: 6px;
    transition: transform 0.3s ease;
    opacity: 0.7;
    flex-shrink: 0;
}

.custom-toc-level-h3.has-children.is-open .custom-toc-toggle-icon {
    transform: rotate(90deg); 
    color: #F4721E;
}


/* =======================================================
   СТИЛИ ДЛЯ ПЛАВАЮЩЕГО ПОПАПА (ПРАВЫЙ НИЖНИЙ УГОЛ)
   ======================================================= */
/* Кнопка-триггер */
.custom-toc-float-btn {
    position: fixed;
    bottom: 20px;
    right: 20px;
    background: #F4721E; /* Твой оранжевый */
    color: #1A1C28; /* Темный текст для контраста */
    padding: 12px 20px;
    border-radius: 8px;
    font-family: 'ShareTechMono', sans-serif;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    z-index: 9998;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    display: flex;
    align-items: center;
    gap: 8px;
    transition: transform 0.2s ease, background 0.2s ease;
}

.custom-toc-float-btn:hover {
    transform: translateY(-2px);
    background: #e06010;
}

.custom-toc-float-btn svg {
    width: 18px;
    height: 18px;
}

/* Само окно оглавления */
.custom-toc-float-window {
    position: fixed;
    bottom: 70px; /* Чуть выше кнопки */
    right: 20px;
    width: 320px;
    max-height: calc(100vh - 120px); /* Чтобы не вылезало за экран */
    background: rgba(18, 20, 30, 0.95); /* Чуть темнее основного фона */
    border: 1px solid rgba(244, 114, 30, 0.3); /* Оранжевая обводка */
    border-radius: 12px;
    padding: 20px;
    z-index: 9999;
    box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    overflow-y: auto;
    
    /* Анимация появления */
    opacity: 0;
    transform: translateY(20px);
    pointer-events: none;
    transition: all 0.3s ease;
}

.custom-toc-float-window.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* Кастомный скроллбар для окна */
.custom-toc-float-window::-webkit-scrollbar {
    width: 4px;
}
.custom-toc-float-window::-webkit-scrollbar-thumb {
    background: rgba(244, 114, 30, 0.5); 
    border-radius: 4px;
}

/* Шапка попапа с крестиком */
.custom-toc-float-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    padding-bottom: 12px;
}

.custom-toc-float-header .custom-toc-title {
    margin-bottom: 0; /* Убираем отступ, чтобы было в линию с крестиком */
    font-size: 16px;
}

.custom-toc-close-btn {
    background: none;
    border: none;
    color: rgba(255,255,255,0.6);
    cursor: pointer;
    padding: 4px;
    display: flex;
    transition: color 0.2s ease;
}
.custom-toc-close-btn:hover {
    color: #F4721E;
}

/* Показываем плавающую кнопку только на больших экранах */
/*@media screen and (max-width: 960px) {*/
/*    .custom-toc-float-btn, */
/*    .custom-toc-float-window {*/
/*        display: none !important;*/
/*    }*/
/*}*/
</style>

<script>
(function() {
    let attempts = 0;
    
    const tocInterval = setInterval(() => {
        attempts++;
        const tocWrapper = document.getElementById('custom-article-toc');
        
        if (tocWrapper && !tocWrapper.hasAttribute('data-built')) {
            const articleBody = tocWrapper.closest('.js-feed-post-text') || tocWrapper.closest('.t-redactor__tte-view');
            
            if (articleBody) {
                const headings = articleBody.querySelectorAll('h1, h2, h3, h4');
                
                if (headings.length > 0) {
                    const tocContainer = document.createElement('div');
                    tocContainer.className = 'custom-toc-container';
                    
                    const tocTitle = document.createElement('div');
                    tocTitle.className = 'custom-toc-title';
                    tocTitle.textContent = 'Содержание статьи';
                    tocContainer.appendChild(tocTitle);

                    const tocList = document.createElement('div');
                    tocList.className = 'custom-toc-list';

                    let currentH3Item = null;
                    let currentH4Wrapper = null;

                    headings.forEach((heading) => {
                        if (!heading.id) {
                            heading.id = 'heading-' + Math.random().toString(36).substr(2, 9);
                        }

                        const tagName = heading.tagName.toLowerCase();
                        
                        if (tagName === 'h3') {
                            const listItem = document.createElement('div');
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-h3';
                            link.textContent = heading.textContent.trim();
                            
                            link.targetHeading = heading;
                            
                            listItem.appendChild(link);
                            tocList.appendChild(listItem);
                            
                            currentH3Item = listItem;
                            currentH4Wrapper = null; 
                            
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                
                                if (link.classList.contains('has-children')) {
                                    const wrapper = link.nextElementSibling;
                                    
                                    if (e.target.closest('.custom-toc-toggle-icon')) {
                                        link.classList.toggle('is-open');
                                        wrapper.classList.toggle('is-open');
                                        return;
                                    }
                                    
                                    if (!link.classList.contains('is-open')) {
                                        link.classList.add('is-open');
                                        wrapper.classList.add('is-open');
                                    } else {
                                        link.targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                        // Опционально: закрывать попап при клике
                                        // document.querySelector('.custom-toc-float-window').classList.remove('is-visible');
                                    }
                                } else {
                                    link.targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                }
                            });
                            
                        } else if (tagName === 'h4' && currentH3Item) {
                            if (!currentH4Wrapper) {
                                currentH4Wrapper = document.createElement('div');
                                currentH4Wrapper.className = 'custom-toc-h4-wrapper';
                                currentH3Item.appendChild(currentH4Wrapper);
                                
                                const parentH3Link = currentH3Item.querySelector('.custom-toc-level-h3');
                                parentH3Link.classList.add('has-children');
                                
                                const chevronSvg = '<svg class="custom-toc-toggle-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>';
                                parentH3Link.insertAdjacentHTML('afterbegin', chevronSvg);
                            }
                            
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-h4';
                            link.textContent = heading.textContent.trim();
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            });
                            currentH4Wrapper.appendChild(link);
                            
                        } else {
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-' + tagName;
                            link.textContent = heading.textContent.trim();
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            });
                            tocList.appendChild(link);
                            
                            currentH3Item = null;
                            currentH4Wrapper = null;
                        }
                    });

                    tocContainer.appendChild(tocList);
                    tocWrapper.appendChild(tocContainer);
                    
                    
                    /* =======================================================
                       СОЗДАЕМ ПЛАВАЮЩИЙ UI
                       ======================================================= */
                    
                    // Кнопка
                    const floatBtn = document.createElement('div');
                    floatBtn.className = 'custom-toc-float-btn';
                    floatBtn.innerHTML = `
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
                        Содержание
                    `;
                    document.body.appendChild(floatBtn);

                    // Окно попапа
                    const floatWindow = document.createElement('div');
                    floatWindow.className = 'custom-toc-float-window';
                    
                    // Шапка окна
                    const floatHeader = document.createElement('div');
                    floatHeader.className = 'custom-toc-float-header';
                    floatHeader.innerHTML = `
                        <div class="custom-toc-title">Содержание статьи</div>
                        <button class="custom-toc-close-btn" aria-label="Закрыть">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
                        </button>
                    `;
                    
                    // Клонируем собранный список ссылок
                    const floatList = tocList.cloneNode(true);
                    
                    // Оживляем клики в клонированном списке
                    floatList.addEventListener('click', function(e) {
                        const link = e.target.closest('.custom-toc-link');
                        if (!link) return;
                        
                        e.preventDefault();
                        
                        // Ищем оригинальный заголовок в статье по тексту
                        const linkText = link.textContent.trim();
                        let targetHeading = null;
                        Array.from(headings).forEach(h => {
                            if (h.textContent.trim() === linkText) targetHeading = h;
                        });

                        if (!targetHeading) return;

                        if (link.classList.contains('has-children')) {
                            const wrapper = link.nextElementSibling;
                            if (e.target.closest('.custom-toc-toggle-icon')) {
                                link.classList.toggle('is-open');
                                wrapper.classList.toggle('is-open');
                                return;
                            }
                            if (!link.classList.contains('is-open')) {
                                link.classList.add('is-open');
                                wrapper.classList.add('is-open');
                            } else {
                                targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            }
                        } else {
                            targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                        }
                    });

                    floatWindow.appendChild(floatHeader);
                    floatWindow.appendChild(floatList);
                    document.body.appendChild(floatWindow);

                    // Логика открытия/закрытия
                    floatBtn.addEventListener('click', () => {
                        floatWindow.classList.toggle('is-visible');
                    });
                    
                    floatWindow.querySelector('.custom-toc-close-btn').addEventListener('click', () => {
                        floatWindow.classList.remove('is-visible');
                    });


                    tocWrapper.setAttribute('data-built', 'true');
                    tocWrapper.id = 'custom-article-toc-ready';
                }
            }
            clearInterval(tocInterval);
        }
        
        if (attempts > 20) {
            clearInterval(tocInterval);
        }
    }, 500);
})();
</script></div><h2  class="t-redactor__h2">Современная архитектура приложений</h2><div class="t-redactor__text">Современные системы уже давно вышли за&nbsp;рамки одного приложения. Теперь они представляют собой множество различных приложений (сервисов), количество которых иногда может достигать несколько сотен, которые должны общаться между собой, а&nbsp;иногда еще и&nbsp;дублироваться. Современную архитектуру можно рассмотреть на&nbsp;таком простом примере магазина:</div><img src="https://static.tildacdn.com/tild3435-6131-4432-b161-666461613430/image.png"><div class="t-redactor__text">У нас есть пользователи, которые пользуются нашим магазином. Их нужно подключить к нашей системе, чтобы они, собственно, могли использовать приложение. Обычно для этого используют gateway – сервис, который раскидывает запросы пользователей по системе, но так же может производить верификацию пользователя и балансировку нагрузки. Далее у нас есть сервисы самого магазина:</div><div class="t-redactor__text">• Catalog — сервис для работы с каталогом товаров<br />• Ordering — сервис для работы с заказами (создание, оплата и т.д.)<br />• Antifraud — сервис для защиты от мошенничества<br />• ML Rec — рекомендации для основе ИИ</div><div class="t-redactor__text">Каждый из этих сервисов имеет свою базу данных. Так, например, если упадет база данных заказов, каталог товаров все равно продолжит работу. Так же, каждый из этих сервисов может иметь копию. Например, если один дата-центр пострадает, благодаря копии сервиса на другом дата-центре приложение продолжит работу. Теперь мы хотим научить наши сервисы взаимодействовать между собой, например, чтобы при создании заказа, сервис заказов взял у сервиса каталога id товаров. Фреймворк gRPC является де-факто стандартным решением этой задачи во многих компаниях.</div><h2  class="t-redactor__h2">Что такое gRPC и как он появился</h2><div class="t-redactor__text">gRPC (Google Remote Procedure Calls) — высокопроизводительная система удаленного вызова процедур, разработанная в Google в 2015 году. Его идея как раз в том, чтобы научить общаться разные приложения, причем эти приложения могут быть написаны на разных стеках . gRPC использует HTTP/2 в качестве транспортного протокола и Protocol Buffers в качестве протокола сериализации.</div><img src="https://static.tildacdn.com/tild3633-3764-4962-b030-636632626231/image.png"><h3  class="t-redactor__h3">История появления gRPC</h3><h4  class="t-redactor__h4">Stubby RPC</h4><div class="t-redactor__text">В 2001 году Google делает для себя RPC фреймворк под названием Stubby. Он был изначально сделан как раз для того, чтобы позволить большому количеству микросервисов общаться между собой, причем еще и в разных дата-центрах. Stubby использовал кастомный транспортный протокол(?) и, предположительно, Protocol Buffers.</div><h4  class="t-redactor__h4">SPDY</h4><div class="t-redactor__text">В 2010 году тот же Google создает транспортный протокол SPDY, который должен был стать улучшением HTTP/1.x. Путем сжатия, мультиплексирования и приоритизации новому протоколу удалось снизить размер заголовков на ~85%, количество соединений на ~40% и ускорить загрузку на ~20-60%. Впоследствии SPDY станет основой для HTTP/2.</div><h4  class="t-redactor__h4">gRPC</h4><div class="t-redactor__text">В 2011 году Роб Пайк на конференции впервые упомянул Stubby RPC и сказал, что возможно его заопенсорсят, но этого так и не произошло. Когда Роба в очередной раз спросили про опенсорс Stubby, он сказал, мол, вам это не надо, лучше используйте RPC, который встроен в Go, так как он по функциональности такой же как Stubby, но не доступен на других языках. По слухам, Stubby не хотели выкладывать в свободный доступ в ожидании, пока он перейдет на более хороший транспортный протокол. И тут в 2014 году на базе SPDY появляется HTTP/2, что и нужно было Stubby. В 2015 году старый фреймворк адаптируют под новый транспортный протокол и выкладывают в свободный доступ под названием gPRC.</div><h2  class="t-redactor__h2">HTTP/2</h2><h3  class="t-redactor__h3">Проблема HTTP/1</h3><div class="t-redactor__text">Рассмотрим стандартную модель взаимодействия в HTTP/1:</div><img src="https://static.tildacdn.com/tild3763-6539-4834-b362-316432373438/image.png"><div class="t-redactor__text">С одним запросом все понятно: мы его отправили и получили ответ. Но когда у нас есть несколько запросов появляется вариативность: мы можем либо отправить запрос, дождаться ответа, а потом отправить следующий запрос, либо отправить несколько запросов, а потом дожидаться ответов на них. Однако для обоих вариантов, отправляя сначала запрос 1 мы всегда первым получим ответ 1. Это происходит из-за того, что HTTP/1 использует протокол TCP, который гарантирует порядок.</div><div class="t-redactor__text">Возникает вопрос: что если ответ на первый запрос очень большой и выгоднее было бы сначала получить ответ на второй запрос, так как он меньше и придет быстрее? Это можно решить установив несколько TCP соединений:</div><img src="https://static.tildacdn.com/tild3633-6361-4462-b436-316234376164/image.png"><div class="t-redactor__text">Но установить соединение не бесплатно и браузеры часто ограничивали количество TCP соединений.</div><h3  class="t-redactor__h3">Head-of-line Blocking</h3><div class="t-redactor__text">Проблемы начинаются, когда теряются пакеты. Допустим, у нас есть два запроса (зеленый и синий) и при передаче первого запроса у нас потерялся пакет:</div><img src="https://static.tildacdn.com/tild6263-3366-4736-b731-383636353063/image.png"><div class="t-redactor__text">Тогда наш сервер сможет получить оставшиеся пакеты, но отправить ответы, пока потерянный пакет не восстановится, не сможет, опять же из-за того, что TCP гарантирует порядок.</div><div class="t-redactor__text">Даже если до потерянного пакета мы получили какие-то запросы, ответы на них все равно не получится отправить, пока этот пакет не восстановится.</div><h3  class="t-redactor__h3">Что делает HTTP/2</h3><div class="t-redactor__text">К сожалению, полностью решить проблему HTTP/2 не может, так как все еще использует TCP под капотом, но, используя эвристику, мы можем отправить ответы на запросы, которые мы получили до потери какого-то пакета.</div><div class="t-redactor__text">Эвристика заключается в том, чтобы посылать пакет разных запросов вперемешку (мультиплексировать), а не последовательно пакеты одного запроса, а потом уже другого. При этом пакеты одного запроса будут идти по порядку, но не обязательно последовательно. Из-за этого появляется шанс, что когда какой-то пакет одного запроса потеряется, у нас будут пакеты другого запроса и мы сможем отправить ответ на этот второй запрос:</div><img src="https://static.tildacdn.com/tild6239-3361-4863-b333-643864383739/image.png"><div class="t-redactor__text">В этом примере, мы получили все пакеты синего запроса до того, как потеряли пакет зеленого запроса, а значит можем отправить ответ на синий, но чтобы отправить ответ на зеленый (и на возможные запросы после потерянного пакета), надо все еще дожидаться восстановления потерянного пакета.</div><div class="t-redactor__text">Полностью эту проблему решили только в HTTP/3, использовав протокол UDP, который не гарантирует порядок.</div><div class="t-redactor__text">Мультиплексирование реализовать приоритизацию и отмену (cancellation). Приоритизация позволяет отправлять пакеты не в совсем случайном порядке, а сказать, какие пакеты мы хотим отправить раньше. Отмена позволяет сказать, что, пакеты какого-то запроса нам больше не нужны и на него не надо присылать ответ.</div><h3  class="t-redactor__h3">Устройство HTTP/2</h3><div class="t-redactor__text">Запрос в HTTP/2 это поток из фреймов. У каждого фрейма есть заголовок, который представляет собой такую структуру:</div><pre class="t-redactor__highlightcode"><code data-lang="html">type http2FrameHeader struct {
	valid bool // caller can access []byte fields in the Frame

	// Type is the 1 byte frame type. There are ten standard frame
	// types, but extension frame types may be written by WriteRawFrame
	// and will be returned by ReadFrame (as UnknownFrame).
	Type http2FrameType

	// Flags are the 1 byte of 8 potential bit flags per frame.
	// They are specific to the frame type.
	Flags http2Flags

	// Length is the length of the frame, not including the 9 byte header.
	// The maximum size is one byte less than 16MB (uint24), but only
	// frames up to 16KB are allowed without peer agreement.
	Length uint32

	// StreamID is which stream this frame is for. Certain frames
	// are not stream-specific, in which case this field is 0.
	StreamID uint32
}</code></pre><div class="t-redactor__text">StreamID — это номер запроса, к которому принадлежит фрейм (аналогично цветам из примеров выше: зеленый - 1, синий - 2).</div><h4  class="t-redactor__h4">Типы фреймов</h4><div class="t-redactor__text">Фреймы могут разных типов, например, данные, заголовки, настройки и т.д. За это отвечает поле Type:</div><pre class="t-redactor__highlightcode"><code data-lang="html">type http2FrameType uint8

const (
	http2FrameData           http2FrameType = 0x0
	http2FrameHeaders        http2FrameType = 0x1
	http2FramePriority       http2FrameType = 0x2
	http2FrameRSTStream      http2FrameType = 0x3
	http2FrameSettings       http2FrameType = 0x4
	http2FramePushPromise    http2FrameType = 0x5
	http2FramePing           http2FrameType = 0x6
	http2FrameGoAway         http2FrameType = 0x7
	http2FrameWindowUpdate   http2FrameType = 0x8
	http2FrameContinuation   http2FrameType = 0x9
	http2FramePriorityUpdate http2FrameType = 0x10
)</code></pre><div class="t-redactor__text">Чаще всего используются фреймы с данными и заголовками. Фреймы с данными описываются такой структурой:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">type http2DataFrame struct {
	http2FrameHeader // заголовок фрейма
	data []byte // сами данные
}</code></pre><div class="t-redactor__text">Фреймы с заголовками — такой:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">type http2HeadersFrame struct {
	http2FrameHeader // заголовок фрейма

	// Priority is set if FlagHeadersPriority is set in the FrameHeader.
	Priority http2PriorityParam // параметры приоритета

	headerFragBuf []byte // not owned // буфер с фрагментом заголовков
}</code></pre><h4  class="t-redactor__h4">Флаги</h4><div class="t-redactor__text">Во фрейм можно так же передать разные флаги, например, сказать, что этот фрейм последний в запросе. Флаги описываются так:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">type http2Flags uint8

// Frame-specific FrameHeader flag bits.
const (
	// Data Frame
	http2FlagDataEndStream http2Flags = 0x1
	http2FlagDataPadded    http2Flags = 0x8

	// Headers Frame
	http2FlagHeadersEndStream  http2Flags = 0x1
	http2FlagHeadersEndHeaders http2Flags = 0x4
	http2FlagHeadersPadded     http2Flags = 0x8
	http2FlagHeadersPriority   http2Flags = 0x20

	// Settings Frame
	http2FlagSettingsAck http2Flags = 0x1

	// Ping Frame
	http2FlagPingAck http2Flags = 0x1

	// Continuation Frame
	http2FlagContinuationEndHeaders http2Flags = 0x4

	http2FlagPushPromiseEndHeaders http2Flags = 0x4
	http2FlagPushPromisePadded     http2Flags = 0x8
)</code></pre><div class="t-redactor__text">В отличие от текстового HTTP/1, HTTP/2 имеет бинарный формат, что позволяет быстрее и проще его парсить.</div><h4  class="t-redactor__h4">Пример HTTP/2 запроса</h4><div class="t-redactor__text">Пусть у нас есть такой HTTP/1 запрос:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">POST /go HTTP/1
Content-Length: 24

{&quot;hello&quot;: &quot;@igorutine&quot;}</code></pre><div class="t-redactor__text">В HTTP/2 он будет выглядеть как поток следующий фреймов: Сначала будет идти HeadersFrame, то есть фрейм с заголовком нашего запроса:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">HeaderFrame{
 StreamId: 1
 Flags: FlagEndHeaders
 [
 :method: POST
 :path: /go
 Content-Length: 24
 ]
}</code></pre><div class="t-redactor__text">Так как фрейм с заголовком у нас один, можно сразу сказать серверу, что он последний, передав флаг FlagEndHeaders. </div><div class="t-redactor__text">Далее идет фрейм с данными:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">DataFrame{
	StreamId: 1,
	Flags: FlagEndStream,
	PayloadLength: 24,
	Data: {&quot;hello&quot;: &quot;...&quot;},
}</code></pre><div class="t-redactor__text">Совпадающие StreamId говорят, что наши HeadersFrame и DataFrame относятся к одному запросу, и так как этот DataFrame последний в запросе, передаем флажок FlagEndStream.</div><div class="t-redactor__text">Пусть ответ на наш запрос в HTTP/1 выглядит так:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">200 OK
Content-Length: 0
Date: Sat, 14 Feb 2026 16:34:56 GMT</code></pre><div class="t-redactor__text">В HTTP/2 он будет выглядеть так:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">HeadersFrame{
 StreamId: 1
 Flags: FlagEndHeaders | FlagEndStream
 [
 :status: 200
 Content-Length: 0
 Date: Sat, ...
 ]
}</code></pre><div class="t-redactor__text">Так как нам никакие данные не возвращают, ответ будет состоять только из HeadersFrame.</div><h4  class="t-redactor__h4">Сжатие</h4><div class="t-redactor__text">Пусть у нас есть два заголовка запросов:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">HeadersFrame{
 StreamId: 1
 Flags: FlagEndHeaders | FlagEndStream
 [
 :method: POST
 :path: /go
 Content-Length: 0
 ]
}

HeadersFrame{
 StreamId: 3
 Flags: FlagEndHeaders | FlagEndStream
 [
 :method: POST
 :path: /go
 Content-Length: 0
 ]
}</code></pre><div class="t-redactor__text">И ответов на эти запросы:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">HeadersFrame{
 StreamId: 1
 Flags: FlagEndHeaders | FlagEndStream
 [
 :status: 20
 Content-Length: 0
 Date: Sat, ...
 ]
}

HeadersFrame{
 StreamId: 3
 Flags: FlagEndHeaders | FlagEndStream
 [
 :status: 20
 Content-Length: 0
 Date: Sat, ...
 ]
}</code></pre><div class="t-redactor__text">Можно заметить, что заголовки этих разных запросов и заголовки ответов на эти запросы отличаются лишь номером самого запроса (StreamId). Постоянно гонять одни и те же данные не выгодно, поэтому в HTTP/2 существует сжатие данных.</div><div class="t-redactor__text">Во-первых, существует <a href="https://httpwg.org/specs/rfc7541.html#static.table.definition" target="_blank" rel="noreferrer noopener">статическая таблица</a>. Вот небольшая ее часть:</div><img src="https://static.tildacdn.com/tild6537-3437-4130-b732-373765623532/image.png"><div class="t-redactor__text">Теперь можно вместо того, чтобы пересылать, например, :method: POST, мы можем переслать байт со значением 3.</div><div class="t-redactor__text">Во-вторых, если чего-то нет в статической таблице, оно сжимается кодом Хаффмана по <a href="https://httpwg.org/specs/rfc7541.html#huffman.code" target="_blank" rel="noreferrer noopener">специальной таблице</a> и добавляется в динамическую таблицу. Вот небольшая часть этой специальной таблицы:</div><img src="https://static.tildacdn.com/tild3035-6462-4364-a137-373763633237/image.png"><div class="t-redactor__text">Рассмотрим пример работы этого сжатия:</div><img src="https://static.tildacdn.com/tild6563-3061-4663-b661-396332633761/image.png"><div class="t-redactor__text">Некоторые данные заголовка есть в статической таблице, например, :method: GET и :scheme: https. Другие же, например, пусть /resource сначала сжимаются, а потом добавляются в динамическую таблицу. В итоге мы получаем намного более легкий заголовок, чем изначально.</div><h2  class="t-redactor__h2">Внутреннее устройство gRPC</h2><div class="t-redactor__text">Как было упомянуто ранее, gRPC использует HTTP/2 в качестве транспортного протокола и Protocol Buffers в качестве протокола сериализации. Про второй поговорим чуть позже, а пока посмотрим как выглядят gRPC запросы в protobuf:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">package grpctest.v1

service HelloService {
    rpc SayHello(HelloRequest) returns (HelloResponse);
}</code></pre><div class="t-redactor__text">Здесь у нас есть имя нашего сервиса HelloWorld, в нем метод SayHello как раз описывает запрос: он принимает описание запроса HelloRequest и возвращает описание ответа HelloResponse.</div><div class="t-redactor__text">В HTTP/2 этот запрос будет выглядеть так:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">:method: POST
:path: /grpctest.v1.HelloService/SayHello
content-type: application/grpc
user-agent: grpc-go/1.52.0

body: [...]</code></pre><div class="t-redactor__text">В описаниях запросов и ответов на них мы как раз указываем, что пользователь отдает серверу, а что сервер ему возвращает:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message HelloRequest {
    string name = 1;
}

message HelloResponse {
    string message = 1;
}</code></pre><div class="t-redactor__text">У каждого поля помимо имени есть еще тип, так как protobuf типизуемый и индекс, который нужен во-первых, для обратной совместимости, а во-вторых, для парсинга байтов.</div><h2  class="t-redactor__h2">Protocol Buffers</h2><h3  class="t-redactor__h3">Преимущества Protocol Buffers</h3><div class="t-redactor__text">В сравнении с другими транспортными протоколами, такими как JSON и XML, у Protocol Buffers есть ряд преимуществ.</div><div class="t-redactor__text">Во-первых, размер. JSON и XML являются текстовыми, в то время как Protocol Buffers – бинарным, что делает его намного более легким. Вот сравнение размеров для этих транспортных протоколов:</div><img src="https://static.tildacdn.com/tild3130-6166-4637-a130-353237623930/image.png"><div class="t-redactor__text">Во-вторых, текстовые JSON и XML довольно сложно и долго парсить, а бинарный Protocol Buffers — наоборот, просто и быстро:</div><img src="https://static.tildacdn.com/tild3330-6561-4366-a465-336662366366/image.png"><div class="t-redactor__text">В-третьих, в JSON хоть и есть типизация, но не особо хорошая, а в XML есть уязвимости, например XML External Entity Attack. В то же время, в Protocol Buffers очень хорошая типизация и отсутствуют уязвимости, как в XML.</div><div class="t-redactor__text">Помимо всего этого, Protocol Buffers совместим с многими языками программирования, то есть по одному описанию можно генерировать код на разных языках, а так же по умолчанию поддерживает средства обратной совместимости.</div><h3  class="t-redactor__h3">Типы в Protocol Buffers</h3><h4  class="t-redactor__h4">Scalar Types</h4><div class="t-redactor__text">Это простые численные типы, строчки, булевы значения и наборы байтов. У каждого типа есть эквивалент в Go:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message ScalarValueTypesExample {
    double double_field = 1; // Go Type: float64
    float api_field = 2; // Go Type: float32
    int32 int32_field = 3; // Go Type: int32
    int64 int64_field = 4; // Go Type: int64
    uint32 uint32_field = 5; // Go Type: uint32
    uint64 uint64_field = 6; // Go Type: uint64
    sint32 sint32_field = 7; // Go Type: int32
    sint64 sint64_field = 8; // Go Type: int64
    fixed32 fixed32_field = 9; // Go Type: uint32
    fixed64 fixed64_field = 10; // Go Type: uint64
    sfixed32 sfixed32_field = 11; // Go Type: int32
    sfixed64 sfixed64_field = 12; // Go Type: int64
    bool bool_field = 13; // Go Type: bool
    string string_field = 14; // Go Type: string
    bytes bytes_field = 15; // Go Type: []byte
}</code></pre><h4  class="t-redactor__h4">Well-Known Types</h4><div class="t-redactor__text">Есть много типов, которые сделали в Google, которые часто используются на практике. Помимо новых типов, здесь есть обертки над скалярными типами, чтобы поддерживать nil. Это нужно, чтобы отличить ситуации, когда нам передали 0 потому, что там действительно 0 или потому, что значения нет и нам возвращают default value:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message WellKnownTypesExample {
    google.protobuf.Any any_field = 1;
    google.protobuf.Api api_field = 2;
    google.protobuf.Duration duration_field = 3;
    google.protobuf.Empty empty_field = 4;
    google.protobuf.FieldMask field_mask_field = 5;
    google.protobuf.SourceContext source_context_field = 6;
    google.protobuf.Struct struct_field = 7;
    google.protobuf.Timestamp timestamp_field = 8;
    google.protobuf.Type type_field = 9;
    google.protobuf.DoubleValue double_field = 10;
    google.protobuf.FloatValue float_field = 11;
    google.protobuf.Int64Value int64_field = 12;
    google.protobuf.UInt64Value uint64_field = 13;
    google.protobuf.Int32Value int32_field = 14;
    google.protobuf.UInt32Value uint32_field = 15;
    google.protobuf.BoolValue bool_field = 16;
    google.protobuf.StringValue string_field = 17;
    google.protobuf.BytesValue bytes_field = 18;
    google.protobuf.Value value_field = 19;
}</code></pre><h4  class="t-redactor__h4">Common Types</h4><div class="t-redactor__text">Здесь содержатся так же часто используемые типы, но уже более узконаправленные, например тип для денег или номера телефона:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message CommonTypes {
    google.type.Interval interval_field = 1;
    google.type.Date date_field = 2;
    google.type.DayOfWeek day_of_week_field = 3;
    google.type.TimeOfDay time_of_day_field = 4;
    google.type.LatLng lat_lng_field = 5;
    google.type.Money money_field = 6;
    google.type.PostalAddress postal_address_field = 7;
    google.type.Color color_field = 8;
    google.type.Month month_field = 9;
    google.type.CalendarPeriod calendar_period_field = 10;
    google.type.Decimal decimal_field = 11;
    google.type.Expr expr_field = 12;
    google.type.Fraction fraction_field = 13;
    google.type.LocalizedText localized_text_field = 14;
    google.type.PhoneNumber phone_number_field = 15;
}</code></pre><h4  class="t-redactor__h4">Optional</h4><div class="t-redactor__text">Как и обертки над скалярными типами, optional тип поддерживает nil:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message OptionalExample {
    int32 required_field = 1; // required (if not provided we get default
value: 0) Go type: int32
    optional int32 optional_field = 2; // optional (if not provided we get
null) Go type: *int32
}</code></pre><h4  class="t-redactor__h4">Перечисления</h4><div class="t-redactor__text">Первое поле перечисления должно быть всегда _UNSPECIFIED с индексом 0, чтобы пир генерациии язык смог вывести zero value:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">enum Corpus {
    CORPUS_UNSPECIFIED = 0;
    CORPUS_UNIVERSAL = 1;
    CORPUS_WEB = 2;
    CORPUS_IMAGES = 3;
    CORPUS_LOCAL = 4;
    CORPUS_NEWS = 5;
    CORPUS_PRODUCTS = 6;
    CORPUS_VIDEO = 7;
}</code></pre><h4  class="t-redactor__h4">Deprecated Fields</h4><div class="t-redactor__text">Поля можно помечать как deprеcated, тогда при генерации будет так же сгенерировано сообщение, что лучше не использовать это поле, так как оно deprecated. Это возможность как раз является одним из способов поддержки обратной совместимости:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message DeprecatedExample {
    // Use new_field instead old_field
    int32 old_field = 1 [deprecated = true];
    int64 new_field = 2;
}</code></pre><div class="t-redactor__text">Если сгенерировать код по этому protobuf, получим код с таким комментарием:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">type DeprecatedExample struct {
    state protoimpl.MessageState
    sizeCache protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields
    // Use new_field instead old_field
    //
    // Deprecated: Do not use.
    OldField int32
`protobuf:&quot;varint,1,opt,name=old_field,json=oldField,proto3&quot;
json:&quot;old_field,omitempty&quot;`
    NewField int64
`protobuf:&quot;varint,2,opt,name=new_field,json=newField,proto3&quot;
json:&quot;new_field,omitempty&quot;`
}</code></pre><h4  class="t-redactor__h4">Reserved Fields</h4><div class="t-redactor__text">Поля и индексы можно зарезервировать для реализации в будущем. Резервировать индексы можно сразу в каком-то диапазоне. Это нужно когда, например, у вас есть функционал, который вы хотите поддерживать, но имплементировать его сейчас не хотите. Если попытаться объявить зарезервированное поле, будет ошибка:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">enum Foo {
    reserved 2, 15, 9 to 11, 40 to max;
    reserved &quot;FOO&quot;, &quot;BAR&quot;;
    FOO = 2;
}</code></pre><pre class="t-redactor__highlightcode"><code data-lang="{$la}">api/examples/enum.proto: Enum value &quot;FOO&quot; uses reserved number 2.
api/examples/enum.proto:22:5: Enum value &quot;FOO&quot; is reserved.</code></pre><h4  class="t-redactor__h4">Вложенные сообщения и массивы</h4><div class="t-redactor__text">Внутри одного сообщения можно объявить другой. Чтобы создать массив элементов, нужно использовать ключевое слово repeated:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message SearchResponse {
    message Result {
        string url = 1;
        string title = 2;
        repeated string snippets = 3;
    }
    repeated Result result = 1;
}</code></pre><h4  class="t-redactor__h4">Oneof</h4><div class="t-redactor__text">oneof позволяет сказать, что поле может быть каким-то из типов:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message Stock {
    // Stock-specific data
}
message Currency {
    // Currency-specific data
}
message ChangeNotification {
    int32 id = 1;
    oneof instrument {
    Stock stock = 2;
    Currency currency = 3;
    }
}</code></pre><h4  class="t-redactor__h4">Map</h4><div class="t-redactor__text">Стандартные и всем знакомые мапы:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">message GetProjectResponse {
    message Project {
    // data
    }
    
    map&lt;string, Project&gt; projects = 1;
}</code></pre><h3  class="t-redactor__h3">Сериализация</h3><div class="t-redactor__text">Рассмотрим пример такого описания и как именно protobuf превращает его в последовательность байтов:</div><img src="https://static.tildacdn.com/tild6432-3562-4438-a465-303931653237/image.png"><div class="t-redactor__text">Индекс поля и его тип объединяются в одно число и это первый байт. Например поле с индексом 1 (00001 в двоичном виде) и типом string (010 в двоичном формате) становятся байтом 0a. То же самое с остальными полями.</div><div class="t-redactor__text">Далее кодируются данные: у строк сначала записывается длинна, а далее сама строка. С числами интереснее: они разбиваются на группы по 7 бит, к каждой группе в начала добавляется флаг — 1, если есть группы далее и 0, если эта группа последняя. Далее полученные байты записываются в Little-Endian. Такая схема позволяет числам занимать столько байтов, сколько им нужно: если в поле int64 будет записано 4, оно займет 1 байт, а не 8.</div><h3  class="t-redactor__h3">Protobuf и gRPC</h3><div class="t-redactor__text">Теперь зная, как работает HTTP/2, и что он позволяет отправлять данные потоками, можно реализовать следующие вещи:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">// Один запрос, один ответ:
rpc Send(Message) returns (Result);

// Один запрос, поток ответов:
rpc Send(Message) returns (stream Result);

// Поток запросов, один ответ:
rpc Send(stream Message) returns (Result);

// Поток запросов, поток ответов:
rpc Send(stream Message) returns (stream Result);</code></pre><div class="t-redactor__text">Потоки в gRPC это те же потоки из HTTP/2.</div><h3  class="t-redactor__h3">gRPC workflow</h3><div class="t-redactor__text">Сначала мы пишем proto-декларацию: описываем сервисы, запросы, сообщения. Далее, генерируем protobuf реализацию и реализацию сервера и клиента на нужном нам языке. Нам сгенерировали большую часть кода, осталось самому дописать нужное нам в реализации сервера и клиента.</div><h3  class="t-redactor__h3">Прямая и обратная совместимость в protobuf</h3><div class="t-redactor__text">Допустим, у нас была какая-то версия контракта. Если не изменять его, а только добавлять в него что-то новое, то есть два сценария:</div><div class="t-redactor__text"><ul><li data-list="bullet">Прямая совместимость: если у клиента новая версия, а у сервера старая, ничего не ломается, потому клиент просто не получит новые поля.</li><li data-list="bullet">Обратная совместимость: если у клиента старая версия, а сервера новая, так же ничего не ломается, потому что клиент просто не будет обрабатывать новые поля.</li></ul></div><div class="t-redactor__text">Самое главное правило для поддержки совместимости, это не менять уже существующее, а только добавлять.</div><h3  class="t-redactor__h3">Protobuf на практике</h3><div class="t-redactor__text">Для начала посмотрим, как сгенерировать protobuf реализацию и сервер с клиентом по proto-декларации.</div><div class="t-redactor__text">Напишем, нашу proto-декларацию в файле .proto:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">syntax = &quot;proto3&quot;;

package user.v1;

option go_package = &quot;example.com/project/gen/user/v1;userv1&quot;;

service UserService {
    rpc GetUser(GetUserRequest) returns (GetUserResponse);
}

message GetUserRequest {
    string id = 1;
}

message GetUserResponse {
    string id = 1;
    string name = 2;
}</code></pre><div class="t-redactor__text">Сначала мы указываем версию protobuf, в нашем случае это proto3. Далее мы указываем пакет user.v1 на уровне protobuf, а не Go. Следующим мы указывает пакет на уровне Go, а потом идет наша привычная реализация сервиса, запросов и сообщений.</div><div class="t-redactor__text">Чтобы сгенерировать по этому описанию реализацию и сервер с клиентом будет использовать утилиту protoc. Для начала нужно ее установить.</div><div class="t-redactor__text">Protoc для генерации protobuf реализации:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">go install google.golang.org/protobuf/cmd/protoc-gen-go@latest</code></pre><div class="t-redactor__text">Protoc для генерации gRPC сервера и клиента:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest</code></pre><div class="t-redactor__text">Команда для генерации кода довольно громоздкая, так что разберем ее по частям:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">protoc \
 -I {{.PROTO_DIR}} \
 --go_out={{.GEN_DIR}} \
 --go_opt=paths=source_relative \
 --go-grpc_out={{.GEN_DIR}} \
 --go-grpc_opt=paths=source_relative \
 {{.PROTO_DIR}}/user/v1/user.proto</code></pre><div class="t-redactor__text">Флагом -I указываем директорию, в которой находятся наши .proto файлы.</div><div class="t-redactor__text">Далее, флагом --go_out указываем путь до директории, в которй надо сгенерировать protobuf реализацию, а опцией --go_opt=paths=source_relative, что путь относительный.</div><div class="t-redactor__text">Затем, флагом --go-grpc_out указываем путь до директории, в которой надо сгенерировать сервер и клиент, опция --go-grpc_opt=paths=source_relative так же говорит, что путь относительный.</div><div class="t-redactor__text">Наконец, указываем входные .proto файлы.</div><div class="t-redactor__text">В сгенерированной protobuf реализации можно найти и наши описания сообщений:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">type GetUserRequest struct {
    state protoimpl.MessageState
    sizeCache protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields
 
    Id string `protobuf:&quot;bytes,1,opt,name=id,proto3&quot; json:&quot;id,omitempty&quot;`
}

type GetUserResponse struct {
    state protoimpl.MessageState
    sizeCache protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields
 
    Id string `protobuf:&quot;bytes,1,opt,name=id,proto3&quot; json:&quot;id,omitempty&quot;`
    Name string `protobuf:&quot;bytes,2,opt,name=name,proto3&quot;
json:&quot;name,omitempty&quot;`
}</code></pre><div class="t-redactor__text">Готовый сериализатор/десериализатор. Вот небольшая его часть:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">var file_user_v1_user_proto_rawDesc = []byte{
 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73,
0x65, 0x72, 0x2e, 0x70,
 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76,
0x31, 0x22, 0x20, 0x0a,
 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12,
...
}</code></pre><div class="t-redactor__text">Полный файл можно либо сгенерировать самому, либо найти <a href="https://github.com/IgorWalther/videos/blob/master/0019_grpc/01_protoc/gen/user/v1/user.pb.go" target="_blank" rel="noreferrer noopener">здесь</a>. <a href="https://github.com/IgorWalther/videos/blob/master/0019_grpc/01_protoc/gen/user/v1/user_grpc.pb.go" target="_blank" rel="noreferrer noopener">В соседнем файле</a> находится реализация сервера и клиента.</div><h2  class="t-redactor__h2">Пишем полноценное приложение</h2><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0019_grpc/02_orders</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Продолжить чтение</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=594</div></td></tr></tbody><colgroup><col style="max-width:254px;min-width:254px;width:254px;"><col style="max-width:254px;min-width:254px;width:254px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Секреты Concurrency: от процессора до атомиков в Go</title>
      <link>https://igoroutine.courses/free-materials/1rh2n86ax1-sekreti-concurrency-ot-protsessora-do-at</link>
      <amplink>https://igoroutine.courses/free-materials/1rh2n86ax1-sekreti-concurrency-ot-protsessora-do-at?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 18:21:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <category>Concurrency</category>
      <enclosure url="https://static.tildacdn.com/tild3338-6430-4132-b331-636266336533/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Секреты Concurrency: от процессора до атомиков в Go</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3338-6430-4132-b331-636266336533/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="9BElnO_VpWU"
    data-vk="oid=-230033131&id=456239041&hd=4"
    data-rutube="4f8a382b150f7b54c76a422292c63460"
    data-dzen="oaUa4zhMJAAA">   
  <amp-youtube
    data-videoid="9BElnO_VpWU"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">На этом открытом уроке мы разберем многопоточность от «А» до «Я» — от устройства транзисторов и законов физики до практического применения атомиков в Go для написания высокопроизводительного кода.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>История и эволюция:</strong> путь от пакетной обработки задач до современных многоядерных систем и вытесняющей многозадачности.</li><li data-list="bullet"><strong>Железо под капотом:</strong> почему одно ядро больше не растет в частоте, как работают протоколы когерентности кэша (MESI) и зачем процессору нужны многопоточные инструкции.</li><li data-list="bullet"><strong>Атомики в Go:</strong> глубокий разбор пакета sync/atomic, реализация собственных атомиков на ассемблере и понимание операции Compare-and-Swap (CAS).</li><li data-list="bullet"><strong>Проблемы синхронизации:</strong> что такое Data Race на уровне кэш-линий процессора и как эвристики компилятора могут замедлить ваш код в 4 раза.</li><li data-list="bullet"><strong>Параллелизм на практике:</strong> пишем и бенчмаркаем параллельное сложение матриц, разбираем закон Амдала и ищем "золотую середину" в количестве потоков.</li><li data-list="bullet"><strong>Текущие абстракции:</strong> как ложные разделения (False Sharing) и кэш-контеншн влияют на реальные бизнес-приложения и как от этого защититься с помощью падинга.</li></ul><br />В итоге у тебя будет фундаментальное понимание того, как на самом деле исполняется конкурентный код: ты узнаешь, за что мы платим при распараллеливании задач и как использовать ресурсы процессора максимально эффективно, избегая типичных ловушек абстракций</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/blob/master/0013_concurrency/003_matrix/main_test.go#L59</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Ссылка на видео про оси</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=360</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Ссылка на видео про память</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=295</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Ссылка на видео про процессора</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=294</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Ссылка на видео про дженерики</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=321</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Ссылка на видео про SIMD</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/72</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="6" data-column="0"><div class="t-table__cell-content">SMT Hyperthreading</div></td><td class="t-table__cell" data-row="6" data-column="1"><div class="t-table__cell-content">https://en.wikipedia.org/wiki/Hyper-threading</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="7" data-column="0"><div class="t-table__cell-content">Материал про false sharing</div></td><td class="t-table__cell" data-row="7" data-column="1"><div class="t-table__cell-content">https://habr.com/ru/companies/intel/articles/143446/</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Устройство операционных систем: от ядра и системных вызовов до Go</title>
      <link>https://igoroutine.courses/free-materials/kk7m9fzim1-ustroistvo-operatsionnih-sistem-ot-yadra</link>
      <amplink>https://igoroutine.courses/free-materials/kk7m9fzim1-ustroistvo-operatsionnih-sistem-ot-yadra?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 18:24:00 +0300</pubDate>
      <category>System Design</category>
      <enclosure url="https://static.tildacdn.com/tild3737-3138-4166-b561-316665336463/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Устройство операционных систем: от ядра и системных вызовов до Go</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3737-3138-4166-b561-316665336463/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="1uNill38z1I"
    data-vk="oid=-230033131&id=456239040&hd=4"
    data-rutube="82184805845090941736082ec77a1fb5"
    data-dzen="oaUalYwAJAAA">   
  <amp-youtube
    data-videoid="1uNill38z1I"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">На этом уроке мы разберем устройство операционных систем — как ядро управляет железом, как программы общаются с ОС через системные вызовы и как все это реализовано внутри языка Go.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Архитектура ядра:</strong> разница между монолитными и микроядерными системами, а также устройство ядра Linux.</li><li data-list="bullet"><strong>Процессы и потоки:</strong> глубокий разбор абстракций ОС, структуры адресного пространства (Stack, Heap, Text) и контекст-свитчинга.</li><li data-list="bullet"><strong>Системные вызовы (Syscalls):</strong> как происходит переход из User Space в Kernel Space на уровне регистров и прерываний процессора.</li><li data-list="bullet"><strong>Планировщик Go vs ОС:</strong> как рантайм Go экономит ресурсы системы, управляя тысячами горутин на ограниченном количестве потоков (модель GMP).</li><li data-list="bullet"><strong>Сигналы и прерывания:</strong> как ОС общается с вашим приложением и как Go обрабатывает сигналы для Graceful Shutdown или работы GC.</li><li data-list="bullet"><strong>Низкоуровневые примеры:</strong> пишем системные вызовы на ассемблере и разбираем, как Go обращается к ядру напрямую.</li></ul><br />В итоге у тебя будет четкое понимание фундаментальных принципов работы ОС: ты узнаешь, что происходит под капотом обычного fmt.Println или открытия файла, и научишься писать более производительный код, понимая ограничения и возможности системы.</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/igoroutine-courses/the_nature_of_computer_science/blob/master/lectures/lecture_5/syscall_perfomance_test.go</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Comparing Linux and Minix</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://lwn.net/Articles/220255/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Видео про профилирование</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://youtu.be/3R6Lke2aGE0</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Планировщик в Go</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://habr.com/ru/articles/891426/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Видео про ассемблер</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://youtu.be/iflItE2PUAU</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Видео про сборщик мусора</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=492</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="6" data-column="0"><div class="t-table__cell-content">Видео про память</div></td><td class="t-table__cell" data-row="6" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine_chat/295</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Создатели Go ОШИБЛИСЬ, создавая ЭТОТ CONCURRENCY примитив</title>
      <link>https://igoroutine.courses/free-materials/z26ev64e81-sozdateli-go-oshiblis-sozdavaya-etot-con</link>
      <amplink>https://igoroutine.courses/free-materials/z26ev64e81-sozdateli-go-oshiblis-sozdavaya-etot-con?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 18:31:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <category>Computer Science</category>
      <enclosure url="https://static.tildacdn.com/tild3335-6632-4331-a131-323931343736/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Создатели Go ОШИБЛИСЬ, создавая ЭТОТ CONCURRENCY примитив</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3335-6632-4331-a131-323931343736/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="RGU3GWwm1p0"
    data-vk="oid=-230033131&id=456239027&hd=4"
    data-rutube="290952394990d3a20c09bc53563d944b"
    data-dzen="oaUYNW4IIAAA">    
  <amp-youtube
    data-videoid="RGU3GWwm1p0"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">Мы разберем одну из самых коварных ошибок в стандартной библиотеке Go — неправильную реализацию примитива sync.Once. Ты узнаешь, почему наивный подход с атомиками не работает и как формальные модели помогают находить баги в многопоточном коде.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Примитив sync.Once:</strong> зачем он нужен, как гарантирует выполнение функции ровно один раз и типичные сценарии использования (закрытие ресурсов, инициализация).</li><li data-list="bullet"><strong>Разбор «ошибки»:</strong> пишем свою реализацию Once на основе Compare-and-Swap (CAS) и выясняем, почему она кажется рабочей, но нарушает спецификацию языка.</li><li data-list="bullet"><strong>Линеаризуемость и инварианты:</strong> что это такое и почему выход из метода Do до завершения функции в другом потоке — это критический баг.</li><li data-list="bullet"><strong>Инструменты проверки:</strong> используем фреймворк Porcupine для визуализации и поиска ошибок в конкурентных исполнениях.</li><li data-list="bullet"><strong>Корректная реализация:</strong> изучаем исходный код sync.Once из стандартной библиотеки и понимаем роль мьютекса в обеспечении гарантий «Happens Before».</li></ul><br />В итоге у тебя будет глубокое понимание тонкостей Concurrency в Go: ты научишься видеть скрытые проблемы синхронизации, которые не ловит даже встроенный race detector, и поймешь, как проектировать примитивы с железными гарантиями корректности.</div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>ТОП-1 Golang Concurrency Pattern</title>
      <link>https://igoroutine.courses/free-materials/6zmi95u8t1-top-1-golang-concurrency-pattern</link>
      <amplink>https://igoroutine.courses/free-materials/6zmi95u8t1-top-1-golang-concurrency-pattern?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 18:36:00 +0300</pubDate>
      <category>Продвинутый Go</category>
      <category>Concurrency</category>
      <enclosure url="https://static.tildacdn.com/tild6632-3063-4231-a166-666339663938/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>ТОП-1 Golang Concurrency Pattern</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild6632-3063-4231-a166-666339663938/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="qGca_8VldmE"
    data-vk="oid=-230033131&id=456239017&hd=4"
    data-rutube="a8f24d6f1369dbf3faaa907937f9ddc1"
    data-dzen="oaUYdpF8IAAA">    
  <amp-youtube
    data-videoid="qGca_8VldmE"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">Разберем самый популярный многопоточный паттерн в Go — <strong>Worker Pool</strong>. Ты узнаешь, как правильно распределять нагрузку между горутинами, избегать утечек памяти и применять этот паттерн для решения реальных задач с собеседований в BigTech.<br /><br /><strong>В этом видео:</strong><br /><br /><ul><li data-list="bullet"><strong>Механика Worker Pool:</strong> как устроено распределение задач из входного канала и сбор результатов в результирующий.</li><li data-list="bullet"><strong>Разбор типичных ошибок:</strong> почему новички часто получают Deadlock, забывая закрывать каналы, и как этого избежать.</li><li data-list="bullet"><strong>Утечки горутин (Goroutine Leaks):</strong> почему важно обрабатывать отмену контекста (context.Done()) при записи в результирующий канал.</li><li data-list="bullet"><strong>Универсальная реализация:</strong> пишем масштабируемый Worker Pool на дженериках, который может работать с любыми типами данных.</li><li data-list="bullet"><strong>Реальный кейс из Ozon:</strong> пошаговое решение задачи по многопоточному обходу списка URL с обработкой ошибок и ограничением количества одновременных запросов.</li><li data-list="bullet"><strong>Смежные паттерны:</strong> коротко о Generator, Fan-In и Fan-Out, и их месте в экосистеме Go Concurrency.</li></ul><br />В итоге у тебя будет готовый шаблон и глубокое понимание того, как писать надежный конкурентный код: от правильного использования sync.WaitGroup до тонкостей работы с select и контекстами.</div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Эта задача валит всех на собеседованиях по Go Concurrency в BigTech</title>
      <link>https://igoroutine.courses/free-materials/farvsxbfh1-eta-zadacha-valit-vseh-na-sobesedovaniya</link>
      <amplink>https://igoroutine.courses/free-materials/farvsxbfh1-eta-zadacha-valit-vseh-na-sobesedovaniya?amp=true</amplink>
      <pubDate>Thu, 26 Mar 2026 18:38:00 +0300</pubDate>
      <category>Собеседования</category>
      <category>Concurrency</category>
      <enclosure url="https://static.tildacdn.com/tild3965-3764-4037-b237-343266653661/telegram-cloud-photo.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Эта задача валит всех на собеседованиях по Go Concurrency в BigTech</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3965-3764-4037-b237-343266653661/telegram-cloud-photo.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="Odntyh2XBz4"
    data-vk="oid=-230033131&id=456239045&hd=4"
    data-rutube="bdcc892352fadda14900bf4da3b3a684"
    data-dzen="oaUaeGR0KAAA?">    
  <amp-youtube
    data-videoid="Odntyh2XBz4"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">Разберем одну из самых популярных и коварных задач на собеседованиях по Go Concurrency в BigTech — вызов «долгой» функции с дедлайном. Ты узнаешь, как правильно использовать каналы, контекст и селекты, чтобы не допустить утечек горутин и корректно обрабатывать ошибки.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Постановка задачи:</strong> как реализовать ограничение времени выполнения (Timeout/Deadline) для функции, которую нельзя отменить напрямую.</li><li data-list="bullet"><strong>Работа с каналами:</strong> превращаем синхронный вызов в асинхронный с помощью паттерна Future и возвращаем канал только для чтения.</li><li data-list="bullet"><strong>Оператор Select:</strong> как правильно организовать ожидание результата и сигнала об окончании времени, используя time.After.</li><li data-list="bullet"><strong>Нюансы закрытия каналов:</strong> разбираем тонкий хак с получением Zero Value при чтении из закрытого канала для возврата результата или ошибки.</li><li data-list="bullet"><strong>Пишем свой Context:</strong> пошаговая реализация упрощенного аналога стандартного пакета context с методами Done(), Err() и функцией отмены.</li><li data-list="bullet"><strong>Generic-реализация:</strong> масштабируем решение задачи для работы с любыми типами данных с помощью дженериков.</li></ul><br />В итоге у тебя будет четкий алгоритм решения задачи, которая проверяет сразу несколько ключевых аспектов Go: от семантики каналов и многопоточности до понимания устройства стандартных абстракций языка.</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/videos/tree/master/0017_slow_function_concurrency</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Видео про дженерики</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=321</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Видео про оси (сигналы)</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://t.me/igoroutine/89?comment=360</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Почему Go-ассемблер и векторизация могут быть полезны: идея для ускорения</title>
      <link>https://igoroutine.courses/free-materials/eud2d54di1-pochemu-go-assembler-i-vektorizatsiya-mo</link>
      <amplink>https://igoroutine.courses/free-materials/eud2d54di1-pochemu-go-assembler-i-vektorizatsiya-mo?amp=true</amplink>
      <pubDate>Mon, 06 Apr 2026 14:35:00 +0300</pubDate>
      <category>Computer Science</category>
      <category>Продвинутый Go</category>
      <enclosure url="https://static.tildacdn.com/tild6331-6361-4636-a139-653934663138/image.png" type="image/png"/>
      <turbo:content><![CDATA[<header><h1>Почему Go-ассемблер и векторизация могут быть полезны: идея для ускорения</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild6331-6361-4636-a139-653934663138/image.png"/></figure><div class="t-redactor__embedcode"><div id="custom-article-toc"></div>

<style>
/* --- Базовые стили основного оглавления --- */
#custom-article-toc:empty { display: none; }

.custom-toc-container {
    background: rgba(26, 28, 40, 0.6); 
    border-radius: 12px;
    padding: 24px;
    font-family: 'ShareTechMono', sans-serif;
    margin-bottom: 24px;
}

.custom-toc-title {
    color: #ffffff;
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 16px;
    font-family: 'ShareTechMono', sans-serif;
}

.custom-toc-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* --- Стили для ссылок --- */
.custom-toc-link {
    color: rgba(255, 255, 255, 0.85) !important;
    text-decoration: none !important;
    font-size: 15px;
    transition: color 0.2s ease, transform 0.2s ease;
    display: flex; 
    align-items: center; 
    line-height: 1.4;
    cursor: pointer;
    font-family: 'ShareTechMono', sans-serif;
}

.custom-toc-link:hover {
    color: #F4721E !important; 
    transform: translateX(2px); 
}

/* --- Иерархия --- */
.custom-toc-level-h1 { margin-left: 0; font-weight: 600; color: #ffffff !important; }
.custom-toc-level-h2 { margin-left: 0; }
.custom-toc-level-h3 { margin-left: 8px; font-size: 14px; color: rgba(255, 255, 255, 0.6) !important; }

/* --- Скрытый блок с h4 --- */
.custom-toc-h4-wrapper {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 0; 
    overflow: hidden;
    transition: max-height 0.4s ease-in-out;
}

.custom-toc-h4-wrapper.is-open {
    max-height: 1000px; 
    margin-top: 6px; 
}

.custom-toc-level-h4 { 
    margin-left: 16px; 
    font-size: 13px; 
    color: rgba(255, 255, 255, 0.4) !important; 
}

/* --- Иконка стрелочки для h3 --- */
.custom-toc-toggle-icon {
    width: 14px;
    height: 14px;
    margin-right: 6px;
    transition: transform 0.3s ease;
    opacity: 0.7;
    flex-shrink: 0;
}

.custom-toc-level-h3.has-children.is-open .custom-toc-toggle-icon {
    transform: rotate(90deg); 
    color: #F4721E;
}


/* =======================================================
   СТИЛИ ДЛЯ ПЛАВАЮЩЕГО ПОПАПА (ПРАВЫЙ НИЖНИЙ УГОЛ)
   ======================================================= */
/* Кнопка-триггер */
.custom-toc-float-btn {
    position: fixed;
    bottom: 20px;
    right: 20px;
    background: #F4721E; /* Твой оранжевый */
    color: #1A1C28; /* Темный текст для контраста */
    padding: 12px 20px;
    border-radius: 8px;
    font-family: 'ShareTechMono', sans-serif;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    z-index: 9998;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    display: flex;
    align-items: center;
    gap: 8px;
    transition: transform 0.2s ease, background 0.2s ease;
}

.custom-toc-float-btn:hover {
    transform: translateY(-2px);
    background: #e06010;
}

.custom-toc-float-btn svg {
    width: 18px;
    height: 18px;
}

/* Само окно оглавления */
.custom-toc-float-window {
    position: fixed;
    bottom: 70px; /* Чуть выше кнопки */
    right: 20px;
    width: 320px;
    max-height: calc(100vh - 120px); /* Чтобы не вылезало за экран */
    background: rgba(18, 20, 30, 0.95); /* Чуть темнее основного фона */
    border: 1px solid rgba(244, 114, 30, 0.3); /* Оранжевая обводка */
    border-radius: 12px;
    padding: 20px;
    z-index: 9999;
    box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    overflow-y: auto;
    
    /* Анимация появления */
    opacity: 0;
    transform: translateY(20px);
    pointer-events: none;
    transition: all 0.3s ease;
}

.custom-toc-float-window.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* Кастомный скроллбар для окна */
.custom-toc-float-window::-webkit-scrollbar {
    width: 4px;
}
.custom-toc-float-window::-webkit-scrollbar-thumb {
    background: rgba(244, 114, 30, 0.5); 
    border-radius: 4px;
}

/* Шапка попапа с крестиком */
.custom-toc-float-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    padding-bottom: 12px;
}

.custom-toc-float-header .custom-toc-title {
    margin-bottom: 0; /* Убираем отступ, чтобы было в линию с крестиком */
    font-size: 16px;
}

.custom-toc-close-btn {
    background: none;
    border: none;
    color: rgba(255,255,255,0.6);
    cursor: pointer;
    padding: 4px;
    display: flex;
    transition: color 0.2s ease;
}
.custom-toc-close-btn:hover {
    color: #F4721E;
}

/* Показываем плавающую кнопку только на больших экранах */
/*@media screen and (max-width: 960px) {*/
/*    .custom-toc-float-btn, */
/*    .custom-toc-float-window {*/
/*        display: none !important;*/
/*    }*/
/*}*/
</style>

<script>
(function() {
    let attempts = 0;
    
    const tocInterval = setInterval(() => {
        attempts++;
        const tocWrapper = document.getElementById('custom-article-toc');
        
        if (tocWrapper && !tocWrapper.hasAttribute('data-built')) {
            const articleBody = tocWrapper.closest('.js-feed-post-text') || tocWrapper.closest('.t-redactor__tte-view');
            
            if (articleBody) {
                const headings = articleBody.querySelectorAll('h1, h2, h3, h4');
                
                if (headings.length > 0) {
                    const tocContainer = document.createElement('div');
                    tocContainer.className = 'custom-toc-container';
                    
                    const tocTitle = document.createElement('div');
                    tocTitle.className = 'custom-toc-title';
                    tocTitle.textContent = 'Содержание статьи';
                    tocContainer.appendChild(tocTitle);

                    const tocList = document.createElement('div');
                    tocList.className = 'custom-toc-list';

                    let currentH3Item = null;
                    let currentH4Wrapper = null;

                    headings.forEach((heading) => {
                        if (!heading.id) {
                            heading.id = 'heading-' + Math.random().toString(36).substr(2, 9);
                        }

                        const tagName = heading.tagName.toLowerCase();
                        
                        if (tagName === 'h3') {
                            const listItem = document.createElement('div');
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-h3';
                            link.textContent = heading.textContent.trim();
                            
                            link.targetHeading = heading;
                            
                            listItem.appendChild(link);
                            tocList.appendChild(listItem);
                            
                            currentH3Item = listItem;
                            currentH4Wrapper = null; 
                            
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                
                                if (link.classList.contains('has-children')) {
                                    const wrapper = link.nextElementSibling;
                                    
                                    if (e.target.closest('.custom-toc-toggle-icon')) {
                                        link.classList.toggle('is-open');
                                        wrapper.classList.toggle('is-open');
                                        return;
                                    }
                                    
                                    if (!link.classList.contains('is-open')) {
                                        link.classList.add('is-open');
                                        wrapper.classList.add('is-open');
                                    } else {
                                        link.targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                        // Опционально: закрывать попап при клике
                                        // document.querySelector('.custom-toc-float-window').classList.remove('is-visible');
                                    }
                                } else {
                                    link.targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                }
                            });
                            
                        } else if (tagName === 'h4' && currentH3Item) {
                            if (!currentH4Wrapper) {
                                currentH4Wrapper = document.createElement('div');
                                currentH4Wrapper.className = 'custom-toc-h4-wrapper';
                                currentH3Item.appendChild(currentH4Wrapper);
                                
                                const parentH3Link = currentH3Item.querySelector('.custom-toc-level-h3');
                                parentH3Link.classList.add('has-children');
                                
                                const chevronSvg = '<svg class="custom-toc-toggle-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>';
                                parentH3Link.insertAdjacentHTML('afterbegin', chevronSvg);
                            }
                            
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-h4';
                            link.textContent = heading.textContent.trim();
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            });
                            currentH4Wrapper.appendChild(link);
                            
                        } else {
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-' + tagName;
                            link.textContent = heading.textContent.trim();
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            });
                            tocList.appendChild(link);
                            
                            currentH3Item = null;
                            currentH4Wrapper = null;
                        }
                    });

                    tocContainer.appendChild(tocList);
                    tocWrapper.appendChild(tocContainer);
                    
                    
                    /* =======================================================
                       СОЗДАЕМ ПЛАВАЮЩИЙ UI
                       ======================================================= */
                    
                    // Кнопка
                    const floatBtn = document.createElement('div');
                    floatBtn.className = 'custom-toc-float-btn';
                    floatBtn.innerHTML = `
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
                        Содержание
                    `;
                    document.body.appendChild(floatBtn);

                    // Окно попапа
                    const floatWindow = document.createElement('div');
                    floatWindow.className = 'custom-toc-float-window';
                    
                    // Шапка окна
                    const floatHeader = document.createElement('div');
                    floatHeader.className = 'custom-toc-float-header';
                    floatHeader.innerHTML = `
                        <div class="custom-toc-title">Содержание статьи</div>
                        <button class="custom-toc-close-btn" aria-label="Закрыть">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
                        </button>
                    `;
                    
                    // Клонируем собранный список ссылок
                    const floatList = tocList.cloneNode(true);
                    
                    // Оживляем клики в клонированном списке
                    floatList.addEventListener('click', function(e) {
                        const link = e.target.closest('.custom-toc-link');
                        if (!link) return;
                        
                        e.preventDefault();
                        
                        // Ищем оригинальный заголовок в статье по тексту
                        const linkText = link.textContent.trim();
                        let targetHeading = null;
                        Array.from(headings).forEach(h => {
                            if (h.textContent.trim() === linkText) targetHeading = h;
                        });

                        if (!targetHeading) return;

                        if (link.classList.contains('has-children')) {
                            const wrapper = link.nextElementSibling;
                            if (e.target.closest('.custom-toc-toggle-icon')) {
                                link.classList.toggle('is-open');
                                wrapper.classList.toggle('is-open');
                                return;
                            }
                            if (!link.classList.contains('is-open')) {
                                link.classList.add('is-open');
                                wrapper.classList.add('is-open');
                            } else {
                                targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            }
                        } else {
                            targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                        }
                    });

                    floatWindow.appendChild(floatHeader);
                    floatWindow.appendChild(floatList);
                    document.body.appendChild(floatWindow);

                    // Логика открытия/закрытия
                    floatBtn.addEventListener('click', () => {
                        floatWindow.classList.toggle('is-visible');
                    });
                    
                    floatWindow.querySelector('.custom-toc-close-btn').addEventListener('click', () => {
                        floatWindow.classList.remove('is-visible');
                    });


                    tocWrapper.setAttribute('data-built', 'true');
                    tocWrapper.id = 'custom-article-toc-ready';
                }
            }
            clearInterval(tocInterval);
        }
        
        if (attempts > 20) {
            clearInterval(tocInterval);
        }
    }, 500);
})();
</script></div><div class="t-redactor__text">Когда речь заходит о производительности в Go, большинство разработчиков полагаются на стандартные библиотеки и встроенные инструменты оптимизации. Но компилятор Go не всегда генерирует оптимальный машинный код. В таких случаях можно взять дело в свои руки и использовать ассемблерные инструкции для ускорения критически важных участков.</div><div class="t-redactor__text">Привет! Меня зовут<a href="/" target="_blank" rel="noreferrer noopener"> </a>Игорь Панасюк, я работаю в Яндекс, преподаю в ИТМО, а также в свободное время выступаю на конференциях, делюсь опытом в соцсетях и помогаю развитию Go-сообщества, веду <a href="https://t.me/igoroutine">Telegram-канал</a> и <a href="https://www.youtube.com/@igoroutine" target="_blank" rel="noreferrer noopener">YouTube-канал</a>. В этой статье по мотивам моего доклада для Golang Conf мы разберём, как с помощью Go-ассемблера можно ускорять код, используя векторные инструкции и аппаратные оптимизации.</div><div class="t-redactor__text">Ассемблер может показаться сложным и пугающим, но он открывает большие возможности для работы с низкоуровневыми оптимизациями. Готовы разобраться, как это работает? Тогда погнали!</div><h3  class="t-redactor__h3">Мотивация</h3><pre class="t-redactor__highlightcode"><code data-lang="{$la}">func SliceContainsV0(s []uint8, target uint8) bool {

return slices.Contains(s, target)

}

func SliceContainsV1(s []uint8, target uint8) bool</code></pre><img src="https://static.tildacdn.com/tild3166-3061-4265-b839-306431646236/e5fd93705ac55229fefe.png"><div class="t-redactor__text">Есть две функции:</div><div class="t-redactor__text"><ol><li data-list="ordered">slideContainsV0 — просто обёртка над стандартной функцией.</li><li data-list="ordered">slideContainsV1 — написанная на ассемблере.</li></ol></div><div class="t-redactor__text">Запустим бенчмарк и увидим, что функция v1 быстрее примерно в 14 раз. Казалось бы, странно — ведь вызвали функцию из стандартной библиотеки.</div><div class="t-redactor__text">Давайте разбираться, почему так. Но сначала выясним, как программа на Go компилируется.</div><h3  class="t-redactor__h3">С помощью чего ускоряем</h3><div class="t-redactor__text">Есть pipeline компиляции, который можно посмотреть командой: GOSSAFUNC=main go tool compile -S -S main.go</div><img src="https://static.tildacdn.com/tild6238-6166-4038-b235-653333376538/db6e6ef512dcd4e08aa9.png"><div class="t-redactor__text">Этой командой можно запустить pipeline и посмотреть все этапы компиляции. Компилятор разбивает наш код на Go на lexemes.</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">/Users/igorwalther/Goland Projects/golangconf-2024
func main() {
    println(&quot;Hello, GolangConf-2024!&quot;)
}</code></pre><div class="t-redactor__text">Далее строится так называемое абстрактное синтаксическое дерево (AST).</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">. BLOCK tc(1) # main.go:4:9
. BLOCK-List
. . CALLFUNC Walked tc(1) # main.go:4:9
. . CALLFUNC-Fun
. . . NAME-runtime.printlock Class:PFUNC Offset:0 Used F
. . CALLFUNC Walked tc(1) # main.go:4:9
. . CALLFUNC-Fun
. . . NAME-runtime.printstring Class:PFUNC Offset:0 Used
. . CALLFUNC-Args
. . . LITERAL-&quot;Hello, GolangConf-2024!\n&quot; string tc(1) #
. . CALLFUNC Walked tc(1) # main.go:4:9
. . CALLFUNC-Fun
. . . NAME-runtime.printunlock Class:PFUNC Offset: 0 Used</code></pre><div class="t-redactor__text">После этого компилятор преобразует его в так называемое Intermediate Representation — своё внутреннее представление, которое принимает одну из форм, называемую SSA.</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">b1:
  v2 (?) = SB &lt;uintptr&gt; : SB
  v1 (?) = InitMem &lt;mem&gt;
  v4 (+4) = CALLstatic &lt;mem&gt;
      {AuxCall{runtime.printlock}} v1
  v5 (4) SelectN &lt;mem&gt; [0] v4
  v14 (4) = MOVDaddr &lt;*uint8&gt; {go:string.&quot;Hello, GolangConf-2024!\n&quot;} v2 : R0
  v13 (4) MOVD const &lt;int&gt; [24] : R1
  v7 (4) CALLstatic &lt;mem&gt;
      {AuxCall{runtime.printstring}} [16] v14 v13 v5 :
      &lt;&gt;
  v8 (4) = SelectN &lt;mem&gt; [0] v7
  v9 (4) = CALLstatic &lt;mem&gt;
      {AuxCall{runtime.printunlock}} v8
  v10 (4) = SelectN &lt;mem&gt; [0] v9
  v11 (+5)= MakeResult &lt;mem&gt; v10
Ret v11 (5)</code></pre><div class="t-redactor__text">{go:string."Hello, GolangConf-2024!\n"}</div><div class="t-redactor__text">Скажу вкратце, что это называется noding. Компилятор на этой стадии применяет разные оптимизации — escape analysis, девиртуализацию, inlining и так далее. Подробнее читайте <a href="https://go.dev/src/cmd/compile/README" target="_blank" rel="noreferrer noopener">тут</a>.<br /><br />После этого компилятор генерирует ассемблер и машинный код.</div><img src="https://static.tildacdn.com/tild3638-3662-4431-b664-613862633039/21228421db6bcaef98c3.png"><pre class="t-redactor__highlightcode"><code data-lang="{$la}">      # /Users/igorwalther/GolandProjects/golangconf-2024/main.go
      00000 (3) TEXT main.main(SB), ABIInternal
      00001 (3) FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
      00002 (3) FUNCDATA $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
v4    00003 (+4) PCDATA $1, $0
v4    00004 (+4) CALL runtime.printlock(SB)
v14   00005 (4) MOVD $go:string.&quot;Hello, GolangConf-2024!\n&quot; (SB), R0
v13   00006 (4) MOVD $24, R1
v7    00007 (4) CALL runtime.printstring (SB)
v9    00008 (4) CALL runtime.printunlock (SB)
b1    00009 (5) RET
      00010 (?) END</code></pre><div class="t-redactor__text">Основная идея в том, что компилятор одновременно умный и в то же время глупый. Он не всегда может эффективно сгенерировать ассемблерный код. Тогда можно написать вручную свой код, который будет отрабатывать быстрее. Пока не понятно, что это будет за код, как его писать, но идея такая.</div><div class="t-redactor__text">В наших компьютерах есть регистры. Если говорить более научным языком, это статические ячейки памяти. Они дороже динамической ячейки, потому что в них шесть транзисторов, а в динамической ячейке только один. Можно рассматривать регистр как быструю память около процессора. На современных системах приняты регистры в 8 байт.</div><img src="https://static.tildacdn.com/tild6535-6539-4263-a233-636439393434/06ac8e5fdfa39a9079bb.png"><div class="t-redactor__text">Но на самом деле есть расширение, подразумевающее, что на железе есть расширенные регистры — не 8 байт, а 16 или 32 байта.</div><img src="https://static.tildacdn.com/tild6332-6361-4261-a535-656532313433/3069bdf101037ec87fff.png"><div class="t-redactor__text">Представьте, что у вас таких регистров не один, а от двух до пяти. Используя их, можно делать разные прикольные вещи.</div><img src="https://static.tildacdn.com/tild6261-3138-4130-a237-653863656232/6d924558f102f768a636.png"><div class="t-redactor__text">Для них нам пригодится классификация архитектур под названием <a href="https://ru.wikipedia.org/wiki/%D0%A2%D0%B0%D0%BA%D1%81%D0%BE%D0%BD%D0%BE%D0%BC%D0%B8%D1%8F_%D0%A4%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0" target="_blank" rel="noreferrer noopener">таксономия Флинна</a>. Она помогает понять, как обрабатываются данные в процессорах. Одной из видов архитектур по этой классификации является SIMD (Single Instruction stream / Multiple Data stream). Это вычислительная система с одиночным потоком команд и множественным потоком данных. Это означает, что одна инструкция может сразу обрабатывать несколько потоков данных одновременно. Для этого используются специальные векторные регистры, которые позволяют выполнять операции не над одним значением, а сразу над целой группой чисел. Такой подход значительно ускоряет вычисления, особенно в задачах, связанных с обработкой изображений, математическими расчётами и машинным обучением.</div><img src="https://static.tildacdn.com/tild3231-3962-4232-b730-613661326637/d449eee06bdf691d5b49.png"><h4  class="t-redactor__h4">В Go нет intrinsic SIMD-функций</h4><div class="t-redactor__text">Разработчики, работающие с C++ и другими языками, где есть intrinsic-функции, могут напрямую вызывать оптимизированные инструкции, которые компилятор понимает и эффективно преобразует в машинный код. Это позволяет получить максимальную производительность без написания ассемблерных вставок.</div><div class="t-redactor__text">К сожалению, в Go таких возможностей пока нет. Стандартный компилятор из Go toolchain не умеет автоматически генерировать такие оптимизированные инструкции. Это ограничение усложняет низкоуровневую оптимизацию и требует написания кода на ассемблере вручную.</div><div class="t-redactor__text">Существует несколько причин, по которым эта функция до сих пор не реализована. Официально разработчики Go объясняют это сложностью создания поддержки для всех архитектур в существующем компиляторе. На данный момент у них просто не получилось реализовать это на должном уровне.</div><div class="t-redactor__text">Ещё один комментарий от команды разработчиков звучал так: <em><a href="https://groups.google.com/g/golang-dev/c/LllE9GEeg6w" target="_blank" rel="noreferrer noopener">«Это было бы здорово, но сейчас есть более важные задачи»</a></em>. Другими словами, они признают, что такая возможность могла бы улучшить язык, но пока не является приоритетной.</div><div class="t-redactor__text">Недавно Go исполнилось 15 лет, и в официальном блоге разработчики отметили, что активно работают над тем, чтобы встроенная поддержка подобных функций появилась в будущем. Их цель — сделать это нативно, красиво и удобно для пользователей. На момент написания этой статьи уже вышел Go 1.24, где SIMD инструкции были использованы в новой swiss-map.</div><blockquote class="t-redactor__quote"><em>«We’re looking at how to support the latest vector and matrix hardware instructions»</em><br /><br /><em>© The Go Blog: Austin Clements</em></blockquote><div class="t-redactor__text">В будущих итерациях и релизах Go, возможно, intrinsic-функции станут частью стандартного функционала языка, и их можно будет использовать «из коробки» без необходимости писать дополнительный код. Но пока нам нужно другое решение.</div><h4  class="t-redactor__h4">Идея оптимизации</h4><div class="t-redactor__text">Основная идея оптимизации, о которой пойдёт речь, заключается в использовании специальных инструкций процессора, которые позволяют ускорять вычисления. Мы рассмотрим векторные инструкции, которые работают с векторными регистрами и могут обрабатывать сразу несколько данных за один такт. Однако ускорение не всегда связано только с векторизацией. Мы также затронем более мощные механизмы, такие как аппаратная поддержка транзакций, основанная на когерентности кэшей, которая также может значительно повысить производительность.</div><div class="t-redactor__text">В целом, мы будем использовать возможности целевой архитектуры, то есть задействовать те инструкции, которые уже есть в процессоре, но которыми компилятор не всегда умеет эффективно пользоваться. Проще говоря, если в документации к процессору указана полезная инструкция, мы можем просто взять и использовать её для ускорения кода.</div><div class="t-redactor__text">Всё это мы будем реализовывать с помощью Go-ассемблера, чтобы вручную управлять процессором и максимально эффективно использовать его возможности.</div><h3  class="t-redactor__h3">$go:string.”Go ассемблер?”(SB)</h3><div class="t-redactor__text">Роб Пайк на одной из конференций сказал, что если вы знаете ассемблер, то понимаете, как работает компьютер в целом, но чуть лучше:</div><blockquote class="t-redactor__quote"><em>«Also, perhaps most important: it is how we talk about the machine. Knowing assembly, even a little, means understanding computers better»</em><br /><br /><em>© GopherCon 2016: Rob Pike</em></blockquote><div class="t-redactor__text">И это правда. Только ради этого стоит хотя бы немного разобраться в ассемблере. Выделю несколько важных фактов о Go-ассемблере, которые нужно вспомнить, чтобы понять о чём я буду рассказывать дальше:</div><div class="t-redactor__text"><strong>Изначально он создавался, чтобы написать runtime Go.</strong> Go-ассемблер используется в стандартной библиотеке в пакете для математики, криптографии и runtime. Изначально ассемблер был введен именно для runtime — это такой legacy Plan 9.</div><div class="t-redactor__text"><strong>Платформозависимый (примеры на arm64).</strong> Если посмотреть на синтаксис ассемблера, становится понятно, что это платформозависимый язык. Но Go-ассемблер уходит корнями в Plan 9, который изначально подразумевался как платформонезависимый. В итоге сейчас это не так, для каждой архитектуры синтаксис отличается. Например, код для arm64 будет отличаться. Поэтому сегодня будут примеры на arm64 для удобства запуска. Но инструкции всё равно будут похожие, при желании их легко переписать для своей архитектуры. </div><div class="t-redactor__text"><strong>Имеет нестандартный синтаксис</strong>. Go-ассемблер имеет довольно нестандартный синтаксис, особенно если сравнивать его с популярными ассемблерами, такими как NASM. Основные отличия заключаются в другом порядке аргументов и немного изменённой структуре инструкций. Эти особенности связаны с историческим наследием операционной системы Plan 9, на основе которой Go-ассемблер и был разработан.</div><div class="t-redactor__text">Если вам интересно, почему Go-ассемблер устроен именно так, а не иначе, рекомендую посмотреть <strong>доклад Филиппа Кулина</strong>, где подробно объясняется, какие принципы закладывались в язык и чем они <a href="https://www.youtube.com/watch?v=ql-uncsqoAU" target="_blank" rel="noreferrer noopener">отличаются</a> от традиционного подхода. Также рекомендую посмотреть <a href="https://youtu.be/R121Xpb28og" target="_blank" rel="noreferrer noopener">доклад</a>, где я подробно разбираю синтаксис Go ассемблера</div><div class="t-redactor__text">А теперь давайте обсудим, наконец, инструкции для работы с векторными регистрами.</div><h4  class="t-redactor__h4">SIMD-инструкции</h4><div class="t-redactor__text">Представим, у нас есть два векторных регистра v0 и v1. Они в Go-ассемблере так и называются.</div><img src="https://static.tildacdn.com/tild6133-3533-4961-b330-653164313036/c3321b336695071ea974.png"><div class="t-redactor__text">Мы можем сделать с регистрами ряд действий, например, сложить. Получим другой векторный регистр с ожидаемым значением.</div><img src="https://static.tildacdn.com/tild3139-6136-4433-b965-356461656164/530fb48358268b229850.png"><div class="t-redactor__text">Одна из инструкций, которую будем сегодня использовать, это инструкция VMOV. </div><img src="https://static.tildacdn.com/tild3865-6462-4530-b230-633862363036/bcecb3a7bf3530744ec3.png"><div class="t-redactor__text">Инструкция VMOV позволяет переносить данные из векторного регистра в обычный. Представьте, что у вас есть векторный регистр, содержащий несколько значений, и вам нужно извлечь одно из них для дальнейшей работы.</div><div class="t-redactor__text"><ol><li data-list="ordered">Вызываем команду VMOV.</li><li data-list="ordered">Указываем, из какого векторного регистра (например, v0) берём значение.</li><li data-list="ordered">Определяем, как именно воспринимать этот регистр. В архитектуре ARM это делается через точку: например, .S означает, что мы рассматриваем содержимое как 4 отдельных числа по 4 байта (Single Precision Value).</li><li data-list="ordered">Выбираем конкретный элемент, обращаясь к нему по индексу (например, v0[0]).</li></ol></div><div class="t-redactor__text">История с Single Precision (одинарной точностью) берёт своё начало из стандарта IEEE 754, который определяет форматы представления чисел с плавающей запятой в компьютерах. Аналогично, Double Precision (двойная точность) также следует этому стандарту, но использует больше бит для хранения значений, что позволяет работать с более высокой точностью и диапазоном чисел.</div><img src="https://static.tildacdn.com/tild6665-6434-4137-a236-643763303661/876ed978b76d7334f5e8.png"><div class="t-redactor__text">Есть таблица синтаксиса регистров:</div><img src="https://static.tildacdn.com/tild6634-3364-4565-b530-633135646263/7ccc620cd6baf9af289d.png"><div class="t-redactor__text">​​Если вам нужно разделить векторный регистр на 2, 4, 8 или 16 частей, вы можете использовать соответствующие суффиксы. Эта возможность тоже основана на стандарте IEEE-754 и позволяет трактовать содержимое регистра как набор меньших элементов. По сути, это способ разбить данные на удобные для обработки фрагменты, не меняя их значения. Можно представить это как разметку памяти, позволяющую работать с отдельными частями регистра.</div><div class="t-redactor__text">Ещё нам понадобится инструкция свёртки со сложением. </div><img src="https://static.tildacdn.com/tild6433-6165-4465-b466-643138306336/444698053dd4b074561d.png"><div class="t-redactor__text">У нас есть векторный регистр, содержащий несколько значений. Мы хотим свести их к одному числу, просуммировав все элементы внутри регистра. Итоговое значение сохраняется в другом векторном регистре, поскольку сумма может быть слишком большой, чтобы уместиться в обычный скалярный регистр.</div><div class="t-redactor__text">Эта операция называется свёрткой (reduction) — мы последовательно складываем элементы и получаем итоговый результат. Такой подход удобен, когда нам нужно быстро агрегировать данные, например, в вычислениях статистики или линейной алгебре.</div><div class="t-redactor__text">Для double precision (двойной точности) процесс работает аналогично: числа с плавающей запятой просто обрабатываются с большей точностью.</div><img src="https://static.tildacdn.com/tild3633-3734-4465-b437-343038343361/fe5b72ec8d4c2afb909d.png"><div class="t-redactor__text">Не нужно бояться суффиксов .D, .S. Они просто дают возможность разбить регистр на фрагменты.</div><div class="t-redactor__text">Мы уже научились выполнять свёртку, сложение и другие бинарные операции. Теперь нам нужно научиться загружать данные из памяти в векторный регистр и, выгружать их обратно.</div><div class="t-redactor__text">Для этого существует удобная инструкция VLD1 (Vector Load 1). Она позволяет загрузить значения из памяти в векторный регистр. Единица в названии говорит о том, что существуют другие версии этой инструкции:VLD2, VLD3, VLD4, которые позволяют загружать сразу несколько блоков данных в разные регистры. Но пока сосредоточимся на VLD1, она загружает один блок данных из памяти в один векторный регистр.</div><img src="https://static.tildacdn.com/tild3739-3835-4235-a366-643762343465/7cc4acff044c5acb7032.png"><div class="t-redactor__text">При использовании VLD1 нужно указать три вещи:</div><div class="t-redactor__text"><ol><li data-list="ordered">Адрес в памяти, откуда будем загружать данные.</li><li data-list="ordered">Векторный регистр, в который загрузим эти данные.</li><li data-list="ordered">Типизацию через точку, чтобы задать способ интерпретации данных (например, как 16 байт).</li></ol></div><div class="t-redactor__text">Пример: если мы указываем адрес R0, а регистр V0, то VLD1 возьмет 16 байт из памяти по адресу R0 и загрузит их в V0.</div><div class="t-redactor__text">Проще говоря, эта инструкция — аналог продвинутого MOV, но для векторных регистров. Мы переносим блок данных из памяти в регистр, чтобы потом быстро работать с ним внутри процессора.</div><img src="https://static.tildacdn.com/tild6130-3636-4137-b735-373436646166/e79ab4745061c95a930e.png"><div class="t-redactor__text">Название VLD1 намекает, что загружается один векторный регистр. Но в архитектуре ARM есть и другие варианты: VLD2 — загружает данные сразу в два векторных регистра (например, V0 и V1), VLD3 — загружает данные в три, VLD4 — в четыре. А вот VLD5 не существует, потому что архитектура не предусматривает загрузку данных в пять регистров за раз. Основная идея в том, что можно сразу загружать большие объёмы данных из памяти в векторные регистры. Это особенно полезно, если мы работаем с массивами, матрицами или большими блоками чисел, которые потом будем обрабатывать с помощью векторных инструкций.</div><div class="t-redactor__text">Мы научились загружать данные из памяти в регистр. Давайте научимся, наоборот, загружать их из регистра в память.</div><div class="t-redactor__text">Инструкция vector store:</div><img src="https://static.tildacdn.com/tild3766-6235-4437-a337-623434646334/6e6d15bf01132435006a.png"><div class="t-redactor__text">Инструкция VST1 (Vector Store 1) работает по той же логике, что и VLD1, но в обратном направлении — она перемещает данные из векторных регистров в память.</div><div class="t-redactor__text">Как это работает:</div><div class="t-redactor__text"><ul><li data-list="bullet">Указываем адрес памяти, куда будем записывать данные.</li><li data-list="bullet">Выбираем векторные регистры, откуда берутся данные.</li><li data-list="bullet">Определяем формат данных (например, 16 байт на регистр).</li></ul></div><div class="t-redactor__text">Если мы используем два регистра (V0 и V1), то получаем, что в памяти окажутся сразу четыре значения — первые два из V0, вторые два из V1.</div><div class="t-redactor__text">Это понятно интуитивно: так же, как мы загружали данные пакетами с помощью VLD, теперь мы можем их выгружать пакетами.</div><div class="t-redactor__text">Хороший вопрос: если у нас есть два набора данных, как определить, есть ли среди них совпадения?</div><div class="t-redactor__text">Здесь есть два возможных подхода:</div><div class="t-redactor__text"><ol><li data-list="ordered">Атомарное определение совпадения – просто узнать, есть ли хотя бы одно совпадение среди элементов.</li><li data-list="ordered">Более детальное сравнение – определить, какие элементы совпали и где именно.</li></ol></div><div class="t-redactor__text">Векторные инструкции позволяют сравнивать сразу целые блоки данных, но важно понимать, как интерпретировать результат.</div><div class="t-redactor__text">Важнейшая операция, которой сегодня будем пользоваться — сравнение двух векторных регистров.</div><img src="https://static.tildacdn.com/tild6637-3062-4233-a666-313035643163/44e57623cf77890a0ac2.png"><div class="t-redactor__text">Операция работает следующим образом:</div><img src="https://static.tildacdn.com/tild3137-6530-4562-b364-613862373834/88106a14d9a7e7f2b22e.png"><div class="t-redactor__text">Когда нам нужно сравнить два набора данных, удобно использовать векторное сравнение. В Go-ассемблере для этого применяется инструкция VCMEQ (Vector Compare Equal).</div><div class="t-redactor__text">Что делает VCMEQ:</div><div class="t-redactor__text"><ul><li data-list="bullet">Берёт два векторных регистра.</li><li data-list="bullet">Проводит побайтовое или поэлементное сравнение (в зависимости от размера данных).</li><li data-list="bullet">Записывает результат в битовую маску:</li></ul>Если элементы совпали — соответствующий блок битов будет 1.<br />Если не совпали — биты в соответствующем будут 0.</div><div class="t-redactor__text">Эта операция особенно полезна, если мы ищем определённый элемент в массиве или сравниваем два набора данных. После этого мы можем сжать результат (например, с помощью VADDV) и быстро понять, было ли хоть одно совпадение.</div><div class="t-redactor__text">Когда выполняется векторное сравнение, результат представляется в виде битовой маски: Единицы ставятся в тех позициях, где элементы совпали (true). Нули — там, где элементы различаются (false).</div><div class="t-redactor__text">Эта маска позволяет анализировать совпадения без явного перебора элементов.</div><div class="t-redactor__text">1. Простая проверка наличия совпадений:<br /><br /><ul><li data-list="bullet">Применяем VADDV (vector reduce add), которая суммирует все значения в маске.</li><li data-list="bullet">Если сумма больше нуля, значит, хотя бы одно совпадение есть.</li></ul><br />2. Анализ позиций совпадений:<br /><br /><ul><li data-list="bullet">Можно использовать маску для фильтрации или индексирования, например, чтобы определить, в каких конкретно местах произошло совпадение.</li></ul></div><div class="t-redactor__text">Этот метод быстрее обычных циклов, потому что обрабатывает сразу целый блок данных, а не по одному элементу за итерацию.</div><div class="t-redactor__text">Также нам сегодня понадобится инструкция дупликации. Мы хотим получить из одного обычного регистра векторный регистр, размножив значения.</div><img src="https://static.tildacdn.com/tild6561-3465-4336-b863-646665373131/239578cdcae2a3e829db.png"><div class="t-redactor__text">Мы используем VDUP (Vector Duplicate), чтобы скопировать одно значение во все ячейки векторного регистра.</div><div class="t-redactor__text"><ol><li data-list="ordered">Берём значение из R0.</li><li data-list="ordered">Копируем его во все четыре слота векторного регистра V0.</li></ol></div><div class="t-redactor__text">То есть одно и то же число заполняет весь регистр, создавая однородный вектор.</div><div class="t-redactor__text">Это полезно, когда нужно сравнивать целый массив с одним значением. Или если нужно выполнять массовые вычисления с одним коэффициентом, например, умножить все элементы на одно число.</div><img src="https://static.tildacdn.com/tild3837-3366-4134-a466-353464313034/4bb9b78601e3792063d6.png"><div class="t-redactor__text">То же самое с double precision:</div><img src="https://static.tildacdn.com/tild6438-3633-4332-a430-633664363437/03c6d889fa5277f0751b.png"><div class="t-redactor__text">На самом деле, расширений и инструкций существует очень много.</div><img src="https://static.tildacdn.com/tild3839-3064-4533-a336-616465363634/7397c03b824b7f8dd218.png"><div class="t-redactor__text">​​Я продемонстрировал работу с ассемблером на ARM64, но аналогичные принципы применимы и к другим архитектурам, например, x86. У разных платформ есть свои расширения. В x86 доступны расширенные векторные регистры вплоть до 512 бит (например, в AVX-512). Это в четыре раза больше, чем 128-битные регистры, которые я использовал в примерах. Чем больше регистр — тем больше данных можно обработать за одну инструкцию, а значит, выше потенциальное ускорение.</div><h4  class="t-redactor__h4">Что понадобится</h4><div class="t-redactor__text">Нам понадобятся векторные инструкции из Go-ассемблера:</div><div class="t-redactor__text"><ul><li data-list="bullet">VADD (vector addition);</li><li data-list="bullet">VDUP (vector duplicate);</li><li data-list="bullet">VADDV (vector reduce add);</li><li data-list="bullet">VLD1 / VST1 / VMOV (vector load1 / vector store1 / vector mov);</li><li data-list="bullet">VCMEQ (vector compare equal);</li></ul></div><div class="t-redactor__text">Кроме векторных инструкций, есть и обычные, которые часто встречаются в ассемблерном коде Go.</div><div class="t-redactor__text"><ul><li data-list="bullet">MOVD (MOV double word)</li></ul></div><div class="t-redactor__text">На ARM64 одно машинное слово — это 4 байта, и MOVD используется для перемещения двойного слова (8 байт). Это стандартная инструкция для загрузки или сохранения данных. Различные способы адресации позволяют гибко управлять данными и оптимизировать доступ к памяти.</div><div class="t-redactor__text"><ul><li data-list="bullet">LDP (load pair aka двойной MOV)</li></ul></div><div class="t-redactor__text">Более прокачанная версия на arm64. Сегодня буду ей пользоваться, потому что она компактней. Обычно, чтобы загрузить два значения, нужно два MOV. LDP позволяет сделать это одной инструкцией. А ещё работает быстрее, так как загружает сразу двойной объём данных.</div><div class="t-redactor__text"><ul><li data-list="bullet">ADD / SUB</li></ul></div><div class="t-redactor__text">Сложение/вычитание, чтобы инкрементировать или увеличивать счетчики, двигать указатели и так далее.</div><div class="t-redactor__text"><ul><li data-list="bullet">B (branch), CBZ, CBNZ (compare branch not/zero)</li></ul></div><div class="t-redactor__text">Инструкции управления — как реализовать циклы. В ассемблерном коде есть инструкции управления, на arm это ходовые branch, compare branch not/zero, на amd64 — jump, jump zero и так далее. Идея везде одна и та же, а нейминг отличается.</div><div class="t-redactor__text"><ul><li data-list="bullet">CMP (compare)</li></ul></div><div class="t-redactor__text">Инструкцию compare можно сравнить с инструкцией test — позволяет сравнить два значения, поменять регистр флагов, чтобы потом сделать какое-то разветвление.</div><div class="t-redactor__text">В следующей части статьи разберём в примерах, как всё это использовать.</div><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Читать далее</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://igoroutine.courses/free-materials/e1btztez01-vektorizatsiya-i-simd-v-go-uskorenie-poi</div></td></tr></tbody><colgroup><col style="max-width:287.5px;min-width:287.5px;width:287.5px;"><col style="max-width:287.5px;min-width:287.5px;width:287.5px;"></colgroup></table></div></div><h3  class="t-redactor__h3">Дополнительные материалы</h3><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/golangconf-2024</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Asm go.dev quick guide</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://go.dev/doc/asm</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Summary of Go assembler</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://www.quasilyte.dev/blog/post/go-asm-complementary-reference/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Почему Golang такой странный / Филипп Кулин (Дремучий лес)
</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=ql-uncsqoAU</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Go Assembler: ускорение кода гомоморфного хэширования в N раз /Евгений Стратоников (Neo SPCC)
</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=foCV6nn4rkc</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">GopherCon 2016: Rob Pike - The Design of the Go Assembler</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=KINIAgRpkDA</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="6" data-column="0"><div class="t-table__cell-content">Binary Search SIMD optimization</div></td><td class="t-table__cell" data-row="6" data-column="1"><div class="t-table__cell-content">https://clement-jean.github.io/simd_binary_search_tree/</div></td></tr></tbody><colgroup><col style="max-width:287.5px;min-width:287.5px;width:287.5px;"><col style="max-width:287.5px;min-width:287.5px;width:287.5px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Векторизация и SIMD в Go: ускорение поиска и сравнения в массивах</title>
      <link>https://igoroutine.courses/free-materials/e1btztez01-vektorizatsiya-i-simd-v-go-uskorenie-poi</link>
      <amplink>https://igoroutine.courses/free-materials/e1btztez01-vektorizatsiya-i-simd-v-go-uskorenie-poi?amp=true</amplink>
      <pubDate>Mon, 06 Apr 2026 14:51:00 +0300</pubDate>
      <category>Computer Science</category>
      <category>Продвинутый Go</category>
      <enclosure url="https://static.tildacdn.com/tild3761-3238-4335-a239-383639663835/image.png" type="image/png"/>
      <turbo:content><![CDATA[<header><h1>Векторизация и SIMD в Go: ускорение поиска и сравнения в массивах</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3761-3238-4335-a239-383639663835/image.png"/></figure><div class="t-redactor__embedcode"><div id="custom-article-toc"></div>

<style>
/* --- Базовые стили основного оглавления --- */
#custom-article-toc:empty { display: none; }

.custom-toc-container {
    background: rgba(26, 28, 40, 0.6); 
    border-radius: 12px;
    padding: 24px;
    font-family: 'ShareTechMono', sans-serif;
    margin-bottom: 24px;
}

.custom-toc-title {
    color: #ffffff;
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 16px;
    font-family: 'ShareTechMono', sans-serif;
}

.custom-toc-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* --- Стили для ссылок --- */
.custom-toc-link {
    color: rgba(255, 255, 255, 0.85) !important;
    text-decoration: none !important;
    font-size: 15px;
    transition: color 0.2s ease, transform 0.2s ease;
    display: flex; 
    align-items: center; 
    line-height: 1.4;
    cursor: pointer;
    font-family: 'ShareTechMono', sans-serif;
}

.custom-toc-link:hover {
    color: #F4721E !important; 
    transform: translateX(2px); 
}

/* --- Иерархия --- */
.custom-toc-level-h1 { margin-left: 0; font-weight: 600; color: #ffffff !important; }
.custom-toc-level-h2 { margin-left: 0; }
.custom-toc-level-h3 { margin-left: 8px; font-size: 14px; color: rgba(255, 255, 255, 0.6) !important; }

/* --- Скрытый блок с h4 --- */
.custom-toc-h4-wrapper {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 0; 
    overflow: hidden;
    transition: max-height 0.4s ease-in-out;
}

.custom-toc-h4-wrapper.is-open {
    max-height: 1000px; 
    margin-top: 6px; 
}

.custom-toc-level-h4 { 
    margin-left: 16px; 
    font-size: 13px; 
    color: rgba(255, 255, 255, 0.4) !important; 
}

/* --- Иконка стрелочки для h3 --- */
.custom-toc-toggle-icon {
    width: 14px;
    height: 14px;
    margin-right: 6px;
    transition: transform 0.3s ease;
    opacity: 0.7;
    flex-shrink: 0;
}

.custom-toc-level-h3.has-children.is-open .custom-toc-toggle-icon {
    transform: rotate(90deg); 
    color: #F4721E;
}


/* =======================================================
   СТИЛИ ДЛЯ ПЛАВАЮЩЕГО ПОПАПА (ПРАВЫЙ НИЖНИЙ УГОЛ)
   ======================================================= */
/* Кнопка-триггер */
.custom-toc-float-btn {
    position: fixed;
    bottom: 20px;
    right: 20px;
    background: #F4721E; /* Твой оранжевый */
    color: #1A1C28; /* Темный текст для контраста */
    padding: 12px 20px;
    border-radius: 8px;
    font-family: 'ShareTechMono', sans-serif;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    z-index: 9998;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    display: flex;
    align-items: center;
    gap: 8px;
    transition: transform 0.2s ease, background 0.2s ease;
}

.custom-toc-float-btn:hover {
    transform: translateY(-2px);
    background: #e06010;
}

.custom-toc-float-btn svg {
    width: 18px;
    height: 18px;
}

/* Само окно оглавления */
.custom-toc-float-window {
    position: fixed;
    bottom: 70px; /* Чуть выше кнопки */
    right: 20px;
    width: 320px;
    max-height: calc(100vh - 120px); /* Чтобы не вылезало за экран */
    background: rgba(18, 20, 30, 0.95); /* Чуть темнее основного фона */
    border: 1px solid rgba(244, 114, 30, 0.3); /* Оранжевая обводка */
    border-radius: 12px;
    padding: 20px;
    z-index: 9999;
    box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    overflow-y: auto;
    
    /* Анимация появления */
    opacity: 0;
    transform: translateY(20px);
    pointer-events: none;
    transition: all 0.3s ease;
}

.custom-toc-float-window.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* Кастомный скроллбар для окна */
.custom-toc-float-window::-webkit-scrollbar {
    width: 4px;
}
.custom-toc-float-window::-webkit-scrollbar-thumb {
    background: rgba(244, 114, 30, 0.5); 
    border-radius: 4px;
}

/* Шапка попапа с крестиком */
.custom-toc-float-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    padding-bottom: 12px;
}

.custom-toc-float-header .custom-toc-title {
    margin-bottom: 0; /* Убираем отступ, чтобы было в линию с крестиком */
    font-size: 16px;
}

.custom-toc-close-btn {
    background: none;
    border: none;
    color: rgba(255,255,255,0.6);
    cursor: pointer;
    padding: 4px;
    display: flex;
    transition: color 0.2s ease;
}
.custom-toc-close-btn:hover {
    color: #F4721E;
}

/* Показываем плавающую кнопку только на больших экранах */
/*@media screen and (max-width: 960px) {*/
/*    .custom-toc-float-btn, */
/*    .custom-toc-float-window {*/
/*        display: none !important;*/
/*    }*/
/*}*/
</style>

<script>
(function() {
    let attempts = 0;
    
    const tocInterval = setInterval(() => {
        attempts++;
        const tocWrapper = document.getElementById('custom-article-toc');
        
        if (tocWrapper && !tocWrapper.hasAttribute('data-built')) {
            const articleBody = tocWrapper.closest('.js-feed-post-text') || tocWrapper.closest('.t-redactor__tte-view');
            
            if (articleBody) {
                const headings = articleBody.querySelectorAll('h1, h2, h3, h4');
                
                if (headings.length > 0) {
                    const tocContainer = document.createElement('div');
                    tocContainer.className = 'custom-toc-container';
                    
                    const tocTitle = document.createElement('div');
                    tocTitle.className = 'custom-toc-title';
                    tocTitle.textContent = 'Содержание статьи';
                    tocContainer.appendChild(tocTitle);

                    const tocList = document.createElement('div');
                    tocList.className = 'custom-toc-list';

                    let currentH3Item = null;
                    let currentH4Wrapper = null;

                    headings.forEach((heading) => {
                        if (!heading.id) {
                            heading.id = 'heading-' + Math.random().toString(36).substr(2, 9);
                        }

                        const tagName = heading.tagName.toLowerCase();
                        
                        if (tagName === 'h3') {
                            const listItem = document.createElement('div');
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-h3';
                            link.textContent = heading.textContent.trim();
                            
                            link.targetHeading = heading;
                            
                            listItem.appendChild(link);
                            tocList.appendChild(listItem);
                            
                            currentH3Item = listItem;
                            currentH4Wrapper = null; 
                            
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                
                                if (link.classList.contains('has-children')) {
                                    const wrapper = link.nextElementSibling;
                                    
                                    if (e.target.closest('.custom-toc-toggle-icon')) {
                                        link.classList.toggle('is-open');
                                        wrapper.classList.toggle('is-open');
                                        return;
                                    }
                                    
                                    if (!link.classList.contains('is-open')) {
                                        link.classList.add('is-open');
                                        wrapper.classList.add('is-open');
                                    } else {
                                        link.targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                        // Опционально: закрывать попап при клике
                                        // document.querySelector('.custom-toc-float-window').classList.remove('is-visible');
                                    }
                                } else {
                                    link.targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                }
                            });
                            
                        } else if (tagName === 'h4' && currentH3Item) {
                            if (!currentH4Wrapper) {
                                currentH4Wrapper = document.createElement('div');
                                currentH4Wrapper.className = 'custom-toc-h4-wrapper';
                                currentH3Item.appendChild(currentH4Wrapper);
                                
                                const parentH3Link = currentH3Item.querySelector('.custom-toc-level-h3');
                                parentH3Link.classList.add('has-children');
                                
                                const chevronSvg = '<svg class="custom-toc-toggle-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>';
                                parentH3Link.insertAdjacentHTML('afterbegin', chevronSvg);
                            }
                            
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-h4';
                            link.textContent = heading.textContent.trim();
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            });
                            currentH4Wrapper.appendChild(link);
                            
                        } else {
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-' + tagName;
                            link.textContent = heading.textContent.trim();
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            });
                            tocList.appendChild(link);
                            
                            currentH3Item = null;
                            currentH4Wrapper = null;
                        }
                    });

                    tocContainer.appendChild(tocList);
                    tocWrapper.appendChild(tocContainer);
                    
                    
                    /* =======================================================
                       СОЗДАЕМ ПЛАВАЮЩИЙ UI
                       ======================================================= */
                    
                    // Кнопка
                    const floatBtn = document.createElement('div');
                    floatBtn.className = 'custom-toc-float-btn';
                    floatBtn.innerHTML = `
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
                        Содержание
                    `;
                    document.body.appendChild(floatBtn);

                    // Окно попапа
                    const floatWindow = document.createElement('div');
                    floatWindow.className = 'custom-toc-float-window';
                    
                    // Шапка окна
                    const floatHeader = document.createElement('div');
                    floatHeader.className = 'custom-toc-float-header';
                    floatHeader.innerHTML = `
                        <div class="custom-toc-title">Содержание статьи</div>
                        <button class="custom-toc-close-btn" aria-label="Закрыть">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
                        </button>
                    `;
                    
                    // Клонируем собранный список ссылок
                    const floatList = tocList.cloneNode(true);
                    
                    // Оживляем клики в клонированном списке
                    floatList.addEventListener('click', function(e) {
                        const link = e.target.closest('.custom-toc-link');
                        if (!link) return;
                        
                        e.preventDefault();
                        
                        // Ищем оригинальный заголовок в статье по тексту
                        const linkText = link.textContent.trim();
                        let targetHeading = null;
                        Array.from(headings).forEach(h => {
                            if (h.textContent.trim() === linkText) targetHeading = h;
                        });

                        if (!targetHeading) return;

                        if (link.classList.contains('has-children')) {
                            const wrapper = link.nextElementSibling;
                            if (e.target.closest('.custom-toc-toggle-icon')) {
                                link.classList.toggle('is-open');
                                wrapper.classList.toggle('is-open');
                                return;
                            }
                            if (!link.classList.contains('is-open')) {
                                link.classList.add('is-open');
                                wrapper.classList.add('is-open');
                            } else {
                                targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            }
                        } else {
                            targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                        }
                    });

                    floatWindow.appendChild(floatHeader);
                    floatWindow.appendChild(floatList);
                    document.body.appendChild(floatWindow);

                    // Логика открытия/закрытия
                    floatBtn.addEventListener('click', () => {
                        floatWindow.classList.toggle('is-visible');
                    });
                    
                    floatWindow.querySelector('.custom-toc-close-btn').addEventListener('click', () => {
                        floatWindow.classList.remove('is-visible');
                    });


                    tocWrapper.setAttribute('data-built', 'true');
                    tocWrapper.id = 'custom-article-toc-ready';
                }
            }
            clearInterval(tocInterval);
        }
        
        if (attempts > 20) {
            clearInterval(tocInterval);
        }
    }, 500);
})();
</script></div><div class="t-redactor__text">Ускорить простые задачи, вроде поиска в массиве и сравнения слайсов, поможет мощь SIMD. Эти векторные инструкции, которые обрабатывают десятки байт данных за один такт процессора, отличная замена традиционным циклам. Во второй части статьи мы погружаемся глубже в практическое применение SIMD в Go-ассемблере, реализуем функцию SliceContainsV1 и изучим, как с помощью VADD, VDUP и других инструкций можно добиться 10–14-кратного ускорения простых задач.</div><div class="t-redactor__text">Из этой статьи вы узнаете:</div><div class="t-redactor__text"><ul><li data-list="bullet">Как устроено сравнение массивов с помощью SIMD-инструкций;</li><li data-list="bullet">Почему векторизация быстрее бинарного поиска;</li><li data-list="bullet">Как правильно работать с регистрами, фреймами и указателями в Go-ассемблере;</li><li data-list="bullet">Что нужно учесть при переносимости и поддержке низкоуровневого кода;</li><li data-list="bullet">Когда ассемблер оправдан и безопасен в реальных проектах на Go.</li></ul></div><div class="t-redactor__text">Информация будет актуальна как разработчикам, оптимизирующим критически важный код, так и тем, кто хочет глубже понять архитектуру выполнения и взаимодействие с «железом».</div><div class="t-redactor__text"><a href="https://igoroutine.courses/free-materials/eud2d54di1-pochemu-go-assembler-i-vektorizatsiya-mo">В первой части статьи</a> мы разобрали саму идею ускорения кода на Go с помощью ассемблера. А в этой разберём её практическое применение.</div><h4  class="t-redactor__h4">Векторное сложение []uint8</h4><div class="t-redactor__text">Начнём с простой задачи — сложение двух слайсов. Представьте, что у нас есть два слайса, и мы хотим сложить их покомпонентно, сохраняя результат в третьем (distance).</div><img src="https://static.tildacdn.com/tild3032-6138-4366-b365-346535643062/f1863de7685066b88032.png"><div class="t-redactor__text">Здесь мы используем инструкцию VADD, которая позволяет складывать элементы поштучно в векторных регистрах. Читаем данные из слайсов не по одному элементу, а сразу блоками (например, по 16 байт). Складываем эти блоки векторно, используя VADD. Записываем результат обратно в distance.</div><div class="t-redactor__text">Чтобы упростить реализацию, рассмотрим выравненные слайсы с длиной, кратной 16 байтам. Это позволит избежать сложных проверок «остатка» (corner case) и работать с чистыми векторными операциями.</div><div class="t-redactor__text">Напомню, как выглядит структура слайсов в Go под капотом:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">type SliceHeader struct {
    Data uintptr
    Len int
    Cap int
}</code></pre><div class="t-redactor__text">В структуре слайса есть три ключевых поля: указатель на данные (начало массива), длина (количество элементов) и ёмкость (capacity). Но в данном случае использовать будем только первые два — указатель и длину.</div><div class="t-redactor__text">Как мы получаем эти значения в ассемблере? Go передаёт аргументы функции через стек (Frame Pointer — FP). Смещение 0 от FP содержит указатель на данные → кладём его в R0 Смещение 8 содержит длину → кладём в R1. Теперь у нас есть два регистра (R0, R1), с которыми мы можем работать дальше!</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">type SliceHeader struct {
    Data uintptr
    Len int
    Cap int
}

MOVD data+0(FP), R0
MOVD data+8(FP), R1</code></pre><div class="t-redactor__text">Давайте, наконец, писать код.</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">#include &quot;textflag.h&quot;

// func vectorAdditionV1(first, second, dst []uint8)
TEXT ·vectorAdditionV1(SB), NOSPLIT, $0
   LDP first_base+0(FP), (R0, R1)
   LDP second_base+24(FP), (R2, R3)
   LDP dst_base+48(FP), (R4, R5)

   MOVD $0, R7

loop:
   CMP R5, R7
   BGE done</code></pre><div class="t-redactor__text"><ol><li data-list="ordered"><strong>Подключаем стандартные директивы. </strong>Например, чтобы использовать NOSPLIT (чтобы избежать автоматического выделения стека), нужно включить нужные макросы.</li><li data-list="ordered"><strong>Объявляем функцию.</strong> Используем TEXT, затем имя функции и директивы. Это точка входа, где начинается исполнение кода функции.</li><li data-list="ordered"><strong>Инициализируем регистры.</strong> Записываем указатель на данные в один регистр (например, R0). Записываем длину массива в другой (R1). Эти значения нам нужны, чтобы работать с массивами.</li><li data-list="ordered"><strong>Готовим цикл.</strong> Дикрементируем счётчик итераций (например, R2). Используем инструкции сравнения и branch (переходы) для организации цикла.</li><li data-list="ordered"><strong> Итеративная обработка.</strong> В цикле будем загружать данные, выполнять вычисления и записывать результат в distance slice.</li></ol></div><div class="t-redactor__text">Дальше самая важная часть текущей функции:</div><img src="https://static.tildacdn.com/tild3037-6461-4339-b764-316263376339/960d0b4c929417839f48.png"><div class="t-redactor__text"><ol><li data-list="ordered"><strong>Загрузка кода. </strong>Используем VLD1, чтобы загрузить 16 байт в векторные регистры. Это позволяет сразу взять «кусочек» данных из первого и второго массива.</li><li data-list="ordered"><strong>Векторное сложение. </strong>Применяем VADD, чтобы сложить соответствующие элементы векторов. Результат записывается в V3.</li><li data-list="ordered"><strong>Запись результата в память.</strong> Используем VST1, чтобы сохранить результат обратно в distance slice. Данные кладём по указателю из R4.</li><li data-list="ordered"><strong>Обновляем указатели</strong>, увеличиваем длину и переходим к следующей итерации цикла.</li><li data-list="ordered"><strong>Цикл повторяется</strong>, пока не обработаем весь массив.</li></ol></div><div class="t-redactor__text">Запустив бенчмарк, видим ускорение кода в 11 раз благодаря векторизации! </div><img src="https://static.tildacdn.com/tild6336-3263-4239-a531-313864313437/13759b4bc2befdfdc5f8.png"><h4  class="t-redactor__h4">Примеры использования</h4><div class="t-redactor__text">Очевидно, что приведённый пример — достаточно простой и даже немного «игрушечный». Но стоит немного расширить его масштаб, и становится ясно: как только мы заменим сложение на более сложные операции, например, умножение, всё сводится к вычислениям с матрицами. А это уже основа многих серьёзных областей:</div><div class="t-redactor__text"><ul><li data-list="bullet"><strong>Машинное обучение</strong> — большинство алгоритмов (например, градиентный спуск или полиномиальная регрессия) опираются на операции с матрицами.</li><li data-list="bullet"><strong>Обработка изображений</strong> — любые фильтры, преобразования и трансформации реализуются через матричные вычисления.</li><li data-list="bullet"><strong>Графические и игровые движки</strong> — перемещения объектов, освещение, анимации — всё это активно использует линейную алгебру.</li></ul></div><div class="t-redactor__text">По сути, матричные операции — это фундамент, на котором строится современный анализ данных, компьютерное зрение и графика. Именно линейная алгебра делает возможной работу с этими технологиями.</div><div class="t-redactor__text">Предлагаю перейти к чему-то более практическому — реализуем проект, который можно будет использовать на практике без подключения графических движков и других тяжёлых зависимостей.</div><div class="t-redactor__text">Наша цель — постепенно разобраться во всех этапах разработки и шаг за шагом прийти к работающему решению. Такой подход поможет не только понять, как всё устроено внутри, но и получить полезный прикладной инструмент.</div><h4  class="t-redactor__h4">Slice contains</h4><div class="t-redactor__text">Реализация Slice Contains на Go выглядит примерно так:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">// 16-bit alignment for example
func Contains(s []uint8, target uint8) bool {
   for _, e := range s {
      if e == target {
         return true
      }
   }

   return false
}</code></pre><div class="t-redactor__text">Мы последовательно проходим по срезу (слайсу) и сравниваем каждый элемент с заданным значением. Если находим совпадение — сразу возвращаем true. Если до конца перебора совпадений не найдено — возвращаем false.</div><div class="t-redactor__text">Представим, что для удобства реализации у нас есть инвариант: длина слайса всегда кратна 16, то есть len(slice) % 16 == 0. Это упростит дальнейшую логику и оптимизацию.</div><div class="t-redactor__text">А теперь представьте, что у нас есть «волшебная» функция compare — чёрный ящик, который умеет быстро обрабатывать слайсы длиной ровно 16 элементов. Например, она может сравнивать или выполнять сложные операции над такими фрагментами слайса за один вызов.</div><div class="t-redactor__text">Что, если использовать эту функцию как строительный блок для ускорения всей обработки?</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">// 16-bit alignment to simplify
func ContainsSIMD(s []uint8, target uint8) bool {
   for i := 0; i &lt; len(s); i += 16 {
      if compare(
    s[i:i+16],
    Duplicate(target, 16),
   ) != 0 {
          return true
       }
    }

    return false
}
</code></pre><div class="t-redactor__text">Мы передаём в функцию compare слайс из 16 элементов — сначала один блок, затем следующий. Грубо говоря, говорим ей: «Пожалуйста, сравни эти два участка».</div><div class="t-redactor__text">В качестве второго аргумента используем слайс duplicate, который содержит повторяющиеся значения нашего целевого элемента (target). Таким образом, мы расширяем target до слайса длиной 16, чтобы корректно сравнивать его с другими 16-элементными блоками.</div><div class="t-redactor__text">Конечно, теоретически мы могли бы заранее подготовить такой дублированный слайс — создать его отдельно перед вызовами. Но в рамках текущего подхода делаем это «на лету», чтобы быстрее протестировать идею и упростить реализацию.</div><div class="t-redactor__text">Идея заключается в следующем: мы хотим реализовать функцию, которая способна сравнивать 16 элементов за один шаг — без явного цикла, с использованием всего одного if.</div><div class="t-redactor__text">Такое поведение возможно благодаря <strong>векторизации</strong> — технологии, которая позволяет процессору выполнять одну и ту же операцию сразу над несколькими элементами данных (например, с помощью SIMD-инструкций).</div><div class="t-redactor__text">Наша цель — написать эту функцию таким образом, чтобы она принимала два слайса по 16 элементов, сравнивала их <strong>одновременно</strong>, и мгновенно возвращала результат. Это позволит значительно ускорить обработку больших массивов данных, особенно в задачах, где много однотипных сравнений.</div><img src="https://static.tildacdn.com/tild3934-3166-4565-b537-323136636138/584c592d62558c280b68.png"><div class="t-redactor__text">В нашей реализации мы будем использовать векторную инструкцию compare, которая позволяет производить параллельные сравнения.</div><div class="t-redactor__text"><strong>Основная идея</strong>: у нас есть два слайса, и мы проходим по ним «окнами» — фрагментами длиной по 16 байт. На каждой итерации мы берём текущее окно из первого и второго слайса, передаём их в векторную функцию compare, и получаем результат в виде векторного регистра, заполненного единицами и нулями — по блоку битов, в зависимости от результата сравнения.</div><div class="t-redactor__text">Если целевая архитектура поддерживает более широкие регистры, например, 512 бит, — то размер окна можно было бы увеличить до 64 байт. Однако в этом примере используется 128-битные регистры.</div><div class="t-redactor__text">Таким образом, весь процесс сравнения происходит без традиционного цикла по каждому элементу — всё делается в один векторный вызов.</div><div class="t-redactor__text">Теперь возникает вопрос: как определить, было ли совпадение хотя бы в одном из 16 элементов, которые мы сравнили с помощью векторной инструкции compare?</div><div class="t-redactor__text">Ответ простой — сворачиваем результат сравнения. Векторный регистр, который мы получили, содержит единицы и нули: 1 — если элементы совпали, 0 — если нет. Если хотя бы один элемент был равен целевому значению (target), сумма всех значений в регистре будет больше нуля.</div><div class="t-redactor__text">Таким образом, логика будет следующей:</div><div class="t-redactor__text"><ol><li data-list="ordered">Выполнить векторное сравнение для текущего окна.</li><li data-list="ordered">Свернуть результат.</li><li data-list="ordered">Если сумма &gt; 0 — значит, совпадение найдено, возвращаем true.</li><li data-list="ordered">Если не найдено — продолжаем искать в следующем окне.</li></ol></div><div class="t-redactor__text">Теперь можно приступить к реализации функции SliceContainsV1, которая будет использовать этот подход.</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">func SliceContainsV0(s []uint8, target uint8) bool {
   return slices.Contains(s, target)
}

func SliceContainsV1(s []uint8, target uint8) bool</code></pre><div class="t-redactor__text">Идея такая же:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">//func SliceContainsV1(s []uint8, target uint8) bool
TEXT ·SliceContainsV1(SB), NOSPLIT, $0
   LDP slice_base+0(FP), (R0, R1)
   MOVB target+24(FP), R2
   VDUP R2, V1.B16

loop:
   CBZ R1, no
   VLD1.P 16(R0), [V2.B16]
   VCMEQ V1.B16, V2.B16, V3.B16
   VADDV V3.B16, V2
   VMOV V2.H[0], R4</code></pre><div class="t-redactor__text">На первом этапе мы загружаем в регистры указатели на данные и значения длины слайсов. Это необходимо, чтобы управлять смещениями, обновлять счётчики и корректно извлекать данные при проходе по массиву.</div><div class="t-redactor__text">Затем используем инструкцию <strong>VDUP </strong>(Vector Duplicate) — она позволяет создать вектор, где все элементы равны нашему целевому значению<strong> (target)</strong>. Этот вектор мы будем использовать как опорный: каждое окно из данных мы будем сравнивать именно с ним.</div><div class="t-redactor__text">После подготовки — переходим к циклу. Логика в цикле та же, что мы описывали выше:</div><div class="t-redactor__text"><ol><li data-list="ordered">Загружаем очередное окно из слайса — 16 байт данных.</li><li data-list="ordered">Выполняем векторное сравнение этого окна с нашим target-вектором, используя SIMD-инструкцию.</li><li data-list="ordered">Получаем результат сравнения — вектор с единицами и нулями.</li><li data-list="ordered">Сворачиваем его (например, через ORR или суммирование).</li><li data-list="ordered">Если хотя бы один бит установлен (т.е. есть совпадение) — завершаем выполнение и возвращаем true.</li><li data-list="ordered">Если совпадений нет — переходим к следующему окну.</li></ol></div><div class="t-redactor__text">Вся эта схема — основа нашей оптимизированной функции SliceContainsV1, реализованной с использованием векторизации.</div><img src="https://static.tildacdn.com/tild3730-3337-4633-a331-303263646464/72b9113818588b6394fe.png"><div class="t-redactor__text">На первой итерации мы берём первое окно из данных и сравниваем его с вектором, заполненным целевым значением (target), который мы заранее подготовили с помощью инструкции VDUP. Это дублированное значение записано во все элементы векторного регистра.</div><div class="t-redactor__text">После применения SIMD-инструкции сравнения мы получаем векторный регистр с результатами сравнения — условно можно представить его как «жёлто-зелёный» регистр, где:</div><div class="t-redactor__text"><ul><li data-list="bullet">1 (или "зелёный") — элементы совпали с target,</li><li data-list="bullet">0 (или "жёлтый") — нет совпадения.</li></ul></div><div class="t-redactor__text">Далее выполняем свёртку (например, логическим OR или суммированием), чтобы понять: было ли хотя бы одно совпадение в этом окне? Если результат больше нуля, значит — совпадение найдено.</div><div class="t-redactor__text">Чтобы выполнить это условие в обычной логике if, мы перемещаем результат из векторного регистра в обычный регистр с помощью инструкции VMOV — и уже после этого сравниваем его обычной командой CMP или аналогичной.</div><div class="t-redactor__text">Если значение больше нуля — значит, матч найден, и можно немедленно вернуть true.</div><div class="t-redactor__text">Таким образом, проверка выполняется эффективно, без цикла по каждому элементу, и с минимальной задержкой.</div><img src="https://static.tildacdn.com/tild6166-3338-4437-a535-656336323133/121f0a45c2a72d73fb9e.png"><div class="t-redactor__text">После этого сравнили:</div><img src="https://static.tildacdn.com/tild3864-3331-4562-b230-623932393462/ca0fbf1372f513e91c85.png"><div class="t-redactor__text">Если векторная свёртка даёт нулевой результат — это значит, что совпадений не найдено в текущем окне. В этом случае продолжаем итерацию: переходим к следующему 16-байтному фрагменту, если элементы ещё остались.</div><div class="t-redactor__text">Если же совпадение обнаружено (значение после свёртки больше нуля), мы немедленно возвращаем<strong> true</strong> — цель достигнута.</div><div class="t-redactor__text">Таким образом, мы реализуем эффективную функцию sliceContains, основанную на векторизации. Если запустить бенчмарк этой реализации, то окажется, что она работает примерно в 14 раз быстрее по сравнению с обычной циклической проверкой — и это действительно впечатляющий прирост производительности.</div><div class="t-redactor__text">Конечно, эффективность может зависеть от типа данных и целевой архитектуры, но в целом — это отличный пример того, как низкоуровневая оптимизация и SIMD-инструкции позволяют добиться значительного ускорения в реальных задачах.</div><img src="https://static.tildacdn.com/tild6639-6666-4863-b965-323632323063/d0675c4c7101ef8f0027.png"><div class="t-redactor__text">Важно понять саму идею: использование векторных инструкций — это универсальный инструмент, который может ускорить практически любую операцию, связанную с математикой, обработкой массивов или поиском значений, если задача позволяет это применить.</div><div class="t-redactor__text">Векторизация особенно эффективна в тех случаях, где операция одинакова над множеством элементов — например, в сравнении, суммировании, фильтрации, проверке условий.</div><div class="t-redactor__text">Более того, если вы примените такую оптимизированную функцию contains даже к отсортированному массиву, то на небольших размерах данных она способна опережать бинарный поиск по скорости.</div><div class="t-redactor__text">Да, несмотря на логарифмическую сложность бинарного поиска, затраты на условные переходы и кеш-промахи делают его менее выгодным при малом объёме данных. В то время как векторные инструкции обрабатывают данные «в лоб» — и это реально быстрее.</div><div class="t-redactor__text">Такой подход действительно используется на практике в системах, где важна производительность, например, в игровых движках, мультимедийных библиотеках, базах данных и высокочастотной обработке данных.</div><div class="t-redactor__text">Это пример того, как низкоуровневая оптимизация может принести реальную пользу в Go. Но возможности не ограничиваются только векторизацией — во третьей части рассмотрим, какие ещё аппаратные инструменты можно использовать, и как управлять сложными случаями, включая поддержку и переносимость таких решений.</div><h3  class="t-redactor__h3">go:string.”Выводы” SRODATA dupok size=12</h3><div class="t-redactor__text">На текущий момент стандартный компилятор Go не поддерживает автоматическую векторизацию. Известно, что работа в этом направлении ведётся, но встроенной поддержки пока нет.</div><div class="t-redactor__text">Ассемблер может быть полезен для оптимизации узких мест, когда стандартные средства языка не дают нужной производительности. Он позволяет использовать низкоуровневые инструкции, которые компилятор не применяет автоматически.</div><div class="t-redactor__text">Важно подходить к оптимизации осознанно: использовать ассемблер имеет смысл только в случаях, когда обнаружена реальная производительная проблема, и есть понимание, как её устранить.</div><div class="t-redactor__text">Поскольку ассемблер платформозависим, рекомендуется:</div><div class="t-redactor__text"><ul><li data-list="bullet">Иметь fallback-реализацию на Go, которую можно использовать в случае необходимости.</li><li data-list="bullet">Писать тесты для обеих реализаций, чтобы убедиться в корректности поведения.</li></ul></div><div class="t-redactor__text">Такой подход помогает безопасно использовать ассемблер в производственном коде и контролировать его влияние на поведение системы.</div><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Часть 1. Почему Go-ассемблер и векторизация могут быть полезны: идея для ускорения
</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://igoroutine.courses/free-materials/eud2d54di1-pochemu-go-assembler-i-vektorizatsiya-mo</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Часть 3. Векторизация на Go: CGo, транзакции, компиляторы, поддержка, байтовые инструкции</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://igoroutine.courses/free-materials/9k6zlakah1-vektorizatsiya-na-go-cgo-tranzaktsii-kom</div></td></tr></tbody><colgroup><col style="max-width:287.735px;min-width:287.735px;width:287.735px;"><col style="max-width:287.735px;min-width:287.735px;width:287.735px;"></colgroup></table></div></div><h3  class="t-redactor__h3">Дополнительные материалы</h3><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/golangconf-2024</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Asm source doc</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://cs.opensource.google/go/go/+/refs/tags/go1.23.2:src/cmd/internal/obj</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Asm go.dev quick guide</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://go.dev/doc/asm</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Summary of Go assembler</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://www.quasilyte.dev/blog/post/go-asm-complementary-reference/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Почему Golang такой странный / Филипп Кулин (Дремучий лес)</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=ql-uncsqoAU</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Go Assembler: ускорение кода гомоморфного хэширования в N раз /Евгений Стратоников (Neo SPCC)</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=foCV6nn4rkc</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="6" data-column="0"><div class="t-table__cell-content">GopherCon 2016: Rob Pike - The Design of the Go Assembler</div></td><td class="t-table__cell" data-row="6" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=KINIAgRpkDA</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="7" data-column="0"><div class="t-table__cell-content">Binary Search SIMD optimization</div></td><td class="t-table__cell" data-row="7" data-column="1"><div class="t-table__cell-content">https://clement-jean.github.io/simd_binary_search_tree/</div></td></tr></tbody><colgroup><col style="max-width:287.735px;min-width:287.735px;width:287.735px;"><col style="max-width:287.735px;min-width:287.735px;width:287.735px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Векторизация на Go: CGo, транзакции, компиляторы, поддержка, байтовые инструкции</title>
      <link>https://igoroutine.courses/free-materials/9k6zlakah1-vektorizatsiya-na-go-cgo-tranzaktsii-kom</link>
      <amplink>https://igoroutine.courses/free-materials/9k6zlakah1-vektorizatsiya-na-go-cgo-tranzaktsii-kom?amp=true</amplink>
      <pubDate>Mon, 06 Apr 2026 15:01:00 +0300</pubDate>
      <category>Computer Science</category>
      <category>Продвинутый Go</category>
      <enclosure url="https://static.tildacdn.com/tild3064-6534-4435-a336-313262646539/image.png" type="image/png"/>
      <turbo:content><![CDATA[<header><h1>Векторизация на Go: CGo, транзакции, компиляторы, поддержка, байтовые инструкции</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3064-6534-4435-a336-313262646539/image.png"/></figure><div class="t-redactor__embedcode"><div id="custom-article-toc"></div>

<style>
/* --- Базовые стили основного оглавления --- */
#custom-article-toc:empty { display: none; }

.custom-toc-container {
    background: rgba(26, 28, 40, 0.6); 
    border-radius: 12px;
    padding: 24px;
    font-family: 'ShareTechMono', sans-serif;
    margin-bottom: 24px;
}

.custom-toc-title {
    color: #ffffff;
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 16px;
    font-family: 'ShareTechMono', sans-serif;
}

.custom-toc-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* --- Стили для ссылок --- */
.custom-toc-link {
    color: rgba(255, 255, 255, 0.85) !important;
    text-decoration: none !important;
    font-size: 15px;
    transition: color 0.2s ease, transform 0.2s ease;
    display: flex; 
    align-items: center; 
    line-height: 1.4;
    cursor: pointer;
    font-family: 'ShareTechMono', sans-serif;
}

.custom-toc-link:hover {
    color: #F4721E !important; 
    transform: translateX(2px); 
}

/* --- Иерархия --- */
.custom-toc-level-h1 { margin-left: 0; font-weight: 600; color: #ffffff !important; }
.custom-toc-level-h2 { margin-left: 0; }
.custom-toc-level-h3 { margin-left: 8px; font-size: 14px; color: rgba(255, 255, 255, 0.6) !important; }

/* --- Скрытый блок с h4 --- */
.custom-toc-h4-wrapper {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 0; 
    overflow: hidden;
    transition: max-height 0.4s ease-in-out;
}

.custom-toc-h4-wrapper.is-open {
    max-height: 1000px; 
    margin-top: 6px; 
}

.custom-toc-level-h4 { 
    margin-left: 16px; 
    font-size: 13px; 
    color: rgba(255, 255, 255, 0.4) !important; 
}

/* --- Иконка стрелочки для h3 --- */
.custom-toc-toggle-icon {
    width: 14px;
    height: 14px;
    margin-right: 6px;
    transition: transform 0.3s ease;
    opacity: 0.7;
    flex-shrink: 0;
}

.custom-toc-level-h3.has-children.is-open .custom-toc-toggle-icon {
    transform: rotate(90deg); 
    color: #F4721E;
}


/* =======================================================
   СТИЛИ ДЛЯ ПЛАВАЮЩЕГО ПОПАПА (ПРАВЫЙ НИЖНИЙ УГОЛ)
   ======================================================= */
/* Кнопка-триггер */
.custom-toc-float-btn {
    position: fixed;
    bottom: 20px;
    right: 20px;
    background: #F4721E; /* Твой оранжевый */
    color: #1A1C28; /* Темный текст для контраста */
    padding: 12px 20px;
    border-radius: 8px;
    font-family: 'ShareTechMono', sans-serif;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    z-index: 9998;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    display: flex;
    align-items: center;
    gap: 8px;
    transition: transform 0.2s ease, background 0.2s ease;
}

.custom-toc-float-btn:hover {
    transform: translateY(-2px);
    background: #e06010;
}

.custom-toc-float-btn svg {
    width: 18px;
    height: 18px;
}

/* Само окно оглавления */
.custom-toc-float-window {
    position: fixed;
    bottom: 70px; /* Чуть выше кнопки */
    right: 20px;
    width: 320px;
    max-height: calc(100vh - 120px); /* Чтобы не вылезало за экран */
    background: rgba(18, 20, 30, 0.95); /* Чуть темнее основного фона */
    border: 1px solid rgba(244, 114, 30, 0.3); /* Оранжевая обводка */
    border-radius: 12px;
    padding: 20px;
    z-index: 9999;
    box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    overflow-y: auto;
    
    /* Анимация появления */
    opacity: 0;
    transform: translateY(20px);
    pointer-events: none;
    transition: all 0.3s ease;
}

.custom-toc-float-window.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* Кастомный скроллбар для окна */
.custom-toc-float-window::-webkit-scrollbar {
    width: 4px;
}
.custom-toc-float-window::-webkit-scrollbar-thumb {
    background: rgba(244, 114, 30, 0.5); 
    border-radius: 4px;
}

/* Шапка попапа с крестиком */
.custom-toc-float-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    padding-bottom: 12px;
}

.custom-toc-float-header .custom-toc-title {
    margin-bottom: 0; /* Убираем отступ, чтобы было в линию с крестиком */
    font-size: 16px;
}

.custom-toc-close-btn {
    background: none;
    border: none;
    color: rgba(255,255,255,0.6);
    cursor: pointer;
    padding: 4px;
    display: flex;
    transition: color 0.2s ease;
}
.custom-toc-close-btn:hover {
    color: #F4721E;
}

/* Показываем плавающую кнопку только на больших экранах */
/*@media screen and (max-width: 960px) {*/
/*    .custom-toc-float-btn, */
/*    .custom-toc-float-window {*/
/*        display: none !important;*/
/*    }*/
/*}*/
</style>

<script>
(function() {
    let attempts = 0;
    
    const tocInterval = setInterval(() => {
        attempts++;
        const tocWrapper = document.getElementById('custom-article-toc');
        
        if (tocWrapper && !tocWrapper.hasAttribute('data-built')) {
            const articleBody = tocWrapper.closest('.js-feed-post-text') || tocWrapper.closest('.t-redactor__tte-view');
            
            if (articleBody) {
                const headings = articleBody.querySelectorAll('h1, h2, h3, h4');
                
                if (headings.length > 0) {
                    const tocContainer = document.createElement('div');
                    tocContainer.className = 'custom-toc-container';
                    
                    const tocTitle = document.createElement('div');
                    tocTitle.className = 'custom-toc-title';
                    tocTitle.textContent = 'Содержание статьи';
                    tocContainer.appendChild(tocTitle);

                    const tocList = document.createElement('div');
                    tocList.className = 'custom-toc-list';

                    let currentH3Item = null;
                    let currentH4Wrapper = null;

                    headings.forEach((heading) => {
                        if (!heading.id) {
                            heading.id = 'heading-' + Math.random().toString(36).substr(2, 9);
                        }

                        const tagName = heading.tagName.toLowerCase();
                        
                        if (tagName === 'h3') {
                            const listItem = document.createElement('div');
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-h3';
                            link.textContent = heading.textContent.trim();
                            
                            link.targetHeading = heading;
                            
                            listItem.appendChild(link);
                            tocList.appendChild(listItem);
                            
                            currentH3Item = listItem;
                            currentH4Wrapper = null; 
                            
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                
                                if (link.classList.contains('has-children')) {
                                    const wrapper = link.nextElementSibling;
                                    
                                    if (e.target.closest('.custom-toc-toggle-icon')) {
                                        link.classList.toggle('is-open');
                                        wrapper.classList.toggle('is-open');
                                        return;
                                    }
                                    
                                    if (!link.classList.contains('is-open')) {
                                        link.classList.add('is-open');
                                        wrapper.classList.add('is-open');
                                    } else {
                                        link.targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                        // Опционально: закрывать попап при клике
                                        // document.querySelector('.custom-toc-float-window').classList.remove('is-visible');
                                    }
                                } else {
                                    link.targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                }
                            });
                            
                        } else if (tagName === 'h4' && currentH3Item) {
                            if (!currentH4Wrapper) {
                                currentH4Wrapper = document.createElement('div');
                                currentH4Wrapper.className = 'custom-toc-h4-wrapper';
                                currentH3Item.appendChild(currentH4Wrapper);
                                
                                const parentH3Link = currentH3Item.querySelector('.custom-toc-level-h3');
                                parentH3Link.classList.add('has-children');
                                
                                const chevronSvg = '<svg class="custom-toc-toggle-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>';
                                parentH3Link.insertAdjacentHTML('afterbegin', chevronSvg);
                            }
                            
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-h4';
                            link.textContent = heading.textContent.trim();
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            });
                            currentH4Wrapper.appendChild(link);
                            
                        } else {
                            const link = document.createElement('a');
                            link.className = 'custom-toc-link custom-toc-level-' + tagName;
                            link.textContent = heading.textContent.trim();
                            link.addEventListener('click', function(e) {
                                e.preventDefault();
                                heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            });
                            tocList.appendChild(link);
                            
                            currentH3Item = null;
                            currentH4Wrapper = null;
                        }
                    });

                    tocContainer.appendChild(tocList);
                    tocWrapper.appendChild(tocContainer);
                    
                    
                    /* =======================================================
                       СОЗДАЕМ ПЛАВАЮЩИЙ UI
                       ======================================================= */
                    
                    // Кнопка
                    const floatBtn = document.createElement('div');
                    floatBtn.className = 'custom-toc-float-btn';
                    floatBtn.innerHTML = `
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
                        Содержание
                    `;
                    document.body.appendChild(floatBtn);

                    // Окно попапа
                    const floatWindow = document.createElement('div');
                    floatWindow.className = 'custom-toc-float-window';
                    
                    // Шапка окна
                    const floatHeader = document.createElement('div');
                    floatHeader.className = 'custom-toc-float-header';
                    floatHeader.innerHTML = `
                        <div class="custom-toc-title">Содержание статьи</div>
                        <button class="custom-toc-close-btn" aria-label="Закрыть">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
                        </button>
                    `;
                    
                    // Клонируем собранный список ссылок
                    const floatList = tocList.cloneNode(true);
                    
                    // Оживляем клики в клонированном списке
                    floatList.addEventListener('click', function(e) {
                        const link = e.target.closest('.custom-toc-link');
                        if (!link) return;
                        
                        e.preventDefault();
                        
                        // Ищем оригинальный заголовок в статье по тексту
                        const linkText = link.textContent.trim();
                        let targetHeading = null;
                        Array.from(headings).forEach(h => {
                            if (h.textContent.trim() === linkText) targetHeading = h;
                        });

                        if (!targetHeading) return;

                        if (link.classList.contains('has-children')) {
                            const wrapper = link.nextElementSibling;
                            if (e.target.closest('.custom-toc-toggle-icon')) {
                                link.classList.toggle('is-open');
                                wrapper.classList.toggle('is-open');
                                return;
                            }
                            if (!link.classList.contains('is-open')) {
                                link.classList.add('is-open');
                                wrapper.classList.add('is-open');
                            } else {
                                targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                            }
                        } else {
                            targetHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                        }
                    });

                    floatWindow.appendChild(floatHeader);
                    floatWindow.appendChild(floatList);
                    document.body.appendChild(floatWindow);

                    // Логика открытия/закрытия
                    floatBtn.addEventListener('click', () => {
                        floatWindow.classList.toggle('is-visible');
                    });
                    
                    floatWindow.querySelector('.custom-toc-close-btn').addEventListener('click', () => {
                        floatWindow.classList.remove('is-visible');
                    });


                    tocWrapper.setAttribute('data-built', 'true');
                    tocWrapper.id = 'custom-article-toc-ready';
                }
            }
            clearInterval(tocInterval);
        }
        
        if (attempts > 20) {
            clearInterval(tocInterval);
        }
    }, 500);
})();
</script></div><div class="t-redactor__text"><a href="https://igoroutine.courses/free-materials/eud2d54di1-pochemu-go-assembler-i-vektorizatsiya-mo">В первой части статьи мы рассмотрели</a>, как можно вручную ускорить Go-код с помощью векторизации и SIMD-инструкций, реализованных через Go-ассемблер. Написали простую, но показательно быструю реализацию sliceContains и увидели, что даже базовая векторизация может дать ускорение в 10–14 раз по сравнению со стандартной реализацией.</div><div class="t-redactor__text"><a href="https://igoroutine.courses/free-materials/e1btztez01-vektorizatsiya-i-simd-v-go-uskorenie-poi">Во второй части статьи</a> погрузились в практическое применение SIMD в Go-ассемблере, реализовали функцию SliceContainsV1 и изучили, как с помощью VADD, VDUP и других инструкций можно добиться 10–14-кратного ускорения простых задач.</div><div class="t-redactor__text">Но возможности оптимизации Go-программ на этом не заканчиваются. В этой части мы пойдём дальше: рассмотрим другие техники низкоуровневой оптимизации — от использования C-кода и альтернативных компиляторов с поддержкой векторизации до работы с аппаратными транзакциями памяти на Intel. Поговорим о том, как внедрять ассемблер в продакшен-код, не боясь за его поддержку, и как обойти ограничения стандартного Go-компилятора.</div><div class="t-redactor__text">Привет! Меня зовут Игорь Панасюк, я работаю в Яндекс, преподаю в ИТМО, а также в свободное время выступаю на конференциях, делюсь опытом в соцсетях и помогаю развитию Go-сообщества, веду <a href="https://t.me/igoroutine" target="_blank" rel="noreferrer noopener">телеграм-канал</a> и <a href="https://www.youtube.com/@igoroutine" target="_blank" rel="noreferrer noopener">youtube-канал</a>. Если вы уже знакомы с базовыми техниками векторизации, эта часть поможет глубже понять, как устроены продвинутые способы ускорения Go-кода и на что стоит обратить внимание при работе с архитектурно-зависимыми оптимизациями.</div><h2  class="t-redactor__h2">CGo</h2><div class="t-redactor__text">Язык Go поддерживает вызовы на C через механизм CGo, что позволяет использовать низкоуровневые оптимизации и сторонние библиотеки, написанные на C. Это особенно полезно, если вы хотите использовать SIMD-инструкции, которые не доступны напрямую в Go.</div><div class="t-redactor__text">Хотя в Go есть встроенные intrinsic-функции (в основном для нужд рантайма и компилятора), полноценной поддержки SIMD intrinsics на уровне языка пока нет. Поэтому для задач, где важна векторизация, логичным шагом будет реализовать соответствующую функцию на C и вызвать её из Go-кода.</div><div class="t-redactor__text">Чтобы написать реализацию contains на C, используя SIMD-инструкции и intrinsics, воспроизводим ту же логику, что ранее реализовали на ассемблере:</div><div class="t-redactor__text"><ol><li data-list="ordered">Загружаем данные в вектор (vec = VLD(...))</li><li data-list="ordered">Выполняем сравнение</li><li data-list="ordered">Сворачиваем результат</li><li data-list="ordered">Возвращаем true, если найдено совпадение</li></ol></div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">bool slice_contains(const uint8_t *slice, size_t size, uint8_t value) {
   uint8x16_t val_vec = vdupq_n_u8(value);

   for (size_t i = 0; i &lt; size; i += 16) {
       uint8x16_t data_vec = vld1q_u8(&amp;slice[i]);
       uint8x16_t result_vec = vceqq_u8(data_vec, val_vec);
       uint16_t result = vaddvq_u8(result_vec);

       if (result) {
           return true;
       }

    }

    return false;

}</code></pre><img src="https://static.tildacdn.com/tild3463-6134-4338-a235-363331326638/image.png"><div class="t-redactor__text"><div class="ql-code-block" data-language="plain"><br /></div></div><div class="t-redactor__text">Затем с помощью CGo подключаем этот C-фрагмент в Go-программу и можем вызывать его как обычную функцию. Получаем сочетание удобства Go и производительности низкоуровневого SIMD-кода на C.</div><div class="t-redactor__text">Посмотрим, что из этого получим. Точно такая же программа, только написанная на C:</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">import &quot;C&quot;

import (
   &quot;unsafe&quot;
)

func SliceContains(data []uint8, target uint8) bool {
   return bool(C.slice_contains((*C.uint8_t)(unsafe.SliceData(data)),
C.size_t(len(data)), C.uint8_t(target)))
}</code></pre><div class="t-redactor__text">Вызовем её из Go, используя CGo.\</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">go test -bench=. -benchmem -cpu=1
goos: darwin
goarch: arm64
pkg: asm/simd/slice_contains
cpu: Apple M3 Pro
BenchmarkSliceContains/SliceContainsV1_(SIMD)               63256          18263 ns/op
BenchmarkSliceContains/SliceContainsVO                       4813         252514 ns/op
BenchmarkSliceContains/SliceContainsCgo                     56691          21913 ns/op 
PASS
ok      asm/simd/slice_contains 4.359s</code></pre><div class="t-redactor__text">Посмотрим на бенчмарки и увидим, что в целом CGo даёт чуть больше накладных расходов, но разница небольшая. В целом, с этим можно жить и использовать.</div><img src="https://static.tildacdn.com/tild6332-3530-4039-a537-356162643234/image.png"><h3  class="t-redactor__h3">Другие способы</h3><div class="t-redactor__text">Кроме CGo есть и другие способы. Я сам их не использовал, но знаю примеры, когда они помогали другим. Речь идёт о компиляторах, способных самостоятельно производить векторизацию на этапе сборки:</div><div class="t-redactor__text"><ul><li data-list="bullet"><strong>gccgo</strong> — компилятор на базе GCC, использующий gofrontend и libgo.</li><li data-list="bullet"><strong>gollvm</strong> — компилятор на основе LLVM, также использующий gofrontend.</li></ul></div><div class="t-redactor__text">У этих двух компиляторов одинаковый фронтенд и рантайм, но разные бэкенды — GCC и LLVM соответственно. Это и даёт им ключевое преимущество: возможность векторизации циклов на уровне компиляции.</div><div class="t-redactor__text">Идея проста: компилятор анализирует код, обнаруживает потенциально оптимизируемые циклы (например, линейные проходы по массивам с арифметикой или сравнениями) и автоматически генерирует SIMD-инструкции. В результате вы получаете более производительный бинарник — без необходимости писать низкоуровневый код вручную.</div><div class="t-redactor__text">Хотя такие случаи пока не слишком распространены, есть реальные примеры, где это давало ощутимый прирост производительности. Если у вас есть простое, но вычислительно нагруженное Go-приложение, содержащее циклы, которые можно эффективно векторизовать, попробуйте собрать его с помощью gccgo или gollvm. Это может дать неожиданный положительный результат.</div><div class="t-redactor__text">Важно: в рамках этой статьи нет конкретных бенчмарков, потому что область остаётся исследовательской. Но если вы работаете с подобными задачами, эксперимент с альтернативными компиляторами — это точно то, что стоит попробовать.</div><div class="t-redactor__text">Хотя компиляторы gccgo и gollvm предлагают потенциальные выгоды в производительности за счёт векторизации, у них есть и некоторые ограничения, особенно в части работы с runtime и сборщиком мусора (GC).</div><div class="t-redactor__text">Один из таких нюансов — это работа со стек-мапами (stack maps). Дефолтный компилятор Go (gc, используемый в go build) генерирует оптимизированные стек-мапы, которые помогают garbage collector (GC) более точно понимать, где именно на стеке находятся указатели на объекты в куче. Это позволяет выполнять сборку мусора быстрее и эффективнее — особенно в многопоточной среде и при больших объёмах аллокаций.</div><div class="t-redactor__text">В отличие от этого, runtime от gollvm и gccgo использует более консервативный подход. Он не всегда может точно указать расположение указателей в стеке, и в результате GC может быть вынужден перестраховываться, сканируя больше данных и работая медленнее.</div><div class="t-redactor__text">Таким образом:</div><div class="t-redactor__text"><ul><li data-list="bullet">В CPU-интенсивных задачах, где сборка мусора почти не задействуется, это может не иметь значения.</li><li data-list="bullet">Но в приложениях с большим количеством аллокаций и активной работой GC влияние может быть ощутимым.</li></ul></div><div class="t-redactor__text">Помимо стандартного Go-компилятора, существуют другие компиляторы, которые поддерживают векторизацию «из коробки». С их помощью можно автоматически ускорить участки кода — особенно те, где активно используются циклы и вычисления. Но придётся жить с небольшими накладными расходами.</div><h2  class="t-redactor__h2">Где ещё может пригодится ассемблер</h2><div class="t-redactor__text">Не только векторизация способна помочь. У современных процессоров существует множество расширений, которые тоже могут упростить жизнь. Простой пример: инструкции работы с криптографией. </div><div class="t-redactor__text">Есть примеры и интереснее: представим, что вы хотите реализовать так называемый Hardware Transaction Manager. Если взять современную базу данных, то под капотом у них будет MVCC. Этот MVCC работает как раз на Software Transaction Manager (управление состояниями).</div><div class="t-redactor__text">Современные процессоры, особенно от Intel, предоставляют уникальные аппаратные инструкции, которые можно использовать для реализации продвинутых механизмов работы с памятью — например, транзакционной памяти.</div><div class="t-redactor__text">Пример: Intel Transactional Synchronization Extensions (TSX). На архитектуре Intel доступен набор инструкций:</div><div class="t-redactor__text"><ul><li data-list="bullet">XBEGIN — начало транзакции</li><li data-list="bullet">XEND — завершение транзакции</li><li data-list="bullet">XABORT — принудительное прерывание транзакции</li></ul></div><div class="t-redactor__text">Эти инструкции позволяют создать транзакции в памяти, работающие на уровне когерентности кэшей. То есть, процессор сам отслеживает изменения в кэш-линии и при конфликте автоматически откатывает изменения — без участия классических блокировок (mutex/spinlock).</div><div class="t-redactor__text">Это даёт возможность реализовать гибридный Transaction Manager: сначала попытка выполнить транзакцию аппаратно, если не получилось — fallback на программную реализацию. Вы получаете потенциально меньше накладных расходов, выше производительность на многопроцессорных системах при правильном использовании. Профит!</div><div class="t-redactor__text">Но важно учесть, что TSX плохо работает с большими объёмами данных — транзакция может прерваться, если затрагиваются слишком много кеш линий или возникают прерывания. Не все процессоры поддерживают TSX.</div><div class="t-redactor__text">Важно понимать, что современные железки содержат множество расширений, которые могут быть полезны для оптимизации под конкретную задачу:</div><div class="t-redactor__text"><ul><li data-list="bullet">Векторные инструкции (SSE, AVX, NEON)</li><li data-list="bullet">Транзакционные расширения (TSX)</li><li data-list="bullet">Специализированные команды для атомарных операций, криптографии и т.д.</li></ul></div><div class="t-redactor__text">Всё это можно использовать вручную через Assembly или встроенные примитивы в Go (или через CGo).</div><div class="t-redactor__text">Если у вас есть узкое место в коде, и вы точно знаете, где и почему оно возникает — имеет смысл посмотреть, какие аппаратные инструкции доступны на вашей архитектуре, и попробовать использовать их напрямую. Иногда это может дать радикальный прирост производительности, особенно в системном программировании или high-performance системах.</div><h2  class="t-redactor__h2">Директивы для голых инструкций </h2><div class="t-redactor__text">Есть специальные директивы WORD и BYTE, которые позволяют вызывать голую инструкцию. Если взять программу на C, ее декомпилируете, получаете буквально байты инструкции.</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">• WORD
• BYTE

gcc main.c
otool -tvj a.out

0000000100003f24          3dc02fe1         ldr q1, [sp, #0xb0]
0000000100003f28          4e21d400         fadd.4s v0, v0, v1
0000000100003f2c          3d802be0         str q0, [sp, #0xa0]
0000000100003f30          3dc02be0         Idr q0, [sp, #0xa0]

ENDIAN!</code></pre><div class="t-redactor__text">Рассматриваемая инструкция выполняет сложение чисел с плавающей точкой (float). По умолчанию она не поддерживается в Go-ассемблере, поэтому её необходимо встраивать вручную через машинные коды.</div><div class="t-redactor__text"><strong>Важно</strong>: учитывайте порядок байтов (endianness) при вставке инструкций напрямую. Например, вы получаете сырые байты инструкции, такие как 4e21d400 и подобные.</div><div class="t-redactor__text">Дальнейшая реализация — аналогична примеру со сложением, показанному ранее: загрузка данных, выполнение инструкции и сохранение результата.</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">#include &quot;textflag.h&quot;

// func vectorFloatAdditionV1(first, second, dst []float32)
TEXT ·vectorFloatAdditionV1(SB), NOSPLIT, $0
   LDP first_base+0(FP), (R0, R1)
   LDP second_base+24(FP), (R2, R3)
   LDP dst_base+48(FP), (R4, R5)

   MOVD $0, R7

loop:
     CMP R5, R7
  BGE done</code></pre><div class="t-redactor__text">Теперь вы можете напрямую вызывать машинную инструкцию в Go-ассемблере, вставляя её в виде байтов. Это позволяет использовать любую инструкцию, которая поддерживается вашей архитектурой процессора, даже если она не предусмотрена синтаксисом Go-ассемблера.</div><pre class="t-redactor__highlightcode"><code data-lang="{$la}">   VLD1 (R0), [V0.S4]
   VLD1 (R2), [V1.S4]

   WORD $0x4e21d400 // fadd.4s v0, v0, v1

   VST1 [V0.S4], (R4)

   ADD $4, R7
   ADD $16, R4
   ADD $16, R0
   ADD $16, R2

   B loop

done:
   RET</code></pre><div class="t-redactor__text">WORD $0x4e21d400</div><div class="t-redactor__text">Я встречал примеры, где разработчики просто вставляют инструкции через WORD-директивы. Такой подход позволяет вызывать любую поддерживаемую инструкцию целевой платформы, что может быть полезно для написания более эффективного низкоуровневого кода.</div><h2  class="t-redactor__h2">Выводы</h2><div class="t-redactor__text">Хотя ассемблер даёт максимальный контроль над производительностью, он не всегда — единственный или даже лучший путь. В этой части мы рассмотрели альтернативные методы, которые позволяют ускорять Go-код без глубокого погружения в низкоуровневую реализацию.</div><div class="t-redactor__text">Вот основные выводы:</div><div class="t-redactor__text"><ul><li data-list="bullet"><strong>CGo</strong> позволяет вызывать функции на C и использовать SIMD intrinsics, если вы не боитесь небольших накладных расходов. Это простой способ внедрить оптимизированные фрагменты, особенно когда такие библиотеки уже существуют.</li><li data-list="bullet"><strong>Альтернативные компиляторы Go</strong> — такие как gccgo и gollvm — могут самостоятельно векторизовать циклы. Это удобно, если вы хотите сохранить чистоту Go-кода, но при этом получить ускорение на этапе сборки. Важно: нужно внимательно тестировать и учитывать различия в runtime и GC.</li><li data-list="bullet"><strong>Векторизация "по умолчанию"</strong> в некоторых компиляторах может дать прирост производительности без ручного труда. Это особенно полезно для кода, содержащего арифметику в циклах, обработку массивов или матриц.</li><li data-list="bullet"><strong>Аппаратные расширения</strong> современных CPU можно использовать в Go, даже, если они не поддержаны в ассемблере на уровне синтаксиса, используя «голые инструкции».</li></ul></div><div class="t-redactor__text">Важно понимать: использовать такие подходы стоит только тогда, когда есть узкое место и профилирование подтверждает необходимость оптимизации. Без этого любые попытки «ускорить на всякий случай» превращаются в преждевременную оптимизацию.</div><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Часть 1. Почему Go-ассемблер и векторизация могут быть полезны: идея для ускорения
</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://igoroutine.courses/free-materials/eud2d54di1-pochemu-go-assembler-i-vektorizatsiya-mo</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Часть 2. Векторизация и SIMD в Go: ускорение поиска и сравнения в массивах</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://igoroutine.courses/free-materials/e1btztez01-vektorizatsiya-i-simd-v-go-uskorenie-poi</div></td></tr></tbody><colgroup><col style="max-width:287.735px;min-width:287.735px;width:287.735px;"><col style="max-width:287.735px;min-width:287.735px;width:287.735px;"></colgroup></table></div></div><h2  class="t-redactor__h2">Дополнительные материалы</h2><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Исходный код</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://github.com/IgorWalther/golangconf-2024</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Asm source doc</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://cs.opensource.google/go/go/+/refs/tags/go1.23.2:src/cmd/internal/obj</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Asm go.dev quick guide</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://go.dev/doc/asm</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="3" data-column="0"><div class="t-table__cell-content">Summary of Go assembler</div></td><td class="t-table__cell" data-row="3" data-column="1"><div class="t-table__cell-content">https://www.quasilyte.dev/blog/post/go-asm-complementary-reference/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="4" data-column="0"><div class="t-table__cell-content">Почему Golang такой странный / Филипп Кулин (Дремучий лес)</div></td><td class="t-table__cell" data-row="4" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=ql-uncsqoAU</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="5" data-column="0"><div class="t-table__cell-content">Go Assembler: ускорение кода гомоморфного хэширования в N раз /Евгений Стратоников (Neo SPCC)</div></td><td class="t-table__cell" data-row="5" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=foCV6nn4rkc</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="6" data-column="0"><div class="t-table__cell-content">GopherCon 2016: Rob Pike - The Design of the Go Assembler</div></td><td class="t-table__cell" data-row="6" data-column="1"><div class="t-table__cell-content">https://www.youtube.com/watch?v=KINIAgRpkDA</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="7" data-column="0"><div class="t-table__cell-content">Binary Search SIMD optimization</div></td><td class="t-table__cell" data-row="7" data-column="1"><div class="t-table__cell-content">https://clement-jean.github.io/simd_binary_search_tree/</div></td></tr></tbody><colgroup><col style="max-width:287.735px;min-width:287.735px;width:287.735px;"><col style="max-width:287.735px;min-width:287.735px;width:287.735px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
    <item turbo="true">
      <title>Как проходить собеседования в IT: полный гайд от HR-скрининга до офера</title>
      <link>https://igoroutine.courses/free-materials/5t0zdcn051-kak-prohodit-sobesedovaniya-v-it-polnii</link>
      <amplink>https://igoroutine.courses/free-materials/5t0zdcn051-kak-prohodit-sobesedovaniya-v-it-polnii?amp=true</amplink>
      <pubDate>Mon, 20 Apr 2026 20:26:00 +0300</pubDate>
      <category>Собеседования</category>
      <enclosure url="https://static.tildacdn.com/tild3635-6232-4336-b132-616135613265/Sobes_final2.jpg" type="image/jpeg"/>
      <turbo:content><![CDATA[<header><h1>Как проходить собеседования в IT: полный гайд от HR-скрининга до офера</h1></header><figure><img alt="" src="https://static.tildacdn.com/tild3635-6232-4336-b132-616135613265/Sobes_final2.jpg"/></figure><div class="t-redactor__embedcode"><div class="video-widget-container"
    data-yt="icrHpP0aHOM"
    data-vk="oid=-231166035&id=456239091&hash=3d07e7e9b1bd24cc&hd=4"
    data-rutube=""
    data-dzen="">     
  <amp-youtube
    data-videoid="icrHpP0aHOM"
    layout="responsive"
    width="16"
    height="9">
  </amp-youtube>
</div></div><div class="t-redactor__text">В этом видео мы разберем полный цикл собеседований в IT: от первого разговора с HR до финалов, офера и переговоров по зарплате. Ты узнаешь, как правильно себя вести на каждом этапе, какие ошибки чаще всего ломают впечатление о кандидате и почему собеседование — это не столько проверка знаний, сколько отдельный навык, который можно натренировать.<br /><br /><strong>В этом уроке:</strong><br /><br /><ul><li data-list="bullet"><strong>Правильный майндсет перед собеседованием:</strong> почему важно идти не с установкой «лишь бы не провалиться», а с пониманием, что в любом случае ты выигрываешь — либо получаешь офер, либо опыт и обратную связь.</li><li data-list="bullet"><strong>Как проходить HR-скрининг:</strong> что именно хочет услышать HR, почему не стоит перегружать разговор техническими деталями, как кратко рассказывать про опыт и какие ответы лучше подготовить заранее.</li><li data-list="bullet"><strong>Первое впечатление и самопрезентация:</strong> как внешний вид, камера, звук, уверенная речь и спокойная подача влияют на решение ещё до технической части.</li><li data-list="bullet"><strong>Как говорить про прошлую работу:</strong> почему нельзя поливать грязью бывших работодателей, как корректно отвечать на вопрос «почему ушёл» и как продавать свой опыт без прямого вранья.</li><li data-list="bullet"><strong>Зарплатные переговоры:</strong> когда лучше называть ожидания, как ориентироваться по вилке, в каких случаях можно отложить разговор о деньгах и как торговаться после офера.</li><li data-list="bullet"><strong>Что такое скрининг и зачем он нужен:</strong> почему компании вводят промежуточный технический фильтр и как вести себя на коротком собеседовании, где оценивают базу и скорость мышления.</li><li data-list="bullet"><strong>Как готовиться к техническим этапам:</strong> почему современные собеседования часто опираются на шаблонные вопросы и типовые задачи, где искать такие вопросы и как готовиться быстрее всего.</li><li data-list="bullet"><strong>Поведение на языковой и технической секции:</strong> как отвечать не сухо, а через реальный опыт, как аккуратно уводить разговор в сильную для тебя сторону и почему это реально повышает шансы.</li><li data-list="bullet"><strong>Алгоритмические собеседования:</strong> как правильно проговаривать решение, оценивать время и память, просить подсказки, тестировать код вслух и не зависать в молчании.</li><li data-list="bullet"><strong>System Design-собеседования:</strong> какие вопросы обязательно уточнять в начале, почему нельзя сразу бежать рисовать архитектуру и как не утонуть в деталях за ограниченное время.</li><li data-list="bullet"><strong>Финалы и culture fit:</strong> что проверяют на последних этапах, какие вопросы могут задать про конфликты, командную работу и процессы, и как отвечать на такие темы по структуре.</li><li data-list="bullet"><strong>Какие вопросы задавать компании:</strong> что спросить про цели команды, процессы, состав, зону ответственности и как по ответам понять, стоит ли вообще идти в этот проект.</li><li data-list="bullet"><strong>Тестовые задания:</strong> когда их есть смысл делать, когда можно попробовать договориться и почему в реальности их часто смотрят гораздо поверхностнее, чем принято думать.</li><li data-list="bullet"><strong>Как не завалиться из-за волнения:</strong> практические способы снизить стресс перед интервью — прогулка, физическая нагрузка, дыхание и правильная внутренняя установка.</li><li data-list="bullet"><strong>Как собеседуют и как ловят читеров:</strong> почему вопросы без однозначного ответа лучше всего показывают реальный уровень кандидата и как интервьюеры проверяют глубину понимания.</li><li data-list="bullet"><strong>Почему знание не равно действию:</strong> главный вывод о том, что побеждает не тот, кто читает гайды, а тот, кто регулярно ходит на реальные собеседования и превращает теорию в навык.</li></ul><br />В итоге у тебя будет целостная картина того, как устроены современные IT-собеседования: что происходит на каждом этапе, как себя презентовать, как отвечать сильнее большинства кандидатов и как снизить стресс, чтобы показать свой реальный уровень. Этот материал поможет проходить интервью спокойнее, увереннее и с гораздо более понятной стратегией.</div><h4  class="t-redactor__h4">Дополнительные материалы к видео</h4><div class="t-table__viewport"><div class="t-table__wrapper"><table class="t-table__table" style="--t-table-border-radius:8px;"><tbody><tr class="t-table__row"><td class="t-table__cell" data-row="0" data-column="0"><div class="t-table__cell-content">Cultural fit вопросы</div></td><td class="t-table__cell" data-row="0" data-column="1"><div class="t-table__cell-content">https://resumekraft.com/cultural-fit-interview-questions-and-answers/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="1" data-column="0"><div class="t-table__cell-content">Пример резюме с акцентом на цифры</div></td><td class="t-table__cell" data-row="1" data-column="1"><div class="t-table__cell-content">https://www.linkedin.com/in/igor-panasyuk/</div></td></tr><tr class="t-table__row"><td class="t-table__cell" data-row="2" data-column="0"><div class="t-table__cell-content">Репозиторий с задачами</div></td><td class="t-table__cell" data-row="2" data-column="1"><div class="t-table__cell-content">https://github.com/liquidslr/leetcode-company-wise-problems</div></td></tr></tbody><colgroup><col style="max-width:359px;min-width:359px;width:359px;"><col style="max-width:359px;min-width:359px;width:359px;"></colgroup></table></div></div>]]></turbo:content>
    </item>
  </channel>
</rss>
