<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Go: Under the Hood</title>
    <link>/ru/</link>
    <description>Recent content on Go: Under the Hood</description>
    <generator>Hugo</generator>
    <language>ru</language>
    <atom:link href="/ru/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Введение</title>
      <link>/ru/preface/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/preface/</guid>
      <description>&lt;h1 id=&#34;введение&#34;&gt;Введение&lt;/h1&gt;&#xA;&lt;p&gt;Язык Go существует уже более десяти лет с момента его появления в 2009 году.&#xA;Оглядываясь на историю большинства языков программирования, удивительно то, что за эти годы эволюции Go&#xA;сам язык не претерпел сильных изменений, и пользователи Go смогли продолжать писать приложения, сохраняющие обратную совместимость.&#xA;С точки зрения дизайна языка, Go с самого начала создавался на принципах низкой стоимости, высокой конкурентности и простоты, и сложно не заинтересоваться механизмами реализации и конкретными принципами работы, стоящими за таким простым дизайном.&#xA;Эта книга обсуждает технические принципы исходного кода Go и путь их эволюции.&lt;/p&gt;</description>
    </item>
    <item>
      <title>How to Read This Book</title>
      <link>/ru/how-to-read/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/how-to-read/</guid>
      <description>&lt;h1 id=&#34;how-to-read-this-book&#34;&gt;How to Read This Book&lt;/h1&gt;&#xA;&lt;p&gt;A reader once told the author that this book is hard going. They had studied Go and used it for a few years, yet when they hit the chapters on scheduling, stealing, and memory, they could still only talk in the abstract. That feedback is sincere, and the author accepts it. Part of the depth this book aims for comes from decades of accumulated work in systems, concurrency, and compilation, and the slope really is steep on first contact. But steep does not mean the only option is to grind through it. What a book can offer is a few paths that make the slope gentler. This page is about how to walk those paths.&lt;/p&gt;</description>
    </item>
    <item>
      <title>1.1 The Evolution of Programming Languages</title>
      <link>/ru/part1overview/ch01intro/history/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch01intro/history/</guid>
      <description>&lt;h1 id=&#34;11-the-evolution-of-programming-languages&#34;&gt;1.1 The Evolution of Programming Languages&lt;/h1&gt;&#xA;&lt;p&gt;To understand why Go turned out the way it did, why it is so restrained, so obsessed with compilation speed, so concerned with concurrency, we first have to look at the language landscape it was born into, and at whose pain it set out to solve. Go was not designed in a vacuum. It is the response of a group of people who spent their careers writing systems software, a response to their long-standing dissatisfaction with the mainstream languages of the time. Understanding this background gives a unified starting point for all the trade-offs the rest of this book examines, in the scheduler, the memory model, and generics.&lt;/p&gt;</description>
    </item>
    <item>
      <title>1.2 An Overview of the Go Language</title>
      <link>/ru/part1overview/ch01intro/go/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch01intro/go/</guid>
      <description>&lt;h1 id=&#34;12-an-overview-of-the-go-language&#34;&gt;1.2 An Overview of the Go Language&lt;/h1&gt;&#xA;&lt;p&gt;This section builds a skeleton for the whole book. It is not a syntax manual; Go&amp;rsquo;s syntax is explained clearly in any introductory book, and repeating it would serve no purpose.&#xA;What we want to do is first establish a &lt;strong&gt;bird&amp;rsquo;s-eye view of the whole&lt;/strong&gt;: which layers make up Go, which &lt;strong&gt;distinctive&lt;/strong&gt; design decisions it carries,&#xA;and where in this book each of those decisions is developed. Once you have read this section, you can dive into any later chapter without losing your way.&lt;/p&gt;</description>
    </item>
    <item>
      <title>1.3 Communicating Sequential Processes</title>
      <link>/ru/part1overview/ch01intro/csp/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch01intro/csp/</guid>
      <description>&lt;h1 id=&#34;13-communicating-sequential-processes&#34;&gt;1.3 Communicating Sequential Processes&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;This section comes with an online talk: &lt;a href=&#34;https://www.youtube.com/watch?v=Z8ZpWVuEx8c&#34;&gt;YouTube&lt;/a&gt;, &lt;a href=&#34;https://changkun.de/s/csp/&#34;&gt;Google Slides&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The intellectual source of Go&amp;rsquo;s concurrency model is &lt;strong&gt;Communicating Sequential Processes&lt;/strong&gt; (CSP), which Hoare proposed in CACM in 1978. This section traces that lineage from the perspective of the history of ideas, because it explains why goroutines and channels look the way they do today. The implementation details of channels and select (the ring buffer in hchan, the pairing of &lt;code&gt;gopark&lt;/code&gt; and &lt;code&gt;goready&lt;/code&gt;, the two-round locking in &lt;code&gt;selectgo&lt;/code&gt;) are left for &lt;a href=&#34;../../../part3concurrency/ch10chan/readme&#34;&gt;Chapter 10&lt;/a&gt;. Here we are concerned with &amp;ldquo;why CSP&amp;rdquo;, and with how that choice has shaped the appearance of Go programs.&lt;/p&gt;</description>
    </item>
    <item>
      <title>2.1 The Plan 9 Assembly Language</title>
      <link>/ru/part1overview/ch02asm/asm/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch02asm/asm/</guid>
      <description>&lt;h1 id=&#34;21-the-plan-9-assembly-language&#34;&gt;2.1 The Plan 9 Assembly Language&lt;/h1&gt;&#xA;&lt;p&gt;Read the source of the Go runtime long enough, and sooner or later you run into code that looks&#xA;like assembly yet not quite like any assembly you are familiar with. That is Go&amp;rsquo;s&#xA;&lt;strong&gt;Plan 9 style assembly&lt;/strong&gt;. We will keep returning to it as we dissect the scheduler, stack&#xA;switching, and atomic operations: &lt;code&gt;gogo&lt;/code&gt;, &lt;code&gt;mcall&lt;/code&gt;, &lt;code&gt;morestack&lt;/code&gt;, and &lt;code&gt;asyncPreempt&lt;/code&gt; are all assembly&#xA;routines. This section explains what it is, why it exists, and the few key concepts you need in&#xA;order to read it. We do not aim to teach the reader to write Plan 9 assembly, which is the subject&#xA;of another book; we aim to turn it into a &lt;strong&gt;reading vocabulary&lt;/strong&gt;: when a later section mentions some&#xA;assembly routine, the reader knows what kind of abstraction layer it lives in and what each symbol&#xA;points to.&lt;/p&gt;</description>
    </item>
    <item>
      <title>2.2 Stack Frames and Symbols in Assembly</title>
      <link>/ru/part1overview/ch02asm/frame/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch02asm/frame/</guid>
      <description>&lt;h1 id=&#34;22-stack-frames-and-symbols-in-assembly&#34;&gt;2.2 Stack Frames and Symbols in Assembly&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././asm&#34;&gt;2.1&lt;/a&gt; gave the pseudo-registers and addressing syntax of Plan 9 assembly, which is about &amp;ldquo;how to write one line of assembly&amp;rdquo;. This section raises our view to a complete routine: how a hand-written assembly function declares its own symbol with &lt;code&gt;TEXT ·name(SB)&lt;/code&gt;, how it states its stack frame size with &lt;code&gt;$framesize-argsize&lt;/code&gt;, and what &lt;code&gt;NOSPLIT&lt;/code&gt; means. We will match these conventions point by point against three real routines in the runtime (&lt;code&gt;Cas&lt;/code&gt;, &lt;code&gt;gogo&lt;/code&gt;, &lt;code&gt;morestack&lt;/code&gt;), and once you finish reading, the assembly symbols in the chapters on the scheduler and stack management turn from gibberish into readable, on-the-spot operations.&lt;/p&gt;</description>
    </item>
    <item>
      <title>2.3 Calling Convention and the Register ABI</title>
      <link>/ru/part1overview/ch02asm/callconv/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch02asm/callconv/</guid>
      <description>&lt;h1 id=&#34;23-calling-convention-and-the-register-abi&#34;&gt;2.3 Calling Convention and the Register ABI&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The register names, stack-frame layout, and prologue code in this text use amd64 as the example.&#xA;Other architectures (arm64, riscv64, and so on) share the same structure but differ in register names; you can&#xA;cross-reference the &amp;ldquo;Architecture specifics&amp;rdquo; section of &lt;code&gt;src/cmd/compile/abi-internal&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;A single function call, at the machine level, has to answer a string of concrete questions: where do the arguments go, where does the return value go, who is responsible for saving which registers, how is the stack frame laid out, where is the return address pushed. Pinning down the answers to these questions is the calling convention, often also called the ABI (application binary interface). The ABI is not a piece of code but a &lt;strong&gt;contract&lt;/strong&gt;: the code generated by the compiler, the hand-written Plan 9 assembly (&lt;a href=&#34;.././asm&#34;&gt;2.1&lt;/a&gt;), and the low-level routines inside the runtime are written independently of one another, yet they have to mesh exactly at the moment of the call. Once the contract is fixed, all three sides arrange their data according to it, and none of them needs to know the internal details of the others.&lt;/p&gt;</description>
    </item>
    <item>
      <title>2.4 Argument Passing and Stack Frame Layout</title>
      <link>/ru/part1overview/ch02asm/args/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch02asm/args/</guid>
      <description>&lt;h1 id=&#34;24-argument-passing-and-stack-frame-layout&#34;&gt;2.4 Argument Passing and Stack Frame Layout&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././callconv&#34;&gt;2.3&lt;/a&gt; laid out the division of labor between the two ABIs and the recursive algorithm for assigning arguments. That was the &amp;ldquo;rule&amp;rdquo;. This section grounds the rule in a concrete example: given a signature that mixes scalars and arrays, how exactly are the arguments arranged in the stack frame, why does a register argument still reserve a spill slot on the stack, and how does the stack growth check that nearly every function pays for in its prologue hitch a ride on preemption. Finally we return to the overall question of &amp;ldquo;why a custom ABI&amp;rdquo; and combine these two sections into a complete answer.&lt;/p&gt;</description>
    </item>
    <item>
      <title>3.1 Начинаем с команды `go`</title>
      <link>/ru/part1overview/ch03life/cmd/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch03life/cmd/</guid>
      <description>&lt;h1 id=&#34;31-начинаем-с-команды-go&#34;&gt;3.1 Начинаем с команды &lt;code&gt;go&lt;/code&gt;&lt;/h1&gt;&#xA;&lt;p&gt;Жизненный цикл программы на Go начинается с запуска команды &lt;code&gt;go&lt;/code&gt;. Команды &lt;code&gt;go build&lt;/code&gt;, &lt;code&gt;go test&lt;/code&gt;&#xA;и &lt;code&gt;go run&lt;/code&gt;, которые читатель набирает каждый день, на первый взгляд лишь «превращают исходный код&#xA;в бинарный файл», но за ними скрывается факт, который часто понимают неверно: &lt;code&gt;go&lt;/code&gt; сама по себе&#xA;не является компилятором. Это &lt;strong&gt;оркестратор сборки&lt;/strong&gt;, который разбивает единый процесс на множество&#xA;отдельных шагов, определяет их порядок и степень параллелизма, а затем последовательно вызывает&#xA;настоящие инструменты: компилятор &lt;code&gt;compile&lt;/code&gt; (&lt;a href=&#34;.././compile&#34;&gt;3.2&lt;/a&gt;), ассемблер &lt;code&gt;asm&lt;/code&gt; и линкер &lt;code&gt;link&lt;/code&gt;&#xA;(&lt;a href=&#34;.././link&#34;&gt;3.4&lt;/a&gt;). В этом разделе мы сначала проясним саму схему оркестрации — она задаёт&#xA;структуру для всех последующих разделов: поняв, как &lt;code&gt;go&lt;/code&gt; декомпозирует сборку в граф и как&#xA;использует кэш на основе содержимого для устранения повторной работы, читатель получит опору&#xA;для погружения в детали компиляции и линковки.&lt;/p&gt;</description>
    </item>
    <item>
      <title>3.2 Конвейер компиляции Go</title>
      <link>/ru/part1overview/ch03life/compile/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch03life/compile/</guid>
      <description>&lt;h1 id=&#34;32-конвейер-компиляции-go&#34;&gt;3.2 Конвейер компиляции Go&lt;/h1&gt;&#xA;&lt;p&gt;В разделе &lt;a href=&#34;.././cmd&#34;&gt;3.1&lt;/a&gt; было показано, что реальную работу &lt;code&gt;go build&lt;/code&gt; выполняют две программы — &lt;code&gt;compile&lt;/code&gt; и &lt;code&gt;link&lt;/code&gt;. Этот раздел фокусируется на &lt;code&gt;compile&lt;/code&gt; и прослеживает полный путь от одного исходного файла &lt;code&gt;.go&lt;/code&gt; до одного объектного файла &lt;code&gt;.o&lt;/code&gt;: что получает каждая стадия, что она порождает и почему работа разделена именно так. Этот раздел представляет собой &lt;strong&gt;панораму&lt;/strong&gt; конвейера компиляции. Внутреннее устройство каждой стадии (как устроена грамматика, какой алгоритм используется для проверки типов, правила оптимизации SSA) раскрывается в &lt;a href=&#34;../../../part5toolchain/ch15compile/readme&#34;&gt;главе 15&lt;/a&gt;. Здесь мы лишь выстраиваем их в единую линию и указываем на две сквозные темы.&lt;/p&gt;</description>
    </item>
    <item>
      <title>3.3 Бутстрап языка</title>
      <link>/ru/part1overview/ch03life/bootstrap/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch03life/bootstrap/</guid>
      <description>&lt;h1 id=&#34;33-бутстрап-языка&#34;&gt;3.3 Бутстрап языка&lt;/h1&gt;&#xA;&lt;p&gt;Один вопрос звучит как парадокс: компилятор, ассемблер, компоновщик и рантайм Go сегодня написаны на Go, так откуда же взялась первая программа, способная скомпилировать Go? Без компилятора Go — как скомпилировать компилятор Go? Это и есть &lt;strong&gt;бутстрап&lt;/strong&gt; (bootstrapping). Он представляет собой одновременно и классическую дилемму курицы и яйца, и конкретный инженерный эпизод из истории инструментария Go, который можно пересказать с точностью до деталей. В этом разделе даётся ответ на три вопроса: как это яйцо было высижено впервые; как после бутстрапа работает цепочка сборки новой версии Go и почему требования к версии бутстрапа растут год от года; и наконец, почему язык, который «реализует себя средствами самого себя», заслуживает серьёзного отношения.&lt;/p&gt;</description>
    </item>
    <item>
      <title>3.4 Компоновка модулей</title>
      <link>/ru/part1overview/ch03life/link/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch03life/link/</guid>
      <description>&lt;h1 id=&#34;34-компоновка-модулей&#34;&gt;3.4 Компоновка модулей&lt;/h1&gt;&#xA;&lt;p&gt;Компилятор (&lt;a href=&#34;.././compile&#34;&gt;3.2&lt;/a&gt;) преобразует каждый пакет в объектный файл, но объектный файл сам по себе не может быть выполнен. Объектные файлы по-прежнему ссылаются на функции и переменные друг друга, адреса ещё не зафиксированы, а поддержка рантайма отсутствует. Сборка этих фрагментов в цельную программу, пригодную для загрузки и исполнения, — задача &lt;strong&gt;компоновщика&lt;/strong&gt; (&lt;code&gt;cmd/link&lt;/code&gt;, обычно вызываемого через &lt;code&gt;go build&lt;/code&gt; как &lt;code&gt;go tool link&lt;/code&gt;). В этом разделе рассматривается, что делает компоновщик и какие решения Go принимает в области компоновки, — именно они объясняют, почему Go-программа так часто представляет собой единственный самодостаточный файл, который «просто работает после копирования на целевую машину».&lt;/p&gt;</description>
    </item>
    <item>
      <title>3.5 Начальная загрузка Go-программы</title>
      <link>/ru/part1overview/ch03life/boot/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch03life/boot/</guid>
      <description>&lt;h1 id=&#34;35-начальная-загрузка-go-программы&#34;&gt;3.5 Начальная загрузка Go-программы&lt;/h1&gt;&#xA;&lt;p&gt;Функция &lt;code&gt;main&lt;/code&gt;, которую пишет читатель, не является первой инструкцией программы. Когда&#xA;операционная система передаёт управление исполняемому файлу Go, первым начинает работу рантайм:&#xA;он должен разметить стек выполнения на главном потоке, привязать локальное хранилище потока&#xA;(TLS), определить количество ядер CPU и размер физической страницы памяти, последовательно&#xA;пробудить аллокатор памяти, сборщик мусора и шедулер, и лишь затем создать горутину,&#xA;несущую &lt;code&gt;main&lt;/code&gt;, и передать её в цикл планирования для запуска. Иначе говоря, бинарный файл Go&#xA;содержит внутри себя миниатюрную операционную систему (&lt;a href=&#34;../../ch01intro/go&#34;&gt;1.2&lt;/a&gt;), которая&#xA;запускается раньше пользовательского кода. В этом разделе мы пройдём всю цепочку начальной&#xA;загрузки от начала до конца — от точки входа, определённой операционной системой, до момента&#xA;планирования первой горутины, — чтобы ясно увидеть, что происходит «до &lt;code&gt;main&lt;/code&gt;».&lt;/p&gt;</description>
    </item>
    <item>
      <title>3.6 Жизнь и смерть главной горутины</title>
      <link>/ru/part1overview/ch03life/main/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part1overview/ch03life/main/</guid>
      <description>&lt;h1 id=&#34;36-жизнь-и-смерть-главной-горутины&#34;&gt;3.6 Жизнь и смерть главной горутины&lt;/h1&gt;&#xA;&lt;p&gt;В &lt;a href=&#34;.././boot&#34;&gt;3.5&lt;/a&gt;, после того как &lt;code&gt;schedinit&lt;/code&gt; собрал фундамент рантайма, прямого вызова &lt;code&gt;runtime.main&lt;/code&gt; не происходит. Вместо этого адрес точки входа этой функции помещается на стек, передаётся в &lt;code&gt;newproc&lt;/code&gt; для создания первой горутины, а затем &lt;code&gt;mstart&lt;/code&gt; запускает цикл планирования и выбирает эту горутину для выполнения. Детали планирования оставлены для &lt;a href=&#34;/ru/part3concurrency/ch09sched/&#34;&gt;9 Шедулер&lt;/a&gt;. В данном разделе внимание сосредоточено на одном конкретном моменте: &lt;strong&gt;первая горутина уже запущена и готова выполнить &lt;code&gt;runtime.main&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>4.1 The Runtime Type System</title>
      <link>/ru/part2lang/ch04type/type/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch04type/type/</guid>
      <description>&lt;h1 id=&#34;41-the-runtime-type-system&#34;&gt;4.1 The Runtime Type System&lt;/h1&gt;&#xA;&lt;p&gt;Go is a statically typed language, and type checking happens at compile time. Yet Go still keeps a substantial amount of &lt;strong&gt;runtime type information&lt;/strong&gt; (RTTI), and it is precisely this that supports interfaces (&lt;a href=&#34;.././interface&#34;&gt;4.2&lt;/a&gt;), type assertions, type switches, reflection, and the garbage collector&amp;rsquo;s precise identification of pointers. This section answers a seemingly simple question: of the type machinery that exists at compile time, what survives into the runtime, in what form is it kept, and what capabilities does it underpin. Once you understand the small structure called the &lt;strong&gt;type descriptor&lt;/strong&gt; in this section, the interface (&lt;a href=&#34;.././interface&#34;&gt;4.2&lt;/a&gt;), reflection, and precise GC (&lt;a href=&#34;/ru/part4memory/ch13gc/&#34;&gt;13&lt;/a&gt;) all gain a common footing.&lt;/p&gt;</description>
    </item>
    <item>
      <title>4.2 Interfaces</title>
      <link>/ru/part2lang/ch04type/interface/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch04type/interface/</guid>
      <description>&lt;h1 id=&#34;42-interfaces&#34;&gt;4.2 Interfaces&lt;/h1&gt;&#xA;&lt;p&gt;Interfaces are the soul of Go&amp;rsquo;s type system. They decouple behavior from implementation, and they do it in an unusual way: structural, implicit satisfaction, unlike most mainstream languages. This section looks at how interfaces are represented at runtime, how methods are dispatched dynamically, how type assertions are realized, and where this design sits within the broader lineage of polymorphism. Following the approach of the previous sections, the structs given below are &lt;strong&gt;trimmed-down sketches&lt;/strong&gt;: they keep only the fields relevant to the design, with comments explaining why each one exists. For the full definitions, compare against &lt;code&gt;runtime/runtime2.go&lt;/code&gt;, &lt;code&gt;internal/abi/iface.go&lt;/code&gt;, and &lt;code&gt;runtime/iface.go&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>4.3 Type Aliases</title>
      <link>/ru/part2lang/ch04type/alias/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch04type/alias/</guid>
      <description>&lt;h1 id=&#34;43-type-aliases&#34;&gt;4.3 Type Aliases&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;type A = B&lt;/code&gt; (a type alias) and &lt;code&gt;type A B&lt;/code&gt; (defining a new type) differ by a single equals sign, yet the&#xA;semantics are fundamentally different. The former merely gives a new name to an existing type; the latter&#xA;creates a brand-new type with its own independent identity. The distinction looks minor, but it pulls on the&#xA;whole set of rules governing type equality, method sets, and assignability (&lt;a href=&#34;.././type&#34;&gt;4.1&lt;/a&gt;), and it also&#xA;pulls on a question Go has always cared about: when a body of code is no longer maintained by its original&#xA;author yet still has to evolve without breaking compatibility, how should a type migrate from one package to&#xA;another. This section first makes the semantics of aliases and defined types clear, then explains why aliases&#xA;were introduced in Go 1.9, and finally looks at the generic capability they gained in Go 1.24.&lt;/p&gt;</description>
    </item>
    <item>
      <title>5.1 Arrays and Slices</title>
      <link>/ru/part2lang/ch05data/slice/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch05data/slice/</guid>
      <description>&lt;h1 id=&#34;51-arrays-and-slices&#34;&gt;5.1 Arrays and Slices&lt;/h1&gt;&#xA;&lt;p&gt;Arrays and slices are the two most basic sequence types in Go. They look similar, yet their memory models differ, and grasping this one point explains in a single stroke all the &amp;ldquo;surprises&amp;rdquo; of append and the traps of slice aliasing. Both share a single theme: a small &lt;strong&gt;header&lt;/strong&gt; describing a stretch of contiguous backing memory. The differences lie entirely in what the header holds and who owns that memory. This section first lays out the layout clearly, then starts from the classic abstraction of the &amp;ldquo;dynamic array&amp;rdquo; to see how Go&amp;rsquo;s &lt;code&gt;append&lt;/code&gt; achieves amortized &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;O(1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.02778em;&#34;&gt;O&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, and finally lands on aliasing and cross-language comparison, the corners we run into day to day. Strings, though they share the same origin as slices, are immutable and form a category of their own; we leave them to &lt;a href=&#34;.././string&#34;&gt;5.2&lt;/a&gt; for dedicated discussion.&lt;/p&gt;</description>
    </item>
    <item>
      <title>5.2 Strings and Zero-Copy Conversion</title>
      <link>/ru/part2lang/ch05data/string/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch05data/string/</guid>
      <description>&lt;h1 id=&#34;52-strings-and-zero-copy-conversion&#34;&gt;5.2 Strings and Zero-Copy Conversion&lt;/h1&gt;&#xA;&lt;p&gt;Strings share their origin with slices: in the runtime both are &amp;ldquo;a header plus memory living elsewhere&amp;rdquo; (for the layout, see &lt;a href=&#34;/ru/part2lang/ch05data/slice/#511-%e4%b8%89%e7%a7%8d%e5%86%85%e5%ad%98%e5%b8%83%e5%b1%80&#34;&gt;5.1.1&lt;/a&gt;). But a string is &lt;strong&gt;read-only and immutable&lt;/strong&gt;, which makes it its own category. Immutability buys us sharing and copy-free use, and it brings one direct cost: converting between &lt;code&gt;string&lt;/code&gt; and &lt;code&gt;[]byte&lt;/code&gt; copies the bytes by default. This section opens up that copy, looks at the cases where the compiler can elide it, then turns to the manual zero-copy tools Go 1.20 provides and the safety contract that comes with them.&lt;/p&gt;</description>
    </item>
    <item>
      <title>5.3 Hash Tables: Principles and Security</title>
      <link>/ru/part2lang/ch05data/map/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch05data/map/</guid>
      <description>&lt;h1 id=&#34;53-hash-tables-principles-and-security&#34;&gt;5.3 Hash Tables: Principles and Security&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Go&amp;rsquo;s &lt;code&gt;map&lt;/code&gt; underwent a rare, complete rewrite in 2024 alongside Go 1.24,&#xA;moving from the classic bucketed hash table it had used for fourteen years to an implementation based on Swiss Tables. This section first clarifies the general principles of hash tables&#xA;and the attack-and-defense around them, leaving the full story of the rewrite to &lt;a href=&#34;.././swisstable&#34;&gt;5.4&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;code&gt;map&lt;/code&gt; is one of only two generic containers Go provides (the other being slice). It is implemented by the runtime with the compiler assisting in layout,&#xA;and at its core it is a hash table. When the reader writes &lt;code&gt;m[k]&lt;/code&gt;, the compiler translates it into calls to the &lt;code&gt;runtime.mapaccess&lt;/code&gt;&#xA;and &lt;code&gt;runtime.mapassign&lt;/code&gt; family of functions; the real storage, lookup, and growth all happen inside the&#xA;&lt;code&gt;internal/runtime/maps&lt;/code&gt; package. This section first lays out the general principles of hash tables and the attack-and-defense around them, to ground the next section where we drop down to Go&amp;rsquo;s own&#xA;two generations of implementation (the classic bucketed design from 1.0 through 1.23, and the Swiss Table design from 1.24 onward). Only by understanding the trade-offs here&#xA;can we see why the latter was worth a rewrite that cut to the bone.&lt;/p&gt;</description>
    </item>
    <item>
      <title>5.4 Swiss Table and the Go 1.24 Implementation</title>
      <link>/ru/part2lang/ch05data/swisstable/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch05data/swisstable/</guid>
      <description>&lt;h1 id=&#34;54-swiss-table-and-the-go-124-implementation&#34;&gt;5.4 Swiss Table and the Go 1.24 Implementation&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././map&#34;&gt;5.3&lt;/a&gt; laid out the general principles of hash tables and the attack-and-defense around them: the two routes for handling collisions, and the defense against hash flooding. This section comes down to Go&amp;rsquo;s own two generations of implementation. We first open up Swiss Table, a modern open-addressing design, then look at how Go 1.24 brought it to ground and what it replaced in the process, then derive from the implementation two language rules that follow from it, and finally place Go within the larger picture of the various hash tables out there.&lt;/p&gt;</description>
    </item>
    <item>
      <title>6.1 Function Calls</title>
      <link>/ru/part2lang/ch06func/func/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch06func/func/</guid>
      <description>&lt;h1 id=&#34;61-function-calls&#34;&gt;6.1 Function Calls&lt;/h1&gt;&#xA;&lt;p&gt;Functions are first-class citizens in Go: they can be assigned, passed, returned, and they can capture&#xA;outer variables to become closures. Behind this lie two implementation questions: how a function value&#xA;(a closure) is represented in memory, and how a single function call passes arguments and returns values&#xA;at the lowest level. The latter also hides a quiet but sweeping change introduced in Go 1.17: passing&#xA;arguments in registers instead of on the stack. This section explains both thoroughly, and at the seam&#xA;between them points out a key fact, the very fact that welds &amp;ldquo;what a closure is&amp;rdquo; and &amp;ldquo;how a call happens&amp;rdquo;&#xA;into one and the same thing.&lt;/p&gt;</description>
    </item>
    <item>
      <title>6.2 Deferred Statements</title>
      <link>/ru/part2lang/ch06func/defer/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch06func/defer/</guid>
      <description>&lt;h1 id=&#34;62-deferred-statements&#34;&gt;6.2 Deferred Statements&lt;/h1&gt;&#xA;&lt;p&gt;The deferred statement &lt;code&gt;defer&lt;/code&gt; did not exist in the earliest Go designs; it was added later as a separate feature, with Robert Griesemer writing the language specification [Griesemer, 2009] and Ken Thompson producing the earliest implementation [Thompson, 2009]. Its semantics read very short: a &lt;code&gt;defer&lt;/code&gt;-red call runs when the enclosing function returns, when a panic occurs, or when &lt;code&gt;runtime.Goexit&lt;/code&gt; is called. Intuitively this looks like a purely compile-time feature, similar to C++&amp;rsquo;s RAII (automatic destruction when leaving a scope); the compiler would seem to only need to &amp;ldquo;move&amp;rdquo; the deferred call to the end of the function, with no runtime cost.&lt;/p&gt;</description>
    </item>
    <item>
      <title>6.3 The panic and recover Builtins</title>
      <link>/ru/part2lang/ch06func/panic/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch06func/panic/</guid>
      <description>&lt;h1 id=&#34;63-the-panic-and-recover-builtins&#34;&gt;6.3 The panic and recover Builtins&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;defer&lt;/code&gt; (&lt;a href=&#34;.././defer&#34;&gt;6.2&lt;/a&gt;) has already settled the question of &amp;ldquo;what to do when a function exits,&amp;rdquo; leaving open the question of &amp;ldquo;what happens when a function is interrupted abnormally.&amp;rdquo; The pair of builtins &lt;code&gt;panic&lt;/code&gt; and &lt;code&gt;recover&lt;/code&gt; answers exactly this: the former interrupts the current normal control flow and begins unwinding up the call stack, while the latter catches it mid-unwind. This section first makes their semantics clear, then comes down to the go1.26 runtime implementation of &lt;code&gt;gopanic&lt;/code&gt; and &lt;code&gt;gorecover&lt;/code&gt;, and finally returns to a more pressing question: what panic should really be taken to be, and why Go did not make it into an exception mechanism like the one in C++ or Java.&lt;/p&gt;</description>
    </item>
    <item>
      <title>7.1 The Evolution of the Problem</title>
      <link>/ru/part2lang/ch07errors/value/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch07errors/value/</guid>
      <description>&lt;h1 id=&#34;71-the-evolution-of-the-problem&#34;&gt;7.1 The Evolution of the Problem&lt;/h1&gt;&#xA;&lt;p&gt;In &lt;a href=&#34;../../ch06func/panic&#34;&gt;6.3&lt;/a&gt; we already drew the dividing line: how to handle errors is one of the&#xA;fundamental choices in language design. The exception camp (C++, Java, Python) uses &lt;code&gt;try/catch&lt;/code&gt; to&#xA;pull the error path out of the normal logic, keeping the main line clean, at the cost of making the&#xA;error path implicit, capable of being thrown from any call site. The value camp (Go, Rust, and C&amp;rsquo;s&#xA;return-code tradition) passes errors explicitly as ordinary return values: verbose, but every error&#xA;path is spelled out in black and white with nowhere to hide. Go chose the value camp, reserving a&#xA;lightweight &lt;code&gt;panic&lt;/code&gt;/&lt;code&gt;recover&lt;/code&gt; only for &amp;ldquo;true exceptions.&amp;rdquo; What this section is about is what happened&#xA;after that choice landed: how the proposition of &amp;ldquo;errors as values&amp;rdquo; itself evolved, from a string you&#xA;could only print into a tree that a program can interrogate layer by layer, asking &amp;ldquo;are you really&#xA;that error?&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>7.2 Inspecting Error Values</title>
      <link>/ru/part2lang/ch07errors/inspect/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch07errors/inspect/</guid>
      <description>&lt;h1 id=&#34;72-inspecting-error-values&#34;&gt;7.2 Inspecting Error Values&lt;/h1&gt;&#xA;&lt;p&gt;Once an error propagates up a call chain, the code that handles it and the code that produced it&#xA;are often separated by many layers. This raises a plain but thorny question: given an &lt;code&gt;error&lt;/code&gt; that&#xA;has been passed up through layer after layer, how does the caller decide &amp;ldquo;is this actually some&#xA;particular error&amp;rdquo;, and how does it retrieve the specific error value that originally carried the&#xA;context? This section answers exactly that, along with the conventions Go has set down for it in&#xA;the &lt;code&gt;errors&lt;/code&gt; package.&lt;/p&gt;</description>
    </item>
    <item>
      <title>7.3 Error Format and Context</title>
      <link>/ru/part2lang/ch07errors/context/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch07errors/context/</guid>
      <description>&lt;h1 id=&#34;73-error-format-and-context&#34;&gt;7.3 Error Format and Context&lt;/h1&gt;&#xA;&lt;p&gt;A good error does not merely say &amp;ldquo;something went wrong.&amp;rdquo; It tells a person what was being done, on account of what, and what went wrong. The evolution of the problem (&lt;a href=&#34;.././value&#34;&gt;7.1&lt;/a&gt;) and value inspection (&lt;a href=&#34;.././inspect&#34;&gt;7.2&lt;/a&gt;) gave us the mechanisms for propagating and examining errors. This section discusses the engineering practice that sits on top of those mechanisms: how the text of an error should be written, how context accumulates layer by layer along the call chain, and how stack traces and structured logs can be added on demand when human-written words are not enough. Running through all of it is one overall tendency Go has regarding error information: human-written semantic context is preferable to machine-collected stack traces.&lt;/p&gt;</description>
    </item>
    <item>
      <title>7.4 Error Semantics</title>
      <link>/ru/part2lang/ch07errors/semantics/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch07errors/semantics/</guid>
      <description>&lt;h1 id=&#34;74-error-semantics&#34;&gt;7.4 Error Semantics&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././inspect&#34;&gt;7.2&lt;/a&gt; and &lt;a href=&#34;.././context&#34;&gt;7.3&lt;/a&gt; stood on the caller&amp;rsquo;s side of an error and worked out how, once&#xA;you hold an &lt;code&gt;error&lt;/code&gt;, to inspect it along the chain and how to layer context onto it. This section turns the&#xA;viewpoint to the other side, standing with the library author and asking a question that comes before&#xA;inspection: when a package wants to expose a failure to the outside world, what &lt;strong&gt;form&lt;/strong&gt; should it give that&#xA;error? Should it export a comparable value, export a type with fields, or export nothing at all?&lt;/p&gt;</description>
    </item>
    <item>
      <title>7.5 The Future of Error Handling</title>
      <link>/ru/part2lang/ch07errors/future/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch07errors/future/</guid>
      <description>&lt;h1 id=&#34;75-the-future-of-error-handling&#34;&gt;7.5 The Future of Error Handling&lt;/h1&gt;&#xA;&lt;p&gt;In the preceding sections we saw the plainness of the &lt;code&gt;error&lt;/code&gt; interface, &lt;code&gt;%w&lt;/code&gt; wrapping, and the inspection of error chains. By this point the reader probably carries an unspoken question: those three lines of &lt;code&gt;if err != nil&lt;/code&gt; boilerplate, spread year after year across every Go program, is there really no way to get rid of them? The question is not a lonely one. From the go2 draft of 2018 to a single conclusion in 2025, the Go team weighed it back and forth for seven years, proposing several syntaxes such as &lt;code&gt;check&lt;/code&gt;/&lt;code&gt;handle&lt;/code&gt;, &lt;code&gt;try&lt;/code&gt;, and &lt;code&gt;?&lt;/code&gt;, and then withdrawing them one by one. This section places that path of failed syntax side by side with another, quietly successful path of library evolution, and lays out the judgment the Go team finally arrived at: for the foreseeable future, error handling will not receive any dedicated syntax.&lt;/p&gt;</description>
    </item>
    <item>
      <title>8.1 The Evolution of Generics Design</title>
      <link>/ru/part2lang/ch08generics/history/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch08generics/history/</guid>
      <description>&lt;h1 id=&#34;81-the-evolution-of-generics-design&#34;&gt;8.1 The Evolution of Generics Design&lt;/h1&gt;&#xA;&lt;p&gt;Generics is the feature Go waited longest for, argued about most fiercely, and that best embodies its design philosophy. From the open-source release in 2009 to its arrival in Go 1.18 in 2022, the thirteen years of hesitation and the eventual choices are themselves a lesson in language design. This section answers three questions: why Go held off on generics for so long, how it finally added them, and how they are implemented underneath. The last question matters most, because Go&amp;rsquo;s implementation copies no existing precedent and instead takes a distinctive middle road, and it is that road that is Go&amp;rsquo;s real answer to the thirteen-year problem. How the design itself evolved along the way (contracts, type sets, several rounds of syntax proposals) is left to &lt;a href=&#34;.././future&#34;&gt;8.4&lt;/a&gt;; this section takes only the skeleton.&lt;/p&gt;</description>
    </item>
    <item>
      <title>8.2 Contract-Based Generics</title>
      <link>/ru/part2lang/ch08generics/contracts/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch08generics/contracts/</guid>
      <description>&lt;h1 id=&#34;82-contract-based-generics&#34;&gt;8.2 Contract-Based Generics&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;This section has a companion online talk: &lt;a href=&#34;https://www.youtube.com/watch?v=E16Y6bI2S08&#34;&gt;on YouTube&lt;/a&gt;,&#xA;&lt;a href=&#34;https://changkun.de/s/go2generics/&#34;&gt;Google Slides deck&lt;/a&gt;. The deck was recorded in 2019, while the contracts&#xA;proposal was still under discussion, and it corroborates this section.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././history&#34;&gt;8.1&lt;/a&gt; glossed over the evolution &amp;ldquo;contracts came first, then a turn toward interfaces as constraints&amp;rdquo;&#xA;in a single sentence. This section unfolds that sentence: what contracts actually looked like, how much expressive&#xA;power they had, and why they were ultimately abandoned. This rejected design is not historical waste; it explains&#xA;where today&amp;rsquo;s &lt;code&gt;[T Ordered]&lt;/code&gt; syntax came from, and it illustrates a move that recurs throughout Go&amp;rsquo;s design: first&#xA;produce a highly expressive but somewhat complex scheme, then turn back and ask &amp;ldquo;can this be expressed with concepts&#xA;we already have&amp;rdquo;, forcing complexity to earn its right to exist.&lt;/p&gt;</description>
    </item>
    <item>
      <title>8.3 Type-Checking Techniques</title>
      <link>/ru/part2lang/ch08generics/checker/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch08generics/checker/</guid>
      <description>&lt;h1 id=&#34;83-type-checking-techniques&#34;&gt;8.3 Type-Checking Techniques&lt;/h1&gt;&#xA;&lt;p&gt;Generics pose a far harder problem to the type checker than before. The old judgment had only one shape: &amp;ldquo;is this value of this type?&amp;rdquo; With type parameters, the checker now has to answer three new questions: whether a type argument satisfies a constraint, whether an operation like &lt;code&gt;a + b&lt;/code&gt; is valid on an unknown type, and what that unwritten &lt;code&gt;T&lt;/code&gt; actually is when you call &lt;code&gt;Max(3, 5)&lt;/code&gt;. These three concerns correspond to the three topics of this section: type sets, core types, and type inference. They are where the design from &lt;a href=&#34;.././history&#34;&gt;8.1&lt;/a&gt; lands as concrete compile-time technique, and they are also a prelude to the front end of &lt;a href=&#34;/ru/part5toolchain/ch15compile/&#34;&gt;15 The Compiler&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>8.4 The Future of Generics</title>
      <link>/ru/part2lang/ch08generics/future/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part2lang/ch08generics/future/</guid>
      <description>&lt;h1 id=&#34;84-the-future-of-generics&#34;&gt;8.4 The Future of Generics&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Generics landed in 1.18 and more than four years have passed. This section no longer dwells on&#xA;speculation about &amp;ldquo;what might be.&amp;rdquo; Instead it looks back and takes inventory: which expectations&#xA;were met, which were deliberately shelved, and where the tension that runs through it all (the&#xA;cost of abstraction) stands today. The author once gave a public talk on Go 2 generics in earlier&#xA;years (&lt;a href=&#34;https://www.youtube.com/watch?v=E16Y6bI2S08&#34;&gt;YouTube&lt;/a&gt;,&#xA;&lt;a href=&#34;https://changkun.de/s/go2generics/&#34;&gt;slides&lt;/a&gt;). Most of those predictions can now be checked against&#xA;reality, and this section accounts for them along the way.&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.1 Задача планирования и модель GMP</title>
      <link>/ru/part3concurrency/ch09sched/model/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/model/</guid>
      <description>&lt;h1 id=&#34;91-задача-планирования-и-модель-gmp&#34;&gt;9.1 Задача планирования и модель GMP&lt;/h1&gt;&#xA;&lt;p&gt;Достаточно написать &lt;code&gt;go f()&lt;/code&gt; — и горутина начинает выполняться. За этой единственной строкой стоит самый сложный механизм рантайма Go: планировщик (шедулер). Он должен ответить на вопрос, который совсем не прост: каким образом десятки тысяч горутин могут поочерёдно использовать небольшое число ядер процессора, работая быстро и оставаясь при этом практически незаметными для пользователя. В этом разделе сначала формулируется задача, которую должен решить планировщик, описывается его общая структура и место среди других конкурентных рантаймов. Последующие разделы подробно рассматривают каждый компонент.&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.2 Планирование с кражей работы</title>
      <link>/ru/part3concurrency/ch09sched/steal/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/steal/</guid>
      <description>&lt;h1 id=&#34;92-планирование-с-кражей-работы&#34;&gt;9.2 Планирование с кражей работы&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././model&#34;&gt;9.1&lt;/a&gt; оставил нам вопрос: у каждого P есть собственная локальная очередь, а значит, работа неизбежно распределяется неравномерно. Одни P перегружены, другие простаивают. Как распределить нагрузку, не создавая центрального узкого места, — это основная трудность конкурентного планирования. Ответ Go — схема с тридцатилетней теоретической базой, воспроизводимая во всей отрасли: кража работы (work stealing).&lt;/p&gt;&#xA;&lt;p&gt;Этот раздел несколько глубже остальных. Сначала мы проясним, что именно делает Go, затем проследим стоящую за этим теорию планирования (почему кража работы «доказуемо хороша»), далее рассмотрим различные воплощения этой идеи в системах вроде Cilk, Java и Rust и, наконец, остановимся на вопросах, которые остаются открытыми.&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.3 Модель MPG и единицы конкурентного планирования</title>
      <link>/ru/part3concurrency/ch09sched/mpg/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/mpg/</guid>
      <description>&lt;h1 id=&#34;93-модель-mpg-и-единицы-конкурентного-планирования&#34;&gt;9.3 Модель MPG и единицы конкурентного планирования&lt;/h1&gt;&#xA;&lt;p&gt;Первый вопрос, на который должен ответить шедулер, — не «как планировать», а «что планировать». Go называет объект планирования горутиной и реализует её на тройке M, P и G. Прежде чем перейти к алгоритму планирования (начиная с &lt;a href=&#34;.././schedule&#34;&gt;9.4&lt;/a&gt;), этот раздел разбирает три единицы планирования: что такое горутина в контексте истории информатики, как кодируется её контекст выполнения, почему само планирование должно происходить на специальном g0, через какие состояния проходит горутина за свою жизнь и как поток M, несущий её, приостанавливается и возобновляется. Когда эти понятия прояснятся, все последующие алгоритмы планирования сведутся к «перемещению G между этими единицами».&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.4 Цикл планирования</title>
      <link>/ru/part3concurrency/ch09sched/schedule/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/schedule/</guid>
      <description>&lt;h1 id=&#34;94-цикл-планирования&#34;&gt;9.4 Цикл планирования&lt;/h1&gt;&#xA;&lt;p&gt;Предыдущие разделы подготовили материал: мы знаем, что такое G, M и P (&lt;a href=&#34;.././mpg&#34;&gt;9.3&lt;/a&gt;), и знаем, как M находит работу (&lt;a href=&#34;.././steal&#34;&gt;9.2&lt;/a&gt;). Этот раздел запускает всё в движение — мы наблюдаем, как цикл планирования непрерывно выбирает горутины и выполняет их в рамках одного потока, и как он удерживает баланс между «позволить отдельной горутине чуть дольше занимать процессор» (пропускная способность и локальность) и «не позволить ни одной горутине оголодать» (справедливость).&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.5 Управление потоками</title>
      <link>/ru/part3concurrency/ch09sched/thread/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/thread/</guid>
      <description>&lt;h1 id=&#34;95-управление-потоками&#34;&gt;9.5 Управление потоками&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././model&#34;&gt;9.1&lt;/a&gt; заложил трёхуровневую структуру GMP: G — единица выполнения на уровне пользователя, P — разрешение на планирование и носитель локальных ресурсов, а M — «нога», одолженная у операционной системы. Предыдущие разделы сосредоточились преимущественно на G и P; этот раздел переключает внимание на M и отвечает на ряд вопросов, откладывавшихся до сих пор: что такое M на самом деле, откуда он берётся, почему GOMAXPROCS ограничивает количество P, тогда как число потоков нередко оказывается больше, почему единственный блокирующий системный вызов не тормозит остальные G, и какую цену платит рантайм, когда пользователь хочет закрепить горутину за конкретным потоком (&lt;code&gt;LockOSThread&lt;/code&gt;).&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.6 Обработка сигналов</title>
      <link>/ru/part3concurrency/ch09sched/signal/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/signal/</guid>
      <description>&lt;h1 id=&#34;96-обработка-сигналов&#34;&gt;9.6 Обработка сигналов&lt;/h1&gt;&#xA;&lt;p&gt;Сигналы операционной системы асинхронны и низкоуровневы: сигнал может прервать любой поток в произвольный момент, а набор действий, допустимых в обработчике сигнала, крайне ограничен. Типичный же запрос разработчика на Go — подключить канал к &lt;code&gt;SIGINT&lt;/code&gt; через &lt;code&gt;signal.Notify&lt;/code&gt; и выполнить корректное завершение по его приходу. Задача рантайма — выстроить мост между этими двумя реальностями: преобразовать непредсказуемый асинхронный сигнал в событие, которое горутина сможет безопасно обработать. Каждое проектное решение на этом мосту диктуется одним жёстким ограничением: что допустимо делать внутри контекста обработчика сигнала. Понимая это ограничение, всю остальную механику данного раздела можно воспринимать как её следствия.&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.7 Кооперация и вытеснение</title>
      <link>/ru/part3concurrency/ch09sched/preemption/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/preemption/</guid>
      <description>&lt;h1 id=&#34;97-кооперация-и-вытеснение&#34;&gt;9.7 Кооперация и вытеснение&lt;/h1&gt;&#xA;&lt;p&gt;В &lt;a href=&#34;.././schedule&#34;&gt;9.5 Цикл планирования&lt;/a&gt; остался открытый вопрос: если некоторая G выполняется слишком долго, как остальные G всё равно смогут получить время процессора? Ответ неизбежно апеллирует к паре понятий из теории планирования — кооперативному и вытесняющему. Кооперативное планирование опирается на добровольную уступку управления планируемой стороной; вытесняющее — на прерывание планируемой стороны планировщиком извне.&lt;/p&gt;&#xA;&lt;p&gt;Рантайм Go не располагает возможностью аппаратного прерывания, подобной ядру операционной системы. Планировщик с перехватом работы (&lt;a href=&#34;.././steal&#34;&gt;9.2&lt;/a&gt;) по своей сути является кооперативным планированием в порядке поступления. Вопрос в том, как он всё же способен принудительно прерывать G, отказывающуюся уступать управление, не отступая при этом от данного принципа — именно это и предстоит прояснить в настоящем разделе. Отправной точкой служит теоретический вопрос: почему рантайм не может остановить горутину в произвольной инструкции?&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.8 Системный монитор</title>
      <link>/ru/part3concurrency/ch09sched/sysmon/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/sysmon/</guid>
      <description>&lt;h1 id=&#34;98-системный-монитор&#34;&gt;9.8 Системный монитор&lt;/h1&gt;&#xA;&lt;p&gt;Штатный путь планировщика описан в &lt;a href=&#34;.././schedule&#34;&gt;9.4 Цикл планирования&lt;/a&gt;: один M привязывается к одному P,&#xA;извлекает горутину из очереди, выполняет её, затем берёт следующую. Этот путь опирается на одно условие — что M вообще&#xA;получает возможность работать. Но стоит всем P застрять в длительных системных вызовах, или какой-либо горутине&#xA;уйти в бесконечный цикл и намертво удерживать P, — обычное планирование встаёт. Никто не забирает P обратно,&#xA;никто не опрашивает сеть. Иными словами, кооперативная логика, выполняемая на P, не способна справиться с ситуацией,&#xA;когда «сам P не может двигаться».&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.9 Опросчик сети</title>
      <link>/ru/part3concurrency/ch09sched/poller/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/poller/</guid>
      <description>&lt;h1 id=&#34;99-опросчик-сети&#34;&gt;9.9 Опросчик сети&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Исходные факты верифицированы по &lt;code&gt;src/runtime/netpoll.go&lt;/code&gt; и его платформенным реализациям&#xA;(&lt;code&gt;netpoll_epoll.go&lt;/code&gt;, &lt;code&gt;netpoll_kqueue.go&lt;/code&gt; и другим), а также по&#xA;&lt;code&gt;src/internal/poll/fd_unix.go&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Сетевой код Go выглядит блокирующим: &lt;code&gt;conn.Read&lt;/code&gt; просто «зависает» в ожидании данных.&#xA;Но если бы он действительно блокировал поток операционной системы, на котором выполняется,&#xA;то десять тысяч горутин, ожидающих данных из сети, удерживали бы десять тысяч потоков, и&#xA;модель M:N, тщательно выстроенная в &lt;a href=&#34;.././model&#34;&gt;9.1&lt;/a&gt;, мгновенно рухнула бы. Возможность&#xA;сохранить блокирующий стиль и при этом обеспечить масштабируемость — заслуга опросчика&#xA;сети (netpoller). За ним стоит долгая история «как обслуживать огромное число соединений&#xA;с помощью горстки потоков». Этот раздел сначала излагает эту историю и определяющие оси&#xA;проектирования, а затем рассматривает, как Go скрывает зрелый механизм событий внутри&#xA;рантайма: программист пишет синхронный код, а под капотом работает событийно-ориентированный&#xA;ввод-вывод.&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.10 Таймеры</title>
      <link>/ru/part3concurrency/ch09sched/timer/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/timer/</guid>
      <description>&lt;h1 id=&#34;910-таймеры&#34;&gt;9.10 Таймеры&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;time.Sleep&lt;/code&gt;, &lt;code&gt;time.After&lt;/code&gt;, &lt;code&gt;time.Timer&lt;/code&gt;, &lt;code&gt;time.Ticker&lt;/code&gt; и даже &lt;code&gt;SetDeadline&lt;/code&gt; для сетевого чтения и записи —&#xA;всё это опирается на одну и ту же инфраструктуру таймеров. Она должна отвечать на вопрос, который выглядит&#xA;простым, но на деле весьма тонок — вопрос о структурах данных: когда одновременно существуют тысячи таймеров,&#xA;как эффективно определить «кого нужно разбудить следующим, и когда», не выжигая при этом отдельный поток?&#xA;Этот раздел начинается с постановки абстрактной задачи, разбирает компромиссы различных решений и приходит к&#xA;выбору Go и его эволюции.&lt;/p&gt;</description>
    </item>
    <item>
      <title>9.11 NUMA-осведомлённость и будущее планировщика</title>
      <link>/ru/part3concurrency/ch09sched/numa/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch09sched/numa/</guid>
      <description>&lt;h1 id=&#34;911-numa-осведомлённость-и-будущее-планировщика&#34;&gt;9.11 NUMA-осведомлённость и будущее планировщика&lt;/h1&gt;&#xA;&lt;p&gt;Планировщик, описанный в предыдущих разделах, опирается на предположение, которое нигде явно не&#xA;сформулировано: каждый M обращается к памяти с одинаковой скоростью, а стоимость перемещения G между&#xA;любыми двумя P одинакова. На ноутбуке или однопроцессорном сервере это предположение почти верно. Но&#xA;стоит запустить программу на крупном многопроцессорном сервере — оно начинает трещать, и чем больше ядер,&#xA;тем шире трещина. Этот раздел посвящён именно ей: откуда она берётся, почему планировщик Go так долго&#xA;её игнорировал, какой NUMA-осведомлённый дизайн был тщательно проработан, но так и не выпущен, и как&#xA;пользователи обходят проблему сегодня.&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.1 Каналы и инженерия CSP</title>
      <link>/ru/part3concurrency/ch10chan/model/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch10chan/model/</guid>
      <description>&lt;h1 id=&#34;101-каналы-и-инженерия-csp&#34;&gt;10.1 Каналы и инженерия CSP&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;К этому разделу прилагается записанный доклад: &lt;a href=&#34;https://www.youtube.com/watch?v=d7fFCGGn0Wc&#34;&gt;онлайн на YouTube&lt;/a&gt;,&#xA;&lt;a href=&#34;https://changkun.de/s/chansrc/&#34;&gt;презентация в Google Slides&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;CSP даёт Go основание утверждать: процессы не разделяют состояние, а координируются исключительно через&#xA;передачу сообщений (&lt;a href=&#34;/ru/part1overview/ch01intro/csp/&#34;&gt;1.3&lt;/a&gt;). Настоящий раздел посвящён другому вопросу:&#xA;чтобы инженерный язык воплотил это утверждение на практике, какую форму должна принять «коммуникация», при&#xA;которой обычный программист мог бы использовать её корректно. Ответ Go — канал, элемент языка первого класса,&#xA;являющийся одновременно средством синхронизации и средством передачи данных. Мы начнём с изложения его модели&#xA;на поверхности языка: тип, синтаксис отправки и приёма, две семантики — буферизованная и небуферизованная,&#xA;ограничение направления и поведение nil — выстраивая интуицию, которая понадобится последующим разделам при&#xA;погружении в реализацию рантайма (&lt;a href=&#34;.././impl&#34;&gt;10.2&lt;/a&gt;–&lt;a href=&#34;.././pattern&#34;&gt;10.7&lt;/a&gt;).&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.2 hchan: внутреннее устройство канала</title>
      <link>/ru/part3concurrency/ch10chan/impl/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch10chan/impl/</guid>
      <description>&lt;h1 id=&#34;102-hchan-внутреннее-устройство-канала&#34;&gt;10.2 hchan: внутреннее устройство канала&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././model&#34;&gt;10.1&lt;/a&gt; рассмотрел каналы в языке с позиции CSP: канал — это явный передаточный&#xA;механизм для сообщений, объединяющий «коммуникацию» и «синхронизацию» в единое целое. Данный&#xA;раздел разбирает его изнутри. В рантайме канал представлен структурой &lt;code&gt;hchan&lt;/code&gt;, весь секрет&#xA;которой сводится к единственной блокировке, кольцевому буферу и двум очередям ожидания.&#xA;Структура невелика, однако каждое поле существует по конкретной проектной причине. Стоит уяснить&#xA;эти немногие элементы — и вся последующая логика отправки, получения и select&#xA;(&lt;a href=&#34;.././sendrecv&#34;&gt;10.3&lt;/a&gt;–&lt;a href=&#34;.././lockfree&#34;&gt;10.6 Модель памяти и эволюция в сторону lock-free&lt;/a&gt;)&#xA;окажется лишь «перемещением данных, парковкой и пробуждением горутин поверх этой единственной&#xA;картины».&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.3 Отправка, получение и прямая передача</title>
      <link>/ru/part3concurrency/ch10chan/sendrecv/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch10chan/sendrecv/</guid>
      <description>&lt;h1 id=&#34;103-отправка-получение-и-прямая-передача&#34;&gt;10.3 Отправка, получение и прямая передача&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././impl&#34;&gt;10.2&lt;/a&gt; очертил скелет &lt;code&gt;hchan&lt;/code&gt;: мьютекс, кольцевой буфер &lt;code&gt;buf&lt;/code&gt;, а также очередь&#xA;отправителей &lt;code&gt;sendq&lt;/code&gt; и очередь получателей &lt;code&gt;recvq&lt;/code&gt;. Настоящий раздел оживляет этот скелет и&#xA;отвечает на вопрос, что именно происходит внутри рантайма при одиночном &lt;code&gt;ch &amp;lt;- v&lt;/code&gt; и одиночном&#xA;&lt;code&gt;v := &amp;lt;-ch&lt;/code&gt;. Разобравшись в этом пути отправки/получения, оба наиболее часто задаваемых вопроса&#xA;о каналах — почему небуферизованный канал является единственной точкой рандеву и почему для&#xA;него получение происходит прежде, чем завершается соответствующая отправка&#xA;(&lt;a href=&#34;../../ch11sync/mem&#34;&gt;11.9&lt;/a&gt;), — сводятся к одному механизму: &lt;strong&gt;прямой отправке / прямому&#xA;получению&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.4 Семантика закрытия канала</title>
      <link>/ru/part3concurrency/ch10chan/close/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch10chan/close/</guid>
      <description>&lt;h1 id=&#34;104-семантика-закрытия-канала&#34;&gt;10.4 Семантика закрытия канала&lt;/h1&gt;&#xA;&lt;p&gt;В предыдущих разделах операции отправки и получения были «парными» рандеву: одна отправка соответствует одному получению, а избыточная сторона блокируется в ожидании. Закрытие — единственная операция над каналом с семантикой «один ко многим». &lt;code&gt;close(ch)&lt;/code&gt; вызывается одной горутиной, однако в тот же момент пробуждает всех получателей, заблокированных на канале, и заставляет немедленно запаниковать всех заблокированных отправителей. Эта возможность «однократного широковещания, пробуждающего всех» превращает закрытие из, казалось бы, малозначимой операции очистки в наиболее распространённый в Go механизм отмены и завершения работы. Паттерн done-канала и отмена через &lt;code&gt;context&lt;/code&gt; (&lt;a href=&#34;/ru/part3concurrency/ch11sync/context/&#34;&gt;11.8&lt;/a&gt;) восходят именно к этой семантике.&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.5 Реализация select</title>
      <link>/ru/part3concurrency/ch10chan/select/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch10chan/select/</guid>
      <description>&lt;h1 id=&#34;105-реализация-select&#34;&gt;10.5 Реализация select&lt;/h1&gt;&#xA;&lt;p&gt;Предыдущие разделы детально рассмотрели отправку и приём данных по одному каналу (&lt;a href=&#34;.././sendrecv&#34;&gt;10.3&lt;/a&gt;). На практике, однако, горутина редко наблюдает лишь один канал: требуется реагировать на &lt;strong&gt;первую готовую операцию&lt;/strong&gt; из нескольких отправок и приёмов и при этом &lt;strong&gt;избегать блокировки, если ни одна из них не готова&lt;/strong&gt;. &lt;code&gt;select&lt;/code&gt; создан именно для этого. Его семантика выглядит просто, однако реализация должна одновременно решить две нетривиальные задачи: как &lt;strong&gt;справедливо&lt;/strong&gt; выбрать ветку, когда несколько из них готовы, и как &lt;strong&gt;избежать взаимоблокировки&lt;/strong&gt; при захвате нескольких каналов в рамках одного select. Оба требования определяют всю структуру &lt;code&gt;selectgo&lt;/code&gt;, и данный раздел построен вокруг них.&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.6 Модель памяти и эволюция без блокировок</title>
      <link>/ru/part3concurrency/ch10chan/lockfree/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch10chan/lockfree/</guid>
      <description>&lt;h1 id=&#34;106-модель-памяти-и-эволюция-без-блокировок&#34;&gt;10.6 Модель памяти и эволюция без блокировок&lt;/h1&gt;&#xA;&lt;p&gt;Предыдущие разделы разобрали канал до составных частей: прямая передача в &lt;a href=&#34;.././sendrecv&#34;&gt;10.3&lt;/a&gt; позволяет&#xA;отправляющей и принимающей сторонам передавать значение напрямую, минуя кольцевой буфер, а &lt;code&gt;select&lt;/code&gt; в&#xA;&lt;a href=&#34;.././select&#34;&gt;10.5&lt;/a&gt; делает случайный выбор среди нескольких готовых веток. Эти механизмы объясняют, как канал&#xA;«работает». Настоящий раздел отвечает на два вопроса более высокого уровня: какую &lt;strong&gt;гарантию видимости&lt;/strong&gt; канал&#xA;даёт конкурентной программе, и на один инженерный вопрос, который поднимается нередко, — почему канал по сей&#xA;день остаётся «мьютексом плюс очередью», а не предположительно более быстрой структурой без блокировок. Первый&#xA;вопрос связывает канал с моделью памяти в &lt;a href=&#34;../../ch11sync/mem&#34;&gt;11.9&lt;/a&gt;; второй представляет собой реальный&#xA;компромисс о том, «как корректность и сопровождаемость побеждают пиковую производительность».&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.7 Инженерная практика и межъязыковое сравнение</title>
      <link>/ru/part3concurrency/ch10chan/pattern/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch10chan/pattern/</guid>
      <description>&lt;h1 id=&#34;107-инженерная-практика-и-межъязыковое-сравнение&#34;&gt;10.7 Инженерная практика и межъязыковое сравнение&lt;/h1&gt;&#xA;&lt;p&gt;В предыдущих разделах мы разобрали внутреннее устройство канала, пути отправки и получения&#xA;данных, а также реализацию &lt;code&gt;select&lt;/code&gt; на самом низком уровне. Разобравшись в механизме,&#xA;закономерно встаёт более трудный и практически ориентированный вопрос: когда следует&#xA;использовать канал, а когда — нет. Слоган Go «не общайтесь через разделяемую память;&#xA;вместо этого делитесь памятью через общение» легко прочитать как «любое разделяемое&#xA;состояние должно проходить через канал», но это не то, что имел в виду его автор. Данный&#xA;раздел сводит этот слоган к конкретному практическому правилу, сопоставляет его с более&#xA;лёгкими инструментами из главы о примитивах синхронизации (&lt;a href=&#34;../../ch11sync/readme&#34;&gt;Глава 11&lt;/a&gt;),&#xA;а затем помещает выбор Go в контекст семейства CSP, чтобы понять его конкретные координаты&#xA;в пространстве проектных решений.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.1 Shared-Memory Synchronization Patterns</title>
      <link>/ru/part3concurrency/ch11sync/basic/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/basic/</guid>
      <description>&lt;h1 id=&#34;111-shared-memory-synchronization-patterns&#34;&gt;11.1 Shared-Memory Synchronization Patterns&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The fact that programs can be constructed from simpler basic primitives is a strong&#xA;guarantee: what these primitives contain stays logically consistent with the rest of the&#xA;programming language.&#xA;&amp;ndash; C. A. R. Hoare&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The whole difficulty of concurrent programming can be distilled into a single sentence:&#xA;multiple units of execution must cooperate over the same shared state, and the order in&#xA;which each of them makes progress cannot be predicted. How to make that cooperation both&#xA;correct and efficient has, over more than half a century, split into two great traditions.&#xA;This chapter is about one of them, shared memory. Before we take apart the concrete&#xA;primitives in Go&amp;rsquo;s standard library &lt;code&gt;sync&lt;/code&gt; and &lt;code&gt;sync/atomic&lt;/code&gt;, this section first lays the&#xA;two traditions side by side to tell them apart, explains why they are dual to each other,&#xA;and then states where Go stands between them. Once you understand this layer of trade-offs,&#xA;the sections from &lt;a href=&#34;.././mutex&#34;&gt;11.2&lt;/a&gt; through &lt;a href=&#34;.././mem&#34;&gt;11.9&lt;/a&gt; are no longer isolated APIs&#xA;but the same design philosophy landing on different scenarios.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.2 Мьютекс</title>
      <link>/ru/part3concurrency/ch11sync/mutex/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/mutex/</guid>
      <description>&lt;h1 id=&#34;112-мьютекс&#34;&gt;11.2 Мьютекс&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;sync.Mutex&lt;/code&gt; — наиболее фундаментальный примитив синхронизации: он допускает в критическую секцию лишь одну горутину одновременно. За этим простым интерфейсом скрывается постоянное балансирование между двумя конкурирующими целями: &lt;strong&gt;пропускная способность&lt;/strong&gt; (передать блокировку как можно быстрее, не оставляя процессор простаивать) и &lt;strong&gt;справедливость&lt;/strong&gt; (не допускать бесконечного ожидания в очереди). Достичь обеих целей в полной мере невозможно: передача блокировки строго по порядку поступления наиболее справедлива, однако сопряжена с переключением контекста при каждой передаче; предоставление только что пробудившемуся ожидающему свободно конкурировать с уже выполняющимися горутинами — наиболее быстро, однако чревато тем, что некоторые ожидающие будут проигрывать снова и снова. Данный раздел сначала излагает теорию и аппаратные основы классической задачи взаимного исключения, а затем рассматривает, как мьютекс Go держит этот баланс.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.3 Атомарные операции</title>
      <link>/ru/part3concurrency/ch11sync/atomic/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/atomic/</guid>
      <description>&lt;h1 id=&#34;113-атомарные-операции&#34;&gt;11.3 Атомарные операции&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;sync/atomic&lt;/code&gt; — это уровень примитивов синхронизации Go, расположенный ближе всего к аппаратуре.&#xA;Все более высокоуровневые инструменты — мьютекс (&lt;a href=&#34;.././mutex&#34;&gt;11.1&lt;/a&gt;) и каналы (&lt;a href=&#34;../ch08channel&#34;&gt;8&lt;/a&gt;) —&#xA;внутри построены на атомарных операциях. Пакет обеспечивает «неделимые» чтения, записи и операции&#xA;«чтение–модификация–запись»: атомарная операция либо выполняется целиком, либо не выполняется вовсе, и ни&#xA;одна горутина никогда не видит её в незавершённом состоянии. Однако смысл атомарных операций простирается&#xA;далеко за пределы «отсутствия частичной записи». Они составляют фундамент неблокирующего (lock-free)&#xA;программирования, а за неблокирующим программированием стоит глубокая теория о том, «какой примитив на что&#xA;способен». Именно эту теорию данный раздел стремится прояснить.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.4 Condition Variables</title>
      <link>/ru/part3concurrency/ch11sync/cond/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/cond/</guid>
      <description>&lt;h1 id=&#34;114-condition-variables&#34;&gt;11.4 Condition Variables&lt;/h1&gt;&#xA;&lt;p&gt;The mutex (&lt;a href=&#34;.././mutex&#34;&gt;11.2&lt;/a&gt;) answers the question of &amp;ldquo;who may enter the critical section.&amp;rdquo; A condition variable answers a different one: &amp;ldquo;a Goroutine has already entered the critical section, but finds it cannot do its work yet; how does it wait until the moment it can, while yielding the critical section to others.&amp;rdquo; The producer and consumer is the most common example. When the queue is full, the producer can neither write nor keep spinning while holding the lock, otherwise the consumer can never acquire the lock to free up space, and both sides deadlock together. The condition variable &lt;code&gt;sync.Cond&lt;/code&gt; offers this way out: let the producer &amp;ldquo;sleep with the lock,&amp;rdquo; atomically hand off the lock and block, and once the consumer frees up space, wake it back up, with the lock back in its hands on waking.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.5 Wait Groups</title>
      <link>/ru/part3concurrency/ch11sync/waitgroup/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/waitgroup/</guid>
      <description>&lt;h1 id=&#34;115-wait-groups&#34;&gt;11.5 Wait Groups&lt;/h1&gt;&#xA;&lt;p&gt;The mutex (&lt;a href=&#34;.././mutex&#34;&gt;11.2&lt;/a&gt;) and the condition variable (&lt;a href=&#34;.././cond&#34;&gt;11.4&lt;/a&gt;) from the previous&#xA;sections solve the problem of &amp;ldquo;multiple Goroutines contending for the same shared state.&amp;rdquo; This&#xA;section turns to &lt;code&gt;sync.WaitGroup&lt;/code&gt;, which solves a different class of problem: one Goroutine spawns&#xA;several subtasks, then waits for all of them to finish before continuing. The former is contention,&#xA;the latter is rendezvous. This is one of the most common structures in concurrent programming,&#xA;called fork-join, and &lt;code&gt;WaitGroup&lt;/code&gt; is exactly the Go standard library&amp;rsquo;s answer to it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.6 Object Pool</title>
      <link>/ru/part3concurrency/ch11sync/pool/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/pool/</guid>
      <description>&lt;h1 id=&#34;116-object-pool&#34;&gt;11.6 Object Pool&lt;/h1&gt;&#xA;&lt;p&gt;Allocating and then discarding the same kind of temporary object over and over puts heavy pressure on&#xA;the garbage collector (&lt;a href=&#34;/ru/part4memory/ch13gc/&#34;&gt;13&lt;/a&gt;). &lt;code&gt;sync.Pool&lt;/code&gt; offers a way out: stash a used object&#xA;and reuse it next time instead of allocating fresh every time. Its typical use is for temporary objects&#xA;like buffers and serializers that are &amp;ldquo;discarded after one use, yet needed again and again&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bufPool&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bufPool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Reset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;           &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// the object you get back may be dirty, so reset it first&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ... use b ...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bufPool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Put&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// put it back when done, for others to reuse&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;&lt;code&gt;New&lt;/code&gt; is the only field the user needs to supply: when the pool has no object to hand out, it falls back&#xA;to &lt;code&gt;New&lt;/code&gt; to make one. So what &lt;code&gt;Get&lt;/code&gt; returns is either an old object someone just put back, or a new one&#xA;freshly made by &lt;code&gt;New&lt;/code&gt;, and the caller has no way, and no need, to tell the two apart. This section first&#xA;covers the ancient idea of reusing objects that the pool rests on, then unpacks the three layers it builds&#xA;for concurrency and GC: per-P sharding, the victim cache, and cooperation with the runtime&amp;rsquo;s GC.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.7 Concurrency-Safe Hash Table</title>
      <link>/ru/part3concurrency/ch11sync/map/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/map/</guid>
      <description>&lt;h1 id=&#34;117-concurrency-safe-hash-table&#34;&gt;11.7 Concurrency-Safe Hash Table&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;code&gt;sync.Map&lt;/code&gt; went through a complete internal rewrite in Go 1.24: the original&#xA;read/dirty two-map design was replaced by a concurrent hash-trie. This section first makes the&#xA;origin of each of these two generations of design clear, then places them back into the&#xA;solution lineage of concurrent hash tables.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The language&amp;rsquo;s built-in &lt;code&gt;map&lt;/code&gt; is not concurrency-safe. When multiple goroutines read and write the same &lt;code&gt;map&lt;/code&gt; simultaneously without synchronization,&#xA;the runtime actively detects the concurrent access and terminates the process with &lt;code&gt;fatal error: concurrent map read and map write&lt;/code&gt;&#xA;(&lt;a href=&#34;/ru/part2lang/ch05data/map/&#34;&gt;5.2&lt;/a&gt; introduced this detection mechanism; it is unrecoverable, and &lt;code&gt;recover&lt;/code&gt;&#xA;cannot intercept it). For services with availability requirements, this is a design question that must be answered head-on: when multiple goroutines need to share&#xA;one table, what structure should be used.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.8 Context</title>
      <link>/ru/part3concurrency/ch11sync/context/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/context/</guid>
      <description>&lt;h1 id=&#34;118-context&#34;&gt;11.8 Context&lt;/h1&gt;&#xA;&lt;p&gt;A request entering a server seldom runs to completion inside a single goroutine. It fans out into&#xA;a tree of goroutines: one queries the database, one calls a downstream RPC, one reads the cache,&#xA;and each of these spawns smaller units of work in turn. Once the originator of the request no longer&#xA;needs the result, say the client has disconnected, or the upstream has already timed out, all the&#xA;work still running across that tree becomes pure waste, holding connections, locks, and memory while&#xA;no one will ever read what it produces. The problem then becomes: &lt;strong&gt;how do we propagate the signal&#xA;&amp;ldquo;stop here&amp;rdquo; from the root of the tree out to every leaf, so each of them can stop on its own.&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.9 Memory Consistency Models</title>
      <link>/ru/part3concurrency/ch11sync/mem/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/mem/</guid>
      <description>&lt;h1 id=&#34;119-memory-consistency-models&#34;&gt;11.9 Memory Consistency Models&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Go&amp;rsquo;s memory model went through an important revision in 2022 with Go 1.19.&#xA;After laying out the current model, this section devotes space to the background of that&#xA;revision (see &lt;a href=&#34;/ru/part3concurrency/ch11sync/mem/#1198-the-evolution-of-the-design&#34;&gt;11.9.8&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The reader may have noticed that in the earlier discussion of the Go runtime and compiler we&#xA;kept steering clear of one topic: Go&amp;rsquo;s memory model. The avoidance was not without reason. To&#xA;explain it properly requires groundwork in concurrency, synchronization primitives, and even&#xA;the hardware layer, and only now is that groundwork in place. As the close of this chapter,&#xA;and as the book&amp;rsquo;s summary of Go&amp;rsquo;s synchronization primitives and patterns, we take up the&#xA;topic here and answer the question still unresolved in the reader&amp;rsquo;s mind: when two Goroutines&#xA;read and write the same variable at once, under what conditions is one side&amp;rsquo;s write&#xA;guaranteed to be seen by the other.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.1 Design Principles</title>
      <link>/ru/part4memory/ch12alloc/basic/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/basic/</guid>
      <description>&lt;h1 id=&#34;121-design-principles&#34;&gt;12.1 Design Principles&lt;/h1&gt;&#xA;&lt;p&gt;By now we have seen how a Go program starts up, and how the scheduler spreads goroutines across operating-system threads for execution. The single thing that scheduled code does most often is request memory: every &lt;code&gt;new&lt;/code&gt;, every local variable that escapes to the heap, every slice growth, all funnel into the same entry point, &lt;code&gt;runtime.mallocgc&lt;/code&gt;. This chapter is about the allocator behind that entry point. This section does not yet touch its parts; it answers a question that comes first: what mutually pulling goals must a memory allocator built for Go satisfy, and on which few judgments does it settle them? Once you understand these trade-offs, the structures and paths in the following sections (&lt;a href=&#34;.././component&#34;&gt;12.2&lt;/a&gt;–&lt;a href=&#34;.././tinyalloc&#34;&gt;12.6&lt;/a&gt;) will all have a clear origin.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.2 Components</title>
      <link>/ru/part4memory/ch12alloc/component/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/component/</guid>
      <description>&lt;h1 id=&#34;122-components&#34;&gt;12.2 Components&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././basic&#34;&gt;12.1&lt;/a&gt; described the allocator as a layered structure that is &amp;ldquo;lock-free on the fast path, locked on the slow path.&amp;rdquo; This section names and locates the few core components of that structure, and grounds them in their actual form in go1.26: what state each component carries, why it is designed the way it is, and how they chain together into a restocking pipeline. Once you have understood these few things, the later allocation paths (&lt;a href=&#34;.././largealloc&#34;&gt;12.4&lt;/a&gt;–&lt;a href=&#34;.././tinyalloc&#34;&gt;12.6&lt;/a&gt;) are just &amp;ldquo;a walk across this same picture.&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.3 Initialization</title>
      <link>/ru/part4memory/ch12alloc/init/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/init/</guid>
      <description>&lt;h1 id=&#34;123-initialization&#34;&gt;12.3 Initialization&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././component&#34;&gt;12.2&lt;/a&gt; broke the allocator down into a few parts: mcache, mcentral, mheap, and arena. But that was a&#xA;static picture. These parts do not fall into place on their own; they must be set up once, at the very start of the&#xA;program. This section answers the question: when &lt;code&gt;main&lt;/code&gt; has not yet run and the first &lt;code&gt;new&lt;/code&gt; has not yet happened, what&#xA;infrastructure does the runtime lay down for the allocator.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.4 Large Object Allocation</title>
      <link>/ru/part4memory/ch12alloc/largealloc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/largealloc/</guid>
      <description>&lt;h1 id=&#34;124-large-object-allocation&#34;&gt;12.4 Large Object Allocation&lt;/h1&gt;&#xA;&lt;p&gt;The allocation hierarchy of &lt;a href=&#34;.././component&#34;&gt;12.2&lt;/a&gt; was built for objects that are &amp;ldquo;small and frequent&amp;rdquo;: a per-P mcache, a mcentral shared by size class, and a global mheap. These three cache layers collapse the vast majority of allocations into a handful of lock-free bit operations. But this delicate machine carries an implicit premise, that the object is small enough to fit into a size class. Once an object exceeds &lt;code&gt;maxSmallSize&lt;/code&gt; (32768 bytes in go1.26, that is 32KB), it no longer fits into any size class slot, and the whole replenishment chain loses its meaning for it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.5 Small Object Allocation</title>
      <link>/ru/part4memory/ch12alloc/smallalloc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/smallalloc/</guid>
      <description>&lt;h1 id=&#34;125-small-object-allocation&#34;&gt;12.5 Small Object Allocation&lt;/h1&gt;&#xA;&lt;p&gt;Small objects are those whose size falls between 16B and 32KB (in go1.26, &lt;code&gt;maxSmallSize = 32768&lt;/code&gt;). They are the most common kind of allocation in a Go program: a struct, the backing array of a modest slice, the result of a string concatenation, the vast majority land in this range. The path the allocator designs for them is the main stage of the mcache → mcentral → mheap hierarchy from &lt;a href=&#34;.././component&#34;&gt;12.2&lt;/a&gt;, and it is where the whole allocator&amp;rsquo;s &amp;ldquo;lock-free fast path&amp;rdquo; design (&lt;a href=&#34;.././basic&#34;&gt;12.1&lt;/a&gt;) makes good on its performance promise.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.6 Tiny Object Allocation</title>
      <link>/ru/part4memory/ch12alloc/tinyalloc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/tinyalloc/</guid>
      <description>&lt;h1 id=&#34;126-tiny-object-allocation&#34;&gt;12.6 Tiny Object Allocation&lt;/h1&gt;&#xA;&lt;p&gt;The previous two sections walked through the two ends of the allocator. Large objects (&lt;a href=&#34;.././largealloc&#34;&gt;12.4&lt;/a&gt;) bypass the cache and ask the mheap for memory by the page directly; small objects (&lt;a href=&#34;.././smallalloc&#34;&gt;12.5&lt;/a&gt;) pull an equal-sized slot from the per-P mcache according to their size class. This section fills in the last and least conspicuous category of object, the &lt;strong&gt;tiny object&lt;/strong&gt;: those smaller than 16 bytes and free of pointers. They are extremely numerous and individually tiny, so if each were also given a slot by size class, the waste would be astonishing. Go sets up a dedicated path for them, packing multiple tiny objects &lt;strong&gt;into the same block&lt;/strong&gt;, and trades one simple &amp;ldquo;bump pointer&amp;rdquo; technique for sizable memory savings.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.7 The Page Allocator</title>
      <link>/ru/part4memory/ch12alloc/pagealloc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/pagealloc/</guid>
      <description>&lt;h1 id=&#34;127-the-page-allocator&#34;&gt;12.7 The Page Allocator&lt;/h1&gt;&#xA;&lt;p&gt;Beneath mheap (&lt;a href=&#34;.././component&#34;&gt;12.2&lt;/a&gt;), the component that answers &amp;ldquo;which pages are free, which are in use&amp;rdquo; is the page allocator. It is the foundation of the entire allocator: when an mcentral runs out of spans it asks mheap for new pages, large objects (&lt;a href=&#34;.././largealloc&#34;&gt;12.4&lt;/a&gt;) are requested directly by page, and every page a span occupies is ultimately carved out from here. The page allocator must also return pages that have stayed idle for a long time back to the operating system. These two responsibilities, one tied to the speed of allocation and the other to the resident memory of the process, are exactly the most enduring tension in Go&amp;rsquo;s memory system.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.8 Memory Statistics</title>
      <link>/ru/part4memory/ch12alloc/mstats/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/mstats/</guid>
      <description>&lt;h1 id=&#34;128-memory-statistics&#34;&gt;12.8 Memory Statistics&lt;/h1&gt;&#xA;&lt;p&gt;The allocator keeps books while it works. Every time it wholesales a stretch of address space from the operating system, carves out a span, or allocates or sweeps an object, the runtime accumulates the corresponding count into a set of global variables (&lt;code&gt;runtime.memstats&lt;/code&gt;). These books are not a statistical report compiled after the fact; they are a running ledger written down in passing during allocation and reclamation. They serve two ends. Outwardly, they let users and monitoring systems see the memory shape of the process. Inwardly, both &lt;a href=&#34;../../ch13gc/pacing&#34;&gt;the GC pacer (13.3)&lt;/a&gt; and &lt;a href=&#34;.././pagealloc&#34;&gt;the soft memory limit &lt;code&gt;GOMEMLIMIT&lt;/code&gt; (12.7)&lt;/a&gt; must read these books to decide &amp;ldquo;at what heap size should the next round of reclamation be triggered.&amp;rdquo; In other words, the bookkeeping closes a feedback loop: allocation produces data, data drives decisions, and decisions in turn constrain allocation.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.9 Past, Present, and Future</title>
      <link>/ru/part4memory/ch12alloc/history/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch12alloc/history/</guid>
      <description>&lt;h1 id=&#34;129-past-present-and-future&#34;&gt;12.9 Past, Present, and Future&lt;/h1&gt;&#xA;&lt;p&gt;The allocator did not arrive fully formed. It has been polished over and over as Go evolved. Tracing this line of evolution makes clear which trade-offs the current design grew out of, and gives a glimpse of where it is headed. One judgment worth keeping in mind first: nearly every major change to the allocator was not made to make &amp;ldquo;allocation itself faster,&amp;rdquo; but to re-place a stone in the three-way game among allocation speed, memory footprint, and cooperation with the garbage collector. Once this main thread is read, the several rewrites below stop looking like isolated version trivia and become instead the same engineering problem being solved again and again.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.1 The Basic Idea of Garbage Collection</title>
      <link>/ru/part4memory/ch13gc/basic/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/basic/</guid>
      <description>&lt;h1 id=&#34;131-the-basic-idea-of-garbage-collection&#34;&gt;13.1 The Basic Idea of Garbage Collection&lt;/h1&gt;&#xA;&lt;p&gt;Garbage collection (GC) frees the programmer from manual &lt;code&gt;free&lt;/code&gt;, at the cost that the runtime must decide for itself which memory is still useful and which can be reclaimed. Go&amp;rsquo;s GC treats low latency as its first priority: it would rather give up a little throughput and memory than let stalls (stop-the-world pauses) grow beyond the sub-millisecond range. This section sets up the theoretical coordinates of GC: reachability, mark-sweep, the tricolor abstraction, and where Go sits within the GC design space. The sections that follow are the elaboration of this basic idea.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.2 Write Barrier Techniques</title>
      <link>/ru/part4memory/ch13gc/barrier/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/barrier/</guid>
      <description>&lt;h1 id=&#34;132-write-barrier-techniques&#34;&gt;13.2 Write Barrier Techniques&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././basic&#34;&gt;13.1&lt;/a&gt; sketched the tricolor abstraction and the outline of concurrent collection: the collector recolors objects from white to grey and from grey to black, driving a &amp;ldquo;grey wavefront&amp;rdquo; that advances monotonically across the object graph, and wherever the wavefront has passed is confirmed live. If the mutator (the user-space code) were to halt while the wavefront advances, the abstraction would be self-consistent and would converge correctly. The trouble is precisely that the mutator does not halt. The fundamental difficulty of concurrent collection is this: the collector traces the object graph while the mutator rewrites it, and the two hold conflicting accounts of one and the same graph.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.3 Trigger Frequency and the Pacing Algorithm</title>
      <link>/ru/part4memory/ch13gc/pacing/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/pacing/</guid>
      <description>&lt;h1 id=&#34;133-trigger-frequency-and-the-pacing-algorithm&#34;&gt;13.3 Trigger Frequency and the Pacing Algorithm&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././barrier&#34;&gt;13.2&lt;/a&gt; explained how the write barrier lets concurrent marking coexist with the mutator, guaranteeing that &amp;ldquo;we can keep allocating even while collection is under way.&amp;rdquo; That leaves a question of timing: when should the next round of GC start? Too late, and the heap has already grown to an unacceptable size; too early, and frequent collection burns CPU for nothing. The answer comes from the &lt;strong&gt;pacer&lt;/strong&gt;, introduced in Go 1.5 and redesigned in 1.18. This section first lays out the full timeline of a GC cycle, then makes clear what kind of feedback controller the pacer is: it must finish marking before the heap reaches its goal, all while the mutator keeps allocating.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.4 Scan Marking and Mark Assist</title>
      <link>/ru/part4memory/ch13gc/mark/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/mark/</guid>
      <description>&lt;h1 id=&#34;134-scan-marking-and-mark-assist&#34;&gt;13.4 Scan Marking and Mark Assist&lt;/h1&gt;&#xA;&lt;p&gt;Marking is the main body of GC&amp;rsquo;s work: starting from the roots (global variables, each goroutine&amp;rsquo;s stack, registers),&#xA;it follows pointers and blackens every reachable object (the tricolor abstraction of &lt;a href=&#34;.././basic&#34;&gt;13.1&lt;/a&gt;). A naive&#xA;implementation would stop the whole world and walk the object graph single-threaded, which is exactly where early Go&amp;rsquo;s&#xA;unbearable pauses came from. After go1.5, marking was reshaped into a form where two things hold at once: it advances&#xA;&lt;strong&gt;concurrently with the user program&lt;/strong&gt;, and when the user allocates too fast it can &lt;strong&gt;amortize the cost&lt;/strong&gt; onto the&#xA;allocator itself. This section answers three questions: how marking unfolds in parallel without the workers stepping on&#xA;each other; how, when scanning an object, we know which of its fields are pointers; and what guarantees that marking&#xA;will never be left permanently behind while the user allocates and marking chases.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.5 Sweeping and Bitmaps</title>
      <link>/ru/part4memory/ch13gc/sweep/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/sweep/</guid>
      <description>&lt;h1 id=&#34;135-sweeping-and-bitmaps&#34;&gt;13.5 Sweeping and Bitmaps&lt;/h1&gt;&#xA;&lt;p&gt;Once marking (&lt;a href=&#34;.././mark&#34;&gt;13.4&lt;/a&gt;) finishes, any object still white is unreachable garbage. Reclaiming the&#xA;memory it occupies and handing it back to the allocator is the last step of collection: &lt;strong&gt;sweeping&lt;/strong&gt;. If we&#xA;followed the textbook recipe of &amp;ldquo;walk the heap and free dead objects one by one,&amp;rdquo; the cost of sweeping would&#xA;be proportional to the number of dead objects, and on a heap with millions of objects that is no small bill.&#xA;Go&amp;rsquo;s sweep sidesteps that bill. Three of its design points are worth making clear: sweeping is done by&#xA;&lt;strong&gt;flipping a bitmap&lt;/strong&gt; rather than processing objects one at a time; it runs &lt;strong&gt;concurrently&lt;/strong&gt; with the user&#xA;program and &lt;strong&gt;lazily&lt;/strong&gt; spreads its cost across the allocation path; and it &lt;strong&gt;does not move objects&lt;/strong&gt;, so it&#xA;accepts fragmentation and forgoes compaction. None of these three is accidental. Each corresponds to a&#xA;definite engineering trade-off, and this section unpacks them one by one.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.6 Mark Termination Phase</title>
      <link>/ru/part4memory/ch13gc/termination/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/termination/</guid>
      <description>&lt;h1 id=&#34;136-mark-termination-phase&#34;&gt;13.6 Mark Termination Phase&lt;/h1&gt;&#xA;&lt;p&gt;Concurrent marking (&lt;a href=&#34;.././mark&#34;&gt;13.4&lt;/a&gt;) carries one nontrivial closing problem: how to &lt;strong&gt;decide that marking is complete&lt;/strong&gt;. In a single-threaded, stop-the-world collector this problem is trivial, the marking thread drains the grey queue and marking is over. But in a concurrent world, &amp;ldquo;my local queue is empty&amp;rdquo; never means &amp;ldquo;there are no grey objects anywhere globally.&amp;rdquo; The instant a worker goroutine drains its own queue, a mutator on another P may happen to execute a pointer write, the write barrier (&lt;a href=&#34;.././barrier&#34;&gt;13.2&lt;/a&gt;) then greys a new object and stuffs it into that P&amp;rsquo;s local cache. Deciding termination is, in essence, confirming a &lt;strong&gt;global property&lt;/strong&gt; in a system that has no global lock and whose work is scattered across each P&amp;rsquo;s local cache: the grey set is empty, and no further grey objects will be produced.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.7 Safe-Point Analysis</title>
      <link>/ru/part4memory/ch13gc/safe/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/safe/</guid>
      <description>&lt;h1 id=&#34;137-safe-point-analysis&#34;&gt;13.7 Safe-Point Analysis&lt;/h1&gt;&#xA;&lt;p&gt;The marking phase (&lt;a href=&#34;.././mark&#34;&gt;13.4&lt;/a&gt;) must scan a goroutine&amp;rsquo;s stack, find every pointer on the stack that points into the heap, and add them to the grey queue as GC roots. This sounds straightforward, but it hides a premise that is easy to overlook: &lt;strong&gt;is a given machine word on the stack actually a pointer?&lt;/strong&gt; A 64-bit word might be a heap address, or it might be an integer that happens to fall within the range of heap addresses, a half-disassembled floating-point value, or a garbage value that has not been written yet. If we mistake a non-pointer for a pointer, we needlessly hold onto a block of memory that should have been reclaimed; if we miss a pointer and treat it as an integer, we reclaim an object that is still in use, and the latter is fatal memory corruption.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.8 The Generational Hypothesis and Generational Collection</title>
      <link>/ru/part4memory/ch13gc/generational/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/generational/</guid>
      <description>&lt;h1 id=&#34;138-the-generational-hypothesis-and-generational-collection&#34;&gt;13.8 The Generational Hypothesis and Generational Collection&lt;/h1&gt;&#xA;&lt;p&gt;People who have studied the JVM or .NET often ask a pointed question: why does Go &lt;strong&gt;not do generational GC&lt;/strong&gt;? Generational collection is standard equipment in the GCs of mainstream managed runtimes such as Java and .NET, treated as the key to efficient collection, almost to the point of becoming the common sense of &amp;ldquo;this is how a modern GC ought to be.&amp;rdquo; Go pointedly does not adopt it. This section explains the idea of generational collection, its power, and Go&amp;rsquo;s reasons for not taking this road. The answer here deserves care: it is not that &amp;ldquo;the Go team did not think of it,&amp;rdquo; but that the Go team &lt;strong&gt;actually implemented a non-moving generational GC, measured its performance, and in the end gave it up&lt;/strong&gt;. This is a case that powerfully illustrates &amp;ldquo;there is no design that fits all circumstances.&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.9 The Request Hypothesis and the Request-Oriented Collector</title>
      <link>/ru/part4memory/ch13gc/roc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/roc/</guid>
      <description>&lt;h1 id=&#34;139-the-request-hypothesis-and-the-request-oriented-collector&#34;&gt;13.9 The Request Hypothesis and the Request-Oriented Collector&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././generational&#34;&gt;13.8&lt;/a&gt; explained why Go did not adopt the generational hypothesis. The reader may go on to ask: did Go ever try a different &amp;ldquo;object lifetime hypothesis&amp;rdquo;? It did. This section is about an experiment the Go team took seriously and ultimately abandoned, the Request-Oriented Collector (ROC). We discuss it not because it made it into Go, but because an abandoned design often reveals where the constraints lie better than a successful one. ROC is like a proof done wrong: the spot where it goes wrong marks exactly the boundary in Go&amp;rsquo;s GC design space that cannot be crossed.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.10 Finalizers</title>
      <link>/ru/part4memory/ch13gc/finalizer/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/finalizer/</guid>
      <description>&lt;h1 id=&#34;1310-finalizers&#34;&gt;13.10 Finalizers&lt;/h1&gt;&#xA;&lt;p&gt;The usual storyline of garbage collection ends at &lt;a href=&#34;.././sweep&#34;&gt;sweeping (13.5)&lt;/a&gt;: marking finds the live objects, and sweeping returns the slots of dead objects to the allocator. But there is a class of object that wants to say one last word before it dies. It wraps a non-memory resource, a file descriptor, an mmap region, a handle allocated on the C side, and when the Go-side wrapper object becomes unreachable, the underlying resource ought to be released along with it. Garbage collection only understands memory, though, and has no idea that a descriptor needs to be &lt;code&gt;close&lt;/code&gt;d. The finalizer is the hook designed for exactly this gap: you register a function for an object, and when garbage collection decides the object is unreachable, the runtime does not free it immediately but first calls that function.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.11 Green Tea: Locality-Aware Marking</title>
      <link>/ru/part4memory/ch13gc/greentea/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/greentea/</guid>
      <description>&lt;h1 id=&#34;1311-green-tea-locality-aware-marking&#34;&gt;13.11 Green Tea: Locality-Aware Marking&lt;/h1&gt;&#xA;&lt;p&gt;The previous sections framed marking as a matter of being &amp;ldquo;correct and timely&amp;rdquo;: the write barrier (&lt;a href=&#34;.././barrier&#34;&gt;13.2&lt;/a&gt;)&#xA;guarantees nothing live is missed, and mark assist (&lt;a href=&#34;.././mark&#34;&gt;13.4&lt;/a&gt;) guarantees marking keeps up with allocation. Yet&#xA;neither touches another account in marking&amp;rsquo;s ledger: &lt;strong&gt;how fast marking actually runs&lt;/strong&gt;. On today&amp;rsquo;s multi-core, large-heap&#xA;machines, the bottleneck of marking often lies neither in the algorithm&amp;rsquo;s correctness nor in the CPU&amp;rsquo;s raw compute, but in&#xA;the &lt;strong&gt;memory wall&lt;/strong&gt;: the processor spends most of its time not computing but waiting for one cache miss after another to&#xA;return. Green Tea goes straight at that account.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.12 Past, Present, and Future</title>
      <link>/ru/part4memory/ch13gc/history/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/history/</guid>
      <description>&lt;h1 id=&#34;1312-past-present-and-future&#34;&gt;13.12 Past, Present, and Future&lt;/h1&gt;&#xA;&lt;p&gt;Having read the previous ten sections, the reader now holds the present state of each part of Go&amp;rsquo;s garbage collector: tri-color marking (&lt;a href=&#34;.././basic&#34;&gt;13.1&lt;/a&gt;),&#xA;the hybrid write barrier (&lt;a href=&#34;.././barrier&#34;&gt;13.2&lt;/a&gt;), the pacer (&lt;a href=&#34;.././pacing&#34;&gt;13.3&lt;/a&gt;), and sweeping and reclamation (&lt;a href=&#34;.././sweep&#34;&gt;13.5&lt;/a&gt;).&#xA;This section takes a different angle: it places these parts back on a timeline and watches how, step by step, they grew into their present shape.&lt;/p&gt;&#xA;&lt;p&gt;The evolution of the collector reads like a curve that converges steadily in one direction. From Go 1.0 to today, pause times have dropped by roughly two&#xA;orders of magnitude, and along the way one constant theme runs through it all: every change serves the goal of completing collection without disturbing user code.&#xA;Once that theme is understood, the many schemes below, both those adopted and those abandoned, can all be measured against the same ruler.&lt;/p&gt;</description>
    </item>
    <item>
      <title>13.13 A Unified Theory of Garbage Collection</title>
      <link>/ru/part4memory/ch13gc/unifiedgc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch13gc/unifiedgc/</guid>
      <description>&lt;h1 id=&#34;1313-a-unified-theory-of-garbage-collection&#34;&gt;13.13 A Unified Theory of Garbage Collection&lt;/h1&gt;&#xA;&lt;p&gt;Having read through this chapter, we have seen Go&amp;rsquo;s concurrent mark-and-sweep, its hybrid write barrier, and its pacer, and we have set them against other schools of thought such as the generational hypothesis and request-oriented collection. The algorithms come in many flavors, and by this point the reader may have a question building up: beyond the fact that they all &amp;ldquo;do garbage collection,&amp;rdquo; is there a common thread that places them inside a single framework?&lt;/p&gt;</description>
    </item>
    <item>
      <title>14.1 Design of Contiguous Stacks</title>
      <link>/ru/part4memory/ch14stack/design/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch14stack/design/</guid>
      <description>&lt;h1 id=&#34;141-design-of-contiguous-stacks&#34;&gt;14.1 Design of Contiguous Stacks&lt;/h1&gt;&#xA;&lt;p&gt;Every goroutine carries an execution stack. It holds the local variables, arguments, and return addresses of function calls, and it is the physical medium that lets a program be &amp;ldquo;currently executing&amp;rdquo;. In the C programs the reader is familiar with, this stack is provided by the operating system thread, its size fixed once at thread creation (&lt;code&gt;ulimit -s&lt;/code&gt; defaults are often 8MB) and unchanged thereafter. Go took a different road: a goroutine&amp;rsquo;s stack is not a thread stack but a segment of memory that the runtime allocates on the heap, manages itself, and can resize, starting at just 2KB.&lt;/p&gt;</description>
    </item>
    <item>
      <title>14.2 Stack Allocation and Caching</title>
      <link>/ru/part4memory/ch14stack/alloc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch14stack/alloc/</guid>
      <description>&lt;h1 id=&#34;142-stack-allocation-and-caching&#34;&gt;14.2 Stack Allocation and Caching&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././design&#34;&gt;14.1&lt;/a&gt; located the execution stack as a contiguous range of memory &lt;code&gt;[lo, hi)&lt;/code&gt;: it is managed by the runtime and ultimately lives on the heap, so the moment a Goroutine starts, someone has to carve out this range of addresses for it. The questions follow immediately: who carves it? how large? and once Goroutines are created and destroyed over and over, if these two- or three-kilobyte chunks of memory had to be requested directly from the operating system or the global heap every time, the cost of locks and system calls would crush the scheduler at once.&lt;/p&gt;</description>
    </item>
    <item>
      <title>14.3 Stack Growth</title>
      <link>/ru/part4memory/ch14stack/grow/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch14stack/grow/</guid>
      <description>&lt;h1 id=&#34;143-stack-growth&#34;&gt;14.3 Stack Growth&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././readme&#34;&gt;14.1&lt;/a&gt; laid out the reasons Go chose contiguous stacks: a stack starts at just 2KB, and on overflow it swaps in a new stack twice as large and copies the entire old stack across. This section answers only one of the questions involved: how the runtime knows, at the &amp;ldquo;right moment,&amp;rdquo; that a stack is about to fill up, and how it hands control all the way over to the code responsible for the growth. The details of the stack copy itself are left to &lt;a href=&#34;.././copy&#34;&gt;14.4&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>14.4 Stack Copying and Pointer Adjustment</title>
      <link>/ru/part4memory/ch14stack/copy/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch14stack/copy/</guid>
      <description>&lt;h1 id=&#34;144-stack-copying-and-pointer-adjustment&#34;&gt;14.4 Stack Copying and Pointer Adjustment&lt;/h1&gt;&#xA;&lt;p&gt;The cost of contiguous stacks is concentrated in one place: when a goroutine&amp;rsquo;s stack overflows, the runtime allocates a new stack twice the size, moves the entire old stack over, and then frees the old stack. The moving step looks like nothing more than a single &lt;code&gt;memmove&lt;/code&gt;, copying the bytes of &lt;code&gt;[old.lo, old.hi)&lt;/code&gt; into &lt;code&gt;[new.lo, new.hi)&lt;/code&gt;. If it really were just copying bytes, a problem would arise: variables on the stack may hold pointers into the stack itself, for example a local variable that has taken the address of another local. After the bytes are moved verbatim to a new address, the values of these pointers are unchanged, still pointing at locations in the old stack, and the old stack is about to be reclaimed. The moment the copy finishes, they become dangling pointers.&lt;/p&gt;</description>
    </item>
    <item>
      <title>14.5 Stack Shrinking and Evolution</title>
      <link>/ru/part4memory/ch14stack/shrink/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part4memory/ch14stack/shrink/</guid>
      <description>&lt;h1 id=&#34;145-stack-shrinking-and-evolution&#34;&gt;14.5 Stack Shrinking and Evolution&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././copy&#34;&gt;14.4&lt;/a&gt; was about the stack &amp;ldquo;growing up&amp;rdquo;: when a function prologue finds the stack&#xA;nearly exhausted, the runtime copies the whole stack onto a larger block of memory, and the stack&#xA;pointer shifts along with it. This is a one-directional force driven by execution itself, and it&#xA;only ever makes the stack larger. Yet a goroutine&amp;rsquo;s stack depth is not monotonic. A recursion may&#xA;descend very deep, then return layer by layer back to a shallow point; the stack it actually needs&#xA;has long since shrunk back, but the large stack expanded for that deep recursion is still held in&#xA;its name. With growth but no return, every goroutine&amp;rsquo;s stack would eventually settle at its&#xA;historical deepest point. For a program that may hold millions of goroutines, this bill is&#xA;unbearable.&lt;/p&gt;</description>
    </item>
    <item>
      <title>15.1 Lexing and Grammar</title>
      <link>/ru/part5toolchain/ch15compile/parse/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch15compile/parse/</guid>
      <description>&lt;h1 id=&#34;151-lexing-and-grammar&#34;&gt;15.1 Lexing and Grammar&lt;/h1&gt;&#xA;&lt;p&gt;The first stop of compilation is turning source text into a structured &lt;strong&gt;abstract syntax tree&lt;/strong&gt; (AST). This takes&#xA;&lt;strong&gt;lexical analysis&lt;/strong&gt; (slicing a character stream into tokens) and &lt;strong&gt;syntactic analysis&lt;/strong&gt; (organizing tokens into a tree&#xA;according to the grammar). &lt;a href=&#34;/ru/part1overview/ch03life/compile/&#34;&gt;3.2&lt;/a&gt; surveyed the whole pipeline from above; this&#xA;section looks only at its front end, and at why Go&amp;rsquo;s grammar was designed to be so &amp;ldquo;easy to parse&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;The package that carries out these two steps is a self-contained part of the compiler, &lt;code&gt;cmd/compile/internal/syntax&lt;/code&gt;. It&#xA;is built from two instruments: the &lt;strong&gt;scanner&lt;/strong&gt; reads characters and emits a token stream; the &lt;strong&gt;parser&lt;/strong&gt; consumes tokens&#xA;in a &lt;strong&gt;recursive descent&lt;/strong&gt; fashion and builds the syntax tree. The package&amp;rsquo;s own comments even note with some pride that&#xA;several of its files, &lt;code&gt;scanner.go&lt;/code&gt;, &lt;code&gt;source.go&lt;/code&gt;, and &lt;code&gt;tokens.go&lt;/code&gt;, do not depend on the rest of the compiler and can be&#xA;compiled on their own into a standalone library. The reason lexing and grammar can be carved out this cleanly lies in the&#xA;simplicity of the Go grammar itself.&lt;/p&gt;</description>
    </item>
    <item>
      <title>15.2 Intermediate Representation</title>
      <link>/ru/part5toolchain/ch15compile/ssa/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch15compile/ssa/</guid>
      <description>&lt;h1 id=&#34;152-intermediate-representation&#34;&gt;15.2 Intermediate Representation&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././parse&#34;&gt;15.1&lt;/a&gt; turned source code into a syntax tree (AST). The AST faithfully records the structure the programmer wrote down: variables, scopes, the nesting of expressions. But the moment we want to optimize, this structure sits too &amp;ldquo;high.&amp;rdquo; Consider the most unremarkable line, &lt;code&gt;x = x + 1&lt;/code&gt;: in the AST, the &lt;code&gt;x&lt;/code&gt; on the left and the &lt;code&gt;x&lt;/code&gt; on the right are the same name, and if the compiler wants to know &amp;ldquo;which assignment produced the value of the &lt;code&gt;x&lt;/code&gt; used here,&amp;rdquo; it has to repeatedly perform scope lookups and data-flow analysis. Names get shadowed, scopes nest, an assignment wipes out the old value, and all of this makes &amp;ldquo;where a value comes from and where it goes,&amp;rdquo; which ought to be the most basic thing, murky. What the optimizer wants most is exactly to lay the data flow out in the open.&lt;/p&gt;</description>
    </item>
    <item>
      <title>15.3 The Optimizer</title>
      <link>/ru/part5toolchain/ch15compile/optimize/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch15compile/optimize/</guid>
      <description>&lt;h1 id=&#34;153-the-optimizer&#34;&gt;15.3 The Optimizer&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././ssa&#34;&gt;15.2&lt;/a&gt; lowered the front end&amp;rsquo;s syntax tree down to the SSA intermediate&#xA;representation and explained why SSA&amp;rsquo;s &amp;ldquo;each variable is assigned exactly once&amp;rdquo;&#xA;makes the optimization passes both accurate and fast to write. This section&#xA;continues from there: on top of this representation, what optimizations does the&#xA;compiler actually run, and why precisely these few.&lt;/p&gt;&#xA;&lt;p&gt;To read the Go optimizer well, we first have to read its disposition. Faced with&#xA;the same task of turning a high-level language into machine code, GCC and LLVM&#xA;are willing to spend seconds or even tens of seconds kneading a function over and&#xA;over to win the last few percent of run-time performance. Go takes a different&#xA;road: it does only the batch of optimizations with the best cost-to-benefit&#xA;ratio, and hands the time it saves back to compilation speed&#xA;(&lt;a href=&#34;/ru/part1overview/ch01intro/history/&#34;&gt;1.1&lt;/a&gt;). This is not a shortfall in&#xA;ability but a clear-eyed ordering of values, and this section will return to that&#xA;red line at the end. We first look at the optimizations Go is willing to do, then&#xA;at Profile-Guided Optimization (PGO), introduced in Go 1.21, which pushes&#xA;optimization from &amp;ldquo;static guessing&amp;rdquo; toward &amp;ldquo;data-driven.&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>15.4 The Pointer Checker</title>
      <link>/ru/part5toolchain/ch15compile/unsafe/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch15compile/unsafe/</guid>
      <description>&lt;h1 id=&#34;154-the-pointer-checker&#34;&gt;15.4 The Pointer Checker&lt;/h1&gt;&#xA;&lt;p&gt;Go is a memory-safe language. In ordinary code, the type system guarantees that every pointer refers to a legal object of its declared type, garbage collection (&lt;a href=&#34;/ru/part4memory/ch13gc/&#34;&gt;13&lt;/a&gt;) guarantees that an object is not reclaimed while it is still referenced, and the runtime keeps out-of-bounds accesses behind bounds checks. These guarantees are not free. They rest on the premise that the compiler always knows the type and layout of every value. Yet there is always a small set of scenarios that need to step outside this system: interoperating with C (&lt;a href=&#34;/ru/part5toolchain/ch15compile/cgo/&#34;&gt;15.6&lt;/a&gt;) means interpreting a span of bytes according to C&amp;rsquo;s memory layout, laying out a system structure for the operating system means placing bytes one by one, and reinterpreting a &lt;code&gt;[]byte&lt;/code&gt; as a &lt;code&gt;string&lt;/code&gt; with zero copies (&lt;a href=&#34;/ru/part2lang/ch05data/slice/&#34;&gt;5.1&lt;/a&gt;) means letting two types share the same underlying memory. Go leaves an escape hatch for these scenarios: the &lt;code&gt;unsafe&lt;/code&gt; package.&lt;/p&gt;</description>
    </item>
    <item>
      <title>15.5 Escape Analysis</title>
      <link>/ru/part5toolchain/ch15compile/escape/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch15compile/escape/</guid>
      <description>&lt;h1 id=&#34;155-escape-analysis&#34;&gt;15.5 Escape Analysis&lt;/h1&gt;&#xA;&lt;p&gt;Go programmers never manually decide whether a variable lives on the stack or the heap; the compiler&amp;rsquo;s &lt;strong&gt;escape analysis&lt;/strong&gt; does it automatically. It is the unsung hero of Go&amp;rsquo;s performance: keeping as many objects on the stack as possible greatly lightens the load on the garbage collector (&lt;a href=&#34;/ru/part4memory/ch13gc/&#34;&gt;13 Garbage Collection&lt;/a&gt;). This section explains how it decides, how it is implemented, and why it matters.&lt;/p&gt;&#xA;&lt;h2 id=&#34;1551-escape-deciding-stack-or-heap&#34;&gt;15.5.1 Escape: Deciding Stack or Heap&lt;/h2&gt;&#xA;&lt;p&gt;The core question: should a variable be allocated on the &lt;strong&gt;stack&lt;/strong&gt; (vanishing automatically when the function returns, at zero GC cost) or on the &lt;strong&gt;heap&lt;/strong&gt; (with an indefinite lifetime, managed by the GC)? The criterion is &lt;strong&gt;lifetime&lt;/strong&gt;: if a reference to a variable may still be used after the function returns, it cannot live on the stack (the stack frame is destroyed on return) and must &lt;strong&gt;escape&lt;/strong&gt; to the heap. Escape analysis answers this question statically, deciding whether a variable&amp;rsquo;s address can leave the scope of the function it lives in.&lt;/p&gt;</description>
    </item>
    <item>
      <title>15.6 cgo</title>
      <link>/ru/part5toolchain/ch15compile/cgo/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch15compile/cgo/</guid>
      <description>&lt;h1 id=&#34;156-cgo&#34;&gt;15.6 cgo&lt;/h1&gt;&#xA;&lt;p&gt;&amp;ldquo;cgo is not Go.&amp;rdquo; This is the verdict Rob Pike handed down on cgo in a blog post. It names a thing that is easy to overlook: when you write &lt;code&gt;import &amp;quot;C&amp;quot;&lt;/code&gt; in a Go source file and then write a single line of &lt;code&gt;C.foo()&lt;/code&gt;, you have already stepped out of the world that the Go language drew for you and into another world, one made of C&amp;rsquo;s ABI, C&amp;rsquo;s stack, and C&amp;rsquo;s memory model. cgo is the bridge between these two worlds. The bridge is useful, but crossing it costs a toll, and the toll is not cheap.&lt;/p&gt;</description>
    </item>
    <item>
      <title>15.7 Past, Present, and Future</title>
      <link>/ru/part5toolchain/ch15compile/future/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch15compile/future/</guid>
      <description>&lt;h1 id=&#34;157-past-present-and-future&#34;&gt;15.7 Past, Present, and Future&lt;/h1&gt;&#xA;&lt;p&gt;The compiler is the part of the Go toolchain that changes most often, yet remains the most transparent to users. Take the same source code, change nothing, recompile it with a new version, and it often comes out faster, smaller, and better, without you ever knowing what happened in between. This section pulls the camera back to look at the road the compiler itself has traveled, then at what it is doing now and where it is heading next. Running through all of it is one unchanging order of priorities: &lt;strong&gt;compilation speed comes first, quality of generated code second, and both are traded off under the constraint of being engineerable&lt;/strong&gt; (&lt;a href=&#34;/ru/part1overview/ch01intro/history/&#34;&gt;1.1&lt;/a&gt;).&lt;/p&gt;</description>
    </item>
    <item>
      <title>16.1 Runtime Deadlock Detection</title>
      <link>/ru/part5toolchain/ch16tools/deadlock/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch16tools/deadlock/</guid>
      <description>&lt;h1 id=&#34;161-runtime-deadlock-detection&#34;&gt;16.1 Runtime Deadlock Detection&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;fatal error: all goroutines are asleep - deadlock!&lt;/code&gt;, almost every Go programmer has seen this error. It&#xA;comes from the runtime&amp;rsquo;s built-in deadlock detector. This section first makes clear how it decides and when&#xA;it fires, then turns to something more important: what it can guarantee in theory, and what it is&#xA;&lt;strong&gt;structurally unable to detect&lt;/strong&gt;. The latter is the root of many production &amp;ldquo;hang&amp;rdquo; mysteries; understanding&#xA;it is worth more than memorizing that error message.&lt;/p&gt;</description>
    </item>
    <item>
      <title>16.2 Race Detection</title>
      <link>/ru/part5toolchain/ch16tools/race/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch16tools/race/</guid>
      <description>&lt;h1 id=&#34;162-race-detection&#34;&gt;16.2 Race Detection&lt;/h1&gt;&#xA;&lt;p&gt;A data race is the most insidious and hardest-to-reproduce class of bug in concurrent programs. Its definition is short: if two goroutines&#xA;access the same memory address concurrently, at least one of them is a write, and no synchronization orders the two, then the program&amp;rsquo;s behavior&#xA;is undefined (&lt;a href=&#34;/ru/part3concurrency/ch11sync/mem/&#34;&gt;11.9&lt;/a&gt;). &amp;ldquo;Undefined&amp;rdquo; is not as gentle as &amp;ldquo;you read a stale value&amp;rdquo;;&#xA;it means the compiler and the processor are both allowed to perform surprising reorderings, and the result may be right one time and wrong the next, may appear only under stress&#xA;load, only on a certain CPU, only in a certain version, just once in a while. There is no hope of catching this kind of bug by human code review. Go&amp;rsquo;s&#xA;&lt;strong&gt;race detector&lt;/strong&gt; (&lt;code&gt;-race&lt;/code&gt;) is exactly the tool that turns this from &amp;ldquo;reproduce it by luck&amp;rdquo; into &amp;ldquo;run it once and it tells you&amp;rdquo;. This section&#xA;explains its principle, the algorithmic cost behind it, and the two capability boundaries you must keep in mind.&lt;/p&gt;</description>
    </item>
    <item>
      <title>16.3 Execution Tracing</title>
      <link>/ru/part5toolchain/ch16tools/trace/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch16tools/trace/</guid>
      <description>&lt;h1 id=&#34;163-execution-tracing&#34;&gt;16.3 Execution Tracing&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;pprof&lt;/code&gt; (&lt;a href=&#34;.././perf&#34;&gt;16.5&lt;/a&gt;) answers the question &amp;ldquo;which functions is the time being spent in&amp;rdquo;. It aggregates CPU samples taken over a window into a call tree and tells you that &lt;code&gt;json.Marshal&lt;/code&gt; accounts for 30% of the CPU. But it cannot answer another class of question: a request took 50ms, and for 45ms of it the goroutine &lt;strong&gt;was not running at all&lt;/strong&gt;, it was waiting. Waiting for what? A lock? The network? Interrupted by GC? Or it wanted to run but had no P available (&lt;a href=&#34;/ru/part3concurrency/ch09sched/steal/&#34;&gt;9.2&lt;/a&gt;)? A CPU profile knows nothing about &amp;ldquo;waiting&amp;rdquo;, because it only samples the stack that is currently executing, and a blocked goroutine is on no CPU at all, so it is naturally never sampled.&lt;/p&gt;</description>
    </item>
    <item>
      <title>16.4 Testing Code</title>
      <link>/ru/part5toolchain/ch16tools/testing/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch16tools/testing/</guid>
      <description>&lt;h1 id=&#34;164-testing-code&#34;&gt;16.4 Testing Code&lt;/h1&gt;&#xA;&lt;p&gt;Testing in Go is not a bolt-on, it is a first-class citizen of the language toolchain. &lt;code&gt;go test&lt;/code&gt; is built in, the &lt;code&gt;testing&lt;/code&gt; package is standard, and convention replaces configuration. This set of design choices has deeply shaped Go&amp;rsquo;s engineering culture: in other languages, &amp;ldquo;whether to write tests at all, and which framework to use&amp;rdquo; is a decision that requires weighing trade-offs; in Go it is the default action. This section explains how this machinery runs, why it was designed this way, and how it grew from unit testing all the way to Go 1.18 fuzzing.&lt;/p&gt;</description>
    </item>
    <item>
      <title>16.5 Benchmarking and Performance Profiling</title>
      <link>/ru/part5toolchain/ch16tools/perf/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch16tools/perf/</guid>
      <description>&lt;h1 id=&#34;165-benchmarking-and-performance-profiling&#34;&gt;16.5 Benchmarking and Performance Profiling&lt;/h1&gt;&#xA;&lt;p&gt;The first principle of optimization is to measure before you act. Guessing at bottlenecks by intuition usually guesses wrong; the real hot spots are often not where you think they are. Go builds every tool this discipline requires straight into the toolchain: benchmarks answer &amp;ldquo;how fast,&amp;rdquo; profiling answers &amp;ldquo;where is it slow, where does the memory go,&amp;rdquo; and &lt;code&gt;benchstat&lt;/code&gt; uses statistics to answer &amp;ldquo;did this change actually make things faster, or is it just noise.&amp;rdquo; This section explains all three tools thoroughly and strings them into a measurement-driven loop of &amp;ldquo;profile to find the hot spot, change, benchmark to verify.&amp;rdquo; One level up, this loop joins with PGO (&lt;a href=&#34;../../ch15compile/optimize&#34;&gt;15.3&lt;/a&gt;) and execution tracing (&lt;a href=&#34;.././trace&#34;&gt;16.3&lt;/a&gt;) to form Go&amp;rsquo;s complete performance-engineering methodology.&lt;/p&gt;</description>
    </item>
    <item>
      <title>16.6 Runtime Metrics</title>
      <link>/ru/part5toolchain/ch16tools/metric/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch16tools/metric/</guid>
      <description>&lt;h1 id=&#34;166-runtime-metrics&#34;&gt;16.6 Runtime Metrics&lt;/h1&gt;&#xA;&lt;p&gt;Profiling (&lt;a href=&#34;.././perf&#34;&gt;16.5&lt;/a&gt;) and tracing (&lt;a href=&#34;.././trace&#34;&gt;16.3&lt;/a&gt;) belong to the family of &amp;ldquo;pull it in to diagnose when something goes wrong&amp;rdquo; tools: they cost a fair amount, produce a lot of output, and suit the case where you already suspect a problem somewhere and want to capture a window of time or a single request to look at closely. But in production the more common question is not &amp;ldquo;profile this for me right now&amp;rdquo;; it is &amp;ldquo;has this service been healthy over the past week, when did it start degrading, and should we page someone at midnight?&amp;rdquo; Answering questions like these does not rely on a one-off deep dive. It relies on &lt;strong&gt;continuous, low-cost, long-retainable numbers&lt;/strong&gt;: how big the heap is, how often GC runs, whether the goroutine count is climbing, whether the tail of scheduling latency is rising. These numbers are &lt;strong&gt;runtime metrics&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>16.7 The Language Server Protocol</title>
      <link>/ru/part5toolchain/ch16tools/gopls/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch16tools/gopls/</guid>
      <description>&lt;h1 id=&#34;167-the-language-server-protocol&#34;&gt;16.7 The Language Server Protocol&lt;/h1&gt;&#xA;&lt;p&gt;Auto-completion, jump-to-definition, live error reporting, refactoring: in modern editors these features, on the Go side, are provided by &lt;strong&gt;&lt;code&gt;gopls&lt;/code&gt;&lt;/strong&gt; (the Go language server, pronounced &amp;ldquo;go please&amp;rdquo;). Behind it stands the &lt;strong&gt;Language Server Protocol&lt;/strong&gt; (LSP). This section makes three things clear: the combinatorial explosion that LSP untangles, how gopls builds on the reusable libraries of the compiler frontend, and why it deserves to be called the culmination of the Go toolchain&amp;rsquo;s design philosophy.&lt;/p&gt;</description>
    </item>
    <item>
      <title>17.1 The Hard Parts of Dependency Management</title>
      <link>/ru/part5toolchain/ch17modules/challenges/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch17modules/challenges/</guid>
      <description>&lt;h1 id=&#34;171-the-hard-parts-of-dependency-management&#34;&gt;17.1 The Hard Parts of Dependency Management&lt;/h1&gt;&#xA;&lt;p&gt;Modern software is almost never written from scratch. For an ordinary server program, one level down the &lt;code&gt;import&lt;/code&gt; list you find a logging library, an HTTP router, a serializer; one level further down are the cryptography, compression, and network primitives that each of those depends on. The code that is actually yours may be less than one percent of the whole dependency graph. This brings the dividend of reuse, and it also brings a question that is not yours by rights yet must be answered by you: &lt;strong&gt;for every package in this graph, which version should you use?&lt;/strong&gt; This section is in no hurry to give Go&amp;rsquo;s answer; first we want to make the difficulty of the problem itself clear. There are two main threads to it: one is &lt;strong&gt;how to choose a version&lt;/strong&gt; (diamond dependencies and constraint solving), the other is &lt;strong&gt;how to keep it from drifting once chosen&lt;/strong&gt; (reproducible builds). Only once these two are understood can you see why the &amp;ldquo;minimum version selection&amp;rdquo; scheme of &lt;a href=&#34;.././minimum&#34;&gt;17.3&lt;/a&gt; deserves its own design, and what price Go paid to get there.&lt;/p&gt;</description>
    </item>
    <item>
      <title>17.2 Semantic Version Management</title>
      <link>/ru/part5toolchain/ch17modules/semantics/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch17modules/semantics/</guid>
      <description>&lt;h1 id=&#34;172-semantic-version-management&#34;&gt;17.2 Semantic Version Management&lt;/h1&gt;&#xA;&lt;p&gt;To manage versions, we first have to make version numbers &lt;strong&gt;mean something&lt;/strong&gt;. &lt;a href=&#34;.././challenges&#34;&gt;17.1&lt;/a&gt;&#xA;reduced the difficulty of dependency management to two points: version conflicts in diamond&#xA;dependencies, and reproducible builds across machines and across time. Whether these two can be&#xA;solved depends on a more basic premise, &lt;strong&gt;what a version number actually promises&lt;/strong&gt;. If the&#xA;relationship between &lt;code&gt;v1.5&lt;/code&gt; and &lt;code&gt;v1.4&lt;/code&gt; were entirely unpredictable, then no selection algorithm could&#xA;get started, and we would be forced back to a solver that &amp;ldquo;tries every pair of versions&amp;rdquo;. The approach&#xA;Go modules take is to first turn the version number into a &lt;strong&gt;contract a machine can trust&lt;/strong&gt;, and then,&#xA;on top of that contract, propose one distinctive and uncompromising rule, &lt;strong&gt;semantic import&#xA;versioning&lt;/strong&gt;. This section makes both of these clear. They are the premise on which the version&#xA;selection algorithm of &lt;a href=&#34;.././minimum&#34;&gt;17.3&lt;/a&gt; can stand, and stand simply enough to be &amp;ldquo;a single graph&#xA;traversal&amp;rdquo;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>17.3 Minimal Version Selection</title>
      <link>/ru/part5toolchain/ch17modules/minimum/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch17modules/minimum/</guid>
      <description>&lt;h1 id=&#34;173-minimal-version-selection&#34;&gt;17.3 Minimal Version Selection&lt;/h1&gt;&#xA;&lt;p&gt;Having settled on semantic versioning (&lt;a href=&#34;.././semantics&#34;&gt;17.2&lt;/a&gt;), one last question remains: when different modules in the dependency graph require different versions of the same dependency, &lt;strong&gt;which version do we pick&lt;/strong&gt;? Go&amp;rsquo;s answer is a counterintuitive yet remarkably concise algorithm, &lt;strong&gt;Minimal Version Selection&lt;/strong&gt; (MVS). It is the most distinctive and the most thought-provoking part of Go&amp;rsquo;s module design: other package managers turned this into a problem that needs a constraint solver, while Go compressed it into a single pass over a graph. This section first lays out the rules, then looks at why it can be this simple, and finally places it back within the broader lineage of the field to see exactly what it trades away and what it gains.&lt;/p&gt;</description>
    </item>
    <item>
      <title>17.4 The vgo versus dep Dispute</title>
      <link>/ru/part5toolchain/ch17modules/fight/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part5toolchain/ch17modules/fight/</guid>
      <description>&lt;h1 id=&#34;174-the-vgo-versus-dep-dispute&#34;&gt;17.4 The vgo versus dep Dispute&lt;/h1&gt;&#xA;&lt;p&gt;Today&amp;rsquo;s Go Modules did not exist from the start. They were born out of a rather dramatic, and rather controversial, community episode: &lt;strong&gt;the vgo versus dep dispute&lt;/strong&gt;. This history is not mere gossip. It reflects the real tension among open-source project governance, technical decision-making, and community sentiment. It is the final piece for understanding why Go Modules are the way they are today, and it is also a concentrated performance, at the toolchain level, of the design philosophy this book has been discussing all along.&lt;/p&gt;</description>
    </item>
    <item>
      <title>18.1 Crossing the FFI Boundary</title>
      <link>/ru/part6hetero/ch18gpu/boundary/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch18gpu/boundary/</guid>
      <description>&lt;h1 id=&#34;181-crossing-the-ffi-boundary&#34;&gt;18.1 Crossing the FFI Boundary&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;/ru/part5toolchain/ch15compile/cgo/&#34;&gt;15.6&lt;/a&gt; already took the cgo bridge apart: a call&#xA;from Go into C must switch to the &lt;code&gt;g0&lt;/code&gt; system stack, lay the arguments out again according&#xA;to C&amp;rsquo;s ABI, &lt;code&gt;entersyscall&lt;/code&gt; to give up the P, make the call, then &lt;code&gt;exitsyscall&lt;/code&gt; to win back&#xA;a P, and the whole round comes out one or two orders of magnitude more expensive than a Go&#xA;call. The conclusion of that section was blunt: cgo suits a &lt;strong&gt;small number of&#xA;coarse-grained&lt;/strong&gt; calls, and its worst enemy is a hot loop that crosses the boundary again&#xA;and again.&lt;/p&gt;</description>
    </item>
    <item>
      <title>18.2 The Scheduler and Blocking Foreign Calls</title>
      <link>/ru/part6hetero/ch18gpu/sched/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch18gpu/sched/</guid>
      <description>&lt;h1 id=&#34;182-the-scheduler-and-blocking-foreign-calls&#34;&gt;18.2 The Scheduler and Blocking Foreign Calls&lt;/h1&gt;&#xA;&lt;p&gt;The prescription &lt;a href=&#34;.././boundary&#34;&gt;18.1&lt;/a&gt; gave was &amp;ldquo;asynchronous, synchronize seldom&amp;rdquo;: push&#xA;commands into the stream, return immediately, wait only once at the end. But that &amp;ldquo;once at&#xA;the end&amp;rdquo; must after all be waited. &lt;code&gt;cudaStreamSynchronize&lt;/code&gt; blocks until the GPU has drained&#xA;the whole stream; a synchronous &lt;code&gt;cudaMemcpy&lt;/code&gt;, a driver call that does not take the&#xA;asynchronous path, will all leave the Go-side thread genuinely stopped inside C. What this&#xA;section asks is this: when a crossing &lt;strong&gt;really does block for a long time&lt;/strong&gt;, how does the&#xA;scheduling machinery of &lt;a href=&#34;/ru/part3concurrency/ch09sched/&#34;&gt;Chapter 9&lt;/a&gt; react? Will it be&#xA;dragged down by a single call stuck on the GPU?&lt;/p&gt;</description>
    </item>
    <item>
      <title>18.3 The Divide Between Device Memory and the Garbage Collector</title>
      <link>/ru/part6hetero/ch18gpu/memory/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch18gpu/memory/</guid>
      <description>&lt;h1 id=&#34;183-the-divide-between-device-memory-and-the-garbage-collector&#34;&gt;18.3 The Divide Between Device Memory and the Garbage Collector&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;/ru/part5toolchain/ch15compile/cgo/&#34;&gt;15.6&lt;/a&gt; covered cgo&amp;rsquo;s pointer rules: Go&amp;rsquo;s objects do&#xA;not belong to C, the GC may move or reclaim them at any time, and so C must not hold an&#xA;unpinned Go pointer after the call returns. That was deduced from a binary world of &amp;ldquo;two&#xA;memories, Go&amp;rsquo;s and C&amp;rsquo;s.&amp;rdquo; The GPU complicates this map: now there are at least &lt;strong&gt;four&lt;/strong&gt; kinds&#xA;of memory, under different jurisdictions and obeying different rules. This section first&#xA;draws that map clearly, then sees where the dividing lines between the garbage collector&#xA;and each of them fall, and which line is most easily trampled in asynchronous transfers.&lt;/p&gt;</description>
    </item>
    <item>
      <title>18.4 The Asynchronous Programming Model</title>
      <link>/ru/part6hetero/ch18gpu/model/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch18gpu/model/</guid>
      <description>&lt;h1 id=&#34;184-the-asynchronous-programming-model&#34;&gt;18.4 The Asynchronous Programming Model&lt;/h1&gt;&#xA;&lt;p&gt;The previous three sections all spoke of the &amp;ldquo;costs&amp;rdquo; on the FFI boundary: cross fast&#xA;(&lt;a href=&#34;.././boundary&#34;&gt;18.1&lt;/a&gt;), crossing occupies a thread (&lt;a href=&#34;.././sched&#34;&gt;18.2&lt;/a&gt;), who owns the&#xA;memory on the bridge (&lt;a href=&#34;.././memory&#34;&gt;18.3&lt;/a&gt;). This section changes the angle and returns to&#xA;the &lt;strong&gt;concurrency model&lt;/strong&gt; itself. Go&amp;rsquo;s concurrency is goroutines and channels, the GPU&amp;rsquo;s&#xA;concurrency is something else, and the CPU hides a third kind. Laying these three&#xA;parallelisms out clearly and seeing how they connect is the close of this chapter, and the&#xA;key to understanding &amp;ldquo;when to push the work across the boundary, and when there is no need&#xA;at all.&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>19.1 The Rendering Pipeline and Where Go Sits</title>
      <link>/ru/part6hetero/ch19graphics/pipeline/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch19graphics/pipeline/</guid>
      <description>&lt;h1 id=&#34;191-the-rendering-pipeline-and-where-go-sits&#34;&gt;19.1 The Rendering Pipeline and Where Go Sits&lt;/h1&gt;&#xA;&lt;p&gt;Chapter 18 read the GPU as a &amp;ldquo;general-purpose parallel compute device.&amp;rdquo; But the GPU&amp;rsquo;s given name is the &lt;strong&gt;Graphics&lt;/strong&gt; Processing Unit, and its first and most native job is to render graphics. For the two decades before &amp;ldquo;using the GPU for general computation&amp;rdquo; became a slogan, the graphics card was already doing parallel arithmetic for every pixel on the screen. Graphics is therefore the &lt;strong&gt;oldest heterogeneous workload&lt;/strong&gt;: the massively parallel hardware on a GPU was built for it in the first place. This chapter returns to that origin to ask what role Go plays in graphics, and the starting point is to see clearly the &lt;strong&gt;rendering pipeline&lt;/strong&gt; that runs through everything, and where exactly Go&amp;rsquo;s code sits on it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>19.2 Graphics Bindings and Thread Affinity</title>
      <link>/ru/part6hetero/ch19graphics/bindings/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch19graphics/bindings/</guid>
      <description>&lt;h1 id=&#34;192-graphics-bindings-and-thread-affinity&#34;&gt;19.2 Graphics Bindings and Thread Affinity&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././pipeline&#34;&gt;19.1&lt;/a&gt; said that Go sits at the application stage of the pipeline and is responsible for issuing draw calls. But before the first draw call can actually go out, there is a threshold in front of every Go graphics program. It does not come from graphics itself, but from a fundamental conflict between Go&amp;rsquo;s concurrency model and a property of graphics APIs: &lt;strong&gt;a graphics context is bound to a thread, while a goroutine migrates across threads&lt;/strong&gt;. This threshold is the subject of this section, and the key that &lt;a href=&#34;../../ch18gpu/sched&#34;&gt;18.2.5&lt;/a&gt; handed us, &lt;code&gt;LockOSThread&lt;/code&gt;, turns here from an optional trick into a necessity.&lt;/p&gt;</description>
    </item>
    <item>
      <title>19.3 Software Rendering and Parallelism</title>
      <link>/ru/part6hetero/ch19graphics/software/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch19graphics/software/</guid>
      <description>&lt;h1 id=&#34;193-software-rendering-and-parallelism&#34;&gt;19.3 Software Rendering and Parallelism&lt;/h1&gt;&#xA;&lt;p&gt;Rendering in the previous two sections always crossed a boundary: hand the work and commands to the local GPU, pay the whole set of tolls from Chapter 18, and still attend to the thread discipline of the graphics context (&lt;a href=&#34;.././bindings&#34;&gt;19.2&lt;/a&gt;). This section takes another road, &lt;strong&gt;software rendering&lt;/strong&gt;: compute every pixel entirely on the CPU, touching no GPU, no driver, no FFI boundary of any kind. This road was once dismissed as &amp;ldquo;the slow and useless fallback,&amp;rdquo; yet it is precisely the best stage on which to put Go&amp;rsquo;s concurrency, and Go 1.27&amp;rsquo;s &lt;code&gt;simd&lt;/code&gt;, to work in graphics.&lt;/p&gt;</description>
    </item>
    <item>
      <title>19.4 Rendering in the Browser</title>
      <link>/ru/part6hetero/ch19graphics/wasm/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch19graphics/wasm/</guid>
      <description>&lt;h1 id=&#34;194-rendering-in-the-browser&#34;&gt;19.4 Rendering in the Browser&lt;/h1&gt;&#xA;&lt;p&gt;Rendering in the previous three sections either pushed the work across the FFI boundary to the local GPU (19.1, 19.2) or kept it on the CPU as software rendering (&lt;a href=&#34;.././software&#34;&gt;19.3&lt;/a&gt;). This section changes the scene to a special runtime environment: the &lt;strong&gt;browser&lt;/strong&gt;. Go can be compiled to WebAssembly (WASM) and run in the browser, and once inside the browser, rendering meets a brand-new boundary. What is interesting is that the shape of this boundary, its cost, and the way to deal with it correspond almost one to one with the heterogeneous computing of the previous two chapters, only lifted up one level. Understanding this section reveals that &amp;ldquo;the FFI boundary&amp;rdquo; is a motif far broader than cgo.&lt;/p&gt;</description>
    </item>
    <item>
      <title>20.1 The Inference Runtime and FFI</title>
      <link>/ru/part6hetero/ch20inference/runtime/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch20inference/runtime/</guid>
      <description>&lt;h1 id=&#34;201-the-inference-runtime-and-ffi&#34;&gt;20.1 The Inference Runtime and FFI&lt;/h1&gt;&#xA;&lt;p&gt;The FFI boundary that recurred through Chapters 18 and 19 wears, with large models, its most&#xA;current face. Training large models is almost the domain of Python and CUDA, but when a model&#xA;is trained and is to be deployed to &lt;strong&gt;serve&lt;/strong&gt; tens of millions of requests, the lead role&#xA;changes, and Go stands firm at this layer. This chapter is about how Go takes on AI&amp;rsquo;s&#xA;inference and serving, and the first section must settle the lowest-level problem: Go does&#xA;not do matrix multiply itself, it has to wire in a local inference runtime, and this wiring&#xA;is, once again, the boundary of Chapter 18.&lt;/p&gt;</description>
    </item>
    <item>
      <title>20.2 Tokenization and Tensors</title>
      <link>/ru/part6hetero/ch20inference/tokenize/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch20inference/tokenize/</guid>
      <description>&lt;h1 id=&#34;202-tokenization-and-tensors&#34;&gt;20.2 Tokenization and Tensors&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././runtime&#34;&gt;20.1&lt;/a&gt; settled the home of weights and tensors on the native runtime side, with&#xA;Go only passing handles and moving small data on the boundary. This section steps into that&#xA;&amp;ldquo;small data&amp;rdquo; itself: how text becomes the numbers a model can eat, and how the numbers turn&#xA;back into text. This seemingly trivial thing hides a detail that will make a Go programmer&#xA;smile in recognition, for it is almost a direct application of Chapter 5&amp;rsquo;s &amp;ldquo;a string is a&#xA;stretch of immutable bytes,&amp;rdquo; and the moment it is neglected, it spits out garbled text in&#xA;streaming output.&lt;/p&gt;</description>
    </item>
    <item>
      <title>20.3 Serving, Batching, and Streaming</title>
      <link>/ru/part6hetero/ch20inference/serving/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch20inference/serving/</guid>
      <description>&lt;h1 id=&#34;203-serving-batching-and-streaming&#34;&gt;20.3 Serving, Batching, and Streaming&lt;/h1&gt;&#xA;&lt;p&gt;The previous two sections laid the underpinnings of a single inference: &lt;a href=&#34;.././runtime&#34;&gt;20.1&lt;/a&gt;&#xA;wired in the runtime through cgo and settled the weights and tensors,&#xA;&lt;a href=&#34;.././tokenize&#34;&gt;20.2&lt;/a&gt; made clear how tokens go in and come out. But a real service must serve&#xA;thousands upon thousands of such requests at once, each continuously spitting tokens. How to&#xA;organize them efficiently and stably is a thoroughly &lt;strong&gt;concurrency and scheduling&lt;/strong&gt; problem,&#xA;and this is Go&amp;rsquo;s home ground. This section brings Chapter 10&amp;rsquo;s channels and Chapter 7&amp;rsquo;s&#xA;context down to large-model serving.&lt;/p&gt;</description>
    </item>
    <item>
      <title>21.1 The Agent Control Loop</title>
      <link>/ru/part6hetero/ch21agent/loop/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch21agent/loop/</guid>
      <description>&lt;h1 id=&#34;211-the-agent-control-loop&#34;&gt;21.1 The Agent Control Loop&lt;/h1&gt;&#xA;&lt;p&gt;Chapter 20 was about Go serving a model: a request goes in, a stream of tokens comes out. But systems today often go a step further: they let the model not only answer, but also call tools, chain together multiple steps, and drive itself forward until a task is done. This is the AI agent. The word sounds mysterious, but the stance of this book is unchanged: set the model&amp;rsquo;s intelligence aside and look only at the runtime side. Seen this way, an agent is nothing more than a control loop, and the control loop is one of the oldest and plainest structures in computer science. This chapter uses that lens to reduce an agent to a concurrency problem a Go programmer already knows.&lt;/p&gt;</description>
    </item>
    <item>
      <title>21.2 Tool Calls and MCP</title>
      <link>/ru/part6hetero/ch21agent/mcp/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch21agent/mcp/</guid>
      <description>&lt;h1 id=&#34;212-tool-calls-and-mcp&#34;&gt;21.2 Tool Calls and MCP&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././loop&#34;&gt;21.1&lt;/a&gt; reduced an agent to a control loop, and the most important action inside that loop is &amp;ldquo;execute a tool&amp;rdquo;. This section is about tools specifically: what a tool is mechanically, how the model decides to call it, how the runtime dispatches the call to real code, and how, when the number and the sources of tools explode, the &lt;strong&gt;Model Context Protocol&lt;/strong&gt; (MCP) standardizes the whole thing with a single protocol. See through this layer and you find that a tool call is, in essence, a structured remote procedure call, and RPC is exactly Go&amp;rsquo;s old trade.&lt;/p&gt;</description>
    </item>
    <item>
      <title>21.3 Streaming, Backpressure, and Cancellation</title>
      <link>/ru/part6hetero/ch21agent/stream/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part6hetero/ch21agent/stream/</guid>
      <description>&lt;h1 id=&#34;213-streaming-backpressure-and-cancellation&#34;&gt;21.3 Streaming, Backpressure, and Cancellation&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././loop&#34;&gt;21.1&lt;/a&gt; stood up the agent&amp;rsquo;s control loop, and &lt;a href=&#34;.././mcp&#34;&gt;21.2&lt;/a&gt; made the tool calls inside it clear. This last section brings the context of Chapter 7 and the concurrency primitives of Chapters 10 and 11 onto the stage for the book&amp;rsquo;s curtain call, because the real difficulty of a production-grade agent lies not in the skeleton of the loop but in three things that run through the entire chain: streaming, backpressure, and cancellation. They are intertwined, and what unifies them is a shape a Go programmer knows all too well, a tree of goroutines bound by a context.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Closing Words: Where Is Go Headed?</title>
      <link>/ru/finalwords/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/finalwords/</guid>
      <description>&lt;h1 id=&#34;closing-words-where-is-go-headed&#34;&gt;Closing Words: Where Is Go Headed?&lt;/h1&gt;&#xA;&lt;p&gt;Having read this far, the reader has walked with the book through Go&amp;rsquo;s six major parts: its panorama and history, its language features, concurrency, memory, the compiler and toolchain, and heterogeneous compute and AI. As a conclusion, let us step back from the specific implementation details and talk about the legacy Go leaves behind, the challenges it currently faces, and where it might be headed.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Appendix A: Glossary</title>
      <link>/ru/glossary/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/glossary/</guid>
      <description>&lt;h1 id=&#34;glossary&#34;&gt;Glossary&lt;/h1&gt;&#xA;&lt;p&gt;This appendix collects the main terms that appear in the book, grouped by topic and ordered alphabetically by their English names. For each term it gives the chapter where the term is mainly developed, so the reader can look it back up easily.&lt;/p&gt;&#xA;&lt;h2 id=&#34;concurrency-and-scheduling&#34;&gt;Concurrency and Scheduling&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Term&lt;/th&gt;&#xA;          &lt;th&gt;Chapter&lt;/th&gt;&#xA;          &lt;th&gt;English&lt;/th&gt;&#xA;          &lt;th&gt;Abbreviation&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Back Edge&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/preemption/&#34;&gt;9.7&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Back Edge&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Cooperative Preemption&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/preemption/&#34;&gt;9.7&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Cooperative Preemption&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Communicating Sequential Processes&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part1overview/ch01intro/csp/&#34;&gt;1.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Communicating Sequential Processes&lt;/td&gt;&#xA;          &lt;td&gt;CSP&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Goroutine&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/mpg/&#34;&gt;9.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Goroutine&lt;/td&gt;&#xA;          &lt;td&gt;G/g&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Machine (Thread)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/mpg/&#34;&gt;9.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Machine&lt;/td&gt;&#xA;          &lt;td&gt;M/m&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Network Poller&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/poller/&#34;&gt;9.9&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Network Poller&lt;/td&gt;&#xA;          &lt;td&gt;netpoll&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Non-spinning&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/schedule/&#34;&gt;9.4&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Non-spinning&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Preemptive&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/preemption/&#34;&gt;9.7&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Preemptive&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Processor&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/mpg/&#34;&gt;9.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Processor&lt;/td&gt;&#xA;          &lt;td&gt;P/p&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Safepoint&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/preemption/&#34;&gt;9.7&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Safepoint&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Scheduler&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;.././part3concurrency/ch09sched/readme&#34;&gt;9&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Scheduler&lt;/td&gt;&#xA;          &lt;td&gt;sched&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Spinning&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/schedule/&#34;&gt;9.4&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Spinning&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;System Monitor&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/sysmon/&#34;&gt;9.8&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;System Monitor&lt;/td&gt;&#xA;          &lt;td&gt;sysmon&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Work Stealing&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch09sched/steal/&#34;&gt;9.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Work Stealing&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;synchronization-and-the-memory-model&#34;&gt;Synchronization and the Memory Model&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Term&lt;/th&gt;&#xA;          &lt;th&gt;Chapter&lt;/th&gt;&#xA;          &lt;th&gt;English&lt;/th&gt;&#xA;          &lt;th&gt;Abbreviation&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Atomic Operation&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/atomic/&#34;&gt;11.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Atomic Operation&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Compare-And-Swap&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/atomic/&#34;&gt;11.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Compare-And-Swap&lt;/td&gt;&#xA;          &lt;td&gt;CAS&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Condition Variable&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/cond/&#34;&gt;11.4&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Condition Variable&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Data Race&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/mem/&#34;&gt;11.9&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Data Race&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Happens-Before&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/mem/&#34;&gt;11.9&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Happens-Before&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Lock-free&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/atomic/&#34;&gt;11.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Lock-free&lt;/td&gt;&#xA;          &lt;td&gt;LF&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Memory Barrier&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/mem/&#34;&gt;11.9&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Memory Barrier&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Sequential Consistency&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/mem/&#34;&gt;11.9&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sequential Consistency&lt;/td&gt;&#xA;          &lt;td&gt;SC&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;False Sharing&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/component/&#34;&gt;12.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;False Sharing&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;True Sharing&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/component/&#34;&gt;12.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;True Sharing&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Wait-free&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part3concurrency/ch11sync/atomic/&#34;&gt;11.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Wait-free&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;memory-allocation&#34;&gt;Memory Allocation&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Term&lt;/th&gt;&#xA;          &lt;th&gt;Chapter&lt;/th&gt;&#xA;          &lt;th&gt;English&lt;/th&gt;&#xA;          &lt;th&gt;Abbreviation&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Arena&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/init/&#34;&gt;12.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Arena&lt;/td&gt;&#xA;          &lt;td&gt;heapArena&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Arena Hint&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/init/&#34;&gt;12.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Arena Hint&lt;/td&gt;&#xA;          &lt;td&gt;arenaHint&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Fast Path&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/basic/&#34;&gt;12.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fast Path&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Free List&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/component/&#34;&gt;12.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Free List&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Heap&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;.././part4memory/ch12alloc/readme&#34;&gt;12&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Heap&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Large Object&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/largealloc/&#34;&gt;12.4&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Large Object&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Page&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/pagealloc/&#34;&gt;12.7&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Page&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Page Allocator&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/pagealloc/&#34;&gt;12.7&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Page Allocator&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Size Class&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/basic/&#34;&gt;12.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Size Class&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Small Object&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/smallalloc/&#34;&gt;12.5&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Small Object&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Slow Path&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/basic/&#34;&gt;12.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Slow Path&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Tiny Allocator&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/tinyalloc/&#34;&gt;12.6&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Tiny Allocator&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Tiny Object&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch12alloc/tinyalloc/&#34;&gt;12.6&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Tiny Object&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;garbage-collection&#34;&gt;Garbage Collection&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Term&lt;/th&gt;&#xA;          &lt;th&gt;Chapter&lt;/th&gt;&#xA;          &lt;th&gt;English&lt;/th&gt;&#xA;          &lt;th&gt;Abbreviation&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Bitmap&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/sweep/&#34;&gt;13.5&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Bitmap&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Collector&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/basic/&#34;&gt;13.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Collector&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Conservative&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/safe/&#34;&gt;13.7&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Conservative&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Finalizer&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/finalizer/&#34;&gt;13.10&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Finalizer&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Garbage Collection&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;.././part4memory/ch13gc/readme&#34;&gt;13&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Garbage Collection&lt;/td&gt;&#xA;          &lt;td&gt;GC&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Generational Hypothesis&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/generational/&#34;&gt;13.8&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Generational Hypothesis&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Hybrid Write Barrier&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/barrier/&#34;&gt;13.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Hybrid Write Barrier&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Liveness&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/basic/&#34;&gt;13.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Liveness&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Mark Assist&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/mark/&#34;&gt;13.4&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Mark Assist&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Mark-Sweep&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/basic/&#34;&gt;13.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Mark-Sweep&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Mutator&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/basic/&#34;&gt;13.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Mutator&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Pacer&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/pacing/&#34;&gt;13.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Pacer&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Reachability&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/basic/&#34;&gt;13.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Reachability&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Remembered Set&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/generational/&#34;&gt;13.8&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Remembered Set&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Stop the World&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/pacing/&#34;&gt;13.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Stop the World&lt;/td&gt;&#xA;          &lt;td&gt;STW&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Tricolour Abstraction&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/basic/&#34;&gt;13.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Tricolour Abstraction&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Write Barrier&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch13gc/barrier/&#34;&gt;13.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Write Barrier&lt;/td&gt;&#xA;          &lt;td&gt;WB/wb&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;execution-stack&#34;&gt;Execution Stack&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Term&lt;/th&gt;&#xA;          &lt;th&gt;Chapter&lt;/th&gt;&#xA;          &lt;th&gt;English&lt;/th&gt;&#xA;          &lt;th&gt;Abbreviation&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Contiguous Stack&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch14stack/design/&#34;&gt;14.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Contiguous Stack&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Prologue&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part1overview/ch02asm/callconv/&#34;&gt;2.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Prologue&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Epilogue&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch14stack/grow/&#34;&gt;14.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Epilogue&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Stack&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;.././part4memory/ch14stack/readme&#34;&gt;14&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Stack&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Stack Copy&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch14stack/copy/&#34;&gt;14.4&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Stack Copy&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Stack Growth&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part4memory/ch14stack/grow/&#34;&gt;14.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Stack Growth&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;language-features-and-the-compiler&#34;&gt;Language Features and the Compiler&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Term&lt;/th&gt;&#xA;          &lt;th&gt;Chapter&lt;/th&gt;&#xA;          &lt;th&gt;English&lt;/th&gt;&#xA;          &lt;th&gt;Abbreviation&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Calling Convention&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part1overview/ch02asm/callconv/&#34;&gt;2.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Calling Convention / ABI&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Defer Bit&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part2lang/ch06func/defer/&#34;&gt;6.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Defer Bit&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Devirtualization&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch15compile/optimize/&#34;&gt;15.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Devirtualization&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Escape Analysis&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch15compile/escape/&#34;&gt;15.5&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Escape Analysis&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;GC Shape&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part2lang/ch08generics/history/&#34;&gt;8.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;GC Shape&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Inlining&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch15compile/optimize/&#34;&gt;15.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Inlining&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Interface Table&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part2lang/ch04type/interface/&#34;&gt;4.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Interface Table&lt;/td&gt;&#xA;          &lt;td&gt;itab&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Open-coded Defer&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part2lang/ch06func/defer/&#34;&gt;6.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Open-coded Defer&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Profile-Guided Optimization&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch15compile/optimize/&#34;&gt;15.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Profile-Guided Optimization&lt;/td&gt;&#xA;          &lt;td&gt;PGO&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Static Single Assignment&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch15compile/ssa/&#34;&gt;15.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Static Single Assignment&lt;/td&gt;&#xA;          &lt;td&gt;SSA&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Type Set&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part2lang/ch08generics/checker/&#34;&gt;8.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Type Set&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Type Descriptor&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part2lang/ch04type/type/&#34;&gt;4.1&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Type Descriptor&lt;/td&gt;&#xA;          &lt;td&gt;_type&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;modules-and-the-toolchain&#34;&gt;Modules and the Toolchain&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Term&lt;/th&gt;&#xA;          &lt;th&gt;Chapter&lt;/th&gt;&#xA;          &lt;th&gt;English&lt;/th&gt;&#xA;          &lt;th&gt;Abbreviation&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Language Server Protocol&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch16tools/gopls/&#34;&gt;16.7&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Language Server Protocol&lt;/td&gt;&#xA;          &lt;td&gt;LSP&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Minimal Version Selection&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch17modules/minimum/&#34;&gt;17.3&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Minimal Version Selection&lt;/td&gt;&#xA;          &lt;td&gt;MVS&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Race Detector&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch16tools/race/&#34;&gt;16.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Race Detector&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Semantic Import Versioning&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch17modules/semantics/&#34;&gt;17.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Semantic Import Versioning&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Semantic Versioning&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;/ru/part5toolchain/ch17modules/semantics/&#34;&gt;17.2&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Semantic Versioning&lt;/td&gt;&#xA;          &lt;td&gt;semver&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;</description>
    </item>
    <item>
      <title>Support the Author</title>
      <link>/ru/donate/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/donate/</guid>
      <description>&lt;h1 id=&#34;support-the-author&#34;&gt;Support the Author&lt;/h1&gt;&#xA;&lt;p&gt;Thank you for reading this far. This book has been free and open from beginning to end, and from the very first line until now, the entire cost of writing, proofreading, and updating it has been borne by the author alone. If these words once helped you make sense of an obscure piece of source code late at night, or saved you some time fumbling through a problem at work, the author would be glad to hear it.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
