Golang | Вопросы собесов
4.46K subscribers
28 photos
2 videos
766 links
Download Telegram
🤔 По какому алгоритму растёт slice?

Slice растёт по экспоненциальной стратегии:
- При нехватке места создаётся новый массив большей ёмкости.
- Обычно размер удваивается, пока не достигает определённого порога, после чего рост может становиться более линейным или адаптивным (зависит от языка/реализации).
Это даёт амортизированную сложность O(1) на добавление, несмотря на редкие дорогостоящие перераспределения.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 В чем разница между императивным и декларативным?

Императивное и декларативное программирование — это два основных подхода, каждый из которых предлагает свои методы для описания того, что и как должна делать программа. Понимание различий между этими стилями может помочь в выборе подходящего подхода для конкретной задачи или проекта.

🚩Императивное программирование

Это стиль программирования, где выражается последовательность команд для выполнения задач. В этом подходе программист указывает машине, как изменять своё состояние пошагово, контролируя поток выполнения через управляющие конструкции, такие как циклы, условные операторы и т.д.
🟠Подробное описание процесса
Программист должен указывать все шаги, которые необходимо выполнить для достижения результата.
🟠Примеры языков
Java, C, Python в их традиционном использовании.
🟠Управление состоянием
Императивные программы часто включают явное управление состоянием и его изменениями.
# Императивный подход к сортировке массива методом пузырька
def bubble_sort(array):
n = len(array)
for i in range(n):
for j in range(0, n-i-1):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
return array


🚩Декларативное программирование

Это стиль программирования, где описывается желаемый результат, но не детализируется процесс его достижения. В декларативном стиле программа определяет, что должно быть сделано, а не как.
🟠Абстракция от процесса
Описываются желаемые свойства результата, а система сама определяет, как его достичь.
🟠Примеры языков
SQL, HTML, CSS, функциональные языки программирования, такие как Haskell.
🟠Сокрытие сложности
Декларативный подход часто предполагает высокий уровень абстракции, что уменьшает количество деталей, которые нужно учитывать.

-- Декларативный запрос в SQL для получения списка сотрудников, отсортированного по зарплате
SELECT name, salary FROM employees ORDER BY salary DESC;


🚩Основные различия

🟠Описание vs. результат
Императивное программирование фокусируется на описании шагов, необходимых для достижения результата, в то время как декларативное программирование описывает желаемый результат без спецификации конкретных шагов.

🟠Управление состоянием
Императивный подход требует активного управления состоянием программы, в то время как в декларативном подходе состояние управляется системой или вовсе абстрагировано.

🟠Поддержка языка
Разные языки поддерживают разные стили программирования. Некоторые языки, как JavaScript, могут поддерживать оба стиля в зависимости от использования.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Какие бывают способы оптимизации?

Зависит от уровня, но в общем:
- На уровне базы: индексация, query plan, нормализация, кэширование.
- На уровне кода: профилирование, снижение сложности алгоритмов.
- На уровне сети: сжатие, уменьшение количества запросов.
- На уровне архитектуры: кэш, очереди, шардирование, микросервисы, отказ от лишнего состояния.
- На уровне ОС/серверов: тюнинг параметров, балансировка, масштабирование.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2
🤔 Что такое len и capacity в slice Go?

Слайсы имеют две основные характеристики: длину (len) и емкость (capacity). Понимание этих характеристик важно для эффективного использования слайсов.

🚩Длина (len)

Это количество элементов, которые в данный момент находятся в слайсе. Она указывает, сколько элементов доступно для чтения или записи.
package main

import "fmt"

func main() {
slice := []int{1, 2, 3, 4, 5}
fmt.Println("Length:", len(slice)) // Length: 5
}


🚩Емкость (capacity)

Это максимальное количество элементов, которые слайс может содержать без выделения дополнительной памяти. Емкость всегда больше или равна длине слайса.
package main

import "fmt"

