today's savings
This commit is contained in:
parent
17f37947f3
commit
171c8d5489
5 changed files with 237 additions and 0 deletions
26
10-linux/00-utils/helix/fill match arms.md
Normal file
26
10-linux/00-utils/helix/fill match arms.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
В **Helix** пока нет встроенной полноценной поддержки сниппетов в том виде, как это сделано в VS Code (где нажал Tab и развернулся шаблон кода), но есть очень мощная альтернатива через **LSP (Language Server Protocol)**.
|
||||||
|
|
||||||
|
То, что ты описываешь ("заполнить match по всем вариантам enum"), — это фича **rust-analyzer**, и она работает в Helix через **Code Actions**.
|
||||||
|
|
||||||
|
### Как сделать "Fill match arms" в Helix:
|
||||||
|
|
||||||
|
1. Напиши `match s.split_once(',') {}`.
|
||||||
|
2. Поставь курсор **внутри** фигурных скобок `{}` или на ключевое слово `match`.
|
||||||
|
3. Нажми **`Space` + `a`** (команда `code_action`).
|
||||||
|
4. В появившемся меню выбери **"Fill match arms"**.
|
||||||
|
|
||||||
|
Rust-analyzer автоматически сгенерирует код:
|
||||||
|
```rust
|
||||||
|
match s.split_once(',') {
|
||||||
|
Some(_) => todo!(),
|
||||||
|
None => todo!(),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Насчет классических сниппетов
|
||||||
|
В Helix нативная поддержка сниппетов (как в VSCode: набрал `for` -> Tab -> развернулся цикл) находится в разработке/планах.
|
||||||
|
Сейчас сообщество использует:
|
||||||
|
1. **Внешние решения:** Например, через автодополнение LSP, если сервер их поддерживает (rust-analyzer кое-что умеет, например `tmod` разворачивает тестовый модуль).
|
||||||
|
2. **Скрипты/Пайпы:** Можно настроить хоткеи на вставку текста через `pipe` из внешних файлов, но это костыльно.
|
||||||
|
|
||||||
|
Так что для Rust твой лучший друг — это `Space` + `a` (Code Actions). Это даже круче сниппетов, потому что оно контекстно-зависимое.
|
||||||
50
10-linux/30-computer-arch/GPU CPU.md
Normal file
50
10-linux/30-computer-arch/GPU CPU.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
Ты абсолютно прав насчет того, что GPU — это "много слабых процессоров", но твои догадки про отсутствие кэша и памяти не совсем верны. Давай разберем "кишочки" архитектурно, используя правильные термины.
|
||||||
|
|
||||||
|
Главное отличие можно описать одной фразой: **CPU оптимизирован под Latency (задержку), а GPU — под Throughput (пропускную способность).**
|
||||||
|
|
||||||
|
### 1. Архитектура кристалла (Die Layout)
|
||||||
|
Если посмотреть на фото кристалла под микроскопом, разница бросается в глаза:
|
||||||
|
|
||||||
|
* **CPU:** Огромную часть площади занимает **Control Unit** (блок управления) и **Кэш** (L1/L2/L3). Сами вычислительные блоки (**ALU** — Arithmetic Logic Unit) занимают меньшую часть места.
|
||||||
|
* *Зачем:* CPU — это менеджер-бюрократ. Его задача — выполнять сложные, запутанные инструкции, где много условий `if/else`. Он тратит миллиарды транзисторов на **Branch Prediction** (предсказание ветвлений) и **Out-of-Order Execution** (внеочередное исполнение), чтобы угадать, какую инструкцию ты захочешь выполнить следующей, пока текущая еще считается.[3][5]
|
||||||
|
* **GPU:** Почти весь кристалл забит тысячами мелких **ALU**. Control Unit крошечный и тупой.
|
||||||
|
* *Зачем:* GPU — это армия рабочих. Им не нужно гадать. Им дают команду: "Умножь эти 10 миллионов чисел на 2". Им не нужен сложный менеджмент, им нужна грубая сила.[1]
|
||||||
|
|
||||||
|
### 2. SIMD и "Проблема ветвлений"
|
||||||
|
Это, пожалуй, самое главное "кишечное" отличие.
|
||||||
|
|
||||||
|
* **CPU (SISD/MIMD):** Каждое ядро может делать что-то свое. Одно ядро играет музыку, другое считает физику.
|
||||||
|
* **GPU (SIMT - Single Instruction, Multiple Threads):**
|
||||||
|
Ядра GPU сгруппированы в "пачки" (у NVIDIA это называется **Warp**, обычно 32 потока).
|
||||||
|
* **Как это работает:** Блок управления посылает **одну** инструкцию сразу на 32 ядра. Все 32 ядра *обязаны* сделать одно и то же действие, но с разными данными.
|
||||||
|
* **Где GPU умирает:** Если ты напишешь код с условием `if (x > 0) { do_A() } else { do_B() }`, и внутри одного варпа у половины потоков `x > 0`, а у другой нет, происходит **Warp Divergence**. Сначала половина ядер делает `do_A`, пока вторая половина *простаивает и ждет*, а потом наоборот. Производительность падает в разы. CPU такие ветвления щелкает как орешки.[6][3]
|
||||||
|
|
||||||
|
### 3. Миф про память и кэш
|
||||||
|
Твое предположение, что у GPU нет кэша и он не работает с памятью — **неверно**.
|
||||||
|
|
||||||
|
* **Память (VRAM):** У GPU есть свой контроллер памяти, и он *намного* мощнее, чем у CPU.
|
||||||
|
* Шина данных CPU: узкая (64-128 бит), но с низкой задержкой.
|
||||||
|
* Шина данных GPU: огромная (128-384 бит и более, память GDDR6X). Это широченная труба. GPU может прокачивать терабайты данных в секунду.
|
||||||
|
* **Кэш:** У GPU есть кэши L1 и L2, и даже Shared Memory (управляемый вручную кэш). Просто они работают иначе. Они нужны не для того, чтобы хранить данные долго (как у CPU), а чтобы "склеивать" (coalescing) запросы. Если 32 потока просят 32 соседних байта из памяти, GPU объединит их в один запрос. Если они просят данные вразнобой — GPU "захлебнется".[5][6]
|
||||||
|
|
||||||
|
### 4. Ответ на Rust-часть: "Графический инжиниринг"
|
||||||
|
Ты правильно уловил суть. Когда ты пишешь на Rust под GPU (будь то `wgpu` для графики или `burn`/`candle` для ML), ты занимаешься **оркестрацией**:
|
||||||
|
|
||||||
|
1. **Host Code (CPU/Rust):** Ты подготавливаешь данные, загружаешь текстуры/веса в VRAM (через шину PCIe) и формируешь **Command Buffer** (список команд).
|
||||||
|
2. **Device Code (Shader/Kernel):** Ты пишешь (или библиотека генерирует) маленькие программы (шейдеры на WGSL/GLSL или ядра CUDA), которые загружаются на GPU.
|
||||||
|
3. **Dispatch:** Ты с CPU пинаешь GPU: "Выполни вот этот шейдер 1 миллион раз".
|
||||||
|
|
||||||
|
Твое взаимодействие "напрямую с видеокартой" заключается в правильном управлении памятью и создании таких структур данных, чтобы тысячи глупых ядер GPU могли читать их линейно, не прыгая по памяти (иначе потеряешь всю мощь).
|
||||||
|
|
||||||
|
**Итог:** GPU — это не просто много слабых CPU. Это архитектура, где пожертвовали *управлением* и *логикой* ради *математической плотности* и *ширины канала памяти*.
|
||||||
|
|
||||||
|
[1](https://aws.amazon.com/ru/compare/the-difference-between-gpus-cpus/)
|
||||||
|
[2](https://timeweb.cloud/blog/gpu-i-cpu-v-chem-raznica)
|
||||||
|
[3](https://habr.com/ru/companies/otus/articles/844260/)
|
||||||
|
[4](https://tproger.ru/articles/gpu-protiv-cpu--chto-nuzhno-znat-dlya-raboty-s-ii-v-2025)
|
||||||
|
[5](https://www.cloud4y.ru/blog/cpu-gpu-what-is-the-difference/)
|
||||||
|
[6](https://www.ittelo.ru/news/cpu-i-gpu-v-chem-raznitsa/)
|
||||||
|
[7](https://oblakoru.ru/blogs/cpu-i-gpu-v-chem-raznicza-i-chto-vybrat/)
|
||||||
|
[8](https://www.fastvideo.ru/blog/cpu-vs-gpu-fast-image-processing.htm)
|
||||||
|
[9](https://mws.ru/blog/cpu-i-gpu-v-chem-otlichie/)
|
||||||
|
[10](https://www.youtube.com/watch?v=R_9B_DCe1-E)
|
||||||
56
20-dev/00-rust/functional examples/Untitled.md
Normal file
56
20-dev/00-rust/functional examples/Untitled.md
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
```rust
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Person {
|
||||||
|
name: String,
|
||||||
|
age: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Person {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
name: String::from("John"),
|
||||||
|
age: 30,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl From<&str> for Person {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
s.split_once(',')
|
||||||
|
.filter(|(name, _)| !name.is_empty())
|
||||||
|
.and_then(|(name, age_str)| {
|
||||||
|
age_str.parse::<u8>().ok().map(|age| (name, age))
|
||||||
|
})
|
||||||
|
.map(|(name, age)| Person {
|
||||||
|
name: name.to_string(),
|
||||||
|
age,
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
как это написать через match:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl From<&str> for Person {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
let Some((name, age_str)) = s.split_once(',') else {
|
||||||
|
return Person::default();
|
||||||
|
};
|
||||||
|
if name.is_empty() {
|
||||||
|
return Person::default();
|
||||||
|
}
|
||||||
|
let Ok(age) = age_str.parse::<u8>() else {
|
||||||
|
return Person::default();
|
||||||
|
};
|
||||||
|
Person {
|
||||||
|
name: name.to_string(),
|
||||||
|
age,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
98
20-dev/00-rust/iterators/it.md
Normal file
98
20-dev/00-rust/iterators/it.md
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
Ты абсолютно прав! Rust ощущается как "Lego Technic": детали (типы, трейты) либо идеально защелкиваются (type check passed), либо ты физически не можешь их соединить. В C++ часто можно "насильно" прикрутить деталь изолентой (reinterpret_cast), и она отвалится на ходу.
|
||||||
|
|
||||||
|
### 1. Как создавать методы для функциональных цепочек?
|
||||||
|
|
||||||
|
Да, ты можешь писать методы, которые "чейнятся" (chaining). Секрет в том, что метод должен:
|
||||||
|
1. Принимать `self` (по значению) — это называется **builder pattern** или **consuming builder**.
|
||||||
|
2. Возвращать `Self`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Config {
|
||||||
|
port: u16,
|
||||||
|
debug: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { port: 80, debug: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Принимает 'mut self', меняет его и возвращает 'Self'
|
||||||
|
fn set_port(mut self, port: u16) -> Self {
|
||||||
|
self.port = port;
|
||||||
|
self // Возвращаем измененный объект дальше по конвейеру
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_debug(mut self) -> Self {
|
||||||
|
self.debug = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Функциональная цепочка!
|
||||||
|
let conf = Config::new()
|
||||||
|
.set_port(8080)
|
||||||
|
.enable_debug();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Если ты про цепочки типа `.map().filter()`, то там возвращается не `Self`, а новый тип-обертка (например, `Map<Iter>`), который реализует тот же трейт `Iterator`.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### 2. Как работает магия `collect()`?
|
||||||
|
|
||||||
|
`collect()` — это, пожалуй, самый "волшебный" метод в Rust. Он умеет превращать итератор во **что угодно**, что реализует трейт `FromIterator`.
|
||||||
|
|
||||||
|
#### Сигнатура collect (упрощенно)
|
||||||
|
```rust
|
||||||
|
fn collect<B>(self) -> B
|
||||||
|
where
|
||||||
|
B: FromIterator<Self::Item>;
|
||||||
|
```
|
||||||
|
Он говорит: "Я могу стать типом `B`, если тип `B` умеет создавать себя из моих элементов".
|
||||||
|
|
||||||
|
#### Как это работает внутри?
|
||||||
|
Когда ты пишешь:
|
||||||
|
```rust
|
||||||
|
let vec: Vec<i32> = iter.collect();
|
||||||
|
```
|
||||||
|
1. Rust видит, что ты хочешь получить `Vec<i32>`.
|
||||||
|
2. Он ищет реализацию `impl FromIterator<i32> for Vec<i32>`.
|
||||||
|
3. В этой реализации (в стандартной библиотеке) написано примерно следующее:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Псевдокод реализации внутри std
|
||||||
|
impl<T> FromIterator<T> for Vec<T> {
|
||||||
|
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for item in iter {
|
||||||
|
vec.push(item);
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
То есть `collect` просто делегирует работу методу `from_iter` целевого типа.
|
||||||
|
|
||||||
|
#### Почему это круто?
|
||||||
|
Ты можешь реализовать `FromIterator` для **своей структуры**, и `collect` начнет работать для неё автоматически!
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct MyCollection(Vec<i32>);
|
||||||
|
|
||||||
|
// Учим нашу коллекцию создаваться из итератора
|
||||||
|
impl FromIterator<i32> for MyCollection {
|
||||||
|
fn from_iter<T: IntoIterator<Item = i32>>(iter: T) -> Self {
|
||||||
|
let mut c = MyCollection(Vec::new());
|
||||||
|
for i in iter {
|
||||||
|
c.0.push(i);
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let my_col: MyCollection = vec![1, 2, 3].into_iter().collect(); // Работает!
|
||||||
|
}
|
||||||
|
```
|
||||||
7
lifestyle?/phylosophy.md
Normal file
7
lifestyle?/phylosophy.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
| Philosopher | Concept | Connection to You |
|
||||||
|
| ------------ | --------------------- | ------------------------------------------------------------------------------ |
|
||||||
|
| John Dewey | Experimentalism | Life is not aboutbeingstable, butdoingexperiments to solve problems. |
|
||||||
|
| Heraclitus | Flux (Panta Rhei) | Stability is a lie; conflict/change is the only reality. |
|
||||||
|
| Nietzsche | Will to Power | Despising the "comfort" of stability; creating the future through strength. |
|
||||||
|
| Nassim Taleb | Antifragility | "Stability" creates hidden risks; volatility/experimentation creates strength. |
|
||||||
|
| Karl Popper | Piecemeal Engineering | Progress comes from small, correctable experiments, not grand stable plans. |
|
||||||
Loading…
Add table
Add a link
Reference in a new issue