Восемь лет спустя
kv75
дневник заведен 05-10-2003
постоянные читатели [82]
закладки:
цитатник:
дневник:
местожительство:
Москва, Россия
интересы [13]
шахматы, грибы, Пратчетт, Иваси, Morrowind, Guild Wars
[1] 08-05-2008 07:37
Альпы

[Print]
Элизабет
02-04-2004 12:28 Заполнение таблицы
Очень приятно ощущать себя не полным идиотом.

Вчера вечером я упомянул о сортировке этого огромного объема данных. Но начинать, как выяснилось, надо было не с этого, а с несколько более сложной (в плане обдумывания, а не в алгоритмическом плане) задачи.

Итак, пишем программу на Дельфи для работы с таблицами в 200 тысяч строк и 40 столбцов. Конечно, в большинстве случаев файлы будут меньше, но такие тоже встречаются.

Помещаем на форму стандартный компонент TStringGrid и пишем функцию заполнения этой таблицы. Разумеется, делаем всё аккуратно, пытаясь в разумных пределах максимизировать скорость. Сначала читаем данные из файла в буферный массив TStringList, затем определяем требуемое количество строк и столбцов таблицы. Устанавливаем эти значения, после чего определяем, что в какую ячейку положить, и заполняем ячейки таблицы.

Результат совершенно обескураживающий, ибо программа не работает, заставляя Винды всё время вспоминать о наличии винчестера. Ставим проверку после каждого этапа. Чтение файла в TStringList - довольно быстро и без проблем. Определение и установка размеров таблицы - аналогично. Заполнение таблицы - висим...

Берём маленький файлик (вместо 200 тысяч - 30 тысяч строк). Всё проходит довольно быстро, этап заполнения таблицы (там ещё парсинг простенький есть - деление на поля, не надо забывать) длится порядка 10 секунд, остальные - порядка секунды. Меняем программу таким образом, чтобы она заполняла одну и ту же "маленькую" таблицу подряд одними и теми же значениями десять раз - с каждым разом заполнение происходит всё дольше, на седьмом проходе висим окончательно...

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

Подсказка номер 1.
У компонента TStringGrid есть парочка private методов. Если бы они были доступны, "утечек памяти" можно было бы легко избежать.


Решение.
В общем, дело оказалось в том, что при каждом занесении очередной строки в ячейку компонент TStringGrid посылает Windows запрос на перерисовку этой ячейки. Пока функция работает, Windows с её окном ничего не делает, а запросы на перерисовку всё копятся и копятся в памяти. Сначала физической, затем начинается свопинг. В конце концов система виснет окончательно, хотя Ctrl+Alt+Del и убиение программы лечат ситуацию. По крайней мере, такова ситуация в Win98, в NT-линейке я не проверял.

К счастью, можно вызывать методы, которые заполняют сразу всю строку или весь столбец. Эти методы и сами по себе работают дольше, так как вызывают те же функции заполнения ячейки, да и для их работы приходится подготавливать StringList, что кушает и память, и время. Зато преимущество этих методов состоит в том, что на время своей работы они запрещают перерисовку ячеек, используя методы BeginUpdate и EndUpdate, упомянутые в подсказке. Таким образом, каждый такой метод вызывает один запрос на перерисовку.

Отсюда легко сообразить, что если выполнять заполнение таблицы по столбцам, указанной проблемы удаётся избежать, поскольку столбцов всего около 40. Заполнение таблицы по строкам, конечно, менее эффективно. При заполнении каждого столбца нам, к сожалению, приходится заполнять довольно большой StringGrid на N(=200000) элементов, но этот расход памяти гораздо менее критичен, чем N*M сообщений Windows.
Комментарии:
02-04-2004 12:44
Незлопастый Брандашмыг
Тут думать нечего Винды с его умной паматью.

Мне удавалось табличкой с 10 тысячами строк повесить систему Обычная операция создания динамического масива такого же размера резервировала память (физическую), и не освобождала до перезагрузки.
После того как физическая память заканчивалась - винды пытались сами себя перенести в виртуальную, что обычно приводило к зависанию.

Как решить проблему - не знаю Я перезагружал комп

ЗЫ Правда я писал на VB, и винды увидев родной as Recordset - с готовностью выделяли память. Навсегда
02-04-2004 12:50
грандмастер свалкограф
Timoty
Нет, ну понятно, что идёт свопинг. Но я до этого ни разу не замечал за Дельфи глюков в работе с памятью. Если в программе на Дельфи происходит утечка памяти - виноват обычно ты сам, надо не забывать выделенную память освобождать. И с Виндами она работает вроде в этом плане довольно корректно.

Но в данном случае я был абсолютно уверен, что виноват не я. А раз при повторном заполнении той же таблицы (я ведь не перераспределяю ничего, просто заново ячейки заполняю) предыдущая память куда-то утекает, то вот и встаёт вопрос - куда она утекает и как закрыть дырку.
03-04-2004 13:12
Незлопастый Брандашмыг
kv75
Так в чем же проблема, и где дырка?
03-04-2004 14:57
грандмастер свалкограф
Проблема была в том, что память уходила на запросы на перерисовку ячеек (или всего окна), которые скапливались в системе и забивали всю память (ячеек-то много!). Пришлось изобретать обходные манёвры, чтобы уменьшить число запросов, посылаемых компонентом TStringGrid.
Безумный трубочник
Мда... А если перед заполнением StringGrid.Visible в False, а после заполнения в True?
08-04-2004 09:59
грандмастер свалкограф
Super Bubba Разумеется, я так и делаю.
Когда я понял, в чём дело, очень удивился, поскольку по всем законам природы при Visible=False запросы на перерисовку посылаться не должны.
Безумный трубочник
kv75 - ну тут как раз ничего особо удивительного

В принципе ты конечно поступил правильно, но я бы просто не стал использовать StringGrid для такой большой таблицы. Не вижу смысла, наверняка бы приделал какую-нибудь хрень, что бы выдавать небольшими порциями данные. Хотя может быть твоя задача этого и требует
10-04-2004 11:01
грандмастер свалкограф
Небольшими порциями - может, это и правильно, но уж больно трудоёмко. Так я все операции делаю непосредственно с ячейками TStringGrid, а иначе пришлось бы делать отдельный массив для хранения данных (чтобы не проводить парсинг каждый раз). Впрочем, мой новый компонент фактически и изменил методику хранения данных в TStringGrid. Что помогло.
Безумный трубочник
kv75 - все что правильно - трудоемко Зато потом легче будет. Нет я конечно тоже не ангел, зафуговал бы все в одну таблицу

Ваш комментарий:
Камрад:
Гость []
Комментарий:
[смайлики сайта]
Дополнительно:
Автоматическое распознавание URL
Не преобразовывать смайлики
Cкрыть комментарий
Закрыть