func main() {
slice := make([]int, 3, 5)
fmt.Println("Length:", len(slice)) // Length: 3
fmt.Println("Capacity:", cap(slice)) // Capacity: 5
}


🚩Взаимосвязь длины и емкости

🟠Длина (`len`)
Определяет текущее количество элементов в слайсе.
Используется для операций чтения и записи.

🟠Емкость (`cap`)
Определяет максимальное количество элементов, которые могут быть добавлены в слайс без выделения новой памяти.
Емкость может увеличиваться автоматически при добавлении элементов через функцию append.

🚩Использование append

Когда вы добавляете элементы в слайс с помощью append, если текущей емкости недостаточно, автоматически выделяет новый массив с большей емкостью, копирует существующие элементы в новый массив и добавляет новые элементы.
package main

import "fmt"

func main() {
slice := make([]int, 2, 2)
slice[0] = 1
slice[1] = 2

fmt.Println("Before append:", slice, "Len:", len(slice), "Cap:", cap(slice)) // [1 2] Len: 2 Cap: 2

// Добавляем элемент, превышающий текущую емкость
slice = append(slice, 3)

fmt.Println("After append:", slice, "Len:", len(slice), "Cap:", cap(slice)) // [1 2 3] Len: 3 Cap: 4
}


🚩Полная форма нарезки (full slice expression)

Позволяет задать начальный индекс, конечный индекс и емкость нового слайса.
package main

import "fmt"

