Всем привет! Стояла задача минимизировать время выполнения кода. Делюсь решением, которое получилось на основе алгоритма от ZVI, которые я использовал. По моим скромным знаниям и тестированию другие пути оказались не столь эффективны. В итоге в массиве из 250 тыс. ячеек и выделение 62 тыс. ячеек расположенных в случайном порядке занимает порядка 60-70 секунд. Решения: 1. Объединение через Range() до 254 знаков 2. Запись адресов без $ - (A1) 3. Минимальное использование Union, для этого создается буферный массив для заполнения Range(), в размере 29 блоков (+текущий =максимально для Union), увеличение быстродействия до 2х раз. 4. Не создавать каждый раз новую строку, а использовать одну (предыдущую строку) как символьный массив, изменения в котором можно выполнять копированием фрагмента памяти с помощью Mid$(StrBuf, Ptr) = StrNew. Mid$() вместо Mid(), потому как Mid$() быстрее. 5. Поиск не по ячейкам, а по массиву (загруженному из range), для этих же целей Set a2. 6. Отключение экрана, событий и автопересчета 7. Открытый для меня новый факт: при переборе столбцы-строки, быстрее чем строки-столбцы, если структура данных вертикальная - от 20 до 70%
Где можно ещё полернуть? Возможно есть другие оригинальные решения?
Ещё раз повторюсь, основная идея от ZVI, за что ему огромное человеческое спасибо!
следует учитывать, что не у всех 64-битная архитектура. На больших массивах эта строка вызовет ошибку переполнения памяти. И зависеть это будет напрямую от кол-ва доступной памяти, что в 32-битных системах критично - там не разгуляешься
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
bedvit написал: хорошо бы понять максимальный массив
Цитата
The_Prist написал: И зависеть это будет напрямую от кол-ва доступной памяти
Поэтому все напрямую зависит от того, какие данные на листе. Если в каждой ячейке длинный текст - то это самое плохое, пожалуй. И таких ячеек потребуется куда меньше для переполнения, чем числовых данных.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
The_Prist, логично. Выходит "хочешь обрабатывать большие массивы данных, ставь офис поновее и поразряднее"... что, вообщем-то логично. Ну или тогда перебирать по ячейкам, что в разы (а может и больше) медленнее...
bedvit, в чем смысл выделения как можно большего числа несмежных ячеек - спортивный интерес? Скопировать такой диапазон невозможно. Пользователю показать - вряд ли человек глазом найдет что-то нужное в этом узоре. Если нужно очистить или закрасить эти ячейки - лучше делать это частями, скажем, по столбцам или по областям не более 10000 ячеек. Я запустил код - опять получил ошибку. Вот содержимое Immediate:
Код
2,75
1,328125
-- после останова по ошибке --
?x,y,selection.areas.count,selection.count
4245 20 31610 47223
?err.Description
Method 'Union' of object '_Global' failed
Формирование строки можно еще ускорить, если копировать куски памяти с помощью RtlMoveMemory - поищите, примеров масса. Получение адреса как addr = a2(x, y).Address(...) - неоптимально, метод Address довольно медленный. Лучше создать массив имен столбцов и получать адрес как addr = arrColNames(y) & x Кстати, чтобы не приклеивать каждый раз запятую, можно строки изначально заполнить запятыми: s(j) = String$(256, ",") вместо s(j) = Space(256) Код, конечно, придется кое-где переделать.
по п.7 думаю это связано с работой Union, позволю себе процитировать (т.к. сам по этой тематике глубокими знаниями не обладаю):
Цитата
Казанский написал: Почему Union работает медленно при большом количестве областей - он пытается соединить смежные диапазоны в одну область. НапримерКод?12?union(range("A1:A3"),range("C1:C5"),range("A4:A7")).Address $C$1:$C$5,$A$1:$A$7А если объединяются несколько диапазонов, каждый из которых состоит из многих областей, Union видимо пытается свести каждую область с каждой, и это очень долго.
из чего можно предположить, что в вертикальном массиве (мало столбцов, много строк) прои вертикальном "проходе" Union меньше сводит соседних областей, чем если "проходить" горизонтально. Мое предположение.
Казанский, сейчас из спортивного интереса, но была и задача удаления лишних строк. Для удаления лишних ячеек, родилась эта. Массивы большие, решил отработать алгоритм. У вас наверное office х32? Попробуйте уменьшить массив (если интересен результат). За дополнения, Спасибо! Буду вносить корректировки. Что бы зачистить массив (без удаления ячеек), есть очень быстрые алгоритмы (без select) , к примеру вот на нашем форуме. Кстати, ваш - мною отмечен как самый оригинальный.
Итак, получил семикратный (и выше) прирост производительности на больших массивах на новом алгоритме. Алгоритм собственный. Итоги: обработка 500тыс. ячеек с выделением 125тыс.-34сек, 1млн. с выделением 250тыс.- 125 сек. (win7+office2010=x64). Отказался от Union. Изначально была идея пойти через именованные диапазоны и хотя в RefersTo:= влезало от 700 до 1280 знаков (почему так, вопрос), в отличии от range (где 255), идея не прошла. Пошел через SpecialCells. Приятным бонусом стало возможность обрабатывать тип данных (формулы, текст и т.д.), поменяв одну цифру (две) в коде. Замечена одна особенность, в office2016х64 код выполняется в два раза дольше при прочих равных, неясно почему, кривые ручки майкросовт? Делюсь)
Итак по SpecialCells: по собственным тестам ...Validation и ...FormatConditions работают быстрее чем другие XlCellType constants. SpecialCells хорош, но не работает с защищенном листом, и убирает форматирование в примечаниях (выложенный алгоритм выше). Поэтому решил сегодня полернуть еще код. Вообщем то вышел вариант который на данный момент самый быстрый. без SpecialCells ничего не трогает, защита неважна. Обработка 250 тыс. ячеек с выделением 60 тыс.-5сек., т.е. в 2 раза быстрее чем выделение по SpecialCells. Кому интересно? :-)
Ну, раз от желающих нет отбоя, допишу свой монолог. Сделано к вышеописанному: 1. Отказался от сложного резервирования памяти (в данном случае слабо сказалось на производительности) 2. Ввел вместо Address (+ в карму за совет от Казанский) массив имен, прирост производительности закрыл с лихвой п.1 3. Максимально использовал функцию Union (все таки вернулся к ней)
Сразу говорю, писал для наибольшей производительности, код локаничностью сильно не отличается, но вполне рабочий и главное быстрее всех, выложенных ранее. Делюсь. Конструктив принимаю.
P.S.Добавил проверку на размер массива и объект на IsEmpty.
К примеру, этот код выполняется 5 сек., из них Union 4,9 сек., остальное 0,1 сек.(Win7x64, Excel2016,2010x64). Итого 98% зависит от union. А чем больше Union сжирает за раз, тем быстрее выполняется код. На этой идее и строится данный алгоритм. Пробовал разными способами скармливать, остановился на этом.