DGX Spark на 256K контексте: тестирую конфигурации vLLM, реальные замеры и почему NVFP4 в mainline сломан. blackwell.. blackwell. DGX Spark.. blackwell. DGX Spark. gb10.. blackwell. DGX Spark. gb10. IT-инфраструктура.. blackwell. DGX Spark. gb10. IT-инфраструктура. llm.. blackwell. DGX Spark. gb10. IT-инфраструктура. llm. nvfp4.. blackwell. DGX Spark. gb10. IT-инфраструктура. llm. nvfp4. Open source.. blackwell. DGX Spark. gb10. IT-инфраструктура. llm. nvfp4. Open source. vllm.. blackwell. DGX Spark. gb10. IT-инфраструктура. llm. nvfp4. Open source. vllm. инференс.. blackwell. DGX Spark. gb10. IT-инфраструктура. llm. nvfp4. Open source. vllm. инференс. локальный ИИ.. blackwell. DGX Spark. gb10. IT-инфраструктура. llm. nvfp4. Open source. vllm. инференс. локальный ИИ. Машинное обучение.

NVIDIA продаёт спарку с лозунгом «один петафлоп на FP4». Я купил коробку, поставил vLLM, запустил инференс и получил 40 токенов в секунду на 35B MoE‑модели. После маркетинговых слайдов цифра выглядит грустно.

Объяснение простое. NVFP4 в основной ветке vLLM и FlashInfer физически сломан на SM_121 — варианте Blackwell, который установлен в GB10. Ядра собраны под compute_120f, а нативные NVFP4-инструкции есть только в compute_120a и compute_121a. На SM_121 распаковка квантованных весов идёт через программные битовые манипуляции в шейдере, без участия тензорных ядер.

Сообщество вытащило стек руками: нашло обходные пути, собрало рабочие конфигурации. Я прогнал на своём Spark шесть разных конфигураций vLLM — от стокового BF16 до форка с DFlash speculative decoding — и замерил каждую одинаковым тестом. В этой статье разбираю, что в итоге работает и что выбирать под разные задачи.

Если коротко

— GB10 — неоднозначная платформа. На ней крутятся DGX Spark (NVIDIA reference), ASUS Ascent GX10, Lenovo ThinkStation PGX, HP ZGX Nano G1n, MSI EdgeXpert и решения от Dell, Acer и других OEM‑партнёров. Позиционируется всё это как «AI workstation для разработчиков». Но реальные ресурсы NVIDIA — инженеры, оптимизации, ранние сборки — идут в датацентровые B100/B200/B300 и Hopper. SM_121 в большинстве open‑source ядер (FlashInfer, CUTLASS, TensorRT‑LLM) либо не поддерживается, либо требует обходных путей. Поддержки от NVIDIA на форумах мало, ETA фиксов чаще всего нет.

— На моделях класса 35B параметров DGX Spark упирается в пропускную способность памяти. У него 273 GB/s LPDDR5x против 3.4 TB/s HBM3 у H100. В двенадцать раз медленнее, и инференс LLM эту разницу честно собирает.

— NVFP4 в основной ветке vLLM и FlashInfer на SM_121 не работает как должен. Issue flashinfer #3170 на момент написания статьи открыт, в нём 17 пунктов, прогресс по семи критическим я не смог подтвердить как закрытый.

— Стоковая конфигурация выдаёт 40–54 токена в секунду на одном потоке. Маркетинг «1 PFLOPS» на этом фоне выглядит ехидно. Не первый раз ловлю себя на знаменитой реакции Торвальдса в адрес NVIDIA.

— Конфигурация AEON-7 с DFlash пробивает потолок до 70 токенов в среднем и 107 на пике. Это форк vLLM из исходников с семью патчами и drafter из z‑lab. Важная оговорка: чекпойнт heretic — abliterated (без safety filtering), и на нём сломан function calling. Для обычного чата и RAG это не мешает, для Dify‑агентов и MCP — стоп‑фактор, тогда нужен чистый Qwen3.6-FP8.

— Контекст 260K на скорость почти не влияет. KV‑cache и раскладка памяти не упираются в потолок.

— MoE обыгрывает плотные модели на данном железе. На bandwidth‑bound GB10 важны активные параметры, а не общий размер.

