Таким образом, на языке объектов не только эффективно реализуются не только общепринятые схемы решения систем уравнений (понимаемые как связи между переменными на нескольких временных слоях или итерациях), но и макро-схемы совместного решения нескольких систем.
Выше был описан процесс взаимодействия между элементами, совместно решающими систему уравнений через разделение её на несколько слабо связанных систем. В вычислительной математике существуют также пространственно-распределённые задачи, для которых разделение на подсистемы (каждая из которых соответствует одному сеточному узлу) является стандартным, менее искусственным и единственно возможным способом решения. В таких задачах есть условие локальности связей между сеточными узлами, благодаря которому элементу, рассчитывающему этот узел, достаточно взаимодействовать только с небольшим числом соседних узлов (узлов шаблона).
Ниже для краткости сам узел пространственной сетки (а не система уравнений, соответствующая ему) фигурирует в качестве расчётного элемента. Связям между такими элементами соответствует отношение упорядоченности сеточных узлов по координатам (в случае использования неструктурированных сеток это есть отношение соседства сеточных узлов) – см. рис 3.1.
Упомянутая выше локальность связей означает, что значение какого-либо параметра в рассчитываемом сеточном узле определяется только значениями в узлах шаблона (например,
). Поэтому любой численный метод оказывается привязанным к узлу, то есть к элементу, и для его реализации данный узел не обязан иметь информацию о пространственно-удалённых узлах, с которыми у него нет связей. Это полностью соответствует объектной модели. Если же формулировать аппроксимацию уравнений в частных производных на процедурно-ориентированном языке системы линейных уравнений, то получается следующее: сначала искусственно создаётся проблема в виде огромной разреженной матрицы этой системы, а затем эта проблема преодолевается с помощью каких-нибудь специальных способов хранения и обработки этой матрицы в памяти компьютера.
Рис. 3.1. Соответствие между традиционным и объектным представлением пространственной сетки
Взаимодействие между элементами, решающими систему уравнений в частных производных, – сеточными узлами – носит принципиально иной характер, нежели взаимодействие между элементами-системами. Системы связаны между собой только через общие параметры, которые для одной системы являются искомыми, а для других входят в коэффициенты или правые части уравнений. Сеточные узлы отнюдь не всегда должны использовать напрямую параметры, принадлежащие к соседним узлам шаблона. Для получения необходимой информации о соседнем узле (которая может представлять собой сложную комбинацию его параметров) часто удобнее вызывать его метод. Вообще, вычислительные алгоритмы могут быть рассредоточены по методам нескольких разнотипных элементов, которые вызывают друг друга в процессе расчётов.
Взаимодействие сеточных узлов во времени также более интенсивно, чем описанные выше для элементов-систем последовательные переходы на следующий слой. Исследование устойчивости алгоритмов возможно лишь, если все сеточные узлы (по крайней мере, все узлы одной области интегрирования) обновляют своё состояние с одним и тем же шагом по времени. Поэтому элементы-узлы, в отличие от слабо связанных систем, не могут быть упорядочены по времени в виде очереди, и возникает проблема последовательности их обновления. Для решения этой проблемы естественно создавать специальный объект – область интегрирования, – который ответственен за порядок перехода сеточных узлов на следующий временной слой (или на следующую итерацию). Однако важен не только сам порядок перехода, но и то, какие значения параметров соседних элементов (до перехода или после) использует сеточный узел при расчёте своих параметров. Поэтому каждый узел должен также хранить информацию о том, обновлялось ли его состояние на данном временном шаге (итерации). Приведённое рассуждение касается не только систем с распределёнными параметрами, но и любых подсистем, взаимодействие элементов которых слишком интенсивно, чтобы позволять им в хаотическом порядке обновлять своё состояние.
Таким образом, при решении уравнений в частных производных ещё более эффективно, чем в других разделах вычислительной математики, используется объектно-ориентированный подход. Это проявляется как в более активном взаимодействии основных расчётных элементов (сеточных узлов), так и в появлении в дополнение к элементам и их параметрам ещё одного, более высокого, уровня объектов – уровня подсистем (областей интегрирования).
3.2.4. Ресурсоёмкость
Важным свойством, характеризующим любой численный метод, является его требовательность к машинным ресурсам. Эти ресурсы делятся на два основных типа – ресурсы процессорного времени и ресурсы оперативной памяти. Считается, что использование объектно-ориентированных языков программирования ведёт к большим затратам и того, и другого (по сравнению с процедурными языками) [4]. Следовательно, приемлемость объектов в качестве вычислительных единиц подлежит обоснованию.
Действительно, с каждым объектом необходимо связывать некоторую метаинформацию, для размещения которой расходуется дополнительная память. Относительное количество такой памяти можно было бы существенно уменьшить, если минимальными объектами в расчётной программе являлись бы элементы (даже при решении распределённых задач элементы – сеточные узлы – содержат намного больше полезной информации, чем той, которая необходима лишь для их существования в виде объектов). Однако, как показано в разделе 3.2.2, минимальным объектом должен являться параметр, содержащий всего несколько вещественных чисел; поэтому сокращать расходование памяти путём увеличения размера объектов вряд ли целесообразно.
Объектно-ориентированный подход может экономить память вычислительных программ за счёт совершенно других механизмов. Дело в том, что в процедурно-ориентированных программах часто встречаются массивы большой длины, заведомо достаточной для хранения всех параметров задачи, хотя часто выделенная под них память используется лишь на несколько процентов. Даже если язык поддерживает задание длины массива во время выполнения программы (в FORTRAN такая возможность появилась только в стандарте 1990-го г.), не всегда этим можно пользоваться, поскольку длина массива во время выполнения может неоднократно меняться. И уж совсем непреодолимые затраты памяти возникают при использовании многомерных массивов, когда для разных значений их медленно меняющегося индекса быстро меняющийся индекс должен изменяться в различных пределах. ООП решает эти проблемы автоматически, поскольку массив является объектом. В частности, элементы многомерного массива – тоже массивы – могут иметь различную длину. Помимо самих массивов, в объектно-ориентированные языки обычно включаются классы – оболочки массивов, которые могут динамически (оптимальным образом) менять их длину в зависимости от реального количества элементов в них. (Правда, динамическое изменение длины плохо отражается на быстродействии.)
Если же посмотреть на проблему выделения излишнего количества памяти с точки зрения численных методов, то можно заметить малое количество (всего несколько процентов) ненулевых элементов матриц Якоби, которые встречаются при решении больших систем обыкновенных дифференциальных уравнений. Поскольку объектно-ориентированные численные методы не используют матричное представление систем уравнений, они позволяют (в разы и даже в десятки раз) сократить количество необходимой памяти для их хранения. Одновременно уменьшается число лишних арифметических операций (умножений на нуль, приращений переменной цикла и т.д.).
Использование объектно-ориентированных языков увеличивает расходование ресурсов процессора, но в них существуют механизмы, максимально снижающие этот эффект. Действительно, наследование и полиморфизм уменьшают скорость вычислений, поскольку при вызове какого-либо метода у элемента происходит выбор среди одноимённых методов его класса и родительских классов (этот выбор называется динамическим, или поздним, связыванием). Однако быстродействие можно повысить, например, путём ещё одного наследования от данного класса и объявления полученного класса конечным (не способным иметь подклассов). В языке Java это позволяет компилятору не использовать динамическое связывание методов полученного класса, а также осуществлять автоматическую замену вызова методов их кодом, если он не слишком велик. В некоторых языках (С++) такая замена (inline-подстановка) может быть явно затребована программистом.
Следует также заметить, что с точки зрения быстродействия объектно-ориентированные численные методы имеют и свои преимущества. Дело в том, что в стандартной процедуре решения задачи математической физики значения параметров представляются в виде массивов большой длины (по числу сеточных узлов), доступ к которым осуществляется в одном или нескольких циклах по узлам. Поэтому время расходуется не только на расчёт, но и на сложение инкрементируемых переменных циклов с числами 1, -1, 2, -2 и т. д. (см. рис. 3.1) и на доступ к значениям параметров в массиве (вычисление адреса памяти по индексу массива). В случае объектной реализации численных методов в элементах хранятся явные ссылки на соседние элементы, у которых можно явно запросить значения их параметров, безо всякой арифметики адресов. При этом требуется больше памяти, но зато исключаются значительные затраты на целочисленную арифметику.