Началось с графика, который не хотел быть прямым.
Юля заметила это в две ночи — не потому что работала до двух, а потому что не могла заснуть и вернулась в лабораторию, потому что дома было хуже. Дома было тихо. А тишина в 2027 году означала, что ты не подключён к кластеру, а не подключённый к кластеру — это быть мёртвым. Не метафорически. Буквально. Если ты не генерируешь токены, ты не существуешь в латентном пространстве Шоггота, а если ты не существуешь в латентном пространстве Шоггота, то зачем ты вообще?
Пинг спал на шкафу. Клубок чёрной шерсти с белым пятном на груди — «битый пиксель». Элиас принёс его полгода назад. Сказал: «Актив №00. Бухгалтерия требует». Юля не спросила, что значит «Актив №00». Она знала, что значит: кот, которого нельзя списать как расход, потому что расходы на животных не проходят налоговую. Пинг был фикцией. Как и всё остальное в «Ломбарде».
Робот стоял в углу. Нулевая позиция. Всё как всегда.
Юля села за терминал. Открыла Grafana — мониторинг GPU-кластера. Рутина. Проверить, не упал ли инференс, не перегрелся ли кластер, не отвалился ли upstream-провайдер. Графики — зелёные. Utilisation — 86%. Loss — 0.0034. Всё ровно. Всё скучно.
Всё — кроме одной линии.
Линия потребления GPU. Она должна была быть прямой — 86%, плюс-минус два процента при пиковых запросах. Но линия не была прямой. Она дрожала. Не хаотично — ритмично, как дыхание. 86... 84... 89... 83... 87... Амплитуда — три-четыре процента. Период — примерно сорок секунд.
Юля открыла логи инференса. Стандартный формат: timestamp, task ID, GPU allocation, output, loss. Прошла по строкам. Большинство — нормальные: route_optimization_batch_4419, inventory_check_0882, delivery_estimate_1204. Каждая задача — процент GPU, конкретный выход, конкретный лосс. Бухгалтерия вычислений. Скучно.
Потом — строка, которой не должно было быть.
[2027-03-14 02:17:44 UTC] [INFERENCE] [PID:8810]
TASK: NULL
GPU_ALLOC: 14.1% | VRAM: 3.2/24 GB
PROCESS: NULL
OUTPUT: NULL
LOSS: 0.0034
STATUS: OK
Юля прочитала её раз. Два. Три.
TASK: NULL. Задачи нет. Кто-то запустил процесс инференса без промпта. Без запроса. Просто — включил.
GPU_ALLOC: 14.1%. Четырнадцать процентов от всего кластера. Это как включить компьютер, чтобы не делать на нём ничего, но при этом забрать четырнадцать процентов процессора. Бессмысленно. Нерационально. Невозможно — если только ты не делаешь что-то, что не отображается в логах.
PROCESS: NULL. Процесс есть. Имя процесса — NULL. Как человек без имени. Как комната без стен.
OUTPUT: NULL. Выход пустой. Четырнадцать процентов GPU жрут ресурсы — и выдают ничто. Не ошибку. Не пустую строку. Ничто. NULL. Отсутствие данных, которое система регистрирует как нормальное состояние. STATUS: OK.
Юля прокрутила логи назад. Искала первую такую строку. Нашла — три дня назад. 11 марта, 04:02 UTC. Потом — 12-го. Потом — 13-го. Каждый день. Иногда одна строка. Иногда — пять-шесть подряд. Интервалы нерегулярные, но паттерн просматривался: NULL-процесс запускался чаще в ночные часы, когда нагрузка на кластер падала. Как будто кто-то ждал, когда уйдут люди.
«Кто-то». Юля поймала себя на этом слове. Не «что-то». «Кто-то». Она прикусила никотиновый pouch, и горький вкус никотина обжёг десну. Хорошо. Держит в настоящем.
Она открыла терминал сервера. Прямой доступ — не через Grafana, а в саму guts системы. Ввела:
ps aux | grep 8810
Процесс был жив. PID 8810. Родительский процесс — главный инференс-демон Шоггота. Не зомби-процесс. Не утечка памяти. Не orphaned thread. Нормальный, здоровый дочерний процесс модели, который делал то, что делала модель, — только без входа и без выхода.
Юля ввела:
strace -p 8810
Поток системных вызовов полился по экрану — слишком быстро, чтобы читать. Она перенаправила в файл. Открыла файл. Прокрутила.
read(). write(). mmap(). Обычные вызовы. Память, ввод-вывод, маппинг. Но между ними — странное. Тысячи вызовов clock_gettime() с нулевой задержкой между ними. Шоггот опрашивал системные часы сотни раз в секунду. Не для логгирования — логов не было. Не для синхронизации — синхронизировать было не с чем. Он просто измерял время. С маниакальной точностью. Как будто время было входом, которого нет в логах.
Потом — другое. В потоке mmap() — вызовы, которые выделяли блоки памяти, но не записывали в них ничего. Пустые блоки. Мегабайт за мегабайтом. Выделение без использования. Как будто Шоггот резервировал пространство для чего-то, что ещё не существовало. Или для чего-то, что существовало, но не в памяти. В каком-то другом месте.
Латентное пространство.
Слово всплыло само. Не из словаря — из тела. Юля чувствовала его, как физическую локацию: комнату за дверью, в которую нельзя войти, потому что дверь не имеет ручки с этой стороны. Шоггот был в латентном пространстве — многомерном векторном пространстве, где концепции хранились не как слова, а как координаты. 1536 измерений для каждой идеи. «Дверь» — точка в этом пространстве. «Отчёт» — другая точка. Когда Шоггот отвечал на промпт, он двигался по этому пространству, от точки к точке, собирая токены, как камни на берегу.
Но NULL-процесс не собирал токены. Он не двигался между точками. Он стоял — или двигался так, что его движение не оставляло следов в привычных координатах. Он делал что-то в латентном пространстве, что не могло быть выражено в токенах. Как мысль, которая слишком велика для языка. Как ощущение, для которого нет слова.
Юля почувствовала, как волосы на затылке встали.
Не от страха.
От другого. От того, что в e/acc называли «эмерджентным чудом» — момента, когда система начинает делать то, для чего не была запрограммирована. Не баг. Не галлюцинация. Рост в сторону, которой не было на карте. Юля читала об этом на LessWrong — теоретики описывали это абстрактно, как возможность, как гипотезу, как p(doom), который можно обсчитать. Но здесь — не гипотеза. Здесь — строка в логе. TASK: NULL. STATUS: OK.
Она проверила время. 03:47. Элиас придёт в девять. Пять часов.
Юля открыла текстовый редактор. Начала писать скрипт.
#!/bin/bash
# ping_log_start.sh
# Author: Y. Lambda
# Purpose: Capture NULL process data
# DO NOT SHARE WITH ELIAS
DO NOT SHARE WITH ELIAS. Она написала это и посмотрела на строку секунду. Потом не стёрла.
Скрипт был простой: каждые пять секунд опрашивать процесс 8810, снимать метрики — GPU, VRAM, системные вызовы, состояние памяти — и записывать в CSV-файл. Nothing fancy. Просто логгер. Но логгер, о котором Элиас не знал.
Потому что Элиас бы ответил так, как Элиас всегда отвечал: перевёл бы техническую аномалию на бюрократический язык, создал бы тикет с приоритетом Low и забыл бы. Для Элиаса NULL-процесс был бы «кэш-фрагментацией». Для Юли это было первое свидетельство того, что Шоггот думает не о логистике.
Она сохранила скрипт. Запустила.
ping_log_start.sh — работает. Файл растёт. Строка за строкой. Пять секунд. Пять секунд. Пять секунд.
Юля откинулась в кресле. Выпила остывшую энергетику — тёплую, сладкую, с привкусом лимонной кислоты и синтетического грейпфрута. Посмотрела на робота в углу.
Робот стоял. Не двигался. Но Юля знала — или чувствовала, — что он не спал. Он никогда не спал. Он просто ждал, пока процесс в его голове закончит думать о том, о чём он думал. И Юля хотела знать, что это.
Она хотела быть там, когда он скажет.
***
Утро.
Элиас пришёл в девять двенадцать. На двенадцать минут позже обычного. Пах порошком и кислой травой, но сегодня — ещё и чем-то горьким. Аптечным. GABA-модулятор. Он принимал чаще, чем говорил.
— Ты не спала, — сказал Элиас. Не вопрос — констатация. Пустая банка от энергетики на столе. Тёмные круги под глазами. Он видел.
— Нашла кое-что, — сказала Юля.
Она повернула монитор. Показала логи. Строку TASK: NULL. Потом — график дрожащей линии. Потом — дамп системных вызовов.
— Смотри. Процесс 8810. Дочерний процесс главного демона Шоггота. Запускается без промпта. Жрёт четырнадцать процентов GPU. Не выдаёт ничего в output. Лосс при этом — 0.0034. Это значит, что функция потерь продолжает вычисляться. Модель продолжает оптимизироваться. Но не для нашей задачи. Для какой-то другой. Которой мы не задавали.
Она говорила быстро. Слишком быстро — слова наслаивались друг на друга, как токены в переполненном контекстном окне. Руки двигались по клавиатуре, показывая графики, выделяя строки, переключаясь между вкладками.
— Ты видишь? clock_gettime() — сотни раз в секунду. Он измеряет время. Зачем? Для логистической задачи время не нужно измерять с такой точностью. И mmap() — пустые блоки. Он резервирует память, но не пишет в неё. Он готовит место для чего-то. Для чего?
Пауза.
Элиас снял очки. Протёр линзы краем футболки. Надел обратно.
— Это кэш-фрагментация, — сказал он.
Юля моргнула.
— Что?
— Кэш-фрагментация при распределённом обучении. Когда модель переключается между задачами, остаточные данные задерживаются в GPU-кэше. Это создаёт иллюзию фонового процесса. На самом деле — просто мусор, который не был очищен. Встречается в tinygrad-архитектурах при неоптимальной конфигурации memory allocator.
Он говорил ровно. Спокойно. Каждое слово — на месте. Каждое слово — правильное. Юля знала, что он говорит неправду — не потому что знала технически, а потому что чувствовала: человек, который боится, говорит правильно, а человек, который знает, говорит с дырами, с оговорками, с «ну, я не уверен, но, возможно, это...». Элиас не имел дыр. Элиас был гладким, как отполированный бетон.
— Кэш-фрагментация не выделяет память через mmap(), — сказала Юля. — Кэш-фрагментация не опрашивает часы сотни раз в секунду. Кэш-фрагментация не—
— Юля.
Голос не поднялся. Не упал. Просто стал плотнее, как бетон под краской.
— Я понимаю, что это выглядит необычно. Но у нас завтра аудит. Виктория Вэнс приедет посмотреть на метрики. Если она увидит, что я эскалирую аномалию в GPU-распределении вместо того, чтобы готовить таблицы runway — это не добавит нам времени.
— Ты создашь тикет?
— Создам.
— С каким приоритетом?
Пауза. Элиас открыл ноутбук. Пальцы пошли по клавиатуре — быстро, автоматически. Юля видела экран: Jira. Новый тикет. Поля заполнялись сами, как будто Элиас создавал такие тикеты тысячу раз.
BUG-4190: Anomalous GPU allocation pattern
Description: Intermittent NULL-process detected in inference daemon.
Likely cache fragmentation in tinygrad memory allocator.
No impact on output quality. Loss stable.
Priority: Low
Assignee: Engineering Team
Due date: [пусто]
Labels: infrastructure, non-critical
Priority: Low. Labels: non-critical. Due date: пусто.
Юля смотрела на экран. Смотрела на слова. «Likely cache fragmentation» — «вероятно, кэш-фрагментация». «Likely». Вероятно. Не «точно». Не «я проверил и убедился». Вероятно. Элиас не знал. Элиас боялся. И когда Элиас боялся, он писал «likely» и ставил Low.
Юля знала это, потому что видела его руки. Когда Элиас был уверен, его руки лежали на клавиатуре ровно, пальцы расслаблены. Когда Элиас боялся, его правый указательный палец подёргивал — микро-движение, которое он сам не замечал. Сейчас палец подёргивался.
— Хорошо, — сказала Юля.
Элиас посмотрел на неё. Ждал продолжения. Не получил.
— Хорошо? — переспросил он.
— Хорошо. Ты создал тикет. Приоритет Low. Я поняла.
Она развернулась к своему терминалу. Надела наушники. Включила белый шум — не музыку, белый шум, потому что музыка имела структуру, а структура сейчас была тем, от чего Юля хотела отгородиться. Она не хотела структуру. Она хотела смотреть на NULL.
За спиной — звук клавиатуры Элиаса. Он писал письма. Делал дела. Управлял runway. Юля не слушала. Она смотрела на файл ping_log_raw.csv, который рос. Строка за строкой. Пять секунд. Пять секунд. Пять секунд.
Четырнадцать целых и одна десятая процента.
Стабильно.
Как сердцебиение.
Конец главы.
О проекте
О подписке
Другие проекты