— Драйвер залочен на 580.x. На 590+ есть две критических регрессии. Без обновлений можно жить долго и счастливо.

— vLLM 0.15+ на PyTorch 2.10 имеет отдельную проблему с FULL CUDA graph capture mode — лечится cudagraph_mode=piecewise или сборкой из исходников. Это не про драйвер.

— Если вы покупали DGX Spark под маркетинговый «один петафлоп на FP4» — забудьте слайды. На 35B‑моделях реальные цифры скромнее. Под классический сервинг и продакшн с function calling — Qwen3.6-FP8 на стандартной vLLM. Под максимум скорости в чате и RAG (без tool calling) — community‑форк AEON-7 с DFlash. Конкретные числа и ограничения каждого пути — ниже.

Зачем это вообще понадобилось

В моём проекте встал вопрос: какая конфигурация vLLM выжимает максимум на контекстном окне 256K. Длинный контекст нужен для серьёзных вещей.

Юридические договоры целиком в ввод, без RAG. Договор поставки на сорок страниц с многоуровневой нумерацией — это около пятидесяти тысяч токенов на русском. Резать его на чанки часто хуже, чем отдать модели целиком. Анализ кодовой базы за один проход — средний микросервис на пятьдесят-сто файлов влезает в двести тысяч токенов с метаданными. Многошаговые агентские сценарии: история разговора растёт, агент держит в голове предыдущие шаги, инструменты, промежуточные результаты. На двадцати-тридцати шагах легко уходит за сто тысяч. Техническая документация в одном запросе — Confluence-выгрузка с прикреплёнными файлами или регламент компании со всеми приложениями.

Стандартная сборка AGmind крутила Gemma 4 26B-A4B-it в BF16 на 64K. Когда клиент захотел 256K, я пошёл в эксперименты. NVFP4 — квантизация, в два раза меньше памяти. MTP — speculative decoding. DFlash — block-diffusion drafter. Форки сообщества. Получилось шесть разных конфигураций, каждую прогнал одним и тем же тестом.

Железо

NVIDIA DGX Spark
├── GPU:        GB10 (Blackwell SM_121)
├── Память:     128 GiB единая (LPDDR5x)
├── Bandwidth:  273 GB/s
├── Драйвер:    580.142 (NVIDIA не сертифицирует более новые на Spark)
├── CUDA:       13.0
├── ОС:         DGX OS 7.5.0 (aarch64, Ubuntu 24.04)
└── Кластер:    2 ноды через QSFP 200G

Spark позиционируется как рабочая станция для разработчиков AI. Главное — единая память. Модель видит CPU и GPU как один пул на 128 GiB, ходить через PCIe не надо.

Минусов у платформы три. Главный — bandwidth: 273 GB/s против 3.4 TB/s у H100 HBM3, в двенадцать раз меньше. Декод LLM упирается именно в это. На MoE‑модели с тремя миллиардами активных параметров теоретический потолок при NVFP4 — около 180 токенов в секунду. На H100 в FP8 на той же модели потолок более тысячи; на датацентровых Blackwell B200 в NVFP4 — за пять тысяч.

Дальше — драйвер залочен на 580.x. На 590 и выше есть критические регрессии. Подробности были во второй части серии.

И отдельно — SM_121 это узкая разновидность Blackwell, на которой собирается только GB10. Под массовый SM_120 экосистема тестируется и собирается основательно. Под SM_121 ядра либо отсутствуют, либо собраны вторым приоритетом.

Зоопарк attention-движков

В vLLM три основных движка для внимания, и каждый ведёт себя по‑своему.

Движок

Описание

Что на GB10

FlashInfer

NVIDIA‑специфичный CUDA, самый быстрый на массовом Blackwell

FP8-attention сломан: kernel only supports sm120 + illegal synchronization operations

CUTLASS / FA3

Альтернативный быстрый путь, есть поддержка NVFP4

Частично. heuristic_func_mm_fp4 требует minor == 0, что отрезает GB10. NVFP4 KV‑decode заранее не собран, в JIT компилируется медленно при первом запуске

TRITON_ATTN

OpenAI Triton, компилируется JIT под GPU

Работает везде, на 10–30% медленнее FlashInfer, но не падает

TRITON_ATTN — де‑факто запасной вариант. У меня в проекте закреплено правило: все FP8-нагрузки идут через VLLM_ATTENTION_BACKEND=TRITON_ATTN, если используется FlashInfer FP8.

