Re: O том, как бы хорошо работали операторы array.



Posted by Владимир Секретев, Клуб Любителей Бухгалтерского Учета on August 16, 1999 at 08:29:40:

In Reply to: O том, как работают операторы array. posted by Аркадий Водяник on August 15, 1999 at 02:31:20:


Аркадий, прежде всего, спасибо за ответ на мой вопрос об операторе array.

Я сознательно не привел своих практических данных о работе этих операторов, желая ознакомиться
сперва с теорией "из первых рук", чтобы утвердиться в том, что мои соображения верны.
Теория - ясна, а вот "чистые" примеры, приведенные Аркадием не показательны, так как работать-то
приходится с реальными базами.

Теперь я публикую тексты ОФ, заставившие меня задуматься об оптимальности реализации операторов
"аrray".

Эксперимент проводился на сервере 3.07. Компьютер HP NetServer E 45, PII-300, ОЗУ 164 MByte, OC - NT.
Для обеспечения чистоты эксперимента "замеры" производились после рестарта сервера, форма
запускалась один раз.

Форма SYS0001 строит оборотный баланс, то есть делает то, что и системная форма __SYS001.
Для определения проводок ей требуется пп F_BUX_P, расположенная в FIRST.RPT, фрагмент которого
приведен ниже.

Array %

Вот так выглядела форма SYS0001 до появления оператора array % (приводится фрагмент):


Вpеменной пpофиль фоpмы SYS0001

