Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Это лучшее, что я когда-либо читал.
После освоения этих статей вы будете гигантом мысли в многопоточке.
— sync.Mutex: Normal and Starvation Mode
— sync.WaitGroup and The Alignment Problem
— sync.Pool and the Mechanics Behind It
— sync.Cond, the Most Overlooked Sync Mechanism
— sync.Once is Simple... Does It Really?
— sync.Map: The Right Tool for the Right Job
Лично смотрел все.
Настоятельно рекомендую!
Please open Telegram to view this post
VIEW IN TELEGRAM
VictoriaMetrics
VictoriaMetrics: Simple & Reliable Monitoring for Everyone
Incredibly fast time series database and easy-to-use high performance monitoring solutions simple and reliable for everyone. VictoriaMetrics Database Solutions.Incredibly fast time series database and easy-to-use high performance monitoring solutions simple…
В программе — хардкорные доклады, живое общение и нетворкинг с топовыми разработчиками
— Даниил Подольский (YADRO) - Почему вы должны научиться использовать дженерики
— Владимир Балун (Независимый эксперт) - Черная магия unsafe в Go
— Никита Томчик (Т-Банк) - Когда возможностей Kubernetes недостаточно. Делаем свой DNS-сервер для Kubernetes в связке с CoreDNS
— Игорь Панасюк (Яндекс) - Особенности и ловушки модели памяти в Go: тайны синхронизации
— Кирилл Кузин (Ви.Tech) - Швейцария в картах Go: путешествие по Swiss Tables
— Михаил Люц (Ozon Банк) - Garbage Collector: от появления до современных алгоритмов
Есть всё, чтобы погрузиться в Go, как следует: трансляции, записи, тусовка, сюрпризы от партнёров.
Я всегда мечтал попасть на GolangConf ввиду того, что это отличная возможность прокачать свои харды, понетворкаться с коллегами из разных компаний, получить тонну эмоций и просто кайфануть от вайба. Настоятельно рекомендую к посещению!
golangconf.ru/2025
До встречи на GolangConf — будет мощно!
Лично буду присутствовать
Please open Telegram to view this post
VIEW IN TELEGRAM
golangconf.ru
Профессиональная конференция для Go-разработчиков и их руководителей 2025
Зачем их вообще создавать и делать это правильно? Тыкнули make и просто кайфуем, нет так ли? - по моему мнению вопрос достаточно неоднозначен и более глубок
Сегодня мы с вами прокатимся по этому полю и все узнаем
Начнем с попсовой внутрянки слайса. Под капотом это вот такая незамысловатая структура:
type slice struct {
data unsafe.Pointer // Нижележащий массив
len int // Длина (то, сколько элементов мы видим у body)
cap int // Емкость (то, сколько элементов мы можем поместить, чтобы не произвести реаллокации body)
}
Исходники: тык
Это мы добавили для общего развития!
Имеем достаточно громадную тележку опций:
1. var s []int - пустое объявление
— Когда? - не хотим выделять память на внутреннюю структуру slice
— Почему? - При инициализации через var переменная аллокацируется с zero value (nil). Это относится ко всем ссылочным переменным (map, chan, slice, func, interface и any type pointer)
— Кейс: очень частое создание горутин, в которых происходит много объявлений тех или иных слайсов с отложенным заполнением. Это избавляет нас от лишних аллокаций, мы в плюсе!
2. s := []int{} - инициализация с аллокацией внутренней структуры slice
— Когда? - не знаем capacity, не хотим ниловый слайс, но нужно объявить элегантно
— Заметка: слайс с len = 0 и cap = 0 является zerobase объектом
3. s := arr[1:333] - инициализация из массива
— Когда? - необходимо читать / менять элементы существующего массива
4. s := make([]int, 3, 5)
— Заметка: len - количество проинициализированных видимых zero value элементов в слайсе, cap - емкость
5. s := *new([]int)
— Заметка: new инициализирует с аллокацией внутренней структуры, но возвращает указатель
6. s := append([]int(nil), oldSlice…)
— Когда? - не знаем точный размер слайса (либо специально не хотим фиксировать размер), но нужно создать новый, где append вызовет growslice и создаст слайс с размером до ближайшего size class
— Кейс: объекты bytes.Buffer или же strings.Builder
используют данный способ для расширения слайса, но пусть для вас это останется для вас непокоримой тайной (ихиххихи)Почитать: тык
Почитать про виды инициализации переменных: тык
Статью писали с Дашей: @dariasroom
Вот как-то так и никак иначе. На сегодня все!
Stay tuned!
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Спасибо, что обратил(ся/ась) к нам, оставь свое честное мнение
Если вам нужно мок-собеседование, смело пишите нам @lovelygopher | @prettygopher | channel direct
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from myStack
if err != nil остаётся
Команда Go решила не менять синтаксис обработки ошибок и закрывает все предложения по упрощению error handling - ни один вариант не получил широкой поддержки ни в команде, ни в сообществе.
Причины:
- Нет консенсуса, нужен ли такой синтаксический сахар
- Привычный способ error handling признан рабочим и идиоматичным
- Изменения усложнят язык и приведут к массовым изменениям в коде
Команда Go решила не менять синтаксис обработки ошибок и закрывает все предложения по упрощению error handling - ни один вариант не получил широкой поддержки ни в команде, ни в сообществе.
Причины:
- Нет консенсуса, нужен ли такой синтаксический сахар
- Привычный способ error handling признан рабочим и идиоматичным
- Изменения усложнят язык и приведут к массовым изменениям в коде
Доставайте тетрадки и ручки
Тыкались мы в make, да не дотыкались. Сегодня будем дотыкиваться и оптимизировать аллокацию наших байтиков так, чтобы она происходила максимально эффективно
Перед прочтением данного поста вам стоит углубиться в тему Escape Analysis тык
Кратко, как работает Escape Analysis. Значение уходит в секцию Heap, когда:
— Из функции возвращается указатель на эту переменную, находящуюся в рамках scope’a этой функции
— Прокидываем аргумент в interface параметр функции, который может быть оценен компилятором как изменяемое значение (к примеру fmt.Println(v … any))
— Размер объекта превышает его максимальный в соответствии с его size class
— Прокидываем в канал указатели
— Используем переменную в замыкании (обращаемся к переменной outer функции в inner функции)
На берегу обсудим пару моментов:
— Slice header может лежать как на стэке, так и на хипе. Все зависит от того, эскейпился он или нет, при этом нижележащий массив также может находиться в одном из этих пространств памяти
— Максимальный размер нижележащего массива слайса, когда он может лежать на стэке - 64 KB. Если превысить данный порог, моментально уйдем на хип
Чтобы узнать, эксейпятся ваши переменные или нет, используйте:
go build -gcflags="-m" main.go
Учтите, что чем больше вы укажете флагов `-m`, тем более развернутый ответ получите
Потоптались на месте, приступим к раскопке гробниц фараонов (примерам):
func ExampleA() []byte {
const size = 64 * 1 << 10 // 64 KB
return make([]byte, 0, size) // Не улетит в хип т.к. капасити известна на момент компиляции
}
func ExampleB(size int) []byte {
return make([]byte, 0, size) // Улетит в хип, т.к это динамически вычисляемый параметр в рантайме
}
С эскейпом разобрались. Перейдем к оптимизациям:
1. Нам нужна быстрая аллокация слайса на стэке, но мы хотим это сделать в обход ограничения в 64 KB. Используем sparse literal оптимизацию: явно определяем значение для индекса maxSize - indexPad с занулением всех элементов до него, а сам слайс обрезаем до [:0]. Получаем len = 0, cap = maxSize
func ExampleC() []byte {
const (
maxSize = 1 << 30
indexPad = 1
)
return []byte{maxSize - indexPad: 0}[:0] // 1 GB попадет на стэк несмотря на ограничение в 64 KB. Это фича, которую я отрыл в исходниках (работает с версии Go 1.17)
}
2. Нам нужно инициализировать слайс быстрее make:
— Наращиваем буфер до нужного размера через strings.Builder (который под капотом динамически расширяет []buf до нужного size class)
— Добираемся до первого байта байт-последовательности нашей строки с помощью unsafe.StringData(...)
— Передаем данный указатель в unsafe.Slice, который вернет нам наш созданный слайс
— Сбрасываем len до нуля с помощью реслайсинга по самому же себе (то есть [:0])
— Заметка: Для работы с unsafe желательно понимать нюансы его использования и иметь высокую экспертизу тык
Этот метод позволит кратно увеличить перформанс!
func ExampleD(size int) []byte {
if size <= 0 {
return nil
}
var b strings.Builder
b.Grow(size)
return unsafe.Slice(unsafe.StringData(b.String()), len(b.String()))[:0] // Работает быстрее дефолтного make
}
Проверим, насколько наши способы эффективнее относительно создания с помощью make
Имеем вот такие бенчмарки:
// goos: darwin
// goarch: arm64
// pkg: workspace
// cpu: Apple M3 Pro
// BenchmarkExampleB-12 100 42463998 ns/op 1073742102 B/op 1 allocs/op
// BenchmarkExampleC-12 100 39844979 ns/op 1073741835 B/op 1 allocs/op
// BenchmarkExampleD-12 100 26116605 ns/op 1073741884 B/op 1 allocs/op
Важно отметить, что для всех бенчмарков использовалась размерность 1<<30 bytes (1 GB)
Выводы:
— Создание через unsafe ExampleD в сравнении с make ExampleA быстрее на 38.50%
— Создание через sparse инициализацию ExampleС в сравнении с ExampleA быстрее на 6.17%
Ваши готовые конспекты и вопросы ждем в комментах
Статью писали с Дашей: @dariasroom
Stay tuned!
Please open Telegram to view this post
VIEW IN TELEGRAM
Это мой первый митап в жизни и я просто в восторге и преисполнен эмоциями. Прошло все феерично и тепло
— Интереснейшие доклады, из которых я подчерпнул оверполезные знания
— Уютная, окутывающая и успокаивающая атмосфера
— Огромное колличество коллег, с которыми можно понетворкаться, что немаловажно для меня
— Про кухню я вообще молчу (отпад всего)
— Познакомился с очень крутыми ребятами, меня это очень радует и драйвит
Организаторам огромная благодарность, вы умнички
@onokonem
@Filiko_floro
Я обязательно буду присутствовать на следующей конференции в качестве спикера. Думаю, что настало мое время зажечь!
golangconf.ru/2025
В качестве знакомства с вами заливаю чуть-чуть себя, живого и радостного
Stay tuned!
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Небольшая затравка. Нет-нет, мы не будем вас травить, а просто оставим ссылку на код: тык
Будет ли здесь датарейс, если да, то почему? Пишите в комментах
Если вам не терпится, можете получить Early Access. Для этого достаточно просто забустить наш канал
BOOST BOOST BOOST
Stay tuned
Please open Telegram to view this post
VIEW IN TELEGRAM
В наших планах не было настолько душнить по слайсам, но это вынужденная мера. Я надеюсь, вы еще не устали от них. Да и даже если устали - терпите!
Кратко по содержанию: тут будут фичи, которые частенько спрашивают на собесах
Разберем приемы, которые помогут использовать слайсы очень изящно
1. Slice Length Zeroing
Занулив длину слайса конструкцией: s[:0], мы можем впоследствии зааппендить только нужные нам элементы без лишних аллокаций, так как работаем с тем же нижележащим массивом
— Когда? - фильтрация по определенному предикату
— Пример: исключаем пустые строки из слайса s
func Filter(s []string) []string {
if len(s) == 0 {
return s
}
filtered := s[:0]
for i := range s {
if s[i] == "" {
continue
}
filtered = append(filtered, s[i])
}
return filtered
}
2. Len raising
— Когда? - у вас не должно возникать этого вопроса, просто используйте и кайфуйте
— Пример:
func RaiseLen(s []int, n int) []int {
if n <= 0 || len(s)+n > cap(s) {
return s
}
return s[:len(s)+n]
}
3. Append and Array
Помним, что массив - это отдельная структура данных, а append(dest, src) принимает в себя только слайсы и прямые перечисления элементов
— Когда? - скорее всего вас это спросят как-нибудь на собесе, вы поплывете и пойдете плакать в угол, так что просто держите это в голове
— Заметка: чтобы сделать аппенд массива в слайс вам нужно сделать реслайсинг с помощью конструкции arr[:]
— Примеры (правильный и нет):
func AppendArrayWrong(s []int, arr [1 << 8]int) []int {
return append(s, arr...) // cannot use arr as int value in argument
}
func AppendArrayRight(s []int, arr [1 << 8]int) []int {
return append(s, arr[:]...)
}
4. FSE (Full Slice Expression)
Наверняка вы видели такое выражение s[0:5:7], где 1 и 2 элемент означают границы слайса, а последний (7) указывает на тот порог емкости, перейдя который, произойдет отвязка от слайса, по которому мы реслайсимся
Поздравляю, вы грокнули FSE. Занавес!
— Когда? - хотим наложить констрейнт на количество добавляемых элементов в реслайснутый слайс
— Пример:
func FullSliceExpressionUsage() {
s := make([]int, 5, 10)
boundedS := append(s[0:5:7], []int{1, 2, 3}...)
s[0] = -1
fmt.Printf("1st elem of \"s\": %d\n", s[0])
fmt.Printf("1st elem of \"s\": %d\n", boundedS[0])
}
5. Slice thread safety
Если коротко: параллельно-конкурентный доступ элементам слайса по разным индексам будет потокобезопасен сам по себе. Но в случае, если мы не можем гарантировать, что у нас будут изменения только по разным индексам, стоит использовать примитивы синхронизации
Первый пример достаточно большой, поэтому мы засунули его в плейграунд тык. Чтобы проверить, будет ли датарейс в этом случае, запустите код с флагом -race, тобишь:
go run -race main.go
— Примеры:
Правильный: Worker Pool, где каждый из воркеров меняет элемент слайса по отдельному индексу (тык). Вас может смутить семантика sync.RWMutex, но не пугайтесь, он нужен для разделения контекста операций с реверсом логики sync.RWMutex. То есть воркеры конкурентно пишут в слайс используя RLock() с целью сохранить толк от concurrent записей, а отдельный поток чтения выполняет fmt.Println() под Lock() для фиксации снимка слайса
Неправильный: data race из за изменения элемента по одному и тому же индексу
// WARNING: DATA RACE
// ...
func SliceDataRace() {
var wg sync.WaitGroup
s := make([]int, 4, 8)
wg.Add(2)
go func() {
defer wg.Done()
s[3] = -1
}()
go func() {
defer wg.Done()
fmt.Println(s[3])
}()
wg.Wait()
}
Со слайсами мы закончили, слава богу! Теперь вы готовы разрубать их как самураи
Будем двигаться дальше. Куда? - не скажем
Статью писали с Дашей: @dariasroom
Stay tuned
Please open Telegram to view this post
VIEW IN TELEGRAM
Готовим много новых статей. Приготовьтесь, мы взлетаем.
По известной схеме: если вам не терпится, можете получить Early Access. Для этого достаточно просто забустить наш канал. Помогите нам сохранить уют, это очень важно
BOOST BOOST BOOST
Stay tuned
Please open Telegram to view this post
VIEW IN TELEGRAM
gIT - это Go To IT, канал, знакомящий с IT под новыми ракурсами и рассказывающий об интересных проектах и технологиях
Ребята во всю берут интервью у лидеров сообществ и компаний, драйвящих рынок, держат в курсе новостей и трендов продуктовой разработки
Так что велком!
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
Go To IT
Go To IT — здесь мы раскрываем для вас информационные технологии с неожиданных ракурсов!
Бьемся челом и на коленях просим прощения за целых 3 лонгрида по слайсам и даем вам глотнуть свежего воздуха
По нашему опыту при работе с defer'ами многие упускают тонкости, описанные в данной статье
Пристегните ваши ремни, откройте рты пошире. Дышать будем медленно и глубоко!
1. Передача параметров в defer функцию
1.1 Прямая передача аргументов
Здесь используется тот же механизм, что и в обычных функциях: переданные аргументы копируются внутрь стека функции defer on the fly (фиксируем стейты передаваемых аргументов в момент вызова defer)
— Пример
// the world
// the sun is shining
// tasty bebra
func DirectPassing() {
s := "bebra"
defer func(s string) {
fmt.Println("tasty", s)
}(s)
s = "the sun"
defer func(s string) {
fmt.Println(s, "is shining")
}(s)
s = "the world"
fmt.Println(s)
}
1.2 Захват переменных замыканием
При использовании внутри defer переменных из outer функции получаем ситуацию равносильную передаче аргументов по указателю, следовательно в defer вызове получим конечные стейты переменных
— Пример
// the world
// the world is shining
// tasty the world
func ClosurePassing() {
s := "bebra"
defer func() {
fmt.Println("tasty", s)
}()
s = "the sun"
defer func() {
fmt.Println(s, "is shining")
}()
s = "the world"
fmt.Println(s)
}
2. Именованные и неименованные возвратные значения
Если данная часть будет вам непонятна, можете почитать про момент вызова defer в Go Specification
2.1 Неименованные возвратные значения
Если мы используем привычную конструкция func A() int, а после попытаемся изменить переменную int, которая была объявлена в рамках scope’а функции, в defer, изменения не затронут возвратное значение функции A(), потому что int копируется в return-регистр ДО вызова defer
— Пример
// num value before function exiting: 200
// num value in the 2nd defer `before`: 200
// num value in the 2nd defer `after`: 300
// num value in the 1st defer `before`: 100
// num value in the 1st after `after`: 777
// Func result: 200
func UnnamedReturn() int {
num := 100
defer func(num int) {
fmt.Printf("num value in the 1st defer `before`: %d\n", num)
num = 777
fmt.Printf("num value in the 1st after `after`: %d\n", num)
}(num)
num = 200
defer func() {
fmt.Printf("num value in the 2nd defer `before`: %d\n", num)
num = 300
fmt.Printf("num value in the 2nd defer `after`: %d\n", num)
}()
fmt.Printf("num value before function exiting: %d\n", num)
return num
}
2.2 Именованные возвратные значения
Когда мы определяем возвратные значения как именуемые в сигнатуре функции, эти аргументы живут ровно до выхода из функции, а значит наши return регистры будут содержать актуальные данные
— Заметка: в примере ниже используется naked return. Идиоматичным подходом является не мешать явные и неявные возвраты, имейте это в виду
— Пример
// Defer with closure close phuong-secrets.txt: file already closed
// Func result: close phuong-secrets.txt: file already closed
func NamedReturn() (err error) {
const (
filePath = "phuong-secrets.txt"
)
f, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("opening the file %q: %w", filePath, err)
}
defer func() {
err = errors.Join(err, f.Close())
fmt.Println("Defer with closure", err)
}()
sr := bufio.NewScanner(f)
for sr.Scan() {
_ = sr.Text()
}
// It's made deliberately to show the changes in defer statement
f.Close()
return
}
Надеюсь, вы очень хорошо надышались и полны сил. Отпускаем вас на заслуженный отдых!
Статью писали с Дашей: @dariasroom
Stay tuned
Please open Telegram to view this post
VIEW IN TELEGRAM