- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF135:OOo
Материал из Linuxformat.
- Пользовательские функции Добавьте электронным таблицам недостающий функционал
Содержание |
OOo Calc: На конвейере
- Напоследок Александр Маджугин рассмотрит сложные функции, которые используют в своей работе данные из множества ячеек, в том числе – рекурсивно.
OOo Calc |
---|
|
В предыдущих статьях этого (к сожалению, подходящего к своему логическому финалу) цикла мы рассмотрели вопросы создания, использования и оптимизации пользовательских функций в OpenOffice.org Calc. Данный же урок будет посвящен оптимизации конвейеров вычислений непосредственно в самой электронной таблице, а также общей оптимизации электронной таблицы, способной значительно улучшить работу с ней.
Что я называю здесь конвейером вычислений? Конвейер вычислений – это последовательность формул, каждая последующая из которых использует как свой аргумент результат предыдущей. Естественно, что для достижения необходимого результата могут быть использованы конструктивно различные конвейеры. И столь же естественно, что скорость вычисления в них также будет различна, причём, как мы увидим далее, отличаться она может достаточно сильно.
Рис. 1. Конвейер вычисления ADX насчитывает 33 «этапа» (синие стрелки), и это еще далеко не предел.
На рис. 1 показан конвейер вычисления третьего в ряде значений технического индикатора ADX. Видно, что для получения результата потребовалось предварительно вычислить 33 значения в других ячейках, а ведь это ещё не самый сложный расчёт.
Измеряем скорость
Как и при оптимизации пользовательских функций, написанных на Basic, здесь нам потребуется инструментарий, как минимум для замера скорости вычислений в конвейере. Однако таким же простым решением, какое мы использовали для замера времени работы кода в LXF134, нам уже не обойтись.
Формализуем задачу. Итак, нам необходимо замерить время расчёта некоторой связной последовательности формул, для чего потребуется засечь время инициации расчёта и время изменения ячейки, возвращающей результат вычисления. Это значит, что понадобятся две процедуры – для запуска расчёта и для регистрации его завершения соответственно. OpenOffice.org – не Perl, но и в нем для решения задачи есть более одного пути; мой представлен на Листинге 1 на диске. Для большего удобства я написал для себя простенький шаблон, который также можно найти на DVD в файле CalcSpeedTester.ods.
Смысл работы кода в следующем: стартовая процедура изменяет содержимое ячейки, которая инициирует вычисление в конвейере, и сохраняет время этого события в глобальной переменной starttimepoint. Когда изменяется содержимое последней ячейки конвейера, выполняется функция CalcTimerStop, вычисляющая время, прошедшее с последнего изменения глобальной переменной starttimepoint.
Для использования шаблона следует ввести два различных значения из области определения конвейера в ячейки A1 и A2 листа «Тестируемая последовательность». Это необходимо для того, чтобы значение ячейки, на которую ссылается конвейер, изменялось при каждом нажатии кнопки Старт, находящейся на листе «Тест». Значения из ячеек A1 и A2 будут чередоваться.
Сам конвейер должен ссылаться на ячейку A3 (start), а в ячейке A4 (finish) должна быть ссылка на последнюю изменяющуюся ячейку последовательности. Конвейер можно разместить тут же – на листе «Тестируемая последовательность».
Рассмотрим простой пример – тест функции SUM в сравнении с простым суммированием значения каждой ячейки. Для увеличения времени расчёта данных должно быть много, а сам конвейер лучше повторить несколько раз с последовательными ссылками следующего конвейера на предыдущий.
Рис. 2. Последовательность конвейеров SUM.
На рис. 2 показана схема связей тестируемой последовательности. Столбцы с G по AY и строки с 6‑й по 98‑ю скрыты для экономии места. Ячейки в строках со 2‑й по 100‑ю содержат просто числовые значения от 2 до 100 соответственно. Ячейка в строке 1 каждого конвейера содержит ссылку на результат предыдущего, чем достигается последовательное вычисление функции SUM. Формулы в строке 101 имеют вид =SUM(#1:#100), где # – имя соответствующего столбца.
Время выполнения этой схемы на моём ноутбуке составило около 7 миллисекунд – этого, как правило, недостаточно для надёжного теста, и последовательность стоило бы увеличить, но в данном случае нас это устраивает, так как для формул вида =#1+#2+...+#99+#100 время расчёта составляет около 170 миллисекунд, что почти в 25 раз дольше.
Таким образом мы приходим к первому правилу оптимизации вычислений в Calc – везде, где это возможно, стоит использовать встроенные функции пакета, а не их реализацию в виде формул, с помощью более простых функций и операторов.
Впрочем, данный приём элементарен и очевиден, а потому малополезен, но зато он хорошо показывает, какими могут быть потери времени при неправильном проектировании конвейеров вычислений.
Другим интуитивным способом ускорения расчётов в Calc, по-видимому, является сворачивание конвейера в минимум формул. Под этими словами понимается такое преобразование формул в последовательно ссылающихся друг на друга ячейках, при котором часть из них встраивается в зависимые формулы для сокращения количества ссылок в конвейере. Например последовательность формул вида C1=A1+B1, D1=C1*32, E1=If(D1>100;1;0), очевидно, приводится к виду E1=If((A1+B1)*32>100;1;0), то есть сворачивается в одну-единственную формулу. Насколько это эффективно?
Для проверки этого приёма в базовом, неоптимизированном представлении, я использовал последовательность из 14 последовательно связанных формул, среди которых были выражения, содержащие самые обычные арифметические действия, функции условий IF и CHOOSE, а также функции для работы с текстом и функции преобразования типов. В виде, свёрнутом в минимум последовательных формул, они представляли собой лишь три последовательно связанных выражения – D2=COUNTIF(F1;”<0”)+1 (F1 – результат вычисления в предыдущей последовательности), E2=IF((D2*3+2)/4=2;56;2) и F2 =VALUE(RIGHT(“00”& TEXT(SQRT(E2*E2);”000000”);2))-14.
В результате выигрыш по времени расчёта составил чуть более 6 %; откровенно говоря – не так уж и много. Однако если взять ту же последовательность и опустить традиционно долгие операции по преобразованию типов и, соответственно, текстовые операции, то это приводит не только к троекратному росту скорости вычисления данной последовательности, но и примерно к такому же росту выигрыша от оптимизации – до 15 %.
Из данного опыта можно сделать вывод, что такой тип повышения производительности если и стоит использовать, то только для математических расчётов и уменьшения размера итогового файла, так как мень шее количество задействованных в расчёте ячеек часто ведёт к сокращению его размеров. В данном случае оптимизированный файл стал (по сравнению с исходным) меньше в два с лишним раза (27 КБ против 65 КБ). А уменьшение размеров файла ведёт к значительному сокращению времени его открытия в OpenOffice.org Calc.
Ещё одним, пусть и менее известным, методом оптимизации как скорости расчёта, так и размера файлов считаются формулы массива. За подробностями о том, что это такое, вы можете обратиться к встроенной справке пакета OpenOffice.org – здесь я не стану описывать их, так как они того не очень-то и заслуживают. Почему я так считаю? Вот об этом-то мы и поговорим подробнее, а заодно постараемся предостеречь вас от излишних трудностей с построением вычислений в надежде на их оптимальность.
Считается, что формулы массива дают при расчётах значительную экономию времени в силу двух причин: во-первых, они потребляют существенно меньше оперативной памяти, во-вторых, при получении данных из влияющих диапазонов извлекают их не по одной ячейке, а сразу целыми диапазонами, на которые ссылаются.
Однако первое критично исключительно для систем с небольшим объёмом ОЗУ (конечно, всё зависит от сложности расчётов, но я бы сказал, что даже при очень больших объёмах данных это не станет критичным фактором, если у вас больше 512 МБ), а второе характерно только для старых версий пакета – в OpenOffice.org от 3.0 и выше извлечение данных из влияющих ячеек значительно оптимизировано, и формулы массивов уже не дают того прироста производительности, какой был раньше.
Единственным преимуществом массивов остаётся значительное, хотя и несколько меньшее, чем при сворачивании конвейеров, уменьшение размера файла, что, как уже говорилось, благотворно сказывается на скорости открытия документа.
Так что же, единственным методом оптимизации, дающим ощутимый прирост в скорости вычислений, является избегание воссоздания функций, встроенных в OpenOffice.org Calc? Нет, конечно – можно порекомендовать ещё несколько достаточно стандартных правил:
- Не делать лишних вычислений и следить за тем, чтобы алгоритм решения был оптимальным.
- Избегать повторных вычислений (например, если у вас есть формула вида E1=If(A1*B1+C1>0;A1*B1+C1;A1*B1+C1+D1), то её лучше разбить на две – E1=A1*B1+C1 и F1=If(E1>0;E1;E1+D1), чтобы не вычислять A1*B1+C1 дважды для каждой строки).
- При преобразовании типов избегайте явного преобразования, особенно когда это касается преобразования текста в число. Это может звучать несколько странно и непривычно для тех, кто знаком с программированием, но это справедливо для Calc – используя функцию VALUE (например, =VALUE(A1)*5 вместо =A1*5), вы можете потерять до 15 % производительности.
Я не буду приводить здесь описание подробного тестирования каждого из приёмов – размер статьи всё же ограничен, а результат применения может очень сильно зависеть от конкретного решения. При этом вы всегда можете сами протестировать любое решение на предмет производительности, используя находящийся на диске файл CalcSpeedTester.ods.
Применение описанных выше правил поможет вам сделать расчёты в Calc несколько более быстрыми, а работу комфортнее, но это ещё далеко не предел.
Визуализация и форматирование
Расчёты в электронных таблицах обычно не ограничиваются самими вычислениями – им сопутствует ещё и визуализация результата, от простого отображения чисел до сложного условного форматирования. И нет ничего неожиданного в том, что способ отображения может влиять на скорость вычислений, причём иногда очень сильно, а иногда и… странно.
Естественно, что разобраться в большом объёме данных зачастую довольно трудно, и в глазах рябит от цифр – трудно понять, где экстремумы функций, где положительные значения сливаются с отрицательными и так далее. В таком случае на помощь приходят возможности условного форматирования. Однако это, казалось бы, никак не связанное с вычислением результата возможность может, в некоторых случаях, замедлить его получение, а иногда и... ускорить его!
Рис. 3. Условное форматирование ячейки возможно как на основании ее значения, так и по формуле.
Как вы, наверное, знаете, существуют два способа задания условия форматирования ячейки с результатом – первый основан на сравнении значения ячейки с константой или значением другой ячейки, а второй основан на вычислении истинности формулы; рис. 3. Так вот, в использовании этих методов наличествует значительная разница. Первый из них совсем не страшен: он почти не снижает скорость расчётов, более того, при некоторых условиях способен значительно повышать её, иногда в разы. Причина такого поведения Calc остаётся неясной, но наблюдаемый эффект достаточно стабилен – ниже я приведу некоторые цифры. Второй способ (когда условием форматирования является истинность формулы), как и можно ожидать, даёт отрицательный эффект, особенно в том случае, если в формуле участвует ссылка, зависящая от значения форматируемой ячейки.
Теперь собственно о цифрах. Для теста я использовал вычисление стоимости тонны нефти в унциях золота, основываясь на цене унции золота и барреля нефти в долларах. Моя таблица исходных данных содержала почасовые котировки приблизительно за два года, а фактически 11 229 значений. Финалом вычисления при этом являлся расчёт средней цены нефти, выраженной в золоте, за указанный период.
Вычисление без форматирования занимало у меня в среднем около 700 миллисекунд. При условном форматировании ячейки, основанном на формуле вида $'Лист'.#X>$'Лист'.$J$2, где $'Лист'.#X – ссылка на форматируемую ячейку (например, $'Лист'.I2 для ячейки I2), а $'Лист'.$I$2 – ссылка на результат, время расчёта возросло до 770 миллисекунд (на 10 %), если лист с вычислениями и форматированием был неактивен, и до 880 миллисекунд (на 25 %), если вычисления выполнялись на активном листе.
Однако куда интереснее результаты измерений для форматирования с условием, опирающимся непосредственно на значение ячейки. Здесь, при вычислении на неактивном листе, скорость падала совершенно незначительно – на 3 % (720 миллисекунд). А вот на вычисление на активном листе Calc тратил всего 230 миллисекунд! То есть в данном случае прирост скорости был около 67 % – более чем в 3 (!) раза.
Этот эффект пропадает, если столбец с условным форматированием скрыть. Эффект наблюдается практически всегда, если при вычислении на активном (видимом) листе содержится хотя бы одна ячейка с условным форматированием, основанном на сравнении значения ячейки с другой ячейкой на этом же листе, даже если стиль данной ячейки при этом не изменяется.
При всесторонней проверке эффекта, иногда удавалось добиться аж десятикратного ускорения. Это действительно неожиданный результат и одновременно отличная возможность понизить временные затраты практически даром.
Диаграммы и графические объекты
Форматирование, конечно же, не единственный способ сделать данные более наглядными – часто, при обработке больших массивов данных, для графического представления результатов используются диаграммы. И нередко, особенно на не очень производительных компьютерах, при отображении диаграмм возникают ощутимые и досадные задержки, преимущественно во время их прокрутки. Ещё одной причиной раздражения пользователя могут стать графические объекты, расположенные на листах Calc. Кстати, обратите внимание, что примечания к ячейкам листа также являются векторными изображениями, и если документ cодержит большое количество примечаний с включенным непрерывным отображением (опция контекстного меню Показать примечание), это тоже может вызывать серьёзные задержки.
Вначале разберёмся с диаграммами и векторными изображениями, созданными здесь же – в самом Calc. Как правило, длительные задержки при прокрутке этих объектов вызваны включённым сглаживанием векторных объектов. Сглаживание делает линии более плавными, убирая эффект «лесенки», но при этом оно достаточно ресурсоёмко.
Рис. 4. Настройка отображения графических объектов.
Отключить сглаживание можно в настройках пакета: меню Сервис > Параметры..., затем в разделе OpenOffice.org открывшегося диалогового окна необходимо выбрать пункт Вид и снять галочку Использовать сглаживание, как показано на рис. 4.
Если ваш компьютер оснащён достаточным количеством оперативной памяти, вы также можете увеличить кэш графических объектов. Для этого необходимо выбрать пункт Память в том же разделе того же диалогового окна Параметры.... Если не жалко, под эти нужды пакета можно отвести до 256МБ. Кроме того, можно указать предел используемой памяти для кэширования одного изображения и время, че рез которое память, занятая под кэш, будет освобождена.
А как влияет наличие диаграммы на время вычисления результата? На скорость самого вычисления диаграмма не влияет практически никак, а вот на скорость отрисовки активного листа может повлиять достаточно ощутимо – при диаграмме для трёх рядов данных и 2000 значений задержка отрисовки листа у меня доходила до 3–4 секунд, и это при выключенном сглаживании; а если оно было включено, то задержка возрастала почти вдвое.
Причиной задержки при отображении вставленных графических объектов – как правило, растровых – может стать вставка объектов по ссылке, когда графический объект не интегрируется в файл документа, а продолжает оставаться на запоминающем устройстве удаленного источника. Здесь задержки возникают при обновлении объектов, при открытии и сохранении файла. Они могут стать особенно длительными, если графические объекты расположены в сети Интернет.
Рис. 5. Интеграция графического объекта в документ.
Чтобы решить эту проблему, необходимо интегрировать рисунки непосредственно в файл документа. Для этого в меню Правка выберите пункт Связи.... Откроется диалоговое окно Редактирование связей, содержащие список изображений, вставленных с сохранением связи с источником. Выберите все пункты списка и нажмите кнопку Разорвать связь – рис. 5. Все изображения будут получены из источников и сохранены в документе. Обратите внимание, что это может занять некоторое время.