func main() {
original := []int{1, 2, 3, 4, 5}
newSlice := original[1:3:4]
fmt.Println("New Slice:", newSlice) // [2 3]
fmt.Println("Length:", len(newSlice)) // 2
fmt.Println("Capacity:", cap(newSlice)) // 3
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Что такое iota?

iota — это встроенный идентификатор для автоматической генерации последовательностей чисел, обычно используемый в декларациях констант. Его значение начинается с 0 и увеличивается на 1 для каждой последующей константы в блоке.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 Какие основные отличия есть у Go перед языками Java, Python?

Обладает несколькими ключевыми отличиями от Java и Python, что делает его уникальным и подходящим для определенных задач.

🟠Компиляция и выполнение
Go: Компилируемый язык, компилируется в машинный код, что обеспечивает высокую производительность и быстрое время выполнения.
Java: Компилируется в байт-код, который выполняется на виртуальной машине Java (JVM). Это обеспечивает переносимость, но может добавлять накладные расходы.
Python: Интерпретируемый язык, что делает его менее производительным по сравнению с компилируемыми языками.

🟠Простота и лаконичность
Go: Разработан для простоты и читаемости, минимизирует синтаксическую сложность, избегает избыточности.
Java: Сложный и многословный синтаксис, требует больше кода для выполнения тех же задач.
Python: Простой и читаемый синтаксис, который делает его легким для изучения и использования.

🟠Управление памятью
Go: Автоматическая сборка мусора, но с управляемыми задержками для обеспечения высокой производительности.
Java: Автоматическая сборка мусора на JVM, что может приводить к задержкам в критических приложениях.
Python: Автоматическая сборка мусора с использованием подсчета ссылок и циклического сборщика мусора.

🟠Конкурентность и параллелизм
Go: Встроенная поддержка конкурентности через горутины и каналы, легковесные потоки с низкими накладными расходами.
Java: Многопоточность с использованием потоков, сложное управление потоками и синхронизацией.
Python: Поддержка многопоточности, но ограниченная глобальной блокировкой интерпретатора (GIL), что затрудняет использование многопоточности для параллельных вычислений.

🟠Статическая и динамическая типизация
Go: Статически типизированный язык, ошибки типа обнаруживаются на этапе компиляции, что повышает надежность кода.
Java: Статически типизированный язык, что позволяет обнаруживать ошибки типа на этапе компиляции.
Python: Динамически типизированный язык, типы проверяются во время выполнения, что может приводить к ошибкам времени выполнения.

🟠Интерфейсы и наследование
Go: Использует интерфейсы для определения поведения без наследования. Интерфейсы реализуются неявно.
Java: Использует классы и интерфейсы, поддерживает множественное наследование через интерфейсы и одиночное наследование классов.
Python: Поддерживает множественное наследование классов, что может усложнять структуру программы.

🟠Стандартная библиотека
Go: Богатая стандартная библиотека с встроенной поддержкой работы с сетью, веб-серверами и другими задачами.
Java: Широкая стандартная библиотека с обширной поддержкой различных API и утилит.
Python: Обширная стандартная библиотека, особенно сильная в области научных вычислений, обработки данных и веб-разработки.

🟠Экосистема и инструменты
Go: Современные и мощные инструменты для сборки, тестирования и профилирования. Простая система управления зависимостями.
Java: Зрелая экосистема с множеством фреймворков и инструментов (например, Maven, Gradle, Spring).
Python: Обширная экосистема пакетов и библиотек (например, pip, virtualenv, Django).

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что известно о GMP паттерне?

GMP — это модель планировщика горутин в Go, состоящая из трёх компонентов:
- G (goroutine) — сама горутина, единица выполняемого кода.
- M (machine) — системный поток ОС, на котором выполняется G.
- P (processor) — логический процессор, владеющий очередью задач и управляющий выполнением G.
Каждому P соответствует одна очередь горутин, и количество P определяет количество одновременно выполняемых горутин (ограничивается через GOMAXPROCS).
Когда M свободен и есть активный P, он берёт G из очереди и выполняет. Это позволяет планировщику балансировать и масштабировать задачи без участия ОС.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🤔 Какие сущности есть в планировщике?

Планировщик задач (Scheduler) управляет выполнением процессов и горутин (в случае Go). В контексте операционных систем и языков программирования, таких как Golang, планировщик отвечает за распределение вычислительных ресурсов.

🟠G (Goroutine) – Горутинa
Это легковесный поток, который выполняет код. В отличие от ОС-потоков, она не требует большого количества памяти и управляется рантаймом Go.
Включает стек, контекст выполнения, а также статус (ждёт, выполняется, завершена).
Переключается между потоками ОС прозрачно для разработчика.
Можно создать десятки тысяч горутин без значительных затрат памяти.
go func() {
fmt.Println("Привет из горутины!")
}()


🟠M (Machine) – Операционный поток
M (Machine) представляет собой реальный поток ОС (kernel thread), на котором выполняются горутины.
Соответствует потокам ОС (pthread в Linux, Windows Thread в Windows).
Может одновременно выполнять только одну горутину.
Количество M зависит от GOMAXPROCS, но обычно Go создаёт столько потоков, сколько ядер в системе.
runtime.GOMAXPROCS(4) // Использовать 4 ядра процессора


🟠P (Processor) – Планировщик (Scheduler)
Это сущность, которая отвечает за распределение горутин (G) между потоками (M).
P управляет очередью горутин и раздаёт их потокам (M).
По умолчанию количество P равно количеству GOMAXPROCS.
Горутину нельзя выполнить без доступного P.
[Processor P1] ----> [Machine M1] ----> Выполняет горутины G1, G2
[Processor P2] ----> [Machine M2] ----> Выполняет горутины G3, G4


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 В чём отличие синхронной репликации от асинхронной и какая лучше для какого кейса?

Синхронная репликация обеспечивает согласованность: запись считается завершённой, только когда подтверждена и основной, и репликой. Это безопаснее, но медленнее. Подходит для критичных к данным систем.
Асинхронная репликация — быстрее, потому что главный сервер не ждёт подтверждения от реплики. Но при сбоях возможна потеря последних данных. Подходит для масштабирования на чтение, резервного копирования и менее чувствительных задач.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что будет если сложить строки?

В Go строки представляют собой неизменяемые последовательности байтов, и операция сложения строк (+) приводит к созданию новой строки, которая является конкатенацией двух или более исходных строк.

🚩Что произойдет при сложении строк?

🟠Создание новой строки
Когда вы складываете строки, Go создает новую строку, содержащую все символы исходных строк, соединенных друг за другом. Исходные строки при этом остаются неизменными.
s1 := "Hello"
s2 := "World"
result := s1 + " " + s2
fmt.Println(result) // Output: Hello World


🟠Новая строка размещается в памяти
Так как строки в Go неизменяемы, результирующая строка создается заново, а память для нее выделяется в куче или на стеке, в зависимости от размера и контекста.

🚩Особенности сложения строк в Go

🟠Эффективность
Сложение строк через оператор + может быть неэффективным при множественных операциях, так как каждый раз создается новая строка, а старые промежуточные результаты удаляются сборщиком мусора.
result := ""
for i := 0; i < 5; i++ {
result += "Go"
}
fmt.Println(result) // Output: GoGoGoGoGo


🟠Использование `strings.Builder` для оптимизации
Если требуется объединить много строк, рекомендуется использовать strings.Builder. Это более эффективный способ, так как Builder работает с буфером и избегает лишних аллокаций.

   import "strings"

func main() {
var builder strings.Builder
for i := 0; i < 5; i++ {
builder.WriteString("Go")
}
fmt.Println(builder.String()) // Output: GoGoGoGoGo
}


🚩Особенности Unicode

Go строки поддерживают Unicode, поэтому сложение строк корректно работает с многобайтовыми символами.
s1 := "Привет"
s2 := "Мир"
result := s1 + " " + s2
fmt.Println(result) // Output: Привет Мир


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 В чем разница между буферизированными и небуферизированными каналами?

Буферизированные каналы в Go имеют внутренний буфер, который позволяет отправлять значения в канал без необходимости немедленного приема значения другой горутиной. Небуферизированные каналы, напротив, требуют, чтобы при каждой отправке значения другая горутина была готова принять это значение, что приводит к блокировке до тех пор, пока отправка или прием не будут выполнены.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Сколько очередей в паттерне?

Паттерн "Очередь" (Queue) в программировании обычно представляет собой структуру данных или архитектурный подход, используемый для обработки сообщений, задач или запросов в определённом порядке (обычно FIFO — "первым вошёл, первым вышел").
Но если рассматривать количество очередей в каком-то конкретном паттерне проектирования, то всё зависит от контекста. Например:

🚩Одиночная очередь (Single Queue)

Все задачи обрабатываются в одной очереди по порядку. Используется, когда важен строгий порядок выполнения. Пример: канал (channel) в Go, куда отправляются задачи для обработки.

🚩Несколько очередей (Multiple Queues)

Используется, когда разные типы задач требуют разной обработки. Например, очереди с разными приоритетами (высокий, средний, низкий). В многопоточных системах каждая очередь может обслуживаться отдельным worker'ом.

🚩Очередь с пуулом воркеров (Worker Pool Queue)

Есть одна очередь задач, но несколько рабочих (goroutines), которые извлекают задачи и обрабатывают их параллельно.
Уменьшает нагрузку на систему и ускоряет выполнение.
     package main

import (
"fmt"
"sync"
"time"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d started job %d\n", id, job)
time.Sleep(time.Second) // имитация работы
fmt.Printf("Worker %d finished job %d\n", id, job)
results <- job * 2
}
}