Ни одна из стpок исходного текста не набpала
пpи пpофилиpовании более 10 тиков таймеpа.
Поэтому показывается только исходный текст:
* Обороты по проводкам
* Очистка параметров %
#План
n1 = [n1 #]
[set %, 'OD'+n1, 0]
[set %, 'OK'+n1, 0]
[set %, 'BA'+n1, 0]
#
..............
..............
..............
..............


При этом профайлер не набрал ни на одном из операторов формы и десяти "тиков".


А так выглядит та же форма, где вместо "прямого" обнуления параметров % применен оператор "array" (приведен полный текст):



Вpеменной пpофиль фоpмы SYS0001

Количество выполнений: для фоpм не запоминается.
На это количество выполнений потpебовалось 788 мс = 100%
Распpеделение вpемени по стpокам исходного текста в относительных %, и мс:

~ ~.0001 * Обороты по проводкам
~ ~.0002 * Очистка параметров %
~ ~.0003 i = 0
~ ~.0004 *#План
~ ~.0005 * n1 = [n1 #]; [set %, 'OD'+n1, 0]; [set %, 'OK'+n1, 0]; [set %, 'BA'+n1, 0]
~ ~.0006 *#
################### 98.2% 774.0007 array %
~ ~.0008 * Коррекция DL
~ ~.0009 ! DL = 0
~ ~.0010 DL = 31
~ ~.0011 ! DF + MF = 1 попавка на 1 января
~ ~.0012 DF = 1
~ ~.0013 !
~ ~.0014 * Расчет входящего сальдо
~ ~.0015 SG = 'SALDO'
~ ~.0016 i1 = [get X, 'L', 0] с начала года
~ ~.0017 *^^^^^^i1^^
~ ~.0018 j2 = [get X, 'L', mf, df-1] по день, предшествующий началу ОП
~ 2.0019 call F_PROCESS
~ ~.0020 * Обороты за отчетный период
~ ~.0021 SG = 'DK'
~ ~.0022 i1 = [get X, 'L', mf, df-1]
~ ~.0023 j2 = [get X, 'L', ml, dl]
~ 1.0024 call F_PROCESS
~ ~.0025 * Распечатка результата
~ ~.0026 TB = 0; TD = 0; TK = 0; TE = 0
~ ~.0027  Счет Вх.сальдо Об.дебета Об.кpедита Исх.сальдо с { по }.
~ ~.0028 #План
~ ~.0029 n1 = [n1 #]
~ 2.0030 OD = [get %, 'OD'+n1]
~ ~.0031 OK = [get %, 'OK'+n1]
~ 5.0032 BA = [get %, 'BA'+n1] + [get *n1, 'EA', 1, 1] l j+ [get %, [user]+'BA'+n1]
~ ~.0033 ! n1 = '41'
~ ~.0034 BA = [get %, 'BA'+n1] + [ged n1+'EA']
~ ~.0035 !
~ 1.0036 if (OD<>0) | (OK<>0) | (BA<>0)
~ ~.0037 EA = BA + OD - OK
~ ~.0038 [set %, 'EA'+n1, EA] сохранение исходящего сальдо для переноса на сл. год
~ 2.0039 ^^^^^^^n1 ^^^^^^^^^^BA^^ ^^^^^^^^^^OD^^ ^^^^^^^^^^OK^^ ^^^^^^^^^^^^EA^^
~ ~.0040 call SUBS
~ ~.0041 TD = TD + OD
~ ~.0042 TK = TK + OK
~ ~.0043 TB = TB + BA
~ ~.0044 TE = TE + EA
~ ~.0045 endif
~ ~.0046 #
~ ~.0047  Итого ^^^^^^^^^^TB^^ ^^^^^^^^^^TD^^ ^^^^^^^^^^TK^^ ^^^^^^^^^^^^TE^^
~ ~.0048 stop
~ ~.0049 ****************************************************************************
~ ~.0050 :SUBS
~ ~.0051 * Количество субсчетов *****************************************************
~ ~.0052 X = '.('+[intsn [as *n1]]+')'
~ ~.0053 ! X = '.(0)' return
~ 1.0054 ^^^^^^^^^^^^X
~ ~.0055 return

774 (миллисекунд)/147 (синтетических счетов) = 5.26 (миллисекунд), что примерно в пятьсот (!!!)
раз больше, чем в "чистом" примере, приведенном Аркадием. Почему - не понятно.

Есть подозрение, что причина заключается в том, что array % обходит ВСЁ дерево индексов
экстрапараметров, а не только свое. В реальном примере оно, это дерево, вовсе не столь оптимально,
как в отвлеченном примере. Необходимость полного обхода возникает, с моей точки зрения, из-за не удачного
расположения обозначения счета в конце индексного пути. Из этого следует, что есть четвертый
способ улучшения временных характеристик array % - изменение индексной структуры экстрапараметров
- перемещение обозначение счета в начало индексного пути. Кроме этого, данная мера приведет к
построению более оптимального индекса и для всех других экстрапараметров и, как следствие -
экономия памяти и увеличение быстродействия.

Теперь об "array буква"

Фрагмент FIRST.RPT


~ ~.2900 *******************************************************************************
~ ~.2901 :F_BUX_P
~ 4.2902 P = PW
~ ~.2903 i = 0
### 16.6% 1526.2904 array D, K, S инициализиуем массив поводок
~ 1.2905 :fnext_pro
~ 6.2906 i = i + 1
0.1% 13.2907 call F_DK_1
0.2% 17.2908 ! DK = 0 goto fend_pro
~ 4.2909 call F_DK_PRO
0.4% 35.2910 ! D = 'X' goto fend_pro
0.3% 32.2911 P = PW
0.1% 12.2912 call F_DK_IZM
0.7% 66.2913 ND = [pa *D]
0.4% 38.2914 NK = [pa *K]
~ ~.2915 *
0.3% 23.2916 if SG = 'DK' or SALDO
1.7% 157.2917 [plus %, 'OD'+D, S] Оборот дебета субсчета
1.3% 117.2918 [plus %, 'OK'+K, S] Оборот кредита субсчета
1.0% 91.2919 if ND <> 'План' [plus %, 'OD'+ND, S]; endif
1.0% 93.2920 if NK <> 'План' [plus %, 'OK'+NK, S]; endif
~ ~.2921 * Можно ускоить, убав if вообще. Пусть плюсует к План.
~ 3.2922 else SALDO
~ ~.2923 [plus %, 'BA'+D, S] Сальдо дебет субсчета
~ ~.2924 [plus %, 'BA'+K,-S] Сальдо кредит субсчета
~ ~.2925 if ND <> 'План' [plus %, 'BA'+ND, S]; endif
~ ~.2926 if NK <> 'План' [plus %, 'BA'+NK,-S]; endif
~ ~.2927 endif
~ ~.2928 * Создадим массив поводок
##### 28.0% 2575.2929 [D i, D]; [K i, K]; [S i, S]
~ 6.2930 goto fnext_pro
~ ~.2931 :fend_pro
~ 2.2932 return
~ ~.2933 *****************************************************************************


Было 1659 обращений к пп F_BUX_P, соответственно и array D, K, S выполнялся столько же раз. Массивы
D, K, S каждый раз имели разную длину, от 1 до "много", но в среднем примерно 6. Массивы D и K
содержат строки, максимальной длиной 9 - это обозначения субсчетов. Массив S содержит числа - это
сумма проводки.

1526(мкс)/1659(раз)/6(элементов)/3(массива) ~= 51 (мкс) против 3 (мкс) в примере Аркадия.

"Растаскивание" массивов по разным array дало неожиданный результат. Инициализировать массив
D, идентичный по структуре и количеству элементов массиву K оказалось примерно в два раза труднее.


# 7.9% 759.2904 array D
3.9% 379.2905 array K
4.3% 420.2906 array S инициализиуем массив поводок

Перестановка местами D и K подтвердило то, что, собственно, буква здесь ни при чем:


## 9.8% 948.2904 array K
4.4% 422.2905 array D
4.4% 424.2906 array S инициализиуем массив поводок

Очевидно, это может быть связано с наличием некоторых "подготовительных" действий (каких?), необходимых для
запуска механизма array, а следующие array идут как "по накатанной".

Мое предложение по оптимизации работы оператора "array буква" таково. Оператор array НЕ ДОЛЖЕН
заниматься высвобожденим памяти или указывать на это системе. Зачем это делать в процессе
работы формы да еще МНОГОКРАТНО? Вполне достаточно ОДНОКРАТНОГО высвобождения памяти ПОСЛЕ
завершения работы формы. Причем высвобождается память, занятая только теми массивами, которые
родились в форме, а не всех. (Все массивы инициализиpуются пpи входе в фоpму.)

Так что же должен делать array? Всего только присваивать нулевому элементу массива значение 0.
При извлечении элемента массива производится сравнение запрашиваемого индекса со значением,
хранящемся в нулевом элементе массива. В том случае, если индекс оказывается больше, возвращается
0. При этом даже поиск элемента массива выполнять нет никакой необходимости. Наверно это будет
работать очень быстро.

Теперь по поводу столь большой разницы в статистических результатах, полученных мною и Аркадием.
Существенная разница между нашими экспериментами заключается в том, что Аркадий производил замеры
в ФК, а я - в ОФ. Может это повлияло?

P.S. Отдельое большое спасибо за оптимизацию работы директивы W FORM.




Пpишедшие ответы: