Читання під обмежені ресурси¶
Прогін справи — найдовша робота в застосунку: сотні аркушів, години. Тому питання не «як швидше», а як укластися в ту машину, що є, і не завалити її.
🔴 Головне: це НЕ задача для відеокарти¶
У ванільному kraken карта простоювала понад 90% часу: найдорожчими були
ridge-фільтр сегментації (skimage.filters.sato) і CPU-геометрія рушія. Патчі
раннера перенесли sato на карту й прискорили геометрію, але розклад лишився
тим самим: більшу частину часу сторінки рахує процесор. Де саме він іде на
твоїй справі — stages у меті кожної сторінки.
Час сторінки = стала (мережа сегментації, ~0.5–1 с на карті) + рядки × ціна рядка; довші рядки дорожчі. Тип документа впливає лише через щільність.
Наслідки, які міняють рішення:
- на машині без карти читання працює — просто повільніше (порядок різниці: хвилини на сторінку замість десятків секунд). Це не привід відмовляти людині;
- докупити карту — найгірша з можливих порад. Швидкість дають ядра процесора й правильний розподіл роботи;
- паралельність упирається в пам'ять карти, а не в кількість ядер.
Важелі nysh read¶
| прапорець | що робить | коли |
|---|---|---|
--workers N (-j N) |
N шардів на одній карті однією командою: сама розкладає --shard і --gpu-lock |
карта є, ядра вільні — замість ручних --shard (з ним разом відмовляє); без карти читає один процес |
--shard k/n |
цей процес бере кожен n-й кадр (round-robin) | є вільні ядра; запускають n процесів паралельно |
--gpu-lock <файл> |
спільний міжпроцесний лок GPU-фази | обов'язково разом із --shard на одній карті |
--no-gpu-sato |
рахувати sato на процесорі, а не на карті | разом із шардингом — див. нижче |
--seg-height <px> |
висота, до якої сегментер стискає сторінку (типово 1800) | коли інакше не влазить; коштує якістю |
--limit N / --pages 1-50 |
прочитати частину справи | розвідка перед повним прогоном |
--one-voice |
без другого рушія | швидше, але сліпіше |
--with latin |
латинська модель третім голосом того самого проходу, вихід <прогін>-<тег> |
мішане письмо в кириличній справі: замість другого прогону |
--form-cells |
клітинки друкованого бланка за його лініями, кожна читається як один рядок; вихід <прогін>-cells |
бланк, який план не впізнав сам (анкета, облікова картка); акти цивільного стану — без прапорця |
Бланки-таблиці: голос клітинок¶
Друкований бланк, заповнений від руки (акти цивільного стану), сегментація, натренована на книгах, читає погано: пропускає більшість рукописних значень, рве слова, обрізає великі літери. Голос клітинок знаходить на кожному кадрі лінії таблиці, ріже клітинки (із запасом угору — рукопис налазить на лінію над собою) і читає кожну записану основною моделлю як один рядок.
Коли вмикається. Сам — для справ актів цивільного стану (ЗАГС, РАГС, РАЦС): план
читає опис справи (назву, тип, фонд Р-, роки від 1919) і пише «📋 бланк поклітинно
(автоматично: акти цивільного стану …)». Те саме робить хмарний захід. Для іншого бланка
— --form-cells або поле form у паспорті.
Зняти автоматичне. Опис справи буває хибним або загальним, а кадри бачить той, хто
запускає. Тому автоматичне ввімкнення завжди супроводжує попередження (⚠ у плані
nysh read, cells_auto у конверті операції плану, примітка nysh cloud go): чому
ввімкнено, що це коштує і як зняти — --no-form-cells на цей запуск або "form": false
у _source.json справи назавжди.
Мішана черга в хмарі. Рішення — по справі: акти разом із метриками в одному заході
читають клітинки лише в актах (form_cells у params кожної справи плану; конвеєр
бокса передає його рядком черги, і один процес раннера вмикає клітинки справа за
справою); cloud go називає мікс одним рядком — «📋 клітинки — у k з N справ черги (…);
решта читається без них», — ще в сухому прогоні, тож чергу з актами варто спершу
прогнати з --dry-run. --no-form-cells знімає їх для всієї черги. Застарілий наглядач (пакет
оренди без позначки form_cells_case у плані) уміє лише один перемикач на захід — тоді
примітка cloud go каже, що клітинки дістануть усі справи черги: оновіть пакет оренди
або розділіть чергу.
За виглядом сторінки голос не вмикається навмисно: метрика XIX ст. — теж друкована
таблиця, сітка на ній є на кожній сторінці, але графа висока, на кілька рядків руки, і
клітинки читають її гірше за звичайну сегментацію. Жодна ознака самої сітки (висота
клітинки, число граф у рядку) актів від метрик не відділяє.
Що це дає. ДАІФО Р-2521, акти 1940-х: звичайне читання — 61 % звірених слів, клітинки — 67 %, обидва голоси разом — 85 %. Пошук бере обидва голоси, тож клітинки нічого не заміняють, лише додають. На актах 1920-х (ДАВО Р-6129) звичайне читання сильніше, і клітинки додають небагато (з 40 звірених слів — 3, яких воно не мало).
Ціна. Порожня клітинка рушію не йде (чорнила поза лініями бланка менше 1 %): рушій не мовчить, а вигадує слово. Сітку рахує фоновий потік, поки карта читає сторінку; на розвороті вона коштує ~2 с процесора.
- На вже прочитаній справі
nysh readдочитує лише клітинки (справа актів — без прапорця, інший бланк — з--form-cells). - Вихід — сестринська тека
<прогін>-cells, ще один голос тієї самої справи:<стор>.txt(рядок на клітинку),<стор>.lines.json(рамки кропів у координатах кадру),_htr_meta.json(voice: cells). Післяnysh text index --case <справа>вона знаходитьсяtext find/grep/ctx, аtext cropріже саме цю клітинку. - Основне читання не повторюється; кути поворотів кадрів беруться з основного прогону.
- Кадри мають лежати на диску. Справа, що має лише
_r2_frames.json, відмовляє. - Сторінка без таблиці (книжкова) не дає ні тексту, ні рамок; підсумок називає, скільки сторінок із сіткою і скільки без.
- Нахил до ±4° сторінка вирівнюється за горизонталями бланка; розворот ділиться по корінцю.
- Картка, де поля — пунктирні рядки (акт 1920-х альбомом), сітки майже не дає: морфологія бачить лише суцільні лінії. Таку сторінку бере звичайне читання.
--model <ваги>читає клітинки іншою моделлю (вихід<прогін>-cells_<тег>).
Шардинг: чому саме з локом і без sato на карті¶
Round-robin, а не блоками: сторінки нерівні за вартістю (порожній зворот проти щільного формуляра), і чергування вирівнює процеси само собою.
🔴 --shard без --gpu-lock на одній карті — найпоширеніша помилка. Два
одночасні проходи сегментації не влазять у пам'ять типової ігрової карти, і
прогін не сповільнюється, а завалюється. Дай усім шардам один і той самий
файл-лок; nysh read попередить, якщо ти забув.
⚠ Виграш дає не шардинг сам собою, а шардинг разом зі знятим sato з карти. З sato на карті найдорожча фаза сторінки йде під локом — і три процеси стають у чергу замість паралельної роботи. Знімеш sato — під локом лишиться коротка фаза, а найдорожче рахуватиметься на вільних ядрах.
✅ --no-gpu-sato не міняє результат, лише спосіб розрахунку: вихід
еквівалентний, і це перевірено окремим верифікатором. Тобто це безпечна
оптимізація, а не компроміс якості — на відміну від наступної.
--seg-height: єдиний важіль, що коштує якістю¶
Менша висота дає квадратичну економію, але платиш прочитаним:
| висота | ціна |
|---|---|
| 1800 (типово) | — |
| 1440 | приблизно −4% слів |
| 1200 | приблизно −10% пошукового відгуку на довгих словах |
🔴 Саме тому воно не вмикається саме собою. Вмикай свідомо і скажи людині, що зробив: інакше згодом «прізвище не знайшлось» припишуть моделі, а не цьому прапорцю.
Скільки шардів ставити¶
Вирішує вільна пам'ять карти, а не кількість ядер. Щойно сумарно впираєшся у стелю, драйвер починає перекладати пам'ять через шину, і прогін завалюється. Пам'ять складається з двох частин:
| частина | скільки | від чого залежить |
|---|---|---|
| зайнятість шарда | ~1.2–1.9 ГБ на кожен | ваги обох голосів і буфери |
| пік сегментації | ~0.1 ГБ + 0.5 ГБ × Мп входу, один на карту | пропорція кадру: вхід = 1800² × ширина/висота |
Пік один, бо сегментація йде під --gpu-lock. Звідси шардів =
⌊(пам'ять карти − пік найширшого кадру) / зайнятість шарда⌋. Портрет (0.75)
дає пік ~1.3 ГБ, розворот (1.3) — ~2.2 ГБ.
🔴 Від матеріалу число не залежить. Тип документа, роздільність скану й кількість рядків пік не рухають (виміряно на метриках, сповідках, судових справах і польських книгах, на трьох картах, з відхиленням у кілька МБ). Спокуса дати щільним аркушам менше шардів, а рідким більше — помилка.
Кадр ширший за 2.5:1 (етикетка плівки, корінець, склейка) раннер сам стискає до
8 Мп входу (--seg-max-mpx, позначка seg_capped у меті), щоб одна аномалія
не диктувала пам'ять на всю справу. Фактичний пік — vram_peak_mb у меті.
✅ Приймач — симптом, а не секунди: після старту подивись завантаження. Пам'ять під стелею при простої процесора означає буксування — знімай шард. Здорове завантаження виглядає інакше: процесор зайнятий.
⚠ Секунди на сторінку міряти на робочій машині безглуздо. Той самий прогін за вечір може дати різницю в рази — залежно від того, що ще крутиться поруч. Порівнювати режими по часу тут не вийде; надійний тільки симптом.
Шарди резюмуються спільно, тож зняти зайвий можна будь-коли без утрат.
Приймач повноти — диск¶
Після прогону порахуй .txt у теці виходу проти .jpg у теці справи.
nysh read робить це сам і друкує рядок «сторінок з текстом: N з M».
🔴 Є клас відмов, за якого сторінка вбиває процес: лог обривається, перелік збоїв порожній, код повернення успішний. Виміряний випадок — 14 сторінок із 18. Тільки диск.
⚠ Для часткового прогону (--limit, --pages, --shard) повнота не міряється —
ти прочитав менше навмисно, і рядок про це скаже.
🛟 Рятувальний прохід і якорі¶
Окремий механізм, про який варто знати, навіть якщо ти його не запускаєш.
Яку діру закриває. Основний рушій проходить справу швидко, далі пошук, далі оком дивляться лише сильні хіти. Нижче певного балу — шум. Між ними на велику справу набирається понад тисяча рядків, яких не подивиться ніхто — і справжня згадка сидить саме там: рушій із мовною моделлю підставляє правдоподібне слово замість написаного, тож рядок виглядає осмислено й не привертає уваги.
Чому другий рушій тут працює. Він судить кадри умовно незалежно — здогадуватись йому нічим, тому він калічить локально, зберігаючи корінь слова. Саме та властивість, щоб витягти згадку зі «сміттєвої» смуги.
Що таке якорі. Рядки на перечитування відбираються двома способами: за балом («сіра смуга» між шумом і хітами) і за якорем — коли в рядку стоїть ім'я або по батькові з відомого складу роду. Другий спосіб знаходить те, у чому саме прізвище скалічене повністю.
🔴 Якорі приходять ззовні, зі складу роду. Порожній перелік якорів проговорюється вголос, а не мовчки: рятунок «за якорем» без якорів — це рятунок, який відібрав нуль рядків, і без попередження це не відрізнити від «нема кого рятувати».
Як запустити. Через nysh read цього немає: рятунок вимагає другої моделі
й переліку якорів, тобто рішень, які ухвалює людина. Ваги обох голосів дає
nysh models get, а сам рятунок — окремий прохід по вже прочитаній справі, а
не режим першого читання.
Порядок дій на слабкій машині¶
nysh doctor --json— чи є карта взагалі, скільки місця на диску.nysh read <тека> --dry-run— план: скільки кадрів, яка модель.nysh read <тека> --limit 20— розвідка: подивитись, що виходить, перш ніж ставити на ніч.- Повний прогін. Якщо ядер кілька й карта слабка:
--shard k/n+ один спільний--gpu-lock+--no-gpu-satoна кожному. - Порахувати
.txtпроти.jpg. - Якщо не влізло навіть так —
--seg-height 1440і сказати людині, що прочитане тепер трохи гірше.
⚠ Не запускай повний прогін «щоб подивитись». Це години роботи машини, і перервати його безболісно можна, але сторінки, прочитані двічі, — ні.