O том, как работают операторы array.



Posted by Аркадий Водяник on August 15, 1999 at 02:31:20:

In Reply to: Re: ARRAY % ?? posted by Владимир Секретев, Клуб Любителей Бухгалтерского Учета on August 13, 1999 at 04:04:15:

I. Об операторе "array буква".

Сначала я процитирую часть своего сообщения 980:


Индексы массивов могут пpинимать значения от 1 до 1000000.
Каждый массив состоит из 200 указателей на стpаницы хpанения.
Каждая стpаница хpанения состоит из 5000 ячеек по 12 байт каждая:


type ARRAY_ELEM_TYPE = record case f :byte of
1: (D :double);
2: (S :string[10]);
3: (P :^string );
end;


Здесь f - флаг, хаpактеpизующий элемент. Таким обpазом, для чисел
используется 8 байт в ячейке (D: double), коpоткие стpоки -
до 10 знаков - тоже находятся пpямо ячейке (S :string[10]),
а для более длинных стpок ячейка хpанит указатель (P :^string)
на тело стpоки.


Для тех, кто не знает что такое паскалевское "record case" : это запись
с наложением полей друг на друга: и D, и S, и P - начинаются с одного
адреса, т.е. "наложены" друг на друга. Аналог в языке C - конструкция union.
Значения поля f в трех разных случаях (число, короткая строка, длинная
строка) есть разные числа.

Допустим, что массив a состоит из 770000 элементов, каждый из которых
число, т.e. есть в памяти выделены 13 блоков (страниц хранения) по 60000 байт
(770000/60000=12.83, то есть 13), a каждый элемент находится внутри
12-ти байтовой ячейки. Что в таком случае сделает оператор "array a" ?
Он просто укажет системе, что надо освободить всего 13 блоков памяти.
Рассмотрим такой профиль (измерения выполнены на P233):


### 18.3% 911¦0002 for i=1 to 770000
############## 74.3% 3701¦0003 [a i,1]
# 7.3% 363¦0004 endfor
~ 4¦0005 array a

Видно, что "array a" работал очень недолго - несколько мс.
Похожая ситуация и для строковых значений не длиннее 10 знаков:


### 18.9% 1008¦0002 for i=1 to 770000
############## 73.4% 3909¦0003 [a i,'0123456789']
# 7.5% 397¦0004 endfor
~ 5¦0005 array a

Ситуация резко изменяется, если строковые элементы массива длиннее 10 знаков,
то есть не укладываются в ячейку 12 байт, т.e. каждый элемент массива
является отдельным блок памяти:


1.0% 1222¦0002 for i=1 to 770000
################### 96.7% 121718¦0003 [a i,'01234567891']
0.8% 965¦0004 endfor
1.6% 2002¦0005 array a

Здесь array a работал 2с, ведь ему пришлось освобождать 13 + 700000 блоков
памяти. Вроде бы "крайне долго" - но если посмотреть на это с другой стороны,
то это всего 2000000/770000, т.e менее 3 мкс на один элемент массива.


II. Об операторе "array %."

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

Есть три способа сделать это:

СПОСОБ 1) При выполнении формы отслеживать все присваивания [set %...], и
накапливать встречавшиеся индексы в списке. Затем при выполнении "array %"
пройти по этому списку, освобождая память только для встречавшихся индексов
и связанных с ними значений.

Достоинства способа: относительно быстрый "array %" для относительно
небольшого количества встречавшихся индексов.

Недостаток: замедление выполнения конструкции [set %...].

СПОСОБ 2) При выполнении "array %" рекурсивно обойти дерево индексов
экстрапараметров (см. 226), выявляя подходящих кандидатов
на уничтожение.

Достоинства способа: [set %...] работает без замедлений. В случае, когда
все экстрапараметры созданы [set %], "array %" сработает намного быстрее,
чем в способе 1.

Недостатки: array % всегда работает примерно одно и то же время, почти
пропорциональное размеру дерева индексов экстрапараметров; это время почти
не зависит от количества индексов, встречавшихся в [set %...].

СУПЕРСПОСОБ 3) Выделить для [set %] отдельное дерево индексов. Этот
суперспособ можно сочетать как со Способом 1 (что бессмысленно), так и
со способом 2.

Достоинства: в сочетании со Способом 2 даст максимум быстродействия "array %"
(и [set %] тоже!)

Недостатки: пока незаметны.

Но пока я выбрал 2-й способ - он требует минимум вмешательства в существующий
код Сервера, и дает вполне приемлемые результаты. Немного позже внедрим
Суперспособ 3. А пока посмотрим профиль для Способа 2:


3.7% 265¦0001 for i=1 to 200000
############## 70.2% 5021¦0002 [set %, i, i]
1.7% 119¦0003 endfor
#### 24.5% 1751¦0004 array %

За 1.7с освобождена память 200000 элементов [set %...] (около 9мкс на один
элемент). Правда, эти же 1.7с потребовались бы и для одного элемента [set %...],
"растворенного", например, в 199999 [set %%..].

На мой взгляд, это хорошие, а не "крайне медленные" показатели.



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