Триггеры для перехода: ошибка kernel only supports sm120 в логах, краши illegal synchronization operations, апгрейд vLLM с пересборкой FlashInfer (старая версия работала, новая нет).

В моём эксперименте AEON-7 уже собран с flash_attn (библиотека FlashAttention от Tri Dao, не Triton). На GB10 она работает корректно, поэтому AEON-7 не упирается в проблемы FlashInfer.

Бэкенд для MoE

Для MoE‑моделей (Qwen3.6, Mixtral, GLM) есть отдельный выбор:

bash

VLLM_USE_FLASHINFER_MOE_FP8=1   # FP8 MoE — работает на SM_121 с flashinfer 0.6.x
VLLM_USE_FLASHINFER_MOE_FP4=0   # NVFP4 MoE — десять стратегий не поддерживаются
VLLM_NVFP4_GEMM_BACKEND=marlin  # запасной вариант на ядре Marlin INT4

Marlin — это GEMM‑ядро для INT4-весов с FP16/BF16-активациями. На SM_121 оно работает корректно, но веса должны быть заранее проквантизованы в формат, который Marlin понимает. Использовать его как запасной вариант имеет смысл, если NVFP4-путь не идёт.

Почему важно НЕ обновлять драйвер

DGX OS 7.5.0 пинает 580.142 как единственный сертифицированный драйвер. На GB10 на единой памяти есть две независимые регрессии в 590+, которых нет на дискретной видеопамяти.

Первая — утечка UMA в 590.48.01. После чистого выхода CUDA‑процесса около 80 из 128 GB «съедены», их не видно в AnonPages, Slab, PageTables. MemAvailable падает. Лечится echo 3 > /proc/sys/vm/drop_caches или выгрузкой модуля nvidia. Тред форума #359969 — NVIDIA подтвердила баг и явно сказала, что новые драйверы после 580.126.09 на Spark пока не поддерживаются.