func main() {
jobs := make(chan int, 5)
results := make(chan int, 5)
var wg sync.WaitGroup

numWorkers := 3
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go worker(i, jobs, results, &wg)
}

for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)

wg.Wait()
close(results)

for res := range results {
fmt.Println("Result:", res)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Что такое тип byte?

byte — это псевдоним для uint8, представляющий один байт данных. Используется для работы с необработанными данными, такими как файлы или сетевые пакеты, а также для операций с массивами и срезами байтов.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 По какому алгоритму растет slice?

В Go срезы (slice) динамически изменяемы, и при добавлении новых элементов их вместимость (capacity) увеличивается по определённому алгоритму.

🚩Как растёт `slice` в Go?

Когда срезу требуется больше места, чем доступно в его текущей capacity, происходит автоматическое выделение нового массива с увеличенным размером, и элементы копируются в новый массив.

🚩Алгоритм роста:

Если cap(slice) < 1024, то новая ёмкость (capacity) удваивается.
Если cap(slice) >= 1024, то увеличение идёт примерно на 25% от текущего размера.

package main

import "fmt"

func main() {
var s []int
prevCap := cap(s)

for i := 0; i < 20; i++ {
s = append(s, i)
if cap(s) != prevCap {
fmt.Printf("Len: %d, New Cap: %d (growth: %.2fx)\n", len(s), cap(s), float64(cap(s))/float64(prevCap))
prevCap = cap(s)
}
}
}


Выходные данные (может отличаться в зависимости от реализации Go)
Len: 1, New Cap: 1 (growth: Inf)
Len: 2, New Cap: 2 (growth: 2.00x)
Len: 3, New Cap: 4 (growth: 2.00x)
Len: 5, New Cap: 8 (growth: 2.00x)
Len: 9, New Cap: 16 (growth: 2.00x)
Len: 17, New Cap: 32 (growth: 2.00x)


🚩Почему так?

🟠Экономия памяти
если бы рост был на 1 элемент, то это вызывало бы частые копирования.
🟠Скорость работы
экспоненциальный рост уменьшает количество выделений памяти.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Что такое closer и принцип его работы?

Это интерфейс, содержащий метод Close() error, который сигнализирует об освобождении ресурсов, например, файлов или соединений. Если объект реализует Closer, его можно безопасно завершить, вызвав Close, что предотвращает утечки ресурсов.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
🤔 Что такое HAVING?

HAVING — это оператор в SQL, который фильтрует группированные (GROUP BY) данные по агрегатным функциям (SUM, COUNT, AVG, MAX, MIN).

🚩Чем отличается `HAVING` от `WHERE`?

WHERE фильтрует отдельные строки до группировки.
HAVING фильтрует группы строк после GROUP BY.

Теперь посчитаем сумму продаж по категориям и оставим только те, где сумма > 250
SELECT category, SUM(amount) AS total_sales
FROM sales
GROUP BY category
HAVING SUM(amount) > 250;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Что такое структура (struct) в Go? Зачем они нужны?

struct — это пользовательский тип данных в Go, который группирует поля с разными типами в единый объект. Структуры используются для моделирования сложных сущностей, таких как объекты реального мира, с их свойствами и поведением. Они позволяют организовать данные и методы, что делает код более читаемым и логичным. В отличие от классов в ООП, структуры Go не поддерживают наследование, но позволяют использовать композицию.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4💊1
🤔 Чем отличается интерфейс Go от интерфейсов в других языках?

Интерфейсы имеют ряд уникальных особенностей и отличий от интерфейсов в других языках программирования, таких как Java, C# или C++.

🚩Основные отличия интерфейсов

🟠Неявная реализация интерфейсов
В Go типы реализуют интерфейсы неявно. Это означает, что если тип имеет методы, определенные в интерфейсе, он автоматически считается реализацией этого интерфейса без явного указания.
package main

import "fmt"

type Stringer interface {
String() string
}

type Person struct {
Name string
Age int
}

func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

func main() {
var s Stringer = Person{Name: "Alice", Age: 30}
fmt.Println(s.String())
}


🟠Отсутствие явного наследования
В Go нет явного наследования интерфейсов или типов. Интерфейсы могут быть составлены из других интерфейсов с помощью композиции, но это не считается наследованием в традиционном смысле.
type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}

