hardware
This commit is contained in:
parent
f2b07c9f30
commit
17f37947f3
8 changed files with 485 additions and 16 deletions
24
10-linux/00-utils/sops/base workflow with sops.md
Normal file
24
10-linux/00-utils/sops/base workflow with sops.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
```
|
||||
age-keygen -y ~/.config/sops/age/keys.txt
|
||||
```
|
||||
Вот это выведет публичный ключ для вставки в .sops
|
||||
|
||||
пример содержимого `.sops`
|
||||
|
||||
```
|
||||
# .sops.yaml
|
||||
creation_rules:
|
||||
- path_regex: secrets.yaml
|
||||
key_groups:
|
||||
- age:
|
||||
- age12345...6789
|
||||
|
||||
```
|
||||
|
||||
затем
|
||||
|
||||
`sops secrets.yaml`
|
||||
|
||||
и вуаля - там уже образец примера файла будет.
|
||||
|
||||
|
||||
2
10-linux/00-utils/ssh/Untitled.md
Normal file
2
10-linux/00-utils/ssh/Untitled.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
скопировать что нибудь
|
||||
`rsync -avz ./themes/ user@remote_host:~/.config/helix/themes/`
|
||||
File diff suppressed because one or more lines are too long
88
10-linux/30-computer-arch/Кэш, регистры, кэш-линия.md
Normal file
88
10-linux/30-computer-arch/Кэш, регистры, кэш-линия.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
Давай разберем эту иерархию от самого быстрого и "близкого" к мозгам процессора до более объемного. Это можно представить как пирамиду: чем ближе к вершине, тем меньше памяти, но доступ к ней мгновенный.
|
||||
|
||||
### 1. Регистры (Registers)
|
||||
Это самая быстрая память, которая находится прямо в ядре процессора.
|
||||
* **Аналогия:** Это твои **руки**. Данные здесь — это то, что ты держишь прямо сейчас.
|
||||
* **Объем:** Крошечный. Регистров всего несколько десятков штук (например, 16 регистров общего назначения в x86-64).
|
||||
* **Размер:** В архитектуре x86-64 (64-битной) один регистр вмещает ровно **64 бита** (8 байт).
|
||||
* **Скорость:** **0 тактов** (мгновенно). Арифметические операции (сложение, умножение) процессор может делать *только* над значениями, которые уже лежат в регистрах.
|
||||
* **SIMD-регистры:** Это особые широкие регистры (128, 256 или 512 бит), в которые можно положить сразу пачку чисел (например, четыре `float32`) и обработать их одной инструкцией. Именно их использует Rust-компилятор для векторизации.
|
||||
|
||||
### 2. Кэш процессора (CPU Cache)
|
||||
Это сверхоперативная память типа **SRAM** (Static RAM), расположенная на кристалле процессора. Она нужна, чтобы процессор не простаивал, ожидая данные из медленной оперативной памяти (RAM).
|
||||
* **Аналогия:** Это **верстак** или рабочий стол. Данные здесь лежат "под рукой".
|
||||
* **Уровни (L1, L2, L3):**
|
||||
* **L1 (Level 1):** Самый маленький (например, 32 КБ для команд + 32 КБ для данных на ядро), но самый быстрый. Доступ занимает ~3-4 такта.
|
||||
* **L2:** Побольше (256 КБ - 1 МБ на ядро), чуть медленнее (~10-12 тактов).
|
||||
* **L3:** Общий для всех ядер (десятки МБ), еще медленнее (~40-50 тактов), но все равно намного быстрее RAM.
|
||||
|
||||
### 3. Кэш-линия (Cache Line)
|
||||
Это **минимальная единица обмена** данными между RAM и кэшем.
|
||||
* **Размер:** Стандарт индустрии — **64 байта**.
|
||||
* **Суть:** Процессор не умеет читать из памяти "один байт". Если твой код просит переменную типа `u8` (1 байт), процессор всё равно загрузит из RAM целую линию (64 байта), в которой лежит этот байт, и поместит её в кэш.
|
||||
* **Почему это важно для структур данных:**
|
||||
* Если у тебя **массив** (или `Vec`), данные лежат плотно. Загрузив одну кэш-линию, процессор получает сразу 16 чисел `i32` (так как 16 * 4 байта = 64). Следующие 15 обращений к массиву будут мгновенными (взяты из L1 кэша).
|
||||
* Если у тебя **связный список**, каждый элемент может лежать в памяти далеко друг от друга. Чтобы прочитать 16 элементов, процессору придется 16 раз сходить в медленную RAM, загружая 16 разных кэш-линий, из которых полезными будут только по 4-8 байт. Это называется **Cache Miss** (промах кэша), и это убивает производительность.
|
||||
|
||||
### Итог
|
||||
1. Процессор загружает данные из RAM **кэш-линиями** (по 64 байта) в **кэш**.
|
||||
2. Из кэша данные попадают в **регистры** (по 8 байт).
|
||||
3. АЛУ (арифметико-логическое устройство) выполняет операции над регистрами.
|
||||
|
||||
В B-Tree мы храним ключи массивом, поэтому одна загрузка кэш-линии дает нам сразу много ключей для сравнения, и мы максимально эффективно используем эту механику.
|
||||
|
||||
[1](https://ru.wikipedia.org/wiki/%D0%9A%D1%8D%D1%88_%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%BE%D1%80%D0%B0)
|
||||
[2](https://market.marvel.ru/blog/komplektuyushchie-i-optsii/kesh-pamyat-protsessora/)
|
||||
[3](https://club.dns-shop.ru/blog/t-100-protsessoryi/37338-chto-takoe-kesh-v-protsessore-i-zachem-on-nujen/)
|
||||
[4](https://man-made.ru/articles/chto-takoe-kesh-pamyat-protsessora/)
|
||||
[5](https://otus.ru/journal/kesh-processora-opisanie-urovni-osobennosti/)
|
||||
[6](https://skyeng.ru/magazine/wiki/it-industriya/chto-takoe-kesh-pamiat/)
|
||||
[7](https://overclockers.ru/blog/Hardware_inc/show/240934/Kesh-processora-chto-eto-takoe-i-kak-on-zastavlyaet-vash-komp-juter-rabotat-bystree-Chast-1)
|
||||
[8](https://compress.ru/article.aspx?id=23541)
|
||||
[9](https://habr.com/ru/companies/vdsina/articles/515660/)
|
||||
[10](https://www.youtube.com/watch?v=7n_8cOBpQrg)
|
||||
|
||||
|
||||
Помимо иерархии памяти (Регистры → Кэш → RAM), для написания высокопроизводительного кода (особенно на Rust/C++) критически важно понимать еще три концепции. Они объясняют, почему "наивный" код часто работает медленнее, чем мог бы.
|
||||
|
||||
### 1. Конвейер (Pipeline) и Предсказание ветвлений (Branch Prediction)
|
||||
|
||||
Современный процессор — это фабрика. Он не делает одну инструкцию за раз; он выполняет их параллельно на разных стадиях (чтение, декодирование, выполнение, запись).
|
||||
* **Проблема:** Когда процессор видит условный переход `if` (ветвление), он не знает, куда пойдет код дальше, пока условие не вычислится.
|
||||
* **Решение:** Процессор **угадывает** (Branch Prediction). Он начинает выполнять ветку `true` заранее (спекулятивное выполнение).
|
||||
* **Цена ошибки:** Если процессор не угадал (Branch Misprediction), он должен выбросить все вычисления и начать заново с правильной ветки. Это огромная потеря времени (10-20 тактов).
|
||||
* **Для разработчика:**
|
||||
* Отсортированные массивы обрабатываются быстрее (паттерн ветвлений предсказуем: TTTTFFFF).
|
||||
* В B-Tree поиск внутри узла часто делают линейным (без ветвлений, через SIMD) или оптимизированным бинарным, чтобы не сбивать предсказатель.
|
||||
|
||||
### 2. Виртуальная память и TLB (Translation Lookaside Buffer)
|
||||
|
||||
Адреса, которые видит твоя программа (например, указатель `0x7ff...`), — ненастоящие. Это **виртуальные адреса**. Процессор должен каждый раз переводить их в **физические адреса** RAM.
|
||||
* **TLB:** Это специальный маленький "кэш для адресов". Он помнит последние переводы страниц памяти.
|
||||
* **TLB Miss:** Если ты прыгаешь по памяти слишком хаотично (например, в огромном графе или HashMap), TLB не находит перевод, и процессору приходится лезть в "таблицы страниц" (Page Walk), что очень дорого.
|
||||
* **Почему это важно:** Локальность данных (B-Tree, массивы) спасает не только кэш данных (L1/L2), но и TLB. Меньше прыжков по страницам — быстрее работа.
|
||||
|
||||
### 3. Суперскалярность и Зависимость по данным (Data Dependency)
|
||||
|
||||
Ядро процессора имеет несколько исполнительных блоков (ALU). Оно может выполнить, например, 4 сложения за один такт, если они независимы.
|
||||
* **Плохо:** `a = b + 1; c = a + 2;` (Второе действие ждет первого).
|
||||
* **Хорошо:** `a = b + 1; c = d + 2;` (Процессор сделает это одновременно).
|
||||
* **Для разработчика:** Иногда развертывание циклов (loop unrolling) или обработка данных независимыми блоками дает ускорение именно за счет загрузки всех ALU ядра.
|
||||
|
||||
***
|
||||
|
||||
### ИТОГ: Ключевые элементы процессора для программиста
|
||||
|
||||
Чтобы писать эффективный код (особенно структуры данных), нужно держать в голове эту "карту железа":
|
||||
|
||||
| Элемент | Что это | Почему важно понимать |
|
||||
| :--- | :--- | :--- |
|
||||
| **Регистры** | Рабочая зона ядра (мгновенно) | Данные должны быть здесь для вычислений. Компилятор пытается держать "горячие" переменные тут. |
|
||||
| **Кэш-линия** | 64 байта данных (транспорт) | Читаем память блоками. **Массивы (Vec) — короли**, связные списки — зло. B-Tree выигрывает за счет плотности данных. |
|
||||
| **Кэш (L1/L2/L3)** | Быстрая память (на кристалле) | **Cache Miss** — главный враг производительности. Чем компактнее твои данные, тем больше их влезет в кэш. |
|
||||
| **Branch Predictor** | Предсказатель `if`'ов | Непредсказуемые условия (рандомные `if`) сбрасывают конвейер. Иногда лучше вычислить лишнее, чем ветвиться. |
|
||||
| **SIMD** | Векторные инструкции | Обработка 4-8 чисел одной командой. Rust делает это сам, если ты используешь итераторы и простые циклы. |
|
||||
| **TLB** | Кэш адресов памяти | Хаотичные прыжки по памяти (Pointer Chasing) забивают не только кэш данных, но и TLB. |
|
||||
|
||||
**Главный вывод для Rust-разработчика:**
|
||||
Думай о памяти как о **ленте**, которую нужно читать подряд. Любой указатель (`Box`, ссылка, узел графа) — это разрыв ленты, который стоит дорого. Структуры данных, которые минимизируют эти разрывы (как B-Tree или `Vec`), всегда будут побеждать на современном железе.
|
||||
Loading…
Add table
Add a link
Reference in a new issue