Когда 1С считает часами: выносим авансы и себестоимость на видеокарту
Часть 1. Боль: почему 1С тормозит на больших объёмах
Симптомы, которые знает каждый
«1С опять зависла» — эта фраза звучит в компаниях каждый день. Бухгалтерия не может закрыть период, менеджеры ждут ОСВ по контрагенту, склад не отгружает товар, потому что система встала на расчёте себестоимости.
| Ситуация | Как это выглядит на практике |
|---|---|
| Проведение документов | Закрытие месяца или проведение партии накладных — часы ожидания |
| Формирование отчётов | ОСВ по 62-му счёту за квартал строится 15–20 минут |
| Расчёт авансов | Миллион записей в регистре — цикл идёт последовательно, строка за строкой |
| Работа в сети | При 5–10 пользователях всё встаёт: конфликты блокировок, таймауты |
Что теряет бизнес
Прямые убытки. 10 бухгалтеров тратят по 30 минут в день на ожидание — в месяц это больше 100 рабочих часов квалифицированных специалистов. Задержки отгрузки из-за тормозов в 1С — клиенты звонят, нервничают, уходят.
Ложное ощущение, что «нужно железо помощнее». Покупают NVMe-диски, добавляют RAM — получают плюс 20–30% и разочарование. Потому что проблема не в железе.
В чём настоящая причина
В 1С есть класс задач, которые работают медленно не из-за слабого сервера, а из-за самой природы алгоритма — последовательного перебора данных:
- Авансы покупателей — миллион записей = миллион итераций с поддержанием текущего остатка по каждому контрагенту
- FIFO себестоимость — списание партий с хранением истории всегда было узким местом при закрытии месяца
- Остатки на каждый день — построение кросс-таблицы за полгода по номенклатуре требует перебора десятков миллионов строк
- Скользящие средние — пересчёт метрик с накоплением грузит сервер квадратично
| Что обычно делают | Почему это не решает проблему |
|---|---|
| Покупают SSD, добавляют ОЗУ | Даёт +20–30%, но проблема не в скорости диска, а в алгоритме |
| Оптимизируют SQL-запросы в 1С | Запросы уже хороши — циклическую логику запросом не заменить |
| Делают свёртку базы | Временная мера. Через год данные снова вырастают |
| Переходят на мощный сервер | 1С использует 1–4 потока на задачу — 32 ядра не помогут |
Ключевая мысль: проблема не в «слабом сервере». Проблема в том, что последовательный перебор миллионов строк — это принципиальное ограничение архитектуры CPU. Именно здесь в игру вступает видеокарта.
Часть 2. Решение: GPU Compute Engine — выносим математику на видеокарту
Почему GPU, а не ещё один поток CPU
Чтобы понять, почему GPU решает задачу принципиально иначе, нужно понять разницу в архитектуре.
Процессор (CPU) — это несколько десятков мощных универсальных ядер, каждое из которых умеет делать всё: сложную логику, ветвление, работу со сложными структурами данных. Именно поэтому CPU хорошо справляется с разнообразными задачами — но плохо масштабируется на однотипные массовые операции. На одну задачу вычисления он выделяет 1–4 потока, остальные ядра заняты другим.
Видеокарта (GPU) — принципиально другая архитектура. Тысячи небольших специализированных ядер, каждое из которых умеет делать простые арифметические операции — зато делает их одновременно, в тысячах параллельных потоков. Именно для этого GPU и создавался: рендеринг — это по сути миллионы одновременных вычислений цвета пикселей. Та же механика работает и для бизнес-расчётов.
| Параметр | CPU (сервер 1С) | GPU (вычислительный шейдер) |
|---|---|---|
| Количество ядер | 8–64 мощных ядра | 1 000–16 000+ лёгких ядер |
| Тип обработки | Последовательная | Параллельная |
| Потоков на задачу | 1–4 | Тысячи одновременно |
| Эффективность с ростом данных | Снижается линейно | Почти не меняется |
| Пример: 100 000 строк, расчёт авансов | ~сотни мс | В 600–700 раз быстрее |
💡 Аналогия: CPU — это 16 опытных бухгалтеров, каждый считает по одной строке. GPU — это 4 000 стажёров, каждый считает свою строку одновременно. На простых повторяющихся операциях стажёры выигрывают в сотни раз. А с ростом данных — ещё больше: 4 000 стажёров одинаково быстро обработают и 100 тысяч строк, и 10 миллионов.
На практике: при расчёте авансов по 100 000 записям GPU обрабатывает данные в 600–700 раз быстрее, чем классический цикл в 1С. И это при том, что видеокарта даже не почувствовала нагрузки — 1 МБ загруженных данных для неё практически незаметен.
Что такое GPU Compute Engine
GPU Compute Engine for 1C — это локальный вычислительный движок, который перехватывает «тяжёлую» математику из 1С и выполняет её на видеокарте через технологию вычислительных шейдеров (Vulkan Compute Shaders). Всё работает на машине клиента. - Но так-же можно перенести процесс расчета на сервер или на облако.
Движок состоит из нескольких файлов:
compute_engine.exe— основной исполняемый файл движкаbase.db— SQLite-база данных, промежуточное хранилищеconfig.json— настройки: какие таблицы брать, как сортировать, как группироватьshader.comp— вычислительный шейдер, сам алгоритм расчётаsolve.py— скрипт-оркестратор, который запускает всю цепочку
Ключевые принципы движка
🔒 Локально и безопасно
Движок работает исключительно на стороне клиента. Данные не покидают инфраструктуру организации — никаких внешних API, никаких облачных очередей, никаких запросов во внешние сервисы. Только локальная видеокарта и локальная база данных. Для организаций с требованиями к безопасности данных это принципиально важно.
💡 Работает через вычислительные шейдеры (Vulkan Compute)
Шейдер — это программа, которая выполняется непосредственно на GPU. Большинство разработчиков знают графические шейдеры (они рисуют пиксели), но вычислительные шейдеры созданы специально для параллельных математических операций: суммирование, сравнение, накопление остатков — именно то, что нужно для 1С-расчётов.
Технология Vulkan Compute поддерживается на всех современных видеокартах: NVIDIA, AMD, Intel Arc — любая видеокарта последних 5–7 лет подойдёт.
🔧 Универсальный и программируемый
Движок не заточен под одну задачу — он программируется под конкретную бизнес-логику. Сегодня это расчёт авансов, завтра — FIFO себестоимость или скользящие средние. Алгоритм описывается в шейдере, настройки — в JSON-конфиге. Под каждую задачу — свой шейдер, один движок.
🔀 Гибридный режим: CPU + GPU + CPU
Далеко не вся бизнес-логика хорошо ложится на GPU. Сложные условия, обращения к справочникам, иерархия групп — всё это удобнее делать на CPU. Движок поддерживает комбинированные пайплайны, где каждый компонент делает то, в чём силён:
| Этап | Где выполняется | Что происходит |
|---|---|---|
| 1. Подготовка | CPU / 1С | Чтение данных, обращение к справочникам, сложная логика, сортировка, группировка |
| 2. Расчёт | GPU | Массовый параллельный расчёт по плоской таблице — тысячи групп одновременно |
| 3. Запись результата | CPU / 1С | Разбор результатов, запись в регистры накопления, дополнительная логика |
Такой подход снимает главное ограничение: сложную бизнес-логику не нужно «втискивать» в шейдер. CPU занимается тем, что умеет лучше всего — сложной логикой. GPU занимается тем, что умеет он — массовыми параллельными вычислениями.
💻 Работает с базой данных, не с 1С напрямую
Это важный архитектурный принцип. Движок не подключается к платформе 1С, не знает про конфигурации, планы счетов и метаданные. Он работает только с данными — чистыми таблицами в реляционной базе данных. 1С выгружает нужные данные в БД, движок их обрабатывает, результат записывается обратно.
| База данных | Поддержка | Назначение |
|---|---|---|
| SQLite | Да (встроено) | Для быстрого старта и небольших объёмов |
| PostgreSQL | Да | Основной вариант для production — 1С на PostgreSQL уже работает |
| MySQL | Да | Для смешанных инфраструктур |
Если у вас 1С на PostgreSQL — дополнительная база и вовсе не нужна: движок читает данные напрямую из рабочей БД 1С (по согласованным таблицам). Это устраняет этап промежуточной выгрузки и упрощает интеграцию.
Отсутствие прямой связи с платформой 1С — это и плюс по безопасности, и независимость от версии конфигурации: движок не сломается при обновлении 1С.
🚫 Никаких лицензий и ограничений
Нет подписки. Нет ключей защиты. Нет ограничений по числу пользователей, объёму данных или количеству задач. Система настраивается так, как нужно заказчику — без оглядки на лицензионную политику вендора. Один раз внедрили, разобрались — и развиваете сами.
Часть 3. Живой пример: расчёт авансов покупателей
Постановка задачи
Рассмотрим конкретную задачу — расчёт авансов покупателей. Задача типовая, есть в любой компании, где ведутся взаиморасчёты с контрагентами.
В регистре накопления «Расчёты с контрагентом» хранятся два типа операций:
- Оплата (payment) — покупатель перечислил деньги
- Отгрузка (sale) — покупатель получил товар
Аванс — это деньги, которые покупатель уже заплатил, но под которые товар ещё не отгружен. То есть компания «должна» покупателю товар.
Логика расчёта классическая:
- Отгрузка → увеличивает долг покупателя (он должен заплатить)
- Оплата → сначала погашает долг. Если оплата больше долга — разница становится авансом
- Если долга нет — вся оплата сразу идёт в аванс
На входе — все операции вперемешку: и оплаты, и отгрузки, отсортированные по контрагенту и дате. На выходе — для каждой операции проставляется сумма аванса.
Пример расчёта
| № | Дата | Тип | Сумма | Долг до | Долг после | Аванс | Пояснение |
|---|---|---|---|---|---|---|---|
| 1 | 01.01 | Оплата | 100 | 0 | 0 | 100 | Долга нет → вся сумма аванс |
| 2 | 02.01 | Оплата | 200 | 0 | 0 | 200 | Долга нет → вся сумма аванс |
| 3 | 03.01 | Отгрузка | 300 | 0 | 300 | 0 | Отгрузка → долг вырос |
| 4 | 04.01 | Оплата | 400 | 300 | 0 | 100 | 300 погасили долг, остаток 100 → аванс |
| 5 | 05.01 | Отгрузка | 800 | 0 | 800 | 0 | Отгрузка → долг вырос |
| 6 | 06.01 | Оплата | 400 | 800 | 400 | 0 | Частично погасили долг, аванса нет |
Как это работает в 1С
Классический подход в 1С — запрос к регистру, сортировка по контрагенту и дате, затем цикл по строкам с поддержанием текущего остатка. Главная проблема — строго последовательная обработка: следующую строку нельзя считать, не зная результата предыдущей.
Вот как выглядит код записи результатов обратно в регистры 1С после расчёта:
&НаСервере
Процедура КорректироватьРегистрыПоАвансам(ТаблицаАвансов) Экспорт
// Собираем номера документов из таблицы авансов
НомераДокументов = Новый Массив;
Для Каждого Строка Из ТаблицаАвансов Цикл
НомераДокументов.Добавить(Строка.doc_id);
КонецЦикла;
// Один запрос — все документы сразу
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПоступлениеДенежныхСредств.Ссылка КАК Ссылка,
| ПоступлениеДенежныхСредств.Номер КАК Номер
|ИЗ
| Документ.ПоступлениеДенежныхСредств КАК ПоступлениеДенежныхСредств
|ГДЕ
| ПоступлениеДенежныхСредств.Номер В (&НомераДокументов)";
Запрос.УстановитьПараметр("НомераДокументов", НомераДокументов);
// Строим соответствие Номер -> Ссылка
СоответствиеДокументов = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СоответствиеДокументов.Вставить(Выборка.Номер, Выборка.ссылка);
КонецЦикла;
// Проходим по результатам расчёта и пишем в регистр
Для Каждого Строка Из ТаблицаАвансов Цикл
ДокСсылка = СоответствиеДокументов[Строка.doc_id];
Если Не ЗначениеЗаполнено(ДокСсылка) Тогда
Продолжить;
КонецЕсли;
СуммаОплаты = Строка.Amount;
СуммаАванса = Строка.AvansAmount;
НаборЗаписей = РегистрыНакопления.РасчетыСКонтрагентом.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ДокСсылка);
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() = 0 Тогда
Продолжить;
КонецЕсли;
НаборЗаписей.Очистить();
Если СуммаОплаты = СуммаАванса Тогда
// Вся сумма — аванс
ЗаполнитьЗапись(НаборЗаписей, ДокСсылка,
Перечисления.ВидыРасчета.Авансы, СуммаОплаты);
Иначе
// Часть — взаиморасчёты, часть — аванс
ЗаполнитьЗапись(НаборЗаписей, ДокСсылка,
Перечисления.ВидыРасчета.Взаиморасчеты, СуммаОплаты - СуммаАванса);
ЗаполнитьЗапись(НаборЗаписей, ДокСсылка,
Перечисления.ВидыРасчета.Авансы, СуммаАванса);
КонецЕсли;
НаборЗаписей.Записать();
КонецЦикла;
КонецПроцедуры
Сам цикл расчёта в 1С работает строго последовательно: пока не обработана строка N, нельзя перейти к строке N+1. При 100 000 записей это нормально. При 10 000 000 — уже проблема.
Как тот же расчёт работает на GPU
Ключевое отличие: разные контрагенты обрабатываются одновременно. Каждый поток GPU получает свой диапазон строк (все операции одного контрагента) и обрабатывает их независимо от других потоков.
Внутри потока логика остаётся последовательной — но тысячи контрагентов обрабатываются параллельно. Именно здесь и возникает выигрыш в сотни раз.
Вот как выглядит алгоритм на шейдере (GLSL):
#version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(std430, binding = 0) readonly buffer InputBuffer { int data[]; };
layout(std430, binding = 1) readonly buffer RangeBuffer { uint ranges[]; };
layout(std430, binding = 2) buffer OutputBuffer { int result[]; };
layout(push_constant) uniform PC {
uint num_cols;
uint col_indices[15];
} pc;
void main() {
// Каждый поток — один контрагент
uint gid = gl_GlobalInvocationID.x;
uint row_start = ranges[gid * 2];
uint row_end = ranges[gid * 2 + 1];
uint col_type = pc.col_indices[1]; // 0 = payment, 1 = sale
uint col_amount = pc.col_indices[2]; // сумма в копейках
int remaining_sales = 0; // долг покупателя (непокрытые отгрузки)
// Цикл по операциям одного контрагента — строго по дате
for (uint i = row_start; i < row_end; i++) {
uint base = i * pc.num_cols;
int op_type = data[base + col_type];
int amount = data[base + col_amount];
int avans = 0;
if (op_type == 1) { // SALE — отгрузка
remaining_sales += amount;
} else { // PAYMENT — оплата
if (remaining_sales > 0) {
if (amount <= remaining_sales) {
remaining_sales -= amount; // погасили долг
} else {
avans = amount - remaining_sales; // остаток = аванс
remaining_sales = 0;
}
} else {
avans = amount; // долга нет — вся оплата аванс
}
}
result[i] = avans; // результат в копейках
}
}
Движок берёт данные из базы, сортирует по контрагенту и дате, формирует диапазоны (row_start / row_end для каждого контрагента), загружает всё на GPU — и запускает тысячи потоков одновременно. Каждый поток обрабатывает своего контрагента.
Конфигурационный файл — связующее звено
Вся настройка задачи описывается в одном JSON-файле. Не нужно менять код движка — только конфиг и шейдер:
{
"task_name": "avans_calc",
"input_table": "Operations",
"output_table": "Operations",
"output_columns": ["avans_amount"],
"output_scale": 100,
"sort_by": ["contractant_id", "operation_date"],
"group_by": "contractant_id",
"input_columns": [
{"name": "id", "type": "int", "scale": 1},
{"name": "contractant_id", "type": "int", "scale": 1},
{"name": "operation_type", "type": "string",
"mapping": {"payment": 0, "sale": 1}},
{"name": "amount", "type": "float", "scale": 100}
]
}
Обратите внимание на scale: 100 для суммы — движок автоматически переводит рубли в копейки при загрузке и обратно при сохранении. GPU работает с целыми числами быстрее, чем с числами с плавающей точкой — это даёт дополнительный прирост точности и скорости.
Результаты замеров
Разрыв только увеличивается
| Метрика | 1С (CPU, цикл) | GPU Compute Engine |
|---|---|---|
| Записей | 100 000 | 100 000 |
| Чистый расчёт данных | Базовое значение | В 600–700 раз быстрее |
| Нагрузка на GPU | — | Практически незаметна (1 МБ данных) |
| Потенциал роста | Снижается с ростом данных | |
| Результат в регистре | Идентичен | Идентичен |
Оба метода дают одинаковый результат в регистре накопления — стандартный отчёт по движениям работает без изменений. GPU считает быстрее, но итог тот же.
Часть 4. Итоги и что дальше
Что даёт этот подход
| Что получаете | Детали |
|---|---|
| Скорость | Ускорение чистого расчёта в 600–700 раз на тестовом объёме. С ростом данных разрыв увеличивается. |
| Безопасность | Всё локально. Данные не покидают инфраструктуру организации. |
| Гибкость | Один движок — разные задачи. Пишете шейдер под свою логику. |
| Гибридность | Сложную логику оставляете на CPU, массовые вычисления отдаёте GPU. |
| Независимость | Нет прямой связи с платформой. Работает с любой конфигурацией и версией 1С. |
| Без лицензий | Никаких подписок, ключей защиты и ограничений по объёму данных. |
Что нужно для запуска
- ПК с видеокартой, поддерживающей Vulkan Compute (все современные видеокарты NVIDIA, AMD, Intel Arc)
- Windows 10 / 11 (минимальная установка Vulkan Runtime — одна программа)
- Программа для связки 1С с SQLite (одна утилита)
- После установки — возможно, перезагрузка ПК
Это всё. Никакого дорогостоящего серверного железа, никаких специальных серверных видеокарт — подходит рабочая станция с обычной игровой или офисной видеокартой.
🎯 Для кого это решение
Решение актуально для организаций, у которых:
-
В регистрах накопления миллионы и десятки миллионов строк
-
Закрытие периода занимает часы
-
Расчёт себестоимости, авансов или остатков — узкое место в бизнес-процессе
-
Есть штатный 1С-программист или команда, которая может развивать решение
Технология открытая — шейдер и конфиг написаны так, чтобы их можно было переписать под другой алгоритм без глубокого погружения в C++ или Vulkan. Если будете разбираться и возникнут вопросы по архитектуре, настройке или адаптации под конкретную задачу — пишите в комментарии, отвечу.
📦 Исходный код
Полный исходный код демо-версии доступен на GitHub:
🔗 GitHub репозиторий: https://github.com/AndreyHhh/GPU-Compute-Engine-1C
Что в репозитории:
src/main_public.cpp— исходный код движка (C++/Vulkan)examples/demo_avg/— готовый пример расчёта средней себестоимостиCMakeLists.txt— сборка под Linux/Windows- Полная документация по конфигурации и запуску
Информационная база 1С и все файлы движка — в приложении к публикации. Скачивайте, тестируйте, задавайте вопросы в комментариях.
💼 Есть вопросы?
Если в вашей системе есть тяжёлые расчёты, которые тормозят бизнес — напишите. Разберём задачу, оценим потенциал ускорения и обсудим варианты внедрения.
Вступайте в нашу телеграмм-группу Инфостарт