type ReadWriter interface {
Reader
Writer
}


🟠Отсутствие методов доступа
В Go нет методов доступа (getter и setter), как в некоторых других языках. Методы интерфейсов определяются исключительно для реализации логики.

🟠Малый и простой интерфейс
В Go часто используются маленькие и простые интерфейсы с одним или двумя методами. Это позволяет создавать более гибкие и переиспользуемые компоненты.
type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}


🟠Композиция интерфейсов
Интерфейсы в Go могут быть составлены из других интерфейсов, что позволяет строить сложные интерфейсы из простых.
type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}

type ReadWriter interface {
Reader
Writer
}


🟠Интерфейсы пустого типа
В Go есть специальный пустой интерфейс interface{}, который может содержать значение любого типа. Это делает его мощным инструментом для работы с обобщенным кодом.
func printValue(v interface{}) {
fmt.Println(v)
}

func main() {
printValue(42)
printValue("hello")
printValue(true)
}


🚩Сравнение с другими языками

🟠Java и C#
Интерфейсы в Java и C# требуют явного указания, какие классы реализуют интерфейсы с использованием ключевого слова implements. Явное наследование интерфейсов. Методы доступа часто используются. Интерфейсы могут содержать свойства (в C#), которые требуют реализации.

🟠C++
Интерфейсы часто реализуются с использованием чисто виртуальных функций. Классы должны явно указывать наследование от интерфейсов. Наследование интерфейсов и классов явно указывается.

