diff --git a/.github/workflows/binary.yml b/.github/workflows/binary.yml index 1c547a21d..a08a4d053 100644 --- a/.github/workflows/binary.yml +++ b/.github/workflows/binary.yml @@ -394,9 +394,9 @@ jobs: # # Счётчиков долга в дереве было три — раздел «Вне языка» в docs/ROADMAP.md, # docs/javascript-inventory.md и числа сайта, — и все три считали ОДИН - # язык: JavaScript. Оболочка (47 файлов, 8930 строк), HTML, CSS, пробы на - # СНЯТО 2026-10-04 файлов *.sh = 47 (перенос scripts/guards/proved-share-vs-tree.sh и guards-without-forgery-probe.sh на планы снял два файла оболочки; до них 49, снято 2026-10-04) (перенос flang/translation/run.sh на план flang/translation/run.fscript снял один файл оболочки; до него 50, снято 2026-10-04) (задачи 0049 и 5821: сняты flang/scripts/word-occupancy.mjs, scripts/seed/seed-freshness.sh и scripts/guards/version-derivations-guard.sh — у каждого двойник на flang доказан прогоном; до них 52, снято 2026-10-04) (задача 5821: семь скриптов scripts/** переехали с оболочки на flang; до них 101, снято 2026-09-17) - # СНЯТО 2026-10-04 строк-в *.sh = 8930 (перенос scripts/guards/proved-share-vs-tree.sh и guards-without-forgery-probe.sh на планы снял 414 строк оболочки; до них 9344, снято 2026-10-04) (перенос flang/translation/run.sh на план flang/translation/run.fscript снял 214 строк оболочки; до него 9558, снято 2026-10-04) (задачи 0049 и 5821: сняты flang/scripts/word-occupancy.mjs, scripts/seed/seed-freshness.sh и scripts/guards/version-derivations-guard.sh — у каждого двойник на flang доказан прогоном; до них 9756, снято 2026-10-04) (задачи 1426 и 8567: поиск коммита отпечатка по содержимому прибавил 135 строк в scripts/bootstrap-reprint.sh, а отменённая хронология предела шагов убрала 141; до них 9762, снято 2026-09-17) (задача 1400: девять проб на подлог и две честные под правила Н6 и О9 прибавили 42 строки в flang/proof/checker/tests/run.sh) (задача 1416: четырнадцать объявлений ярлыков заведены в скриптах и зов «Сбора» — в хуке; до них 23372, снято 2026-09-17) (задача 5821: семь скриптов scripts/** переехали с оболочки на flang; до них 23980, снято 2026-09-22) (ADR-0046: довод в шапках двух сторожей имён — сперва почему flang/proof не судится, потом почему вошёл в область — прибавил 9 строк; до него 23971, снято 2026-09-17) + # язык: JavaScript. Оболочка (46 файлов, 8886 строк), HTML, CSS, пробы на + # СНЯТО 2026-10-07 файлов *.sh = 46 (перенос scripts/guards/proved-share-vs-tree.sh и guards-without-forgery-probe.sh на планы снял два файла оболочки; до них 49, снято 2026-10-04) (перенос flang/translation/run.sh на план flang/translation/run.fscript снял один файл оболочки; до него 50, снято 2026-10-04) (задачи 0049 и 5821: сняты flang/scripts/word-occupancy.mjs, scripts/seed/seed-freshness.sh и scripts/guards/version-derivations-guard.sh — у каждого двойник на flang доказан прогоном; до них 52, снято 2026-10-04) (задача 5821: семь скриптов scripts/** переехали с оболочки на flang; до них 101, снято 2026-09-17) + # СНЯТО 2026-10-07 строк-в *.sh = 8886 (перенос scripts/guards/proved-share-vs-tree.sh и guards-without-forgery-probe.sh на планы снял 414 строк оболочки; до них 9344, снято 2026-10-04) (перенос flang/translation/run.sh на план flang/translation/run.fscript снял 214 строк оболочки; до него 9558, снято 2026-10-04) (задачи 0049 и 5821: сняты flang/scripts/word-occupancy.mjs, scripts/seed/seed-freshness.sh и scripts/guards/version-derivations-guard.sh — у каждого двойник на flang доказан прогоном; до них 9756, снято 2026-10-04) (задачи 1426 и 8567: поиск коммита отпечатка по содержимому прибавил 135 строк в scripts/bootstrap-reprint.sh, а отменённая хронология предела шагов убрала 141; до них 9762, снято 2026-09-17) (задача 1400: девять проб на подлог и две честные под правила Н6 и О9 прибавили 42 строки в flang/proof/checker/tests/run.sh) (задача 1416: четырнадцать объявлений ярлыков заведены в скриптах и зов «Сбора» — в хуке; до них 23372, снято 2026-09-17) (задача 5821: семь скриптов scripts/** переехали с оболочки на flang; до них 23980, снято 2026-09-22) (ADR-0046: довод в шапках двух сторожей имён — сперва почему flang/proof не судится, потом почему вошёл в область — прибавил 9 строк; до него 23971, снято 2026-09-17) # C, Python, awk и Erlang не считались нигде и ни в одной проверке. Долг, # которого никто не считает, не убывает: его не видно ни в отчёте, ни в # ленте, и растёт он молча. @@ -407,9 +407,9 @@ jobs: # оболочке или на Python останавливает работу в тот же день. # # Замер на этом дереве: 17,7 с и 471 МБ, двоичному хватает `git` и `wc`. - # Предел шагов поднят с умолчания (10 млн): опись обходит 199 файлов и + # Предел шагов поднят с умолчания (10 млн): опись обходит 198 файлов и # семнадцать языков, и в умолчание не укладывается. - # СНЯТО 2026-10-04 файлов *.sh,*.c,*.h,*.py,*.html,*.css,*.awk,*.erl,*.js,*.mjs,*.java,*.cs,*.ex,*.go,*.rs,*.lua,*.vim,*.rb,*.cpp,*.cc,*.hpp,*.hh,ярлык,packaging/asdf/bin/download,packaging/asdf/bin/install,packaging/asdf/bin/list-all,.githooks/pre-push,.githooks/commit-msg,.githooks/pre-commit = 199 (мера починена: правило «Язык пути» описи знает только *.ex, а водители пробы на .exs и .ts языком не считает, зато считает .githooks/commit-msg и .githooks/pre-commit — их в мере не было; по прежней мере вышло бы 198, снято 2026-10-04) (задача 1413: семь водителей пробы flang/proof/probes/exact-integer-in-targets/drivers/* на C, C++, Java, C#, Go, Rust и JS — судья, который считает десятичную запись точного целого на самой цели; до них 190, снято 2026-10-04) (перенос scripts/guards/proved-share-vs-tree.sh и guards-without-forgery-probe.sh на планы снял два файла вне flang; до них 192, снято 2026-10-04) (перенос flang/translation/run.sh на план flang/translation/run.fscript снял один файл вне flang; до него 193, снято 2026-10-04) (задачи 0049 и 5821: сняты flang/scripts/word-occupancy.mjs, scripts/seed/seed-freshness.sh и scripts/guards/version-derivations-guard.sh — у каждого двойник на flang доказан прогоном; до них 196, снято 2026-10-04) (две честные пары сличителя перевода: отбор по признаку и объявленная мера; до них 194, снято 2026-09-17) (задача 5821: семь проверок scripts/** стали планами; до них 251, снято 2026-09-27) (задача 5821: семь скриптов scripts/** переехали с оболочки на flang; до них 256, снято 2026-09-17) + # СНЯТО 2026-10-07 файлов *.sh,*.c,*.h,*.py,*.html,*.css,*.awk,*.erl,*.js,*.mjs,*.java,*.cs,*.ex,*.go,*.rs,*.lua,*.vim,*.rb,*.cpp,*.cc,*.hpp,*.hh,ярлык,packaging/asdf/bin/download,packaging/asdf/bin/install,packaging/asdf/bin/list-all,.githooks/pre-push,.githooks/commit-msg,.githooks/pre-commit = 198 (мера починена: правило «Язык пути» описи знает только *.ex, а водители пробы на .exs и .ts языком не считает, зато считает .githooks/commit-msg и .githooks/pre-commit — их в мере не было; по прежней мере вышло бы 198, снято 2026-10-04) (задача 1413: семь водителей пробы flang/proof/probes/exact-integer-in-targets/drivers/* на C, C++, Java, C#, Go, Rust и JS — судья, который считает десятичную запись точного целого на самой цели; до них 190, снято 2026-10-04) (перенос scripts/guards/proved-share-vs-tree.sh и guards-without-forgery-probe.sh на планы снял два файла вне flang; до них 192, снято 2026-10-04) (перенос flang/translation/run.sh на план flang/translation/run.fscript снял один файл вне flang; до него 193, снято 2026-10-04) (задачи 0049 и 5821: сняты flang/scripts/word-occupancy.mjs, scripts/seed/seed-freshness.sh и scripts/guards/version-derivations-guard.sh — у каждого двойник на flang доказан прогоном; до них 196, снято 2026-10-04) (две честные пары сличителя перевода: отбор по признаку и объявленная мера; до них 194, снято 2026-09-17) (задача 5821: семь проверок scripts/** стали планами; до них 251, снято 2026-09-27) (задача 5821: семь скриптов scripts/** переехали с оболочки на flang; до них 256, снято 2026-09-17) # # СКОЛЬКО ИЗ ЭТИХ ПЯТИДЕСЯТИ МИЛЛИОНОВ УХОДИТ, замер 29 августа 2026. # Меряно перебором самого предела: при 10 500 000 опись умирает diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4d63559a..bf1980b49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -213,8 +213,8 @@ jobs: # 31 августа 2026 в дереве нашлось 31 место в 14 файлах, где записанное рукой # число разошлось с тем, что лежит рядом: `docs/tree-inventory.md` считала # `seed-parses-sources-guard.sh` в 83 строки при 195, оболочку — в 66 файлов - # при 71 тогдашних — сегодня их 47; - # СНЯТО 2026-10-04 файлов *.sh = 47 (перенос scripts/guards/proved-share-vs-tree.sh и guards-without-forgery-probe.sh на планы снял два файла оболочки; до них 49, снято 2026-10-04) (перенос flang/translation/run.sh на план flang/translation/run.fscript снял один файл оболочки; до него 50, снято 2026-10-04) (задача 5821: scripts/guards/version-derivations-guard.sh снят — сверка производных версии стала планом scripts/guards/version-derivations-guard.fscript, приговор доказан ядром (13 из 13), хук зовёт план; до него 51, снято 2026-10-04) (задача 0049: scripts/seed/seed-freshness.sh снят — проверка давно живёт планом scripts/seed/seed-freshness.fscript, а переходник ещё и терял довод (exec без "$@"); до него 52, снято 2026-10-04) (задача 5821: семь скриптов scripts/** переехали с оболочки на flang; до них 101, снято 2026-09-21) `flang/cat/SPEC.md` обещала восемнадцать поручений при 22; + # при 71 тогдашних — сегодня их 46; + # СНЯТО 2026-10-07 файлов *.sh = 46 (перенос scripts/guards/proved-share-vs-tree.sh и guards-without-forgery-probe.sh на планы снял два файла оболочки; до них 49, снято 2026-10-04) (перенос flang/translation/run.sh на план flang/translation/run.fscript снял один файл оболочки; до него 50, снято 2026-10-04) (задача 5821: scripts/guards/version-derivations-guard.sh снят — сверка производных версии стала планом scripts/guards/version-derivations-guard.fscript, приговор доказан ядром (13 из 13), хук зовёт план; до него 51, снято 2026-10-04) (задача 0049: scripts/seed/seed-freshness.sh снят — проверка давно живёт планом scripts/seed/seed-freshness.fscript, а переходник ещё и терял довод (exec без "$@"); до него 52, снято 2026-10-04) (задача 5821: семь скриптов scripts/** переехали с оболочки на flang; до них 101, снято 2026-09-21) `flang/cat/SPEC.md` обещала восемнадцать поручений при 22; # `flang/PLAN.md` — пять вариантов «Поручение» при 22. Каждое из этих чисел # было ВЕРНО В ДЕНЬ ЗАПИСИ и солгало назавтра, и сторожа не было ни на одном. # diff --git a/docs/adr/0072-normal-parallelism-is-the-pool-the-pure-map-and-a-proved-monoid.md b/docs/adr/0072-normal-parallelism-is-the-pool-the-pure-map-and-a-proved-monoid.md new file mode 100644 index 000000000..269c4846f --- /dev/null +++ b/docs/adr/0072-normal-parallelism-is-the-pool-the-pure-map-and-a-proved-monoid.md @@ -0,0 +1,202 @@ +# ADR-0072. Нормальный параллелизм — это пул процессов без общей памяти, параллельный `отобразить` над тотальной функцией и свёртка только по доказанному моноиду + +**Состояние:** ПРЕДЛОЖЕНИЕ, владельцем не рассмотрено. +**Рядом:** [ADR-0003](0003-own-machine-instead-of-emitting-to-beam.md) — своя машина +вместо печати в BEAM; [ADR-0005](0005-preemption-and-hot-swap-in-the-evaluator.md); +[ADR-0061](0061-killing-a-process-and-bounding-its-resources.md); +`docs/flang/concurrency/SPEC.md` — два режима исполнения (проверочный и рабочий); +`docs/scheduler-benchmark.md` — стенды и числа пула. + +--- + +## 1. Что есть: числа прогонов + +Машина: 256 процессоров, 499 ГБ памяти, рядом идёт чужая работа (средняя нагрузка +20–31). Время — минимум из трёх повторов. Стенды собраны из +`flang/concurrency/bench/gen.flang` (функция «Текст стенда»), напечатаны +`bootstrap/flang emit --target c` и собраны `make`; ядра мерены планом +`flang/concurrency/bench/by-cores.fscript`. + +**Занимает ли программа больше одного ядра.** + +| что | как мерили | ответ | +|---|---|---| +| вычислитель `bootstrap/flang run`, чистая функция (свёртка по 600 числам, внутри свёртка) | `/usr/bin/time` | 99 % процессора, 14,4 с: одно ядро | +| вычислитель `bootstrap/flang test` на программе с процессами | прогон стенда «пачка» | «исполнено 0 — исполнить их нечем: планировщика в этом двоичном нет» | +| напечатанный C, та же чистая функция (3 000 и 6 000 чисел) | `/usr/bin/time` | 99 %: одно ядро; `pthread` в рантайме только ради стека нужной глубины | +| напечатанный C, процессы, проверочный режим (по умолчанию) | стенд «пинг» | 99 %: одно ядро, порядок по семени | +| напечатанный C, процессы, рабочий режим (`"workers": N`) | стенд «пачка» | N потоков заняты, ускорения нет — §2 | + +**Как процессы ложатся на потоки ОС.** Рабочий режим `flang_conc.c` — уже M:N: +N потоков, у каждого свой склад готовых процессов, поток берёт ПРОЦЕСС и +вычерпывает его ящик пачкой до `FL_CONC_BATCH` писем, чужие склады он +подворовывает через `pthread_mutex_trylock`. Куча у каждого процесса своя (две +половины, живое переезжает после пробега), письмо при доставке копируется в кучу +адресата (`fl_conc_clone`), общей изменяемой памяти между процессами язык не +даёт. Общее хозяйство — счёт пробегов, журнал, таймеры — под одним возвратным +замком и берётся ломтями, а не на каждом пробеге. + +**Пропускная способность, один поток, стенд «пинг»:** наклон между 1 000 000 и +9 000 000 пробегов — 0,381 мкс на доставку, то есть 2,62 миллиона сообщений в +секунду. **BEAM на этой же машине** (`flang/concurrency/bench/beam.erl`, 256 +планировщиков): 1,62 миллиона сообщений в секунду пинг-понгом. + +**Порождение, стенд «рой»:** 1 000 000 процессов за 9,02 с процессорного +времени, наклон между 200 000 и 2 000 000 пробегов — 9,16 мкс на порождённый +процесс вместе с его первым пробегом; пиковый RSS 2 062 336 КиБ, 2 112 байт на +процесс. BEAM: 195 661 `spawn` в секунду (5,1 мкс), 2 648 байт на процесс. + +## 2. Найдено: пул напечатанного C не масштабируется, и причина — две строки рантайма + +Стенд «пачка», 1024 процесса, письмо соседу раз в 64 пробега, 4 000 000 +пробегов: + +| потоков | сегодня, мс | раз | без двух записей, мс | раз | +|---|---|---|---|---| +| 1 | 2 322 | 1 | 2 314 | 1 | +| 2 | 1 429 | 1,62 | 1 022 | 2,26 | +| 4 | 3 453 | 0,67 | 530 | 4,36 | +| 8 | 4 446 | 0,52 | 288 | 8,03 | +| 16 | 5 088 | 0,45 | 162 | 14,28 | +| 32 | 4 029 | 0,57 | 99 | 23,37 | +| 64 | 4 604 | 0,5 | 69 | 33,53 | + +Машина при этом свободна: 32 одновременные копии однопоточного прогона идут +2,48 с против 2,29 с у одной. Число команд не растёт (callgrind: 988 и 969 +миллионов на 200 000 пробегов при одном и четырёх потоках), системных вызовов +нет (`strace -c`: 5 вызовов `futex` на весь прогон). Значит потоки не ждут, а +дерутся за строки кэша. + +ThreadSanitizer на 20 000 пробегах и четырёх потоках — 83 предупреждения, +все в трёх местах `flang/src/emit/c/flang_runtime.c`: + +| место | что пишется | когда | +|---|---|---| +| `fl_tick`, `fl_enter`: `fl_now = function` | имя работающей функции для выборки по времени и ведомости зарядов | каждый виток и каждый вход | +| `fl_enter`: `fl_watch_frames[ctx->depth] = function` | окно наблюдения | каждый вход | +| `fl_watch_open`: `fl_watch_ctx = ctx` | чей расчёт смотрит окно | каждый `fl_ctx_init`, то есть каждый поток пула | + +Обе первые записи — приборы, добавленные после замеров пула; к пулу их не +примеряли. Без двух первых ThreadSanitizer оставляет одно предупреждение +(`fl_watch_open`), а таблица выше получает правый столбец. Ответ проверочного +режима побайтово тот же (md5 ответа на семенах 1, 7 и 4172, с журналом и без), +сумма поля «всего» по процессам в рабочем режиме на 64 потоках равна 4 000 000. + +Третья запись — ещё и висячий указатель: `fl_watch_ctx` остаётся указывать на +контекст на стеке потока пула после того, как поток кончился. + +Для сравнения — BEAM, 512 пар, письмо соседу на КАЖДОМ пробеге (`+S N:N`): +2,67 миллиона сообщений в секунду на одном планировщике, 13,6 миллиона на 64 +(5,1 раза). Напечатанный C без двух записей на таком же стенде («пачка», K = 1): +2,10 с на одном потоке, 0,90 с на четырёх, дальше не растёт (1,20 с на 64). + +## 3. Решение: модель + +Нормальный параллелизм — три вещи, и все три стоят на одном свойстве языка: +тотальная функция без поручений — это функция своих аргументов, и больше ничего. +Состояния, которое она могла бы изменить, нет; мира, который она могла бы +спросить, нет; поручение — значение, а исполняет его хозяин. + +1. **Пул процессов M:N** — тот, что уже есть: потоки ОС по числу `workers`, свой + склад у каждого, подворовывание, своя куча у каждого процесса, письмо + копируется. Проверочный режим остаётся умолчанием и остаётся побайтовым. +2. **Параллельный `отобразить`.** Если функция под `отобразить` тотальна и + список длиннее порога, напечатанная программа режет список на куски и + отдаёт их тем же потокам. Каждый кусок считается в своём контексте и своей + арене; итог копируется в арену вызывающего в порядке кусков. Порядок + элементов результата тот же, что у последовательного обхода, по построению. +3. **Параллельная свёртка — только по доказанному моноиду.** Свёртку можно + резать, только если операция ассоциативна и у неё есть единица, и это + обязано быть ДОКАЗАНО ядром, а не посчитано на сетке. Сложение машинных + чисел не ассоциативно: `(0.1 плюс 0.1) плюс 0.6` и `0.1 плюс (0.1 плюс 0.6)` + различаются. Значит свёртка суммой `число` остаётся последовательной, а + свёртка точными целыми, точными дробями, минимумом и максимумом по `вес` — + режется. Объявление `моноид` в языке уже есть + (`flang/ct/fold-by-cores.flang`), но законы его бинарник считает на сетке; + параллельный путь откроет только закон, закрытый ядром. + +Чего нет и не будет: общей изменяемой памяти, замков в языке, атомарных +переменных, `par`/`seq` от руки. Параллельность выбирает печать, а не автор. + +## 4. Что обязаны гарантировать ядро, вычислитель и рантайм + +| гарантия | кто держит | как проверяется | +|---|---|---| +| тотальная функция без поручений детерминирована | язык: поручение — значение, а не вызов | уже держится; новое не нужно | +| каждый кусок `отобразить` завершается | тотальность, доказанная ядром | уже держится | +| ассоциативность и единица у операции свёртки | ядро, теоремой о носителе | новое обязательство; на сетке — отказ от параллельного пути, не ошибка | +| в рантайме нет записи в общую память на пути вызова | правило рантайма | ThreadSanitizer как ворота, §6 | +| отказ параллельного пути неотличим от последовательного | рантайм: при любом отказе куска весь `отобразить` пересчитывается последовательно, и наружу уходит его отказ | сверка текстов отказов, §6 | +| предел шагов значит то же | шаги кусков складываются; превышение — повторный последовательный счёт | сверка на пределе | +| память запоминания вызовов (`fl_memo`) не делится между потоками без замка | рантайм | ThreadSanitizer; запоминание сегодня ни одной напечатанной функцией не зовётся | + +Ядро доказательств не меняется вовсе: детерминизм рантайма — свойство +напечатанного кода, а не логики, и оно проверяется прогоном, а не теоремой. +Это названо, а не спрятано: теоремы о рантайме C нет и в этом решении не +появляется. + +## 5. Печать в десять целей + +| цель | процессы сегодня | `отобразить` | свёртка по моноиду | +|---|---|---|---| +| C | свой пул (`workers`) | куски на тех же потоках | дерево кусков | +| C++ | отказ `FLANG_CONC_UNSUPPORTED` | рантайм C | рантайм C | +| Go | отказ | горутины по кускам, `sync.WaitGroup` | то же | +| Rust | отказ | `std::thread::scope` по кускам | то же | +| Java | отказ | `ForkJoinPool` | `ForkJoinPool` | +| C# | отказ | `Parallel.For` по кускам | то же | +| Elixir | процесс BEAM | `Task.async_stream` с `ordered: true` | то же | +| JavaScript, TypeScript | свой планировщик, один поток | последовательно | последовательно | +| Python | отказ | последовательно (GIL) | последовательно | + +Последовательная печать — законный исход, а не долг: параллельный путь меняет +только время. Отказ `FLANG_CONC_UNSUPPORTED` про процессы остаётся там, где он +есть; это решение его не снимает. + +## 6. Как проверяется + +1. **Побайтовая сверка.** Каждая программа корпуса с `отобразить` печатается и + гоняется при 1, 2, 8 и 64 потоках; ответ, число шагов и текст отказа обязаны + совпасть с последовательным побайтово. Для процессов — то, что уже есть: + проверочный режим повторяет журнал свидетеля по семени. +2. **Детерминированный повтор.** Рабочий режим процессов не повторяем по + природе; его исход обязан лежать среди исходов проверочного. На + конфлюэнтных стендах («пачка», «пары») это проверяемо числом: итоговые + состояния и контрольная сумма пробегов совпадают при любом числе потоков. +3. **Ворота ThreadSanitizer.** Стенды «пачка» и «пары» собираются с + `-fsanitize=thread` и гоняются при четырёх потоках; ворота — ноль + предупреждений. Сегодня их 83. +4. **Подделки.** + - рантайм с возвращённой записью `fl_now` на пути вызова — ворота обязаны + покраснеть (и краснеют сегодня: это и есть 83 предупреждения); + - свёртка по `число` с объявленным `моноид` — параллельного пути быть не + должно: ассоциативность на сетке проходит, ядром не доказывается; + - моноид с единицей, которая единицей не является, — ядро отвергает закон, + путь последовательный, ответ тот же; + - обработчик процесса не может прочитать чужое состояние: такой программы не + собрать, отказ `FLANG_TYPE` — примеры `*-forged.flang` в + `flang/concurrency/examples/` уже это держат. +5. **Ядра числом.** План `flang/concurrency/bench/by-cores.fscript` на стенде + «пачка»: при 8 потоках ускорение не ниже 6 раз, иначе красное. + +## 7. Шаги и цена в печатях + +| шаг | что меняется | печать | +|---|---|---| +| 1. Убрать общие записи с пути вызова | `flang/src/emit/c/flang_runtime.c`: имя функции и окно наблюдения пишутся только контекстом, открывшим наблюдение; остальные контексты (потоки пула) не пишут | вход печати точки раскрутки: едет с ближайшей пакетной печатью, отдельной не требует; `flang/self` не трогается | +| 2. Ворота ThreadSanitizer и подделка к ним | план над стендами, без `flang/self` | без печати | +| 3. Параллельный `отобразить` в C | `flang/self/emit-c.flang` (печать цикла `map`), куски в `flang_conc.c` или в новом файле рантайма | одна печать | +| 4. Закон моноида ядром и параллельная свёртка | `flang/self/monoid.flang`, `flang/self/types.flang`, печать свёртки | одна печать; можно вместе с шагом 3 | +| 5. Go, Rust, Java, C#, Elixir | `flang/self/emit-*.flang` | вместе с шагом 3 или 4 | + +Итого: шаг 1 — попутно с пакетной печатью, дальше две печати, а если шаги 3–5 +сводить в одну — одна. + +## 8. Чего это решение не делает + +- Не вытесняет обработчик посреди вычисления: длинный обработчик держит свой + процесс и свой поток, как и сейчас (`SPEC.md`, «Цена, которую платим»). +- Не даёт планировщика вычислителю `bootstrap/flang`: программа с процессами + там по-прежнему не исполняется. +- Не делает пул быстрее BEAM на стенде, где письмо уходит соседу на каждом + пробеге: там пул без двух записей упирается в четыре потока. diff --git a/docs/javascript-inventory.md b/docs/javascript-inventory.md index b14fa7656..be5ac7b37 100644 --- a/docs/javascript-inventory.md +++ b/docs/javascript-inventory.md @@ -68,8 +68,8 @@ JavaScript стало 55, строк 29 733; в трёх каталогах о Эта опись считает ОДИН язык. Остальные шестнадцать — оболочка, C, C++, Python, HTML, CSS, awk, Erlang, Java, C#, Elixir, Go, Rust, Lua, vimscript, Ruby — -считает [`tree-inventory.md`](tree-inventory.md) (4 октября 2026: 199 файлов вне flang, - +считает [`tree-inventory.md`](tree-inventory.md) (4 октября 2026: 198 файлов вне flang, + долг вне JavaScript — **55 файлов, 5 607 строк при потолке 63**: храповик зелён, запас 8 файлов, разбор прежней красноты — задачи 4838 и 7405). Там же названы 569 строк JavaScript, лежащих ВНУТРИ файлов `.html`: счёт по именам файлов их не видит, и diff --git a/docs/scheduler-benchmark.md b/docs/scheduler-benchmark.md index 2fab2e2e6..115a053dd 100644 --- a/docs/scheduler-benchmark.md +++ b/docs/scheduler-benchmark.md @@ -269,7 +269,38 @@ gcc 15.2.0, Erlang/OTP 29 (erts-17.0.5, JIT), Elixir на нём же. > **Миллион одновременно готовых процессов — около 11 мкс на переключение** > (13,83 секунды на 1 280 000 пробегов). -### Ядра: столько, сколько попросят +### Ядра: столько, сколько попросят — после правки рантайма + +**Напечатанный сегодня пул не ускоряет вовсе.** Стенд «пачка», 1024 процесса, +K = 64, 4 000 000 пробегов, план `by-cores.fscript`, минимум из трёх повторов: + +| потоков | мс | раз | +|---|---|---| +| 1 | 2 322 | 1 | +| 2 | 1 429 | 1,62 | +| 8 | 4 446 | 0,52 | +| 64 | 4 604 | 0,5 | + +Причина — две записи в общую память на КАЖДОМ вызове функции, в +`flang/src/emit/c/flang_runtime.c`: имя работающей функции `fl_now` (в `fl_tick` +и `fl_enter`) и окно наблюдения `fl_watch_frames[depth]` (в `fl_enter`). Потоки +пула пишут в одни и те же строки кэша, и строка гуляет между ядрами на каждом +вызове. ThreadSanitizer на 20 000 пробегах и четырёх потоках даёт 83 +предупреждения о гонке, все на этих двух местах и на `fl_watch_open`. Тот же +стенд с двумя записями, снятыми в копии рантайма: + +| потоков | мс | раз | +|---|---|---| +| 1 | 2 314 | 1 | +| 2 | 1 022 | 2,26 | +| 8 | 288 | 8,03 | +| 64 | 69 | 33,53 | + +Ответ проверочного режима при этом тот же побайтово (семена 1, 7 и 4172, с +журналом и без). Правка рантайма — вход печати точки раскрутки, поэтому она едет +с ближайшей пакетной печатью; решение и порядок работ — +[ADR-0072](adr/0072-normal-parallelism-is-the-pool-the-pure-map-and-a-proved-monoid.md). +Таблицы ниже описывают пул без этих двух записей. Число потоков задаётся полем `workers` в запросе. Пул не будит НИКОГО: положивший процесс на склад никого не будит, спящий поток просыпается сам и обходит склады @@ -534,9 +565,10 @@ for k in 1 2 4 8 16 32 64; do K=$k TYAZH=32 bash $F/assemble.sh пачка 1024; REP=5 bash $F/threshold.sh 4000000 8 1 2 4 8 16 32 64 REP=5 TYAZH=32 bash $F/threshold.sh 400000 8 1 2 4 8 16 32 64 -# 12. Где пул не окупается +# 12. Где пул не окупается: путь к стенду ПОЛНЫЙ, потоки — списком bash $F/assemble.sh пинг 16 -REP=5 bash $F/by-cores.sh b-пинг-16 2000000 1 2 4 8 16 +bootstrap/flang io flang/concurrency/bench/by-cores.fscript --plan "Растёт ли с ядрами" \ + -- "$PWD/b-пинг-16" 2000000 1 2 4 8 16 # 13. Где падает печать (двоичный поиск, каждый шаг — настоящая печать) LC_ALL=C.UTF-8 node $F/stenka.mjs 1000 100000 diff --git a/docs/tree-inventory.md b/docs/tree-inventory.md index b5fc9cb71..6b39bff8f 100644 --- a/docs/tree-inventory.md +++ b/docs/tree-inventory.md @@ -1,5 +1,5 @@ -# Опись дерева по языкам: 199 файлов вне flang, долг вне JavaScript — 55 при потолке 63 - +# Опись дерева по языкам: 198 файлов вне flang, долг вне JavaScript — 55 при потолке 63 + ⚠ **ХРАПОВИК ДОЛГА ЗЕЛЁН С 4 ОКТЯБРЯ 2026: долг 55 файлов при потолке 63, запас 8.** Прогон `bootstrap/flang run-script inventory:languages` **4 октября 2026** отвечает кодом 0: «ДОЛГ ВНЕ @@ -82,7 +82,7 @@ $ bootstrap/flang io scripts/guards/tree-inventory.fscript --max-steps 50000000 | язык | файлов | строк | долг файлов | долг строк | |---|---:|---:|---:|---:| -| оболочка | 53 | 9 247 | 48 | 5 017 | +| оболочка | 52 | 9 203 | 47 | 4 973 | | C | 40 | 893 525 | 1 | 107 | | C++ | 2 | 511 | 1 | 107 | | Python | 5 | 2 997 | 0 | 0 | diff --git a/flang/concurrency/bench/by-cores.fscript b/flang/concurrency/bench/by-cores.fscript new file mode 100644 index 000000000..59b217b9a --- /dev/null +++ b/flang/concurrency/bench/by-cores.fscript @@ -0,0 +1,572 @@ +модуль «By cores bench» + +объект «Счёт» + «ещё»: число + «мин»: число + «старт»: число + «база»: число + +объект «Замер» + «потоки»: список строки + «стенд»: строка + «пробегов»: строка + «строки»: список строки + «счёт»: «Счёт» + +тип «Ход» + вариант «Просим доводы» + вариант «Ждём доводы» + вариант «Ждём начало» содержит замер: «Замер» + вариант «Ждём прогон» содержит замер: «Замер» + вариант «Ждём конец» содержит замер: «Замер» + вариант «Готово» содержит отчёт: строка + вариант «Отказ» содержит код: строка, сообщение: строка + вариант «Не мерили» содержит код: строка, сообщение: строка + +тотальная функция «Начало» + возвращает «Ход» + пример «план начинается с просьбы о доводах» + ожидается вариант «Просим доводы» + вариант «Просим доводы» + +тотальная функция «Повторов» + возвращает число + обеспечивает «повторов больше нуля» результат больше 0 + пример «минимум из трёх повторов» + ожидается 3 + 3 + +тотальная функция «Шапка» + возвращает строка + пример «четыре столбца» + ожидается "потоков мс пробегов/с раз" + "потоков мс пробегов/с раз" + +тотальная функция «Запрос» + принимает пробегов: строка, потоков: строка + возвращает строка + пример «тот же запрос, что собирала оболочка» + дано пробегов равно "2000000" + дано потоков равно "8" + ожидается "{\"run\":\"стенд\",\"seed\":\"1\",\"turns\":\"2000000\",\"journal\":\"0\",\"workers\":\"8\"}" + соединить ["{\"run\":\"стенд\",\"seed\":\"1\",\"turns\":\"", пробегов, "\",\"journal\":\"0\",\"workers\":\"", потоков, + "\"}"] по "" + +тотальная функция «Сценарий оболочки» + возвращает строка + пример «оболочка подаёт запрос на вход стенда и отдаёт только начало ответа» + ожидается "echo \"$1\" | \"$0/flang_cli\" | head -c 12" + "echo \"$1\" | \"$0/flang_cli\" | head -c 12" + +тотальная функция «Поручение прогона» + принимает замер: «Замер» + возвращает «Поручение» + пример «стенд зовётся через sh, потому что у хозяина нет поручения с вводом» + дано замер равно запись «Замер» с «потоки» равным [ + "2"] и «стенд» равным "/z/b" и «пробегов» равным "5" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + ожидается вариант «Запустить процесс» с программа равным "sh" и аргументы равным ["-c", + "echo \"$1\" | \"$0/flang_cli\" | head -c 12", "/z/b", + "{\"run\":\"стенд\",\"seed\":\"1\",\"turns\":\"5\",\"journal\":\"0\",\"workers\":\"2\"}"] + пусть «запрос» равно («Запрос» от (замер.«пробегов») и («Первые потоки» от (замер.«потоки»))) + вариант «Запустить процесс» с программа равным "sh" и аргументы равным ["-c", («Сценарий оболочки»), + (замер.«стенд»), «запрос»] + +тотальная функция «Целое» + принимает х: число + возвращает число + пример «дробь отбрасывается» + дано х равно 7.75 + ожидается 7 + пример «целое остаётся» + дано х равно 12 + ожидается 12 + х минус (х остаток от 1) + +тотальная функция «Сотые» + принимает х: число + возвращает число + пример «две цифры после точки» + дано х равно 2.9187 + ожидается 2.91 + («Целое» от (х умножить на 100)) делить на 100 + +тотальная функция «Пробегов в секунду» + принимает пробегов: число, мс: число + возвращает число + пример «миллион за полсекунды» + дано пробегов равно 1000000 + дано мс равно 500 + ожидается 2000000 + пример «нулевое время не делит на ноль» + дано пробегов равно 10 + дано мс равно 0 + ожидается 0 + если мс больше 0 + то «Целое» от ((пробегов умножить на 1000) делить на мс) + иначе 0 + +тотальная функция «Во сколько раз» + принимает база: число, мс: число + возвращает число + пример «вдвое быстрее» + дано база равно 1000 + дано мс равно 500 + ожидается 2 + пример «нулевое время не делит на ноль» + дано база равно 1000 + дано мс равно 0 + ожидается 0 + если мс больше 0 + то «Сотые» от (база делить на мс) + иначе 0 + +тотальная функция «Число довода» + принимает довод: строка + возвращает число + пример «число» + дано довод равно "64" + ожидается 64 + пример «не число даёт нуль» + дано довод равно "много" + ожидается 0 + разбор (к числу или беда довод) + случай вариант «Разобрано» с значение как значение + то значение + случай вариант «Не разобрано» с код как код и сообщение как сообщение + то 0 + +тотальная функция «Строка замера» + принимает потоков: строка, пробегов: строка, мс: число, база: число + возвращает строка + пример «восемь потоков вдвое быстрее одного» + дано потоков равно "8" + дано пробегов равно "4000000" + дано мс равно 1000 + дано база равно 2000 + ожидается "8 1000 4000000 2" + соединить [потоков, (к строке мс), (к строке («Пробегов в секунду» от («Число довода» от пробегов) и мс)), + (к строке («Во сколько раз» от база и мс))] по " " + +тотальная функция «Меньшее» + принимает лучшее: число, новое: число + возвращает число + пример «первое время берётся как есть» + дано лучшее равно -1 + дано новое равно 900 + ожидается 900 + пример «из двух берётся меньшее» + дано лучшее равно 800 + дано новое равно 900 + ожидается 800 + если (лучшее меньше 0) или (новое меньше лучшее) + то новое + иначе лучшее + +тотальная функция «Негодные числа» + принимает доводы: список строки + возвращает список строки + пример «нуль и слово негодны» + дано доводы равно ["64", "0", "много"] + ожидается ["0", "много"] + отфильтровать доводы где довод → не ((«Число довода» от довод) больше 0) + +тотальная функция «Прогон годен» + принимает код: число, вывод: строка + возвращает признак + пример «кончился нулём и сказал ok» + дано код равно 0 + дано вывод равно "{\"ok\":true,\"run\":\"стенд\"}" + ожидается да + пример «отказ стенда замером не считается» + дано код равно 0 + дано вывод равно "{\"ok\":false}" + ожидается нет + (код равен 0) и притом (вывод содержит "\"ok\":true") + +тотальная функция «База или» + принимает база: число, мс: число + возвращает число + пример «первая точка становится базой» + дано база равно 0 + дано мс равно 700 + ожидается 700 + пример «база не меняется» + дано база равно 500 + дано мс равно 700 + ожидается 500 + если база больше 0 + то база + иначе мс + +тотальная функция «Первые потоки» + принимает потоки: список строки + возвращает строка + пример «голова списка» + дано потоки равно ["8", "16"] + ожидается "8" + пример «пустой список — пустая строка» + дано потоки равно пустой список + ожидается "" + разбор потоки + случай пусто + то "" + случай голова и хвост + то голова + +тотальная функция «Хвост потоков» + принимает потоки: список строки + возвращает список строки + пример «без головы» + дано потоки равно ["8", "16"] + ожидается ["16"] + разбор потоки + случай пусто + то пустой список + случай голова и хвост + то хвост + +тотальная функция «Новый счёт» + принимает база: число + возвращает «Счёт» + обеспечивает «база названа» (результат.«база») равен база + пример «повторов три, лучшего пока нет» + дано база равно 700 + ожидается запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 700 + запись «Счёт» с «ещё» равным («Повторов») и «мин» равным -1 и «старт» равным 0 и «база» равным база + +тотальная функция «Замер с» + принимает замер: «Замер», ч: «Счёт», т: список строки, р: список строки + возвращает «Замер» + обеспечивает «счёт заменён» (результат.«счёт») равен ч + пример «счёт, потоки и строки заменены, стенд и пробеги те же» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + дано ч равно запись «Счёт» с «ещё» равным 2 и «мин» равным -1 и «старт» равным 0 и «база» равным 0 + дано т равно ["2"] + дано р равно ["x"] + ожидается запись «Замер» с «потоки» равным [ + "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + "x" + ] и «счёт» равным (запись «Счёт» с «ещё» равным 2 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + пусть ст равно (замер.«стенд») + пусть п равно (замер.«пробегов») + запись «Замер» с «потоки» равным т и «стенд» равным ст и «пробегов» равным п и «строки» равным р и «счёт» равным ч + +тотальная функция «Замер из доводов» + принимает ст: строка, п: строка, т: список строки + возвращает «Замер» + обеспечивает «потоки названы все» (длина (результат.«потоки»)) равен (длина т) + пример «каталог, пробеги и два числа потоков» + дано ст равно "/z/b" + дано п равно "100" + дано т равно ["1", "2"] + ожидается запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + пусть ч равно («Новый счёт» от 0) + пусть р равно (пустой список) + запись «Замер» с «потоки» равным т и «стенд» равным ст и «пробегов» равным п и «строки» равным р и «счёт» равным ч + +тотальная функция «С началом» + принимает замер: «Замер», мс: число + возвращает «Замер» + пример «отметка до прогона записана в счёт» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + дано мс равно 5000 + ожидается запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 5000 и «база» равным 0) + пусть ч равно (замер.«счёт») + пусть о равно (ч.«ещё») + пусть л равно (ч.«мин») + пусть «новый» равно (запись «Счёт» с «ещё» равным о и «мин» равным л и «старт» равным мс и «база» равным (ч.«база»)) + «Замер с» от замер и «новый» и (замер.«потоки») и (замер.«строки») + +тотальная функция «Ещё повтор» + принимает замер: «Замер», мс: число + возвращает «Замер» + пример «первый повтор становится лучшим, повторов остаётся два» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 5000 и «база» равным 0) + дано мс равно 700 + ожидается запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 2 и «мин» равным 700 и «старт» равным 0 и «база» равным 0) + пусть ч равно (замер.«счёт») + пусть о равно ((ч.«ещё») минус 1) + пусть л равно («Меньшее» от (ч.«мин») и мс) + пусть «новый» равно (запись «Счёт» с «ещё» равным о и «мин» равным л и «старт» равным 0 и «база» равным (ч.«база»)) + «Замер с» от замер и «новый» и (замер.«потоки») и (замер.«строки») + +тотальная функция «Следующие потоки» + принимает замер: «Замер» + возвращает «Замер» + пример «первая точка становится базой и строкой отчёта» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 0 и «мин» равным 700 и «старт» равным 0 и «база» равным 0) + ожидается запись «Замер» с «потоки» равным [ + "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + "1 700 142 1" + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 700) + пусть ч равно (замер.«счёт») + пусть т равно (замер.«потоки») + пусть «база» равно («База или» от (ч.«база») и (ч.«мин»)) + пусть «строка» равно («Строка замера» от («Первые потоки» от т) и (замер.«пробегов») и (ч.«мин») и «база») + «Замер с» от замер и («Новый счёт» от «база») и («Хвост потоков» от т) и (добавить «строка» к (замер.«строки»)) + +тотальная функция «Отчёт» + принимает стенд: строка, пробегов: строка, строки: список строки + возвращает строка + пример «заголовок, шапка, потом строки» + дано стенд равно "/z/b" + дано пробегов равно "100" + дано строки равно ["1 700 142 1"] + ожидается "/z/b пробегов 100, повторов 3\nпотоков мс пробегов/с раз\n1 700 142 1" + пусть «заголовок» равно (соединить [стенд, " пробегов ", пробегов, ", повторов ", (к строке («Повторов»))] по "") + пусть «верх» равно [«заголовок», («Шапка»)] + пусть «всё» равно (свёртка строки начиная с «верх» как собрано и строка → добавить строка к собрано) + соединить «всё» по "\n" + +тотальная функция «Пустить» + принимает замер: «Замер» + возвращает «Ход» + пример «потоков не осталось — отчёт» + дано замер равно запись «Замер» с «потоки» равным [ + ] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + "1 700 142 1" + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + ожидается вариант «Готово» с отчёт равным "/z/b пробегов 100, повторов 3\nпотоков мс пробегов/с раз\n1 700 142 1" + разбор (замер.«потоки») + случай пусто + то вариант «Готово» с отчёт равным («Отчёт» от (замер.«стенд») и (замер.«пробегов») и (замер.«строки»)) + случай голова и хвост + то вариант «Ждём начало» с замер равным замер + +тотальная функция «После конца» + принимает замер: «Замер», мс: число + возвращает «Ход» + пример «последний повтор закрывает число потоков и переходит к следующему» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 1 и «мин» равным 800 и «старт» равным 5000 и «база» равным 0) + дано мс равно 5700 + ожидается вариант «Ждём начало» с замер равным (запись «Замер» с «потоки» равным [ + "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + "1 700 142 1" + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 700)) + пусть «прошло» равно (мс минус (замер.«счёт».«старт»)) + пусть «дальше» равно («Ещё повтор» от замер и «прошло») + если («дальше».«счёт».«ещё») больше 0 + то вариант «Ждём начало» с замер равным «дальше» + иначе «Пустить» от («Следующие потоки» от «дальше») + +тотальная функция «Мало доводов» + возвращает «Ход» + пример «назван код отказа» + ожидается вариант «Не мерили» с код равным "FLANG_BENCH_ARGUMENTS" и сообщение равным "нужны стенд, пробеги, потоки" + вариант «Не мерили» с код равным "FLANG_BENCH_ARGUMENTS" и сообщение равным "нужны стенд, пробеги, потоки" + +тотальная функция «Начать замер» + принимает стенд: строка, пробегов: строка, потоки: список строки + возвращает «Ход» + пример «без потоков мерить нечего» + дано стенд равно "/z/b" + дано пробегов равно "100" + дано потоки равно пустой список + ожидается вариант «Не мерили» с код равным "FLANG_BENCH_ARGUMENTS" и сообщение равным "нужны стенд, пробеги, потоки" + разбор потоки + случай пусто + то «Мало доводов» + случай голова и хвост + то «Пустить» от («Замер из доводов» от стенд и пробегов и потоки) + +тотальная функция «Судить потоки» + принимает стенд: строка, числа: список строки, потоки: список строки + возвращает «Ход» + пример «нуль потоков отбит до запуска» + дано стенд равно "/z/b" + дано числа равно ["100", "0"] + дано потоки равно ["0"] + ожидается вариант «Отказ» с код равным "FLANG_BENCH_ARGUMENTS" и сообщение равным "названо негодное число: 0" + разбор («Негодные числа» от числа) + случай голова и хвост + то вариант «Отказ» с код равным "FLANG_BENCH_ARGUMENTS" и сообщение равным (соединить [ + "названо негодное число: ", голова] по "") + случай пусто + то «Начать замер» от стенд и («Первые потоки» от числа) и потоки + +тотальная функция «Судить числа» + принимает стенд: строка, числа: список строки + возвращает «Ход» + пример «без пробегов мерить нечего» + дано стенд равно "/z/b" + дано числа равно пустой список + ожидается вариант «Не мерили» с код равным "FLANG_BENCH_ARGUMENTS" и сообщение равным "нужны стенд, пробеги, потоки" + разбор числа + случай пусто + то «Мало доводов» + случай голова и хвост + то «Судить потоки» от стенд и числа и хвост + +тотальная функция «Судить доводы» + принимает доводы: список строки + возвращает «Ход» + пример «без доводов мерить нечего» + дано доводы равно пустой список + ожидается вариант «Не мерили» с код равным "FLANG_BENCH_ARGUMENTS" и сообщение равным "нужны стенд, пробеги, потоки" + разбор доводы + случай голова и хвост + то «Судить числа» от (голова) и (хвост) + случай пусто + то «Мало доводов» + +тотальная функция «После доводов» + принимает отклик: «Отклик» + возвращает «Ход» + пример «сбой хозяина доезжает своим кодом» + дано отклик равно вариант «Сбой» с код равным "FLANG_IO_DENIED" и сообщение равным "нет часов" + ожидается вариант «Не мерили» с код равным "FLANG_IO_DENIED" и сообщение равным "нет часов" + разбор отклик + случай вариант «Доводы» с доводы как доводы + то «Судить доводы» от доводы + случай вариант «Сбой» с код как код и сообщение как сообщение + то вариант «Не мерили» с код равным код и сообщение равным сообщение + случай любое + то вариант «Отказ» с код равным "FLANG_IO_ORDER" и сообщение равным "ждали доводы" + +тотальная функция «После часов до прогона» + принимает замер: «Замер», отклик: «Отклик» + возвращает «Ход» + пример «отметка записана, стенд пускается» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + дано отклик равно вариант «Отметка времени» с миллисекунды равным 5000 + ожидается вариант «Ждём прогон» с замер равным (запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 5000 и «база» равным 0)) + разбор отклик + случай вариант «Отметка времени» с миллисекунды как мс + то вариант «Ждём прогон» с замер равным («С началом» от замер и мс) + случай вариант «Сбой» с код как код и сообщение как сообщение + то вариант «Не мерили» с код равным код и сообщение равным сообщение + случай любое + то вариант «Отказ» с код равным "FLANG_IO_ORDER" и сообщение равным "ждали отметку времени" + +тотальная функция «Судить прогон» + принимает замер: «Замер», код: число, вывод: строка, ошибки: строка + возвращает «Ход» + пример «отказ стенда назван и замером не считается» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + дано код равно 1 + дано вывод равно "" + дано ошибки равно "нет памяти" + ожидается вариант «Отказ» с код равным "FLANG_BENCH_RUN" и сообщение равным "стенд отказал: 1 нет памяти" + пусть «слово» равно (соединить ["стенд отказал: ", (к строке код), " ", ошибки] по "") + если «Прогон годен» от код и вывод + то вариант «Ждём конец» с замер равным замер + иначе вариант «Отказ» с код равным "FLANG_BENCH_RUN" и сообщение равным «слово» + +тотальная функция «После прогона» + принимает замер: «Замер», отклик: «Отклик» + возвращает «Ход» + пример «вместо конца процесса пришло другое» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + дано отклик равно вариант «Показано» + ожидается вариант «Отказ» с код равным "FLANG_IO_ORDER" и сообщение равным "ждали конца стенда" + разбор отклик + случай вариант «Процесс завершён» с код как код и вывод как вывод и ошибки как ошибки + то «Судить прогон» от замер и код и вывод и ошибки + случай вариант «Сбой» с код как код и сообщение как сообщение + то вариант «Не мерили» с код равным код и сообщение равным сообщение + случай любое + то вариант «Отказ» с код равным "FLANG_IO_ORDER" и сообщение равным "ждали конца стенда" + +тотальная функция «После часов после прогона» + принимает замер: «Замер», отклик: «Отклик» + возвращает «Ход» + пример «без часов не мерим» + дано замер равно запись «Замер» с «потоки» равным [ + "1", "2"] и «стенд» равным "/z/b" и «пробегов» равным "100" и «строки» равным [ + ] и «счёт» равным (запись «Счёт» с «ещё» равным 3 и «мин» равным -1 и «старт» равным 0 и «база» равным 0) + дано отклик равно вариант «Сбой» с код равным "FLANG_IO_DENIED" и сообщение равным "нет часов" + ожидается вариант «Не мерили» с код равным "FLANG_IO_DENIED" и сообщение равным "нет часов" + разбор отклик + случай вариант «Отметка времени» с миллисекунды как мс + то «После конца» от замер и мс + случай вариант «Сбой» с код как код и сообщение как сообщение + то вариант «Не мерили» с код равным код и сообщение равным сообщение + случай любое + то вариант «Отказ» с код равным "FLANG_IO_ORDER" и сообщение равным "ждали отметку времени" + +тотальная функция «Шаг» + принимает ход: «Ход», отклик: «Отклик» + возвращает «Ход» + пример «после начала план ждёт доводы» + дано ход равно вариант «Просим доводы» + дано отклик равно вариант «Пока ничего» + ожидается вариант «Ждём доводы» + разбор ход + случай вариант «Просим доводы» + то вариант «Ждём доводы» + случай вариант «Ждём доводы» + то «После доводов» от отклик + случай вариант «Ждём начало» с замер как замер + то «После часов до прогона» от замер и отклик + случай вариант «Ждём прогон» с замер как замер + то «После прогона» от замер и отклик + случай вариант «Ждём конец» с замер как замер + то «После часов после прогона» от замер и отклик + случай любое + то ход + +тотальная функция «Продолжение хода» + принимает ход: «Ход» + возвращает «Продолжение» + пример «ждать доводы — значит попросить их» + дано ход равно вариант «Ждём доводы» + ожидается вариант «Сделать» с поручение равным (вариант «Прочитать доводы») и потом равным (вариант «Ждём доводы») + пример «готовый отчёт кончает работу» + дано ход равно вариант «Готово» с отчёт равным "отчёт" + ожидается вариант «Конец работы» с значение равным "отчёт" + разбор ход + случай вариант «Просим доводы» + то вариант «Сделать» с поручение равным (вариант «Прочитать доводы») и потом равным (вариант «Ждём доводы») + случай вариант «Ждём доводы» + то вариант «Сделать» с поручение равным (вариант «Прочитать доводы») и потом равным ход + случай вариант «Ждём начало» с замер как замер + то вариант «Сделать» с поручение равным (вариант «Текущее время») и потом равным ход + случай вариант «Ждём прогон» с замер как замер + то вариант «Сделать» с поручение равным («Поручение прогона» от замер) и потом равным ход + случай вариант «Ждём конец» с замер как замер + то вариант «Сделать» с поручение равным (вариант «Текущее время») и потом равным ход + случай вариант «Готово» с отчёт как отчёт + то вариант «Конец работы» с значение равным отчёт + случай вариант «Отказ» с код как код и сообщение как сообщение + то вариант «Провал» с код равным код и сообщение равным сообщение + случай вариант «Не мерили» с код как код и сообщение как сообщение + то вариант «Не проверено» с код равным код и сообщение равным сообщение + +тотальная функция «Дальше» + принимает ход: «Ход», отклик: «Отклик» + возвращает «Продолжение» + пример «первый шаг — просьба о доводах» + дано ход равно вариант «Просим доводы» + дано отклик равно вариант «Пока ничего» + ожидается вариант «Сделать» с поручение равным (вариант «Прочитать доводы») и потом равным (вариант «Ждём доводы») + «Продолжение хода» от («Шаг» от ход и отклик) + +план «Растёт ли с ядрами» + состояние «Ход» + начинает с «Начало» + обрабатывает «Дальше» diff --git a/flang/concurrency/bench/by-cores.sh b/flang/concurrency/bench/by-cores.sh deleted file mode 100644 index 89e907f96..000000000 --- a/flang/concurrency/bench/by-cores.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# Растёт ли пропускная способность с числом потоков. -# -# Тот же стенд, тот же предел пробегов, меняется одно — поле «workers» в запросе. -# Печатается: сколько потоков, настенное время, пробегов в секунду и во сколько -# раз быстрее одного потока. Настенное, а не процессорное: вопрос стенда — -# «быстрее ли стало», а процессорное время многопоточного прогона по -# определению больше, и сравнивать по нему значило бы мерить не то. -# -# Минимум из REP повторов: помеха может замер только удлинить. -# -# $1 каталог сборки, $2 предел пробегов, дальше — числа потоков. -# PROCS — предел числа процессов (нужен стендам с порождением). -set -u -source "$(dirname "${BASH_SOURCE[0]}")/common.sh" -DIR=$(stend "$1"); shift -TURNS=$1; shift -REP=${REP:-3} -PROCS=${PROCS:-0} -MORE="" -if [ "$PROCS" != 0 ]; then MORE=",\"processes\":\"$PROCS\""; fi - -echo "--- $DIR пробегов $TURNS, повторов $REP" -printf '%8s %10s %14s %10s %8s\n' потоков "настен,с" "пробегов/с" "раз" "цпу,%" -BASE="" -for W in "$@"; do - REQ="{\"run\":\"стенд\",\"seed\":\"1\",\"turns\":\"$TURNS\",\"journal\":\"0\",\"workers\":\"$W\"$MORE}" - BEST="" - CPU="" - ERR=$(mktemp) - for _ in $(seq 1 "$REP"); do - echo "$REQ" | /usr/bin/time -f '%e %P' "$DIR/flang_cli" > /dev/null 2>"$ERR" - code=$? - if [ $code -ne 0 ]; then echo "ОТКАЗ код=$code"; cat "$ERR"; rm -f "$ERR"; exit $code; fi - read -r w p < <(tail -1 "$ERR") - if [ -z "$BEST" ] || [ 1 -eq "$(awk -v x="$w" -v y="$BEST" 'BEGIN{print (x