Що Ви дізнаєтеся з цієї статті?
- Ви вивчіть варіанти прискорення операцій обробки даних
- Навчіться запускати в «1С: Підприємство 8» кілька потоків
- Дізнаєтеся про обмеження многопоточного режиму в 1С
Періодично виникає потреба у прискоренні деяких ділянок коду, в яких при побіжному ознайомленні немає потенціалу для прискорення.
Розглянемо як приклад вивантаження і / або завантаження великої кількості даних.
В даному випадку «планкою продуктивності» часто виступає безпосередньо продуктивність апаратних засобів. При цьому самі процедури завантаження-вивантаження виконуються досить тривалий час, часто вимірюється в годиннику.
Не всі знають, що в платформі «1С: Підприємство 8» частину операцій можна виконувати паралельно. шляхом одночасного виконання в кілька потоків.
Така многопоточная обробка цілком застосовна в разі, коли потрібно обробити незалежні блоки даних. Зокрема, створити великий обсяг елементів даних, здійснити проведення документів, які не перетинаються за значеннями набору вимірювань, зробити масове оновлення елементів довідників, виконати завантаження даних в регістри і т.д.
Для прикладу розглянемо задачу: необхідно провести оновлення реквізиту «Ціна» для всіх елементів довідника «Товари». Кількість елементів довідника «Товари» одно 100 000.
Найпростішим і очевидним рішенням виступає нижченаведений код обробки, який швидко пишеться, але при цьому дуже довго працює:
На сервері
Процедура ОбновітьЦену ()
ВремяНачала = ТекущаяДата ();
ТабліцаТоваров = Запрос.Виполніть () .Вигрузіть ();
Тривалість = ТекущаяДата () - ВремяНачала;
Повідомити ( «Тривалість:« + Тривалість + »сек.»);
У нашому випадку обробка виконувалася 1 187 секунд або 19,7 хвилини.
Тепер скористаємося багатопотоковим виконанням програмного коду. Перепишемо код обробки наступним чином:
На сервері
Процедура ОбновітьЦену ()
ТабліцаТоваров = Запрос.Виполніть () .Вигрузіть ();
// визначаємо максимальну кількість потоків
ЧіслоПотоков = 8;
// обсяг порції даних для обробки кожним потоком
РазмерПорціі = Цілий (ЧіслоСтрокаВТабліце / ЧіслоПотоков);
// масив, де будуть зберігатися фонові завдання
МассівЗаданій = Новий Масив;
Для НомерПотока = 1 За ЧіслоПотоков Цикл
// визначаємо індекс для початку обробки даних даним потоком
// різні потоки обробляють різні частини таблиці
ІндексНачала = (НомерПотока - 1) * РазмерПорціі;
Якщо (НомерПотока = ЧіслоПотоков) Тоді
// якщо це останній потік, то він обробляє всі залишилися дані
// тому що число потоків може не бути кратно кількості рядків в таблиці
РазмерПорціі = ЧіслоСтрокВТабліце - (ЧіслоПотоков * РазмерПорціі) + РазмерПорціі;
КонецЕсли;
// визначаємо масив параметрів для процедури
НаборПараметров = Новий Масив;
НаборПараметров.Добавіть (ТабліцаТоваров);
НаборПараметров.Добавіть (ІндексНачала);
НаборПараметров.Добавіть (РазмерПорціі);
// запуск фонового завдання
Завдання = ФоновиеЗаданія.Виполніть ( «ОбщійМодуль1.ОбновітьЦенуТовара». НаборПараметров);
// додаємо завдання в масив, щоб потім відстежити виконання
МассівЗаданій.Добавіть (Завдання);
// перевіримо результат виконання фонових завдань
Якщо МассівЗаданій.Колічество ()> 0 Тоді
спроба
ФоновиеЗаданія.ОжідатьЗавершенія (МассівЗаданій);
виняток
// дії в разі помилки
КонецПопиткі;
КонецЕсли;
Тривалість = ТекущаяДата () - ВремяНачала;
Повідомити ( «Тривалість:« + Тривалість + «сек.»);
Код загального модуля:
Процедура ОбновітьЦенуТовара (ТабліцаТоваров. ІндексНачала. РазмерПорціі) Експорт
Націнка = 1.10; // націнка 10%
// оновлюємо ціну тільки для певної частини таблиці
Для Сч = 1 За РазмерПорціі Цикл
Індекс =. (Сч = 1. ІндексНачала. Індекс + 1);
У нашому випадку використовувалося вісім потоків, оновлення цін було виконано за 859 секунд або 14,3 хвилини.
Тобто обробка однієї і тієї ж Таблиці Значний паралельно різними потоками призводить до виграшу в швидкості.
Код виконувався на віртуальній машині з одним процесором без RAID масивів. На реальному і хорошому «залізі» виграш у швидкості буде істотно більше.
Зауважимо, що це завдання можна вирішити по-іншому, ми привели лише один приклад реалізації.
Важливо! Не потрібно встановлювати занадто велику кількість потоків, так як великого приросту швидкості ви від цього все одно не отримаєте, а стабільність роботи може порушитися.
Найкращим буде використання 8-10 потоків, а їх оптимальна кількість можна визначити експериментально.
Спробуйте самостійно провести експеримент, базу та обробки можна завантажити тут.
PDF-версія статті для учасників групи ВКонтакте
35 навчальних годин, підготовка до 1С: Експерт, правильна настройка серверної частини, оптимізація коду, моніторинг завантаженості обладнання та інші дорослі речі.
29 Responses to Як прискорити 1С - Многопоточная обробка даних
Добрий день.
Є конфігурація з таким кодом: «ФоновиеЗаданія.ОжідатьЗавершенія», цей код в файлової основі не буде зовсім працювати або буде працювати дуже повільно?
Проблема в тому що при запуску цього методу, немає ніякого видимого процесу роботи: ні звернення до процесора, ні до файлів, очікував більше 20 хвилин без видимого результату. У конфігураторі не бачу створеного фонового завдання.
Розгорнув на Sql сервері базу метод відпрацьовує за пару хвилин.
В чому може бути проблема?
Дякуємо.
«Проблема» в файлової базі, там фонові завдання працюють не так як в клієнт-серверному варіанті. Даний приклад призначений для клієнт-серверного варіанту.
Добридень!
Якщо цей спосіб розпаралелювання використовувати для обробки великої таблиці значень яка отримується не запитом а кодом, то при передачі її в фонове завдання був помічений непропорційне зростання обсягу зайнятої оперативної пам'яті, що дорівнює 2-3 обсягами переданої таблиці, при цьому якщо запуск фонових завдань відбувається досить часто через 2-3 дня відбувається страшне уповільнення роботи робочих процесів сервера додатків, рятує тільки перезапуск. Є якийсь більш ефективний спосіб передачі даних в фонове завдання, без запису в базу? Або спосіб боротьби з ростом і вивільненням обсягу використаної пам'яті?
Наприклад, оптимізація подібної ситуації є складовою частиною фінального завдання курсу по оптимізації.
1. Є якийсь більш ефективний спосіб передачі даних в фонове завдання, без запису в базу?
-
Може і є, але мені він не відомий. Хоча я не бачу нічого поганого в тому, що б для обміну між ФЗ писати частина даних в базу.
2. Або спосіб боротьби з ростом і вивільненням обсягу використаної пам'яті?
-
Як мінімум стандартна рекомендація, ставити інтервал перезапуску робочих процесів 1 раз на добу.
Так що там, часто багато користувачів не здогадуються про елементарщину - можливості відкриття кількох сеансів для роботи з базою, і просиджують з ранку до обіду, чекаючи перепроведення кварталу або формування важкого звіту, з розумним виглядом втупивши ясні очі в монітор.
P.S. А строго кажучи, стосовно описаної задачі найшвидшим (на практиці в десятки, а нерідко і в сотні разів, залежить від даних) буде щось на зразок:
UPDATE _Reference53 SET _Fld9204 = _Fld9204 * (значення коефіцієнта) ;-))
Думаю присутні тут експерти зі мною погодяться.
На жаль, платформа 1С такі підходи не реалізує, і в завданнях зміни серйозних обсягів даних доводиться викручуватися саме таким чином. Час - дорогий ресурс.
Згоден з вами, звичайно через прямі запити SQL було б швидше, їх речі теж можна було б запустити в кілька потоків, але завдання як раз в тому, як зробити це виключно за допомогою 1С.
Наведений спосіб теж не найоптимальніший, і одним з фінальних завдань тренінгу буде оптимізація даного способу.
Доброго дня. Організувала многопоточную обробку даних для обробки табличній частині документа. За часом оптимізація просто колосальна. Але! Фонове завдання не повертає дані назад. (Чи можна це якось обійти? Чи можна якось організувати повернення значень від фонового завдання допустимо в таблицю значення \ або декількох таблиць, які потім об'єднуватися в одну і будуть поміщені в табличну частину документа?
Трішки не переконливий приклад.
В даному випадку при 8 потоках прискорення всього склало приблизно 1,38. Якщо поміряти ефективність (відношення прискорення до кількості потоків), то воно склало всього 0,17. Як би 80% зусиль ми витратили на накладні витрати.
Підхід, звичайно, хороший. Але потрібно вибирати завдання, в якій обробка даних потоку займе значно більше часу, ніж робота з накладними витратами. Тоді і цифри будуть гарніше, та й ефект від зусиль відчутніше.
Ще один момент, коли ми розбиваємо завдання на окремі частини, потрібно також враховувати рівномірне завантаження кожного потоку. Інакше наша ефективність також може полетіти в трубу.
Я розумію, що моє запитання вийде за рамки теми топіка, але думаю доречний в рамках поставленого завдання
Для прикладу розглянемо задачу: необхідно провести оновлення реквізиту «Ціна» для всіх елементів довідника «Товари». Кількість елементів довідника «Товари» одно 100 000.
Як ви пропонуєте організувати транзакцію і можливість «відкотитися» в разі якщо метод буде завершено з помилкою?
Поправте, якщо я не правий, але фонові завдання будуть виконані як ізольовані транзакції (в окремих сеансах).
Привіт, Євген!
Все вірно, в даному випадку можливість «відкотиться» можна організувати тільки самостійно, це плата за паралельність.
Тут треба добре подумати, а чи потрібна нам взагалі можливість відкоту, наприклад, для даної задачі це не обов'язково.
У разі помилки ми можемо просто запустити обробку повторно і товари у яких ціна вже оновилася просто не будуть оновлені.
В інших випадках можна придумати різні механізми контролю.
«У разі помилки ми можемо просто запустити обробку повторно і товари у яких ціна вже оновилася просто не будуть оновлені.»
За якою ознакою в даній обробці визначається, що ціна вже оновилася?
Добрий день, Юрій!
Минулого разу ми відповіли стосовно іншої задачі, коли потрібно було обробити товари, тільки з певним значенням ціни.
Для даного завдання дійсно необхідно буде додати ознака того, що ціна вже оновилася і оновлювати її більше не слід, тобто транзакційність треба буде робити самостійно.