🟠Python
Используется динамическая типизация и протоколы, похожие на интерфейсы. Протоколы не требуют явного указания реализации. Python использует утиную типизацию, похожую на неявную реализацию интерфейсов в Go.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Сколько очередей в GMP паттерне?

В модели планировщика GMP (Goroutine, Machine, Processor) используется одна локальная очередь goroutine на каждый P (Processor).
Дополнительно есть:
- Глобальная очередь, из которой P может забирать задачи, если его собственная очередь пуста.
- Stealing-механизм — если P простаивает, он может "украсть" goroutine из другой очереди.
Итого:
- У каждого логического процессора (P) — своя очередь.
- Плюс одна глобальная очередь.
- Всего: N локальных + 1 глобальная очередь.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Какие бывают способы оптимизации?

Оптимизация кода в Go может быть выполнена на нескольких уровнях:

🟠Оптимизация алгоритмов и структур данных
Использование более эффективных алгоритмов (например, O(log n) вместо O(n^2)). Выбор подходящих структур данных (например, map вместо срезов для поиска по ключу).

🟠Оптимизация работы с памятью
Снижение количества аллокаций (использование sync.Pool, предварительное выделение памяти). Использование []byte вместо string, если требуется частое изменение строки. Минимизация копирования данных, например, передача []byte по ссылке вместо копирования.

🟠Оптимизация работы с горутинами
Использование worker pool, чтобы избежать избыточного создания горутин. Ограничение количества параллельных задач (runtime.GOMAXPROCS). Использование каналов правильной ёмкости для минимизации блокировок.

🟠Оптимизация ввода-вывода (I/O)
Буферизация (bufio.Reader, bufio.Writer). Использование асинхронных операций при работе с файлами или сетью. Использование io.Pipe() для потоковой обработки данных.

🟠Использование профилирования и анализа производительности
pprof для анализа CPU, памяти и блокировок. race detector (-race флаг) для выявления проблем с конкурентным доступом. Инструменты трассировки (trace).

🚩Пример оптимизации работы с памятью

Пример неэффективного кода
func inefficient() []string {
var result []string
for i := 0; i < 1000; i++ {
result = append(result, fmt.Sprintf("Item %d", i)) // Частые аллокации
}
return result
}


Оптимизированный вариант
func efficient() []string {
result := make([]string, 0, 1000) // Выделяем память заранее
for i := 0; i < 1000; i++ {
result = append(result, fmt.Sprintf("Item %d", i))
}
return result
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1