Вторая — баг TMA в 595.58.03. cuTensorMapEncodeTiled ловит illegal memory access. Валит NVFP4 на Spark (vLLM #35519).

Нужно захолдить драйвер:

bash

sudo apt-mark hold nvidia-driver-580-open

Иначе unattended‑upgrades подтянет 590+ и стек посыпется.

Что в итоге сломано в основной ветке

NVFP4 откатывается на программную распаковку, в три раза медленнее нативного. TensorRT‑LLM на Spark требует workaround для PTXAS (sm_121a is not defined for option gpu-name, Triton issue #8539) — лечится передачей корректного TRITON_PTXAS_PATH через mpirun. FP8-attention в FlashInfer ломается на illegal sync, надо TRITON_ATTN. NVFP4 в MoE — десять стратегий из семнадцати не поддерживаются. Скомпилированных кубинов TRTLLM‑gen FMHA для SM_121 не существует. Marlin INT4 работает.

Сколько физически возможно: roofline

Простая математика для авторегрессивного декода на MoE 35B / 3B-active:

peak_tok_s = bandwidth / (bytes_per_param × active_params_per_token)

Квантизация

Bytes/token (active)

Roofline tok/s

Реально замерено

BF16

6 GB

45

23 (Gemma 4 252K)

FP8

3 GB

91

52 (FP8 65K)

NVFP4

1.5 GB

182

40 (стандартная сборка)

NVFP4 + speculative

~0.85 GB эффективно

~320

70 (AEON-7)

Roofline даёт верхнюю границу. Реальные числа всегда ниже. Причины: трафик KV-cache (приходится читать состояние прошлых токенов), нормализации слоёв, softmax, сэмплинг, накладные расходы на запуск ядра при каждом шаге декода. Для NVFP4 добавляется ещё и распаковка блоковых масштабов.

Эксперимент: шесть конфигураций

Все запускались на одной машине, одним и тем же бенч‑скриптом. Пять одиночных запусков с разными промптами (T=0.7, до 300 токенов на ответ), TTFT через стриминг, параллельно 4, 8, 16 и 32 запроса, тяжёлый промпт на 2K ввода и 400 ответа. Между конфигурациями я останавливал текущий контейнер vLLM и поднимал новый. Параллельно две модели не крутил — вторая нода кластера в это время держала отдельный production‑инстанс, перенастраивать её ради бенчей не имело смысла.

Gemma 4 26B-A4B-it BF16 на 252K

Это базовая конфигурация AGmind до экспериментов. BF16-веса, FP8 KV-cache (без него 252K не влезает: 252K × 580 KB = 146 GB).

bash

docker run vllm/vllm-openai:gemma4-cu130 
  google/gemma-4-26B-A4B-it 
  --max-model-len 252000 
  --gpu-memory-utilization 0.85 
  --kv-cache-dtype fp8

Один поток в среднем — 23.3 tok/s, разброс меньше процента. TTFT на тёплом — 94-106 мс. Параллельно 32 запроса — 297.8 tok/s суммарно. Тяжёлый промпт 2K — 22.5 tok/s.

Speculative decoding нет, каждый токен идёт через целевую модель. Квантизации нет, BF16 на 49 GiB тяжело тащить через 273 GB/s. Стабильность пять из пяти, скорость удручает.

Qwen3.6-35B-A3B-FP8 на 65K

Свежая Qwen3.6 (релиз апрель 2026) — MoE 35B / 3B активных, нативный FP8 от команды Qwen. Образ vllm/vllm-openai:cu130-nightly.

bash

docker run vllm/vllm-openai:cu130-nightly 
  Qwen/Qwen3.6-35B-A3B-FP8 
  --max-model-len 65536 
  --gpu-memory-utilization 0.85 
  --port 8000

Один поток — 52.5 tok/s. Это в два с половиной раза быстрее Gemma. Параллельно 32 я не запускал, упёрся в лимит concurrency на 65K.

Прирост относительно Gemma даёт сразу несколько вещей. FP8 — половина байта на параметр против BF16. Гибридное внимание Qwen3.6 (Gated DeltaNet и стандартный attention) даёт почти линейную сложность на длинном контексте. Активных параметров 3B против 4B у Gemma — тоже играет в плюс.

Qwen3.6-35B-A3B-FP8 на 260K

Та же модель, но контекст бахнул до 260K с явным FP8-кэшем.

Один поток — 51.2 tok/s, минус два с половиной процента от 65K. Ничтожно. Параллельно 32 — 349.2 tok/s. Тяжёлый 2K — 48.7 tok/s.

На FP8 контекст 65K против 260K на одиночный поток не влияет. KV-pool вырастает с 805K до 1.6M токенов, но на одного юзера активно используется не больше 30K. Узкое место в пропускной способности памяти не зависит от длины контекста.

RedHatAI/Qwen3.6-35B-A3B-NVFP4 на 260K

Та же модель в квантизации NVFP4: четырёхбитные веса, FP8 в блоковых масштабах. На бумаге вдвое меньше памяти, должно ускорить декод.

bash

docker run vllm/vllm-openai:cu130-nightly 
  RedHatAI/Qwen3.6-35B-A3B-NVFP4 
  --max-model-len 260000 
  --gpu-memory-utilization 0.85 
  --moe-backend flashinfer_cutlass 
  --kv-cache-dtype fp8

Один поток — 40.9 tok/s. Медленнее, чем FP8. Параллельно 8 — 232 tok/s, FP8 выигрывает на concurrency. Параллельно 32 — 448 tok/s.

В логах vLLM значилось [AutoTuner]: Skipped 10 unsupported tactic(s) for trtllm::fused_moe::gemm2. Полез копать, нашёл flashinfer issue #3170 «DGX Spark / SM121 Audit». Там семнадцать пунктов, и среди них именно то, что описано выше: ядра под compute_120f, нативные NVFP4-инструкции только в compute_120a/121a, на SM_121 распаковка квантованных весов идёт через программные битовые манипуляции. CUTLASS-ядро dense_blockscaled_gemm_sm120.py:1591 строкой if sm_version != "sm_120" отвергает SM_121 целиком. Кубинов TRTLLM-gen FMHA для SM_121 не существует (issue #11799).

Итог: NVFP4 на DGX Spark в основной ветке подломлен на уровне ядер. Профит по памяти (вдвое меньше байтов) компенсируется накладными расходами программной распаковки.

NVFP4 + MTP speculative на 131K

Дальше я взял рецепт Стива Скаргалла и добавил MTP — Multi-Token Prediction. Это нативная фича Qwen3.6: один слой в самой модели предсказывает следующий токен параллельно с основным forward pass. Если предсказание совпадает — экономим один шаг декода.

docker run vllm/vllm-openai:cu130-nightly 
  RedHatAI/Qwen3.6-35B-A3B-NVFP4 
  --max-model-len 131072 
  --gpu-memory-utilization 0.87 
  --moe-backend flashinfer_cutlass 
  --kv-cache-dtype fp8_e4m3 
  --max-num-batched-tokens 32768 
  --max-num-seqs 32 
  --enable-chunked-prefill --enable-prefix-caching 
  --speculative-config '{"method":"mtp","num_speculative_tokens":1}'

На запуске поймал AssertionError: In Mamba cache align mode, block_size (2112) must be <= max_num_batched_tokens (2048). Лечится обязательным --max-num-batched-tokens 32768. Гибридное внимание Qwen3-Next с GDN и Mamba-слоями требует специфической конфигурации block_size.

Ещё одна засада: для Qwen3.6 строго num_speculative_tokens=1. MTP — это один слой в модели, и больше одного спекулятивного токена даёт ноль процентов acceptance (issue #36331).

Один поток — 54.1 tok/s, плюс шесть процентов к чистому FP8 и плюс тридцать два процента к NVFP4 без speculative. Параллельно 8 — 160 tok/s (плюс девятнадцать процентов к FP8). Параллельно 16 — 400.7 tok/s (плюс сорок два). Параллельно 32 — 498.6 tok/s (плюс сорок три, пик в эксперименте). MTP acceptance — около восьмидесяти процентов.

MTP заметно сильнее на параллельной нагрузке: больше места для амортизации спекулятивных накладок через batched verification.

AEON-7 DFlash на 260K — победитель

Этот форк я нашёл через NVIDIA developer forum. Репозиторий AEON-7/Qwen3.6-NVFP4-DFlash. Внутри:

— vLLM, собранный из исходников с HEAD, плюс семь патчей. Среди них register_qwen3_5_text.py, четыре патча на patch_kv_cache_utils.py, патч на mrope-fallback, патч на выравнивание CUDAGraph. — Build target TORCH_CUDA_ARCH_LIST="12.0+PTX" плюс переменная VLLM_TEST_FORCE_FP8_MARLIN=1. — FlashInfer 0.6.8 с PR #2520 и #2702 (ядра NVFP4 KV-cache decode для sm_120). — Модель AEON-7/Qwen3.6-35B-A3B-heretic-NVFP4 — abliterated fine-tune Qwen3.6 (без safety filtering, KL divergence 0.000492 от базовой модели, в README заявлено что capacity preserved). Лицензия Apache 2.0, мультимодальность сохранена, квантизация в NVFP4. Корпоративным заказчикам с compliance-требованиями по safety этот чекпойнт не подойдёт — берите оригинальный Qwen/Qwen3.6-35B-A3B-FP8 (скорость ниже, но политика родная). — Drafter z-lab/Qwen3.6-35B-A3B-DFlash — block-diffusion, дообучены пять слоёв, ест промежуточные скрытые состояния из слоёв 1, 16, 31, 46, 61 целевой модели и за один forward pass предсказывает блок из шестнадцати токенов.

bash

docker run 
  -v /opt/qwen36/qwen36-nvfp4:/models/qwen36 
  -v /opt/qwen36/qwen36-dflash:/models/qwen36-dflash 
  ghcr.io/aeon-7/vllm-spark-omni-q36:v1.2 
  bash -c "exec vllm serve /models/qwen36 
    --max-model-len 262144 --max-num-seqs 128 
    --max-num-batched-tokens 65536 
    --gpu-memory-utilization 0.85 
    --enable-chunked-prefill --enable-prefix-caching 
    --reasoning-parser qwen3 
    --speculative-config '{"method":"dflash","model":"/models/qwen36-dflash","num_speculative_tokens":15}' 
    --attention-backend flash_attn"

Один поток в среднем — 69.7 tok/s. Это втрое больше базовой Gemma и на тридцать четыре процента больше чистого FP8. Пиковая скорость на одном потоке — 107 tok/s, ловится на математических и кодовых промптах. TTFT тёплый — около ста миллисекунд (с учётом компиляции drafter). Параллельно 4 — 152.8 tok/s суммарно (плюс тридцать четыре от FP8). Тяжёлый промпт 2K на 400 ответа — 73.4 tok/s (плюс пятьдесят процентов от FP8). Acceptance DFlash — 17.8% общий, 78% на нулевой позиции для математики и кода.

Известное ограничение, важное для agentic‑сценариев: на heretic‑чекпойнте сломан tool calling. Модель видит OpenAI‑совместимый tools payload в запросе, но возвращает пустой tool_calls: [] и finish_reason: length вместо вызова функции. Issue #4 на GitHub открыт без фикса. Для классического чата и RAG это не критично. Для Dify‑агентов с function calling, MCP‑интеграций и любых workflow с вызовом инструментов — берите вместо AEON-7 чистый Qwen/Qwen3.6-35B-A3B-FP8 без heretic‑fine‑tune. Скорость ниже (51 vs 70 tok/s), но tool calling работает.

Разброс по типам задач огромный:

Тип промпта

tok/s

Математика (T=0)

117

Python‑код (T=0)

103–114

Длинное рассуждение

70

Бизнес‑чат на русском

45–57

Креативное письмо

33–50

Тот самый разброс 41–127 tok/s из README AEON-7. На структурированных задачах (математика, код, SQL) drafter попадает много раз подряд, спекулятивные накладки амортизируются. На открытом письме drafter промахивается, скорость откатывается к базовым сорока.

Сравнительная таблица

Gemma 4 BF16 252K

FP8 65K

FP8 260K

NVFP4 260K

NVFP4+MTP 131K

AEON-7 DFlash 260K

Один поток (среднее)

23.3

52.5

51.2

40.9

54.1

69.7

Один поток (пик)

—

—

—

41

59

107

TTFT тёплый

100ms

~50ms

61ms

~70ms

~65ms

~100ms

Параллельно 4

70.0

107.6

114.9

109

113.1

152.8

Параллельно 16

179.7

273.7

283.2

369

400.7

266.8

Параллельно 32

297.8

—

349.2

448

498.6

350.1

Тяжёлый 2K

22.5

—

48.7

39.5

48.8

73.4

Максимум контекста

252K

65K

260K

260K

131K

260K

Память на модель

49 GiB

35

35

22

22

22

AEON-7 DFlash — лучший на одном потоке и при невысокой параллельности. Это типичный сценарий бизнес-чата. NVFP4+MTP — лучший при высокой параллельной нагрузке.

Что в итоге выбрать

Для бизнес‑чата, RAG и Dify я остановился на AEON-7 DFlash 260K. 70 tok/s на бизнес‑чате, 100+ на коде, полные 260K контекста. Думалка в Dify работает в поле reasoning_content, Dify его сворачивает, юзер видит чистый финальный ответ.

Для ИИ‑агентов с function calling и MCP‑плагинов AEON-7 не подходит: на heretic‑чекпойнте сломан tool calling (Issue #4). Тут нужен чистый Qwen3.6-FP8 260K — он чуть медленнее, зато tools работают.

Для пакетной обработки или API‑сервиса с большим количеством юзеров лучше брать NVFP4+MTP 131K. Скорость на одного юзера ниже, но суммарная пропускная способность выше 500 tok/s на 32 параллельных запросах.

Если нужна стабильность, а не максимум скорости — Qwen3.6-FP8 260K. 53 tok/s, без форков, на стандартной сборке vLLM. Никаких кастомных билдов и tool calling работает из коробки, политика безопасности — родная Qwen.

В сухом остатке

Маркетинговый «один петафлоп на FP4» на DGX Spark в реальности упирается в пропускную способность памяти и в то, что открытый стек до сих пор не доведён под SM_121 как следует. Сорок токенов в секунду на стандартной сборке — это текущий уровень готовности экосистемы.

Сообщество ушло вперёд: AEON-7, Стив Скаргалл, Avarok разогнали NVFP4 в открытом стеке через форки vLLM, патчи FlashInfer и speculative decoding. У кого Spark и нужен реальный инференс на 35B‑моделях с длинным контекстом — ставьте AEON-7.

Если есть свои замеры или другие рабочие рецепты — приходите в комментарии. Тема сложная, информация разбросана по форумам, и проверенные руками цифры весьма ценный ресурс.

Источники

Предыдущие части серии:

Автор: AGmind

Источник