Предисловие В третьем томе Трудов Института системного программирования публикуются статьи, посвященные результатам некоторых важных исследовательских проектов, выполнявшихся в Институте в последние годы. Работа А.В. Чернова «Анализ запутывающих преобразований программ» посвящена исследованию свойств преобразований программ, применяемых для их маскировки. Маскировка программы состоит в таком ее преобразовании, что преобразованная программа (она называется замаскированной или запутанной) обладает следующими двумя свойствами: 1. На всех допустимых для исходной программы входных данных замаскированная программа выдаёт тот же самый результат, что и исходная, 2. Замаскированную программу существенно труднее анализировать, понимать и модифицировать, чем исходную программу. Одним из способов получения замаскированной программы является применение маскирующих преобразований к исходной программе. В рассматриваемой работе маскирующие преобразования графа потока управления программы, опубликованные к настоящему времени, изучаются с точки зрения их устойчивости к различным видам анализа программ. Приведена классификация маскирующих преобразований с точки зрения методов анализа программ, которые могут быть использованы для восстановления исходной программы. Показано, что для каждого класса рассмотренных маскирующих преобразований существуют методы анализа программ, которые позволяют эффективно противодействовать этим преобразованиям. Приведены примеры распутывания программ, как замаскированных вручную, так и с помощью автоматических маскировщиков. В последние годы широкое распространение получили многопроцессорные вычислительные системы, называемые кластерами. Кластер можно определить как множество рабочих станций (узлов кластера), связанных коммуникационной средой и способных работать как единая вычислительная система. Работой каждого узла кластера управляет операционная система (как правило, это одна из версий ОС Linux), но для управления работой кластера в целом требуется дополнительное программное обеспечение, называемое системой управления кластером. От качества проектных решений, принятых при разработке системы управления кластером во многом зависит одно из важнейших свойств кластера – его масштабируемость (кластер называется масштабируемым, если добавление новых узлов пропорционально повышает его производительность). В ИСП РАН построен кластер на базе восьми двухпроцессорных узлов AMD Athlon XP, объединенных коммутационной системой Myrinet. Ведется разработка системы управления для этого кластера. В обзорной статье А.И. Аветисяна, Д. А. Грушина и А.Г. Рыжова «Системы управления кластерами» 3
рассмотрены наиболее интересные из существующих систем управления кластерами, с целью обосновать решения, принятые ими при проектировании системы управления кластером. Система управления кластером обеспечивает удаленный доступ к кластеру и управление ресурсами кластера, когда он применяется для выполнения нескольких программ (последовательных или параллельных). В статье П.Н Яковенко. «Средства анализа параллельных SPMD программ» представлен обзор методов и средств анализа производительности и масштабируемости SPMD-программ при их выполнении на параллельных вычислительных системах с распределенной памятью. Основной акцент сделан на такие проблемы организации отладки параллельных программ, как визуализация различных аспектов выполнения программы, моделирование параллельной программы при помощи алгебраических формул, зависящих от размерности задачи и числа процессоров, мониторинг (контролируемое выполнение). Обзор основан на анализе большого числа отладочных средств, разрабатываемых в рамках академических проектов, а также популярных коммерческих продуктов. Цель обзора – обоснование проекта подсистемы отладки и мониторинга параллельных программ интегрированной среды ParJava. В статье О.И. Самоварова, И.В. Арапова, В.В. Бабковой «Объектная модель JSCALA» рассматривается объектная модель, реализующая функциональность пакета прикладных программ SCALAPACK в среде ParJava. Эта модель позволяет создавать переносимые, масштабируемые, параллельные программы решения задач линейной алгебры в среде ParJava. На примере модели JSCALA показана методика включения в среду ParJava высокоуровневых объектных моделей параллельного программирования. В статье В.Н. Юдина “Система информационной поддержки врачебных решений, основанная на модифицированном методе динамического кластерного анализа” описывается система «Спутник Врача», предназначенная для информационной поддержки врачебных решений в медицине с использованием современных информационных технологий, в частности, методов распознавания образов. В системе используется общая методика принятия решений с использованием дифференциального ряда и метода аналогий. В отличие от применяемых в медицине экспертных систем, «Спутник Врача» помогает принимать решения в условиях неоднозначной классификации при неполном наборе показателей пациента. Метод аналогий позволяет врачу предвидеть случаи, когда заболевание выходит за пределы своей обычной симптоматики, проявляясь через симптомы другого заболевания. В качестве математического аппарата используется метод кластерного анализа, модифицированный для работы с переменным набором признаков. Приводится формальная постановка предлагаемого метода классификации. На основе метода создан Универсальный Количественный Классификатор, 4
который может использоваться как классифицирующий модуль в системах для оценки состояний, прогнозирования и принятия решений. Приводится пример классификации объекта, у которого часть признаков отсутствует. В статье описывается конкретная система медицинской диагностики, но разработанные методы могут быть применены во многих других областях. Статья А.А. Жданова и М.В. Караваева “Применение нечеткой логики в имитационной системе автономного адаптивного управления” посвящена исследованию возможностей объединения в одной управляющей системе двух технологий управления – нечеткой логики и метода «автономного адаптивного управления». Обладая своими достоинствами и ограничениями, оба метода при объединении дают новое качество, наделяя систему управления новыми свойствами: 1) системы нечеткой логики оказываются удобными для представления в адаптивной системе управления априорной информации о свойствах объекта управления. Это позволяет в процессе работы системы существенно сократить фазу обучения и быстрее достигать высокого качества управления; 2) использование подсистемы нечеткой логики в адаптивной системе управления повышает как качество управления так и эффективность метода ААУ в целом. Основной результат работы состоит в том, что впервые показана возможность использования нечеткой логики для адаптивного управления, т.е. для управления, автоматически изменяющего свои свойства непосредственно в процессе управления, в то время как обычным использованием нечеткой логики является построение систем управления на основе знаний эксперта В статье В.В. Рубанова “Способы отображения объектов в реляционных базах данных” рассматривается современное состояние в области алгоритмов хранения данных объектно-ориентированных программ в реляционных базах данных. Центральное место занимает исследование различных способов организации объектно-реляционного отображения. Этот механизм является основной проблемой при создании систем обеспечения долговременного хранения объектов в реляционных базах данных. Проблема возникает из-за сильного несоответствия понятий объектной и реляционных моделей. Целью статьи является анализ специфики каждой из этих моделей для нахождения возможных путей решения исходной задачи путем комбинации классических и современных решений. Статья В.В. Рубанова, М.А. Миткевича, Д.А. Марковцева, А.И. Гриневич “Ядро объектно-реляционной системы ODESTOR” посвящена описанию ядра системы. Обсуждаются возможности системы при прямом и обратном проектировании приложений. В первом случае реляционные таблицы могут быть сформированы системой автоматически. Во втором случае имеется заданная структура реляционных таблиц, и требуется с помощью специальных средств описать, как объектные данные будут отражаться в данных таблицах. Член-корреспондент РАН
В.П. Иванников 5
6
Анализ запутывающих преобразований программ Чернов А. В. E-mail:
[email protected] Аннотация. Запутанной (obfuscated) называется программа, которая на всех допустимых для исходной программы входных данных выдаёт тот же самый результат, что и оригинальная программа, но более трудна для анализа, понимания и модификации. Запутанная программа получается в результате применения к исходной незапутанной программе запутывающих преобразований (obfuscating transformations). Данная работа посвящена анализу запутывающих преобразований графа потока управления программы (функции), опубликованных в открытой печати, с точки зрения их устойчивости к различным видам анализа программы. В данной работе запутывание изучается на уровне языка Си, то есть исходная программа написана на языке Си, и целевая запутанная программа генерируется также на языке Си. В работе приведена классификация запутывающих преобразований с точки зрения методов анализа программ, которые могут быть использованы для их распутывания. Показано, что для каждого класса рассмотренных запутывающих преобразований существуют методы анализа, которые позволяют эффективно противодействовать таким преобразованиям. Для иллюстрации этого приведены несколько практических примеров распутывания программ, запутанных как вручную, так и автоматическими запутывателями.
1. Введение В данной работе исследуется проблема анализа запутывающих преобразований графа потока управления функций на языке Си. В работе сделана попытка анализа запутывающих преобразований, опубликованных в открытой печати, с точки зрения их устойчивости к различным видам статического и динамического анализа программ. Запутывание изучается на уровне языка Си, то есть и исходная программа написана на языке Си, и целевая запутанная программа генерируется также на языке Си. В работе приведена классификация запутывающих преобразований с точки зрения методов анализа программ, которые могут быть использованы для их распутывания. Показано, что для каждого класса рассмотренных запутывающих преобразований существуют методы анализа, которые 7
позволяют эффективно противодействовать таким преобразованиям. Для иллюстрации этого приведены несколько практических примеров распутывания программ, запутанных как вручную, так и автоматическими запутывателями. Задачи запутывания и анализа запутанных программ имеют три аспекта: теоретический, включающий в себя разработку новых алгоритмов преобразования графа потока управления или трансформации данных программы, а также теоретическую оценку сложности их анализа и раскрытия. Прикладной аспект включает в себя разработку конкретных методов запутывания (распутывания), то есть наилучших комбинаций алгоритмов, эмпирический сравнительный анализ различных методов, эмпирический анализ устойчивости методов, и т. д. Третий аспект, психологический пока не поддаётся формализации, но не может игнорироваться. Обратная инженерия (понимание) программ – это процесс, результатом которого является некоторое знание субъекта, изучающего программу, который является неотъемлемой частью процесса понимания [18]. Методы запутывания должны максимально использовать свойства (точнее, слабости) человеческой психики. Не умаляя ценности теоретических исследований, следует заметить, что теоретические выводы должны подтверждаться результатами практического применения предложенных методов. В данной работе исследуется прикладной аспект задачи запутывания. В настоящее время широко распространены языки программирования, такие как Java, в которых «исполняемой» формой программы является не машинный код для некоторого типа процессоров, но машинно-нейтральное представление. Задача декомпиляции программы из такого представления обратно в программу на языке Java значительно проще, чем декомпиляция из машинного кода. Существует большое число декомпиляторов для языка Java как распространяемых свободно, так и коммерческих, например [20], что упрощает несанкционированное использование, обратную инженерию и модификацию Java-программ. В качестве одного из способов борьбы с этим рассматривается запутывание программ. Уже разработано около двух десятков различных запутывателей Javaпрограмм, среди которых есть и коммерческие, например [25]. Простые запутыватели удаляют таблицы символов и отладочную информацию из скомпилированных классов и заменяют исходные имена методов бессмысленными короткими именами. В результате размер файлов уменьшается (до 50%), а скорость выполнения программы значительно возрастает, поэтому такое запутывание может рассматриваться и как один из способов оптимизации программ. Более развитые запутыватели программ на языке Java, а также запутыватели программ на других языках программирования выполняют преобразования 8
графа потока управления программы и её структур данных. Методы, используемые в них, как правило, подобраны эмпирически и слабо обоснованы теоретически. Сравнительный анализ запутывателей Java-программ, доступных через Интернет, проведён в работе [14]. Возможны разные уровни постановки задачи запутывания и анализа запутывающих преобразований. Во-первых, запутывание может рассматриваться в рамках языка Java. В этом случае исходная программа написана на языке Java, и запутанная программа также написана на языке Java. Однако язык Java допускает только структурные программы, то есть графы потока управления Java-программ всегда сводимые, что существенно ограничивает диапазон применимых преобразований графа потока управления. Мы рассматриваем задачу анализа запутывающих преобразований в рамках языка Си. Поскольку Си – язык более низкого уровня, чем Java или даже байткод Java, задачи запутывания и анализа для этих языков оказываются вложенными в соответствующие задачи для языка Си. Возможна постановка задачи запутывания на ещё более низком уровне, когда запутывается программа на языке ассемблера или даже объектная программа в машинном коде (в последнем случае она должна генерироваться специальным запутывающим компилятором). В ассемблерных и объектных программах можно использовать специфические особенности работы целевой машины, добившись того, что восстановление программы на Си будет крайне затруднено [17]. Но с другой стороны, методы запутывания, применимые к одной архитектуре, могут оказаться неприменимы к другой архитектуре. Заметим, что проблема низкоуровневого запутывания в настоящее время исследована слабо. Нам не известно каких-либо опубликованных методов низкоуровневого запутывания программ, поэтому проблему низкоуровневого запутывания мы в этой работе рассматривать не будем. Если программа для анализа представлена в исполняемом или объектном коде, и известно, что к программе не применялись низкоуровневые методы запутывания, задача анализа таких программ может быть разбита на две относительно независимых подзадачи. На первом этапе программа декомпилируется [4] в программу на языке Си, затем программа на языке Си распутывается, то есть применяются алгоритмы анализа программ, которые приводят к её возможной перестройке, выделению в ней циклов, условных операторов и других конструкций высокого уровня. Декомпиляция программ – самостоятельная задача, которая может решаться отдельно. В данной работе мы ограничим класс запутываемых программ программами пакетной обработки, то есть программами, которые получают все исходные данные в начале работы и выдают результат по ходу работы. Во время работы программа не взаимодействует с пользователем и другими программами. Кроме того, потребуем, чтобы программа не использовала аппарат исключений в работе. Появление исключительной ситуации приводит к завершению работы
программы. Эти ограничения связаны с тем, что все опубликованные методы запутывания применимы только к таким программам. Запутывание программ – достаточно молодое направление исследований. Обзор (таксономия) запутывающих преобразований, известных на тот момент, был опубликован в работе [5] группы, возглавляемой К. Колбергом и К. Томборсоном. В дальнейших работах [6], [7], [8], [9], [15] этой группы опубликованы результаты исследований конкретных алгоритмов запутывания графа потока управления и данных программы, а также приложения запутывания программ к смежным областям, таким как обеспечение устойчивости программы к несанкционированной модификации (tamperresistance) или внесение в программу «водяных знаков» (watermarking). Классификация, введённая в работе [5], широко используется, и получила дальнейшее развитие в работах [10], [13], [22]. В работах [23], [24] был предложен новый подход к запутыванию графа потока управления программы, который заключается в преобразовании графа в «плоскую» форму. Чтобы затруднить статическое определение порядка следования базовых блоков используется преобразование, вводящее в программу алиасы. Показывается, что статический анализ запутанной программы с целью восстановления порядка следования базовых блоков является NP-трудной задачей. В дальнейшем этот подход был развит в работе [3], которая дополнительно предлагает использовать переплетение базовых блоков совместно запутываемых функций и недетерминированный выбор следующего базового блока из множества эквивалентных альтернатив. Доказывается, что статический анализ запутанной программы с целью восстановления порядка следования базовых блоков является PSPACE-трудной задачей. В работе [2] получен результат, который определяет верхний предел силы запутывающих преобразований. Авторы доказали, что универсального запутывателя не существует. Под универсальным понимается такой запутыватель, который для любой программы строит запутанную программу, такую что определение любого свойства программы, легко определимого по исходной программе, неэффективно по запутанной программе. Тем не менее, можно показать, что если взять некоторое специальное свойство программ, то запутыватель для этого свойства всё же существует. Вопрос о том, для каких классов программ и каких свойств запутыватель существует, остаётся открытым. В данной работе рассматриваются только запутывающие преобразования графа потока управления программы. Мы сознательно оставляем в стороне преобразования данных программы, а также так называемые превентивные преобразования, которые нацелены против определённых методик декомпиляции программы, реализованных в определённых декомпиляторах. 10
9
Мы не пытаемся подтвердить или опровергнуть тезис работы [2] о невозможности запутывания программ, но мы делаем попытку показать, что для всех опубликованных в открытой печати методов запутывания программ существуют достаточно эффективные практически, хотя, возможно, пока не совсем обоснованные теоретически способы противодействия. Данная работа имеет следующую структуру. В разделе 2 даётся формальное определение понятия запутывания, приводится классификация запутывающих преобразований, описываются запутывающие преобразования графа потока управления. В разделе 3 описываются наиболее важные методы, которые применяются на различных стадиях работы компилятора и могут быть использованы для получения информации о запутанной программе, а также специальные методы анализа запутанных программ. В разделе 4 методы запутывания программ сопоставляются с методами их анализа, вводится классификация методов запутывания по уровню необходимых преобразований распутывания. В разделе 5 приводятся примеры применения некоторых методов анализа программы для распутывания программ. Наконец, в разделе 5.3 подводятся итоги и указываются направления дальнейшей работы.
2. Запутывание В этом разделе мы дадим формальное определение запутывателя свойства πкласса программ P. Мы введём некоторые показатели качества запутывающих преобразований и перечислим разнообразные запутывающие преобразования, каждое из которых по отдельности усложняет граф потока управления программы. Опубликованные методы запутывания, которые в настоящее время применяются в программных инструментах, как свободно-распространяемых, так и коммерческих, являются комбинацией нескольких перечисленных запутывающих преобразований. Некоторые из методов запутывания будут описаны в конце раздела. В дальнейшем мы будем рассматривать программы из класса Π неинтерактивных, нереактивных программ. Программе поступают на вход данные характерного размера n. При работе программа не возбуждает и не использует исключительных ситуаций.
вычисляет функцию f p : Input → Output , подмножество π ⊆ Π называется функциональным свойством если ∀p1 , p 2 ∈ Π ( f p1 = f p2 ⇒ ( p1 ∈ π ⇔ p 2 ∈ π )) . 2. Пусть π – функциональное свойство, P ⊆ Π – класс программ такой, что существует эффективная программа c такая, что для любой программы p∈P ⎧1, если p ∈ π c( p) = ⎨ ⎩0, если p ∉ π Другими словами, для функционального свойства π мы определяем класс программ P таких, что существует эффективная программараспознаватель c свойства π по программе p из класса P . 3. Вероятностная программа o называется запутывателем класса P относительно свойства π , ( P , π )-запутывателем, если выполняются условия: (эквивалентность преобразования запутывания). Для любой p ∈ P и p ′ ∈ o( p ) f p = f p′ , p ′ = poly ( p ) ,
∀x ∈ Dom f p
time p′ ( x) = poly (time p ( x))
Здесь y = poly ( x ) означает, что y ограничен полиномом некоторой степени от переменной x , time p ( x ) – время выполнения программы p на входе x , p – размер программы p .
(трудность определения свойств по запутанной программе). Для любого полинома q и для любой программы (вероятностной машины Тьюринга) a такой, что a(o( P)) = {0,1} , и для любой p ∈ P выполняется timea (o( p )) = poly ( o( p ) ) , существует программа
2.1. Определение Приводимое здесь определение сформулировано В. А. Захаровым. Эффективное вычисление – это вычисление, требующее полиномиального от длины входа времени и полиномиальной от длины входа памяти. Эффективная программа (машина Тьюринга) – программа, работающая полиномиальное от длины входа время и требующая полиномиальную от длины входа рабочую память на всех входах, на которых программа завершается. 1. Пусть Π – множество всех программ (машин Тьюринга), удовлетворяющих сформулированным выше ограничениям, и пусть программа p ∈ Π 11
(вероятностная машина Тьюринга с оракулом) b , и при этом для 1 p любой p ∈ P | Pr( a(o( p )) = c ( p )) − Pr(b p (1 ) = c( p )) |≤ . q(| p |) Другими словами, вероятность определить свойство π по запутанной программе равна вероятности определения свойства π только по входам и выходам функции f p . То есть, наличие текста запутанной программы ничего не даёт для выявления свойств этой программы. 12
Универсальный запутыватель – это программа O , которая для любого класса программ P и любого свойства π является ( P , π )-запутывателем. Как показано в работе [2], универсального запутывателя не существует. Доказательство заключается в построении специального класса программ P и выборе такого свойства π , что для любого преобразования программы из этого класса свойство π устанавливается легко. Однако вопрос о том, существуют ли запутыватели для отдельных классов свойств программ, и насколько широки и практически значимы эти классы свойств, остаётся открытым. С практической точки зрения запутывание программы можно рассматривать как такое преобразование программы, которое делает её обратную инженерию экономически невыгодной. Несмотря на слабую теоретическую проработку, уже разработано большое количество инструментов для запутывания программ.
оценивается как дорогое, если выполнение программы p ′ требует экспоненциально больше ресурсов, чем выполнение исходной программы. Практически применимыми являются, по-видимому, только бесплатные и дешёвые методы запутывания.
2.3. Описание запутывающих преобразований Запутывающие преобразования можно разделить на несколько групп в зависимости от того, на трансформацию какой из компонент программы они нацелены.
2.2. Стоимость запутывающих преобразований Запутывание преобразует программу, затрудняя её обратную инженерию. В результате запутанная программа может оказаться больше по размеру и работать медленнее. Стоимость (cost) преобразования [5] – это метрика, которая позволяет оценить, насколько больше требуется ресурсов (памяти, процессорного времени) для выполнения запутанной программы, чем для выполнения исходной программы. Стоимость определяется по следующей шкале: 〈бесплатная, дешёвая, умеренная, дорогая〉. Стоимость преобразования позволяет оценить, насколько увеличивается размер функции в результате запутывания. Бесплатное преобразование увеличивает размер функции на O(1), дешевое преобразование увеличивает размер на O(m), где m – размер функции, умеренное по стоимости преобразование увеличивает размер функции на O(m p ) , где p > 1 . Наконец, дорогое преобразование экспоненциально увеличивает размер запутанной функции по сравнению с исходной. Стоимость выполнения позволяет оценить, насколько больше требуется ресурсов при выполнении программы. Стоимость оценивается как функция от характерного размера входных данных n. Преобразование оценивается как бесплатное, если выполнение преобразованной программы p ′ требует на O(1) больше ресурсов, чем выполнение оригинальной программы. Преобразование оценивается как дешёвое, если выполнение программы p ′ требует на O (n) ресурсов больше, чем выполнение исходной программы, где n – размер входных данных. Преобразование оценивается как умеренное по стоимости, если выполнение программы p ′ требует на O(n p ) больше ресурсов, где p >1 . Преобразование
Преобразования форматирования, которые изменяют только внешний вид программы. К этой группе относятся преобразования, удаляющие комментарии, отступы в тексте программы или переименовывающие идентификаторы.
Преобразования структур данных, изменяющие структуры данных, с которыми работает программа. К этой группе относятся, например, преобразование, изменяющее иерархию наследования классов в программе, или преобразование, объединяющее скалярные переменные одного типа в массив. В данной работе мы не будем рассматривать запутывающие преобразования этого типа.
Преобразования потока управления программы, которые изменяют структуру её графа потока управления, такие как развёртка циклов, выделение фрагментов кода в процедуры, и другие. Данная статья посвящена анализу именно этого класса запутывающих преобразований.
Превентивные преобразования, нацеленные против определённых методов декомпиляции программ или использующие ошибки в определённых инструментальных средствах декомпиляции.
2.3.1. Преобразования форматирования К преобразованиям форматирования относятся удаление комментариев, переформатирование программы, удаление отладочной информации, изменение имён идентификаторов. Удаление комментариев и переформатирование программы применимы, когда запутывание выполняется на уровне исходного кода программы. Эти преобразования не требуют только лексического анализа программы. Хотя удаление комментариев – одностороннее преобразование, их отсутствие не затрудняет сильно обратную инженерию программы, так как при обратной инженерии наличие хороших комментариев к коду программы является скорее исключением, чем правилом. При переформатировании программы исходное форматирование теряется безвозвратно, но программа всегда может быть 14
13
переформатирована с использованием какого-либо инструмента для автоматического форматирования программ (например, indent для программ на Си). Удаление отладочной информации применимо, когда запутывание выполняется на уровне объектной программы. Удаление отладочной информации приводит к тому, что имена локальных переменных становятся невосстановимы. Изменение имён локальных переменных требует семантического анализа (привязки имён) в пределах одной функции. Изменение имён всех переменных и функций программы помимо полной привязки имён в каждой единице компиляции требует анализа межмодульных связей. Имена, определённые в программе и не используемые во внешних библиотеках, могут быть изменены произвольным, но согласованным во всех единицах компиляции образом, в то время как имена библиотечных переменных и функций меняться не могут. Данное преобразование может заменять имена на короткие автоматически генерируемые имена (например, все переменные программы получат имя v<номер> в соответствии с их некоторым порядковым номером). С другой стороны, имена переменных могут быть заменены на длинные, но бессмысленные (случайные) идентификаторы в расчёте на то, что длинные имена хуже воспринимаются человеком.
2.3.2. Преобразования потока управления Преобразования потока управления изменяют граф потока управления одной функции. Они могут приводить к созданию в программе новых функций. Краткая характеристика методов приведена ниже. Открытая вставка функций (function inlining) [5], п. 6.3.1 заключается в том, что тело функции подставляется в точку вызова функции. Данное преобразование является стандартным для оптимизирующих компиляторов. Это преобразование одностороннее, то есть по преобразованной программе автоматически восстановить вставленные функции невозможно. В рамках данной статьи мы не будем рассматривать подробно прямую вставку функций и её эффект на запутывание и распутывание программ. Вынос группы операторов (function outlining) [5], п. 6.3.1. Данное преобразование является обратным к предыдущему и хорошо дополняет его. Некоторая группа операторов исходной программы выделяется в отдельную функцию. При необходимости создаются формальные параметры. Преобразование может быть легко обращено компилятором, который (как было сказано выше) может подставлять тела функций в точки их вызова. Отметим, что выделение операторов в отдельную функцию является сложным для запутывателя преобразованием. Запутыватель должен провести глубокий анализ графа потока управления и потока данных с учётом указателей, чтобы быть уверенным, что преобразование не нарушит работу программы. 15
Непрозрачные предикаты (opaque predicates) [5], п. 6.1. Основной проблемой при проектировании запутывающих преобразований графа потока управления является то, как сделать их не только дешёвыми, но и устойчивыми. Для обеспечения устойчивости многие преобразования основываются на введении непрозрачных переменных и предикатов. Сила таких преобразований зависит от сложности анализа непрозрачных предикатов и переменных. Переменная v является непрозрачной, если существует свойство π относительно этой переменной, которое априори известно в момент запутывания программы, но трудноустанавливаемо после того, как запутывание завершено. Аналогично, предикат P называется непрозрачным, если его значение известно в момент запутывания программы, но трудноустанавливаемо после того, как запутывание завершено. Непрозрачные предикаты могут быть трёх видов: P F – предикат, который всегда имеет значение «ложь», P T – предикат, который всегда имеет значение «истина», и P ? – предикат, который может принимать оба значения, и в момент запутывания текущее значение предиката известно. В работах [3], [7], [23] разработаны методы построения непрозрачных предикатов и переменных, основанные на «встраивании» в программу вычислительно сложных задач, например, задачи 3-выполнимости1. Некоторые возможные способы введения непрозрачных предикатов и непрозрачных выражений вкратце перечислены ниже.
1
Использование разных способов доступа к элементы массива [23]. Например, в программе может быть создан массив (скажем, a), который инициализируется заранее известными значениями, далее в программу добавляются несколько переменных (скажем, i}, j), в которых хранятся индексы элементов этого массива. Теперь непрозрачные предикаты могут иметь вид a[i] == a[j]. Если к тому же переменные i} и j в программе изменяются, существующие сейчас методы статического анализа алиасов позволят только определить, что и i, и j могут указывать на любой элемент массива a.
Использование указателей на специально создаваемые динамические структуры [7]. В этом подходе в программу добавляются операции по созданию ссылочных структур данных (списков, деревьев), и
Задача формулируется следующим образом: дана булевская формула σ
σ2
1
2
f ( x1 , K , x n ) = C1 ∧ C 2 ∧ K ∧ C m , где C i = xi 1 ∨ xi
σ
∨ x i 3 , над множеством 3
переменных X = {x1 , K , x n } . Определить, существует ли такой набор значений переменных X , при котором значение f равно 1. Известно, что эта задача NP-полна. 16
добавляются операции над указателями на эти структуры, подобранные таким образом, чтобы сохранялись некоторые инварианты, которые и используются как непрозрачные предикаты.
значение, уже вычисленное ранее. Для внесения избыточного кода можно использовать алгебраические преобразования выражений исходной программы или введение в программу математических тождеств. Например, можно 8
Конструирование булевских выражений специального вида [3].
Построение сложных булевских выражений с помощью эквивалентных преобразований из формулы true. В простейшем случае мы можем взять k произвольных булевских переменных x1 , K , x k и построить из
воспользоваться комбинаторным тождеством
них тождество ( x1 ∨ x1 ) ∧ K ∧ ( x k ∨ x k ) . Далее с помощью эквивалентных алгебраических преобразований часть скобок (или все) раскрываются, и в результате получается искомый непрозрачный предикат. n
Использование комбинаторных тождеств, например
∑ C ni = 2 n . i =0
Внесение недостижимого кода (adding unreachable code). Если в программу внесены непрозрачные предикаты видов P F или P T , ветки условия, соответствующие условию «истина» в первом случае и условию «ложь» во втором случае, никогда не будут выполняться. Фрагмент программы, который никогда не выполняется, называется недостижимым кодом. Эти ветки могут быть заполнены произвольными вычислениями, которые могут быть похожи на действительно выполняемый код, например, собраны из фрагментов той же самой функции. Поскольку недостижимый код никогда не выполняется, данное преобразование влияет только на размер запутанной программы, но не на скорость её выполнения. Общая задача обнаружения недостижимого кода, как известно, алгоритмически неразрешима. Это значит, что для выявления недостижимого кода должны применяться различные эвристические методы, например, основанные на статистическом анализе программы. Внесение мёртвого кода (adding dead code) [5], п. 6.2.1. В отличие от недостижимого кода, мёртвый код в программе выполняется, но его выполнение никак не влияет на результат работы программы. При внесении мёртвого кода запутыватель должен быть уверен, что вставляемый фрагмент не может влиять на код, который вычисляет значение функции. Это практически значит, что мёртвый код не может иметь побочного эффекта, даже в виде модификации глобальных переменных, не может изменять окружение работающей программы, не может выполнять никаких операций, которые могут вызвать исключение в работе программы. Внесение избыточного кода (adding redundant code) [5], п. 6.2.6. Избыточный код, в отличие от мёртвого кода выполняется, и результат его выполнения используется в дальнейшем в программе, но такой код можно упростить или совсем удалить, так как вычисляется либо константное значение, либо 17
∑ C8i = 28 = 256
и заменить
i =0
везде в программе использование константы 256 на цикл, который вычисляет сумму биномиальных коэффициентов по приведённой формуле. Подобные алгебраические преобразования ограничены целыми значениями, так как при выполнении операций с плавающей точкой возникает проблема накопления ошибки вычислений. Например, выражение sin 2 x + cos 2 x при вычислении на машине практически никогда не даст в результате значение 1. С другой стороны, при операциях с целыми значениями возникает проблема переполнения. Например, если использование 32-битной целой переменной x заменено на выражение x*p/q, где p и q гарантированно имеют одно и то же значение, при выполнении умножения x*p может произойти переполнение разрядной сетки, и после деления на q получится результат не равный p. В качестве частичного решения задачи можно выполнять умножение в 64битных целых числах. Преобразование сводимого графа потока управления к несводимому (transforming reducible to non-reducible flow graph) [5], п. 6.2.3. Когда целевой язык (байт-код или машинный язык) более выразителен, чем исходный, можно использовать преобразования, «противоречащие» структуре исходного языка. В результате таких преобразований получаются последовательности инструкций целевого языка, не соответствующие ни одной из конструкций исходного языка. Например, байт-код виртуальной машины Java содержит инструкцию goto, в то время как в языке Java оператор goto отсутствует. Графы потока управления программ на языке Java оказываются всегда сводимыми, в то время как в байт-коде могут быть представлены и несводимые графы. Можно предложить запутывающее преобразование, которое трансформирует сводимые графы потока управления функций в байт-коде, получаемых в результате компиляции Java-программ, в несводимые графы. Например, такое преобразование может заключаться в трансформации структурного цикла в цикл с множественными заголовками с использованием непрозрачных предикатов. С одной стороны, декомпилятор может попытаться выполнить обратное преобразование, устраняя несводимые области в графе, дублируя вершины или вводя новые булевские переменные. С другой строны, распутыватель может с помощью статических или статистических методов анализа определить значение непрозрачных предикатов, использованных при запутывании, и 18
устранить никогда не выполняющиеся переходы. Однако, если догадка о значении предиката окажется неверна, в результате получится неправильная программа. Устранение библиотечных вызовов (eliminating library calls) [5], п. 6.2.4. Большинство программ на языке Java существенно используют стандартные библиотеки. Поскольку семантика библиотечных функций хорошо известна, такие вызовы могут дать полезную информацию при обратной инженерии программ. Проблема усугубляется ещё и тем, что ссылки на классы библиотеки Java всегда являются именами, и эти имена не могут быть искажены. Во многих случаях можно обойти это обстоятельство, просто используя в программе собственные версии стандартных библиотек. Такое преобразование не изменит существенно время выполнения программы, зато значительно увеличит её размер и может сделать её непереносимой. Для программ на традиционных языках эта проблема стоит менее остро, так как стандартные библиотеки, как правило, могут быть скомпонованы статически вместе с самой программой. В данном случае программа не содержит никаких имён функций из стандартной библиотеки. Переплетение функции (function interleaving) [5], п. 6.3.2. Идея этого запутывающего преобразования в том, что две или более функций объединяются в одну функцию. Списки параметров исходных функций объединяются, и к ним добавляется ещё один параметр, который позволяет определить, какая функция в действительности выполняется. Клонирование функций (function cloning) [5], п. 6.3.3. При обратной инженерии функций в первую очередь изучается сигнатура функции, а также то, как эта функция используется, в каких местах программы, с какими параметрами и в каком окружении вызывается. Анализ контекста использования функции можно затруднить, если каждый вызов некоторой функции будет выглядеть как вызов какой-то другой, каждый раз новой функции. Может быть создано несколько клонов функции, и к каждому из клонов будет применён разный набор запутывающих преобразований. Развёртка циклов (loop unrolling) [5], п. 6.3.4. Развёртка циклов применяется в оптимизирующих компиляторах для ускорения работы циклов или их распараллеливания. Развёрка циклов заключается в том, что тело цикла размножается два или более раз, условие выхода из цикла и оператор приращения счётчика соответствующим образом модифицируются. Если количество повторений цикла известно в момент компиляции, цикл может быть развёрнут полностью. Разложение циклов (loop fission) [5], п. 6.3.4. Разложение циклов состоит в том, что цикл с сложным телом разбивается на несколько отдельных циклов с простыми телами и с тем же пространством итерирования.
Реструктуризация графа потока управления [24]. Структура графа потока управления, наличие в графе потока управления характерных шаблонов для циклов, условных операторов и т. д. даёт ценную информацию при анализе программы. Например, по повторяющимся конструкциям графа потока управления можно легко установить, что над функцией было выполнено преобразование развёртки циклов, а далее можно запустить специальные инструменты, которые проанализируют развёрнутые итерации цикла для выделения индуктивных переменных и свёртки цикла. В качестве меры противодействия может быть применено такое преобразование графа потока управления, которое приводит граф к однородному («плоскому») виду. Операторы передачи управления на следующие за ними базовые блоки, расположенные на концах базовых блоков, заменяются на операторы передачи управления на специально созданный базовый блок диспетчера, который по предыдущему базовому блоку и управляющим переменным вычисляет следующий блок и передаёт на него управление. Технически это может быть сделано перенумерованием всех базовых блоков и введением новой переменной, например state, которая содержит номер текущего исполняемого базового блока. Запутанная функция вместо операторов if, for и т.д. будет содержать оператор switch, расположенный внутри бесконечного цикла. Локализация переменных в базовом блоке [3]. Это преобразование локализует использование переменных одним базовым блоком. Для каждого запутываемого базового блока функции создаётся свой набор переменных. Все использования локальных и глобальных переменных в исходном базовом блоке заменяются на использование соответствующих новых переменных. Чтобы обеспечить правильную работу программы между базовыми блоками вставляются так называемые связующие (connective) базовые блоки, задача которых скопировать выходные переменные предыдущего базового блока в входные переменные следующего базового блока. Применение такого запутывающего преобразование приводит к появлению в функции большого числа новых переменных, которые, однако, используются только в одном-двух базовых блоках, что запутывает человека, анализирующего программу. При реализации этого запутывающего преобразования возникает необходимость точного анализа указателей и контекстно-зависимого межпроцедурного анализа. В противном случае нельзя гарантировать, что запись по какому-либо указателю или вызов функции не модифицируют настоящую переменную, а не текущую рабочую копию. Расширение области действия переменных [5], п. 7.1.2. Данное преобразование по смыслу обратно предыдущему. Это преобразование пытается увеличить время жизни переменных настолько, насколько можно. Например, вынося блочную переменную на уровень функции или вынося
20 19
локальную переменную на статический уровень, расширяется область действия переменной и усложняется анализ программы. Здесь используется то, что глобальные методы анализа (то есть, методы, работающие над одной функцией в целом) хорошо обрабатывают локальные переменные, но для работы со статическими переменными требуются более сложные методы межпроцедурного анализа. Для дальнейшего запутывания можно объединить несколько таких статических переменных в одну переменную, если точно известно, что переменные не могут использоваться одновременно. Очевидно, что преобразование может применяться только к функциям, которые никогда не вызывают друг друга непосредственно или через цепочку других вызовов.
2.4. Применение запутывающих преобразований Существующие методы запутывания и инструменты для запутывания программ используют не единственное запутывающее преобразование, а некоторую их комбинацию. В данном разделе мы рассмотрим некоторые используемые на практике методы запутывания. В работах Ч. Ванг [23], [24] предлагается метод запутывания, и описывается его реализация в инструменте для запутывания программ на языке Си. Предложенный метод запутывания использует преобразование введения «диспетчера» в запутываемую функцию. Номер следующего базового блока вычисляется непосредственно в самом выполняющемся базовом блоке прямым присваиванием переменной, которая хранит номер текущего базового блока. Для того чтобы затруднить статический анализ, номера базовых блоков помещаются в массив, каждый элемент которого индексируется несколькими разными способами. Таким образом, для статического прослеживания порядка выполнения базовых блоков необходимо провести анализ указателей. В работе [3] предлагается метод запутывания, основанный на следущих запутывающих преобразованиях: каждый базовый блок запутываемой функции разбивается на более мелкие части (т.н. piece) и клонируется один или несколько раз. В каждом фрагменте базового блока переменные локализуются, и для связывания базовых блоков создаются специальные связующие базовые блоки. Далее в каждый фрагмент вводится мёртвый код. Источником мёртвого кода может быть, например, фрагмент другого базового блока той же самой функции или фрагмент базового блока другой функции. Поскольку каждый фрагмент использует свой набор переменных, объединяться они могут безболезненно (при условии отсутствия в программе указателей и вызовов функций с побочным эффектом). Далее из таких комбинированных фрагментов собирается новая функция, в которой для переключения между базовыми блоками используется диспетчер. Диспетчер принимает в качестве параметров номер предыдущего базового блока и набор булевских переменных, которые используются в базовых блоках для вычисления условий перехода, и вычисляет номер следующего блока. При этом следующий блок 21
может выбираться из нескольких эквивалентных блоков, полученных в результате клонирования. Выражая функцию перехода в виде булевской формулы, можно добиться того, что задача статического анализа диспетчера будет PSPACE-полна. Работа [3] описывает алгоритм запутывания, но не указывает, реализован ли этот алгоритм в какой-либо системе. Запутыватели для языка Java, например Zelix KlassMaster [25], как правило, используют следующее сочетание преобразований: из .class-файлов удаляется вся отладочная информация, включая имена локальных переменных; классы и методы переименовываются в короткие и семантически неосмысленные имена; граф потока управления запутываемой функции преобразовывается к несводимому графу, чтобы затруднить декомпиляцию обратно в язык Java. Побочным эффектом такого запутывания является существенное ускорение работы программы. Во-первых, удаление отладочной информации делает .class-файлы существенно меньше, и соответственно их загрузка (особенно по медленным каналам связи) ускоряется. Во-вторых, резкое уменьшение длины имён методов приводит к тому, что ускоряется поиск метода по имени, выполняемый каждый раз при вызове. Как следствие, запутывание Javaпрограмм часто рассматривается как один из способов их оптимизации.
3. Методы анализа программ В данном разделе мы рассмотрим методы, которые применяются при анализе программ в компиляторах. Цель таких методов – выявление зависимостей между компонентами программы, что даёт возможность применить определённые оптимизационные преобразования, или накладывает ограничения на проводимые оптимизационные преобразования. Методы анализа программ могут быть разделены на 4 группы:
Синтаксические. К этой группе относятся методы, основанные только на результатах лексического, синтаксического и семантического анализа программы.
Статические. К этой группе относятся методы анализа потоков управления и данных и методы, основанные на результатах анализа потоков управления и данных. Статические методы анализа работают с программой, не используя информацию о работе программы на конкретных начальных данных.
Динамические. Динамические методы анализа программ используют информацию, полученную в результате «наблюдения» за работой программы на конкретных входных данных. Заметим, что сами по себе динамические методы редко применяются для анализа программ, поскольку, как правило, необходима информация о поведении программы
22
на разных наборах входных данных, которая собирается с помощью статистических методов анализа.
Статистические. Статистические методы используют информацию, собранную в результате значительного количества запусков программы на большом количестве наборов входных данных.
Краткая характеристика важнейших для нас методов анализа программ приведена ниже.
3.1. Методы статического анализа Статический анализ алиасов (alias analysis) [11] необходим в языках, в которых несколько имён могут быть использованы для доступа к одной и той же области памяти. Например, некоторый элемент массива a может быть адресован в программе как a[0] (каноническое имя элемента массива), как a[j] или вообще как b[-4]. В результате анализа алиасов каждому оператору, выполняющему косвенную запись в память или косвенное чтение из памяти, ставится в соответствие множество имён переменных, которые могут затрагиваться данной операцией. Если язык допускает алиасы, проведение в той или иной мере анализа указателей необходимо для корректного анализа потоков данных и для преобразования программ. В случае доступа к элементам массивов и полям структур мы можем в простейшем случае предполагать, что считывается или модифицируется сразу весь массив или вся структура. Для указателей или ссылок в простейшем случае («консервативный» анализ) мы можем исходить из предположения о том, что косвенное чтение из памяти затрагивает все локальные и глобальные переменные, а косвенная запись в память может все их модифицировать. Такая схема слишком груба и в действительности блокирует глубокую трансформацию практически любой программы. Известно, что общая задача точного анализа указателей как минимум NPтрудна. В настоящее время существуют методы, работающие за полиномиальное время, для указателей на локальные переменные в случае нерекурсивных функций. Анализ указателей не может быть непосредственно использован для запутывания или распутывания программы, но он является ключевым для точного анализа свойств программы. Статическое устранение мёртвого кода (dead-code elimination) [19], разд. 18.10. имеет целью выявить в программе код, который выполняется, но не оказывает влияние на результат работы программы. Статическая минимизация количества переменных (variable minimization) [19], разд. 16.3. имеет целью уменьшить количество используемых в функции локальных переменных за счёт объединения переменных, времена жизни значений в которых не пересекаются, в одну переменную. Стандартная техника, которая используется для минимизации количества переменных, 23
состоит в построении графа перекрытия переменных с помощью итерационного решения уравнения потока данных и последующей раскраске вершин этого графа в минимальное или близкое к минимальному количество цветов. Статическое продвижение констант и копий (constant and copy propagation) [19], разд. 12.5, 12.6. заключается в продвижении константных выражений как можно дальше по тексту функции. Если выражение использует только значения переменных, которые в данной точке программы заведомо содержат одно известное при анализе программы значение, такое выражение может быть вычислено на этапе анализа программы. Если в выражении используется переменная, которая в данной точке программы заведомо является копией какой-то другой переменной, в выражение может быть подставлена исходная переменная. Статический анализ доменов (domain analysis) является расширением алгоритма продвижения констант. Он позволяет определить множество значений, которые может принимать данная переменная в данной точке программы, если это множество не велико. Статический слайсинг (slicing) [21] – это построение «сокращённой» программы, из которой удалён весь код, не влияющий на вычисление заданной переменной в заданной точке (обратный слайс), но при этом программа остаётся синтаксически и семантически корректной и может быть выполнена. Кроме описанного выше обратного слайсинга разработаны алгоритмы прямого слайсинга. Прямой слайсинг оставляет в программе только те операторы, которые зависят от значения переменной, вычисленного в данной точке программы. Методы слайсинга могут быть полезны при разделении «переплетённых» вычислений, когда одновременно вычисляются две независимые друг от друга величины. Например, в одном цикле может вычисляться скалярное произведение двух векторов, а также минимальный и максимальный элемент каждого вектора, и такие циклы могут быть расщеплены с помощью построения слайсов.
3.2. Методы динамического и статистического анализа Статистический анализ покрытия базовых блоков программы позволяет установить, выполнялся ли когда-либо при выполнении программы на заданном множестве наборов входных данных заданный базовый блок. Статистическое сравнение трасс позволяет выявить, одинаковы ли трассы программы, полученные при разных запусках на одном и том же наборе входных данных. Статистическое построение графа потока управления строит граф потока управления на основании информации о порядке следования базовых блоков на одном наборе или на множестве наборов входных данных.
24
Динамическое продвижение копий вдоль трасс необходимо для точного межпроцедурного анализа зависимостей по данным на основе трассы выполнения программы. Поскольку трасса выполнения программы, по сути, является одним большим базовым блоком, продвижение копий – несложная задача. Динамическое выделение мёртвого кода позволяет выявить инструкции программы, которые выполнялись при данном запуске программы, но не оказали никакого влияния на результат работы программы. Если анализируется совокупность запусков программы на множестве наборов входных данных, можно говорить о статистическом выделении мёртвого кода. Динамический слайсинг оставляет в трассе программы только те инструкции, которые повлияли на вычисление данного значения в данной точке программы (прямой динамический слайсинг), или только те инструкции, на которые повлияло присваивание значения данной переменной в данной точке программы. Заметим, что о точности динамических методов анализа можно говорить, только если известно полное тестовое покрытие программы (построение полного тестового покрытия – алгоритмически неразрешимая задача). В противном случае статистическое выявление свойств программы не позволяет нам утверждать, что данное свойство справедливо на всех допустимых наборах входных данных. Например, условие if (leap_year(current_data)) всегда будет равно значению «истина», если текущий год високосный, и значению «ложь» в противном случае, однако удаление этого ператора из программы приведёт к её неправильной работе. Поэтому описанные выше динамические методы не могут применяться в автоматическом инструменте анализа программ. Роль этих методов в том, чтобы привлечь внимание пользователя инструмента анализа программ к особенностям работы программы. В дальнейшем пользователь может изучить «подозрительный» фрагмент кода более детально с применением других инструментов, чтобы подтвердить или опровергнуть выдвинутую гипотезу. Если непрозрачные предикаты и недостижимый код устраняются только на основании статистического анализа, всегда остаётся возможность, что предикат был существенным (как в примере выше). Чтобы всё же упростить программу, можно, например, вынести предположительно недостижимый код из общего графа потока управления функции в обработчик специального исключения, которое возбуждается каждый раз, когда предикат примет значение, отличное от обычного. С одной стороны, граф потока управления и потока данных основной программы в результате упростится, а с другой стороны, программа сохранит свою функциональность.
4. Анализ запутанных программ В данном разделе мы сопоставим методы запутывания, описанные в разделе 2, и методы анализа программ, рассмотренные в разделе 3. На основании этого сопоставления вводится новая мера устойчивости запутывающих преобразований, а именно: Метод запутывания
Метод распутывания
Искажение имён переменных
Переименование переменных
Использование специфических языковых конструкций
Упрощение специфических языковых конструкций
Развёртка цикла
Визуализация графа потока управления функции для выделения потенциальных кандидатов на свертку в цикл; выявление индуктивной переменной, что требует (интерактивного) сравнения базовых блоков и (интерактивных) эквивалентных преобразований выражений, причём при таких преобразованиях выражение может даже (незначительно) усложняться; свёртка цикла
Использование уникальных переменных в базовых блоках
Продвижение копий (статическое и статистическое), минимизация количества используемых переменных
Введение диспетчера
детерминированного
Статистическое восстановление графа потока управления
Введение диспетчера
недетерминированного
Сравнение трасс, полученных на одном и том же наборе входных данных
Переплетение кода нескольких базовых блоков в один запутанный базовый блок
Статистическое устранение мёртвого кода
Введение непрозрачных предикатов
Статистический анализ покрытия для выявления потенциальных непрозрачных предикатов, поиск по образцам известных непрозрачных предикатов, алгебраическое упрощение, доказательство теорем
все методы
Свёртка констант, продвижение констант, продвижение копий, статическое устранение мёртвого кода – могут выполняться после каждого шага распутывающих преобразований
Таблица 1. Методы запутывания программ и методы, которые могут применятся для их анализа.
26 25
запутывающее преобразование называется устойчивым относительно некоторого класса методов анализа программ, если методы этого класса не позволяют надёжно раскрыть данное запутывающее преобразование Перечисление некоторых методов запутывания программ с точки зрения методов их возможного распутывания дано в таблице 1. Преобразование
Сложность распутывания (необходимый тип анализа)
Удаление комментариев
Одностороннее преобразование
Переформатирование программы
Синтаксический
Удаление информации
Одностороннее преобразование
отладочной
Изменение идентификаторов
имён
Автоматизируемость (тип распутывателя) автомат.
Синтаксический
полуавтомат.
Языково-специфические преобразования
Синтаксический
автомат.
Открытая функций
Одностороннее преобразование
поддерж.
вставка
действия переменных
полуавтомат.
Таблица 2. Классификация запутывающих преобразований
В таблице 2 приведена классификация методов запутывания по отношению к требуемым методам анализа программ. В третьем столбце таблицы указана степень, в которой возможно автоматическое распутывание. Степень автоматизма оценивается по следующей шкале: автоматический – поиск в программе запутанных фрагментов и их распутывание возможны полностью автоматически; полуавтоматический – поиск в программе подозрительных фрагментов и их распутывание по отдельности выполняются автоматически, но пользователь должен подтвердить применение распутывающего преобразования; поддерживаемый – поиск в программе запутанных фрагментов и применение распутывающих преобразований требуют существенного участия человека, но процесс может быть поддержан специальными инструментальными средствами; неавтоматизируемый – автоматизация выполнения распутывающего преобразования принципиально затруднена. Для некоторых видов запутывающих преобразований требуемые инструменты (синтаксические, статические, статистические) зависят от того, каким способом было реализовано преобразование. Например, непрозрачные предикаты могут быть самого разного вида, от простейших if(0), до очень сложных. Для анализа и устранения простейших непрозрачных предикатов достаточно инструментов уровня синтаксического анализа, которые работают автоматически, а для устранения сложных непрозрачных предикатов требуется статистический анализ, либо сложные инструменты, такие как полуавтоматический доказатель теорем. Поэтому некоторые ячейки таблицы содержат несколько необходимых видов анализа или несколько оценок автоматизируемости.
Вынос группы операторов
Синтаксический
автомат.
Непрозрачные предикаты и выражения
Синтаксический – статистический (зависит от вида предиката)
автомат. – поддерж.
Внесение недостижимого кода
Зависит от стойкости непрозрачных предикатов
автомат., полуавтомат.
Внесение мёртвого кода
Синтаксический – статистический
автомат., полуавтомат.
Внесение избыточного кода
Синтаксический – статистический
автомат. – поддерж.
Внесение несводимости в граф
Статический, но зависит от стойкости непрозрачных предикатов
автомат., полуавтомат.
Устранение библиотечных вызовов
Одностороннее преобразование
поддерж.
5. Практическое использование
Переплетение функций
Статический – статистический
автомат. – поддерж.
В данном разделе мы приведём примеры использования анализирующих преобразований для анализа запутанных программ.
Клонирование функций или базовых блоков
Статический
автомат. – поддерж.
Развёртка циклов
Одностороннее преобразование
поддерж.
Разложение циклов
Статический
автомат. – поддерж.
Введение диспетчера
Статический – статистический
автомат., полуавтомат.
Локализация переменных в базовом блоке
Статический – статистический
автомат., полуавтомат.
Расширение
Статический – статистический
автомат.,
области
5.1. Устранение анализа
диспетчера
на
основе
арсенала
динамического
Рассмотрим в качестве простейшего примера запутывание функции fib, которая принимает один параметр n и вычисляет n-е число Фибоначчи. Преобразуем её граф потока управления введением диспетчера. Текст исходной функции и текст функции с введённым диспетчером приведены на рис. 1. 28 27
Int fib(int n) { int a, b, c; a = 1; b = 1; if (n <= 1) return 1; for (; n > 1; n--) { c = a + b; a = b; b = c; } return c; }
static int transtable_fib[12] = { 1, 1, 2, 3, 0, 0, 4, 5, 3, 3, 0, 0 }; int fib(int L3) { int _r1, L29, L30, L5, L6, L7; L29 = 0; L30 = 0; L35: L29 = transtable_fib[L29 * 2 + L30]; switch (L29) { case 1: goto L26; case 2: goto L27; case 3: goto L22; case 4: goto L28; case 5: goto L23; } L47: goto L9; L26: L5 = 1; L6 = 1; L30 = (L3 > 1); goto L35; L27: _r1 = 1; goto L35; L22: L30 = (L3 <= 1); goto L35; L28: L7 = (L5 + L6); L5 = L6; L6 = L7; L3--; goto L35; L23: _r1 = L7; goto L35; L9: return _r1; }
Рис. 2. Граф потока управления исходной и запутанной функции fib
Рис. 1. Исходный текст функции fib и её запутанный вариант
Для анализа запутанной программы были проделаны следующие действия:
Запутанная программа была проинструментирована для сбора трасс. В начало каждого базового блока был добавлен вызов специальной функции, которая записывала в файл трассы номер базового блока.
По собранным трассам был построен граф потока управления, вид которого совпадал с графом потока управления запутанной функции, поскольку собранные трассы обеспечивали полное покрытие.
Поскольку граф потока управления, построенный по трассам, имел характерную регулярную структуру, указывающую на использование диспетчера, блок диспетчера был в трассах проигнорирован, что позволило вскрыть изначальный порядок следования базовых блоков в функции. Рис. 3. Исходный граф потока управления функции и восстановленный по трассам граф потока управления функции.
Сравнение графа потока управления исходной программы и графа потока управления, полученного в результате анализа трасс, приведено на рис. 3. 30 29
5.2. Анализ специфических языковых алгебраические преобразования
конструкций
(zero-value analysis). Все это позволило разделить один внутренний цикл на два независимых цикла.
и
В качестве второго примера мы выбрали запутанную программу, которая существенно использует специфические языковые конструкции. Эта программа – победитель 1998 года международного конкурса запутанного кода на Си [12]. В конкурсе участвуют программы на языке Си, запутанные вручную. Победители выявляются в таких номинациях, как «самый предательский код на Си», «лучшее анти-использование ANSI C».
#include <stdio.h> #include <string.h> #include
int main() { signed char *_r53, _r112, _r65, *a, d[999], *c, b;
#include<stdio.h> #include<string.h> main() { char*O,l[999]="'`acgo\177~|xp .-\0R^8)NJ6%K4O+A2M(*0ID57$3G1FBL"; while(O=fgets(l+45,954,stdin)){ *l=O[strlen(O)[O-1]=0,strspn(O,l+11)]; while(*O)switch((*l&&isalnum(*O))-!*l){ case-1:{char*I=(O+=strspn(O,l+12)+1)-2,O=34; while(*I&3&&(O=(O-16<<1)+*I---'-')<80); putchar(O&93?*I&8||!(I=memchr(l,O,44))?'?':I-l+47:32); break; case 1:;}*l=(*O&31)[l-15+(*O>61)*32]; while(putchar(45+*l%2),(*l=*l+32>>1)>35); case 0: putchar((++O,32));} putchar(10);}}
memcpy(d, "'`acgo\177~|xp .-\0R^8)NJ6%K4O+A2M(*0ID57$3G1FBL", 45); while ((a = fgets(d+45,954,stdin))) { a[strlen(a) - 1] = 0; *d = a[strspn(a, " .-")]; if (!*d) { while (*a) { a = a + strspn(a, ".-") + 1; c = a - 2; b = 34; while (1) { if ((*c & 3) == 0) break; _r53 = c; c--; b = (b - 16 << 1) + *_r53 - 45; if (b >= 80) break; }
Рис. 4. Конвертер ASCII/код Морзе (автор Frans van Dorsselaer).
Программа выполняет перекодирование символов, считываемых со стандартного потока, из кода Морзе и обратно. Она имеет запутанную структуру графа потока управления, её внутренние таблицы закодированы. Для распутывания программы были проделаны следующие шаги: 1. Программа была оттранслирована во внутреннее представление. 2. Умышленно трудновоспринимаемые идентификаторы (I, l) были переименованы в более простые a, b, c и т. д. 3. Над программой во внутреннем представлении были выполнены некоторые эквивалентные алгебраические преобразования, например (a-1)[strlen(a)] было заменено на a[strlen(a)-1]. 4. Анализ алиасов позволил установить, что только элемент a[0] изменяется в процессе работы программы, следовательно в вызовах функций strspn, memchr вместо массива a могут использоваться строковые литералы. 5. Анализ доменов позволил определить, что выражения, записанные в условном операторе, могут принимать всего два или три значения. Решая Диофантовы уравнения, можно значительно упростить условия. 6. В анализируемой программе далее были проведены анализ избыточных выражений (partial redundancy elimination), анализ нулевых выражений
if ((b & 93) == 0) { _r65 = 32; } else { if ((*c & 8) != 0) { _r65 = '?'; } else { c = memchr(d,b,44); if (c != 0) _r65 = (c - d) + 47; else _r65 = '?'; } } putchar(_r65); } } else { while (*a) { if (isalnum(*a)) { *d = d[(*a & 31) - 15 + (*a > 61) * 32]; do { putchar(45 + *d % 2);
32 31
_r112 = *d + 32 >> 1; *d = _r112; } while (_r112 > 35);
k2 = R+i-1; if (up_[k1] != 0) { if (down_[k2+1] != 0) { up_[k1] = 0; down_[k2 + 1] = 0; row[i]=0; x[R]=i; if (R==7) print(); else queens(R+1); row[i]=1; up_[k1] = 1; down_[k2 + 1] = 1; } /* if (down_) */ } /* if (up_) */
} a++; putchar(32); } } putchar(10); } return 0; }
Рис. 5. Конвертер ASCII/MORSE после преобразований.
7 Программа из внутреннего представления была переведена обратно в Си, при этом был проведён структурный анализ графа потока управления для выявления циклов и представления их в высокоуровневой форме. На рис. 5 приведён текст программы после всех описанных преобразований. Несмотря на то, что он стал больше, чем текст исходной программы, его структура стала намного проще и понятнее. После этого не составляет труда установить правила перекодирования кода Морзе в код ASCII и обратно.
} } } return 0; }
Рис. 6. Распутанная программа решения задачи «8 ферзей»
Short up_[16], down_[16], x[8], row[8]; int queens(int R) { int i, k1, k2; for (i=0;i<8;i++) { if (row[i]!=0) { if ((i + R) % 2 == 0) { k1 = 6+i-R; k2 = R+i; if (up_[k1+1] != 0) { if (down_[k2] != 0) { up_[k1 + 1] = 0; down_[k2] = 0; row[i]=0; x[R]=i; if (R==7) print(); else queens(R+1); row[i]=1; up_[k1 + 1] = 1; down_[k2] = 1; } /* if (down_) */ } /* if (up_) */ } else { k1 = 7+i-R;
5.3. Устранение мёртвого кода и свёртка циклов В качестве третьего примера мы опишем анализ программы, запутанной одним из коммерческих запутывателей программ на Си. Распутывание программы состояло из следующих шагов.
34 33
Был построен граф потока управления запутанной программы. Вид графа показал, что запутанная программа имеет две ветки условного оператора, не отличающиеся структурой потока управления друг от друга. Каждая из веток состояла из повторяющегося 8 раз фрагмента, что позволило предположить, что при запутывании была выполнена развёртка цикла.
Поскольку запутанная программа была представлена на языке Си, и содержала некоторые конструкции, использованные специально для увеличения размера и затруднения восприятия программы, все избыточные конструкции (ключевое слово register, ключевое слово signed в типе signed int, ненужные приведения типов и ненужные скобки) были удалены. В результате размер программы сократился на 20 Кб.
Далее было проведено статическое устранение мёртвого кода, которое позволило уменьшить размер программы до 64 Кб.
Далее была выполнена минимизация количества локальных переменных. Запутанная программа определяла в начале функции около 800 локальных переменных, время жизни которых в функции было невелико. Был применён алгоритм минимизации количества переменных, который выявил всего 11 необходимых переменных. Лишние определения переменных были удалены. Новые переменные получили имена вида r_1, r_2 вместо использовавшихся в запутанной программе имён вида r_dddddd, где d – десятичная цифра. В результате всех преобразований размер программы сократился до 26 Кб.
Далее в программы были выполнены преобразования по свёртке цикла. Были идентифицированы точные границы итерации цикла; все тела итерации цикла были сопоставлены друг с другом, в результате определились места, зависящие от номера итерации; выражения, зависящие от номера итерации, были переписаны в виде, позволившем ввести переменную цикла. Данные шаги были проведены полуавтоматически с использованием простейших средств сравнения базовых блоков (на текстуальном уровне). Это преобразование привело к тому, что размер программы достиг 3.5 Кб.
проверки как теоретических положений, так и новых методов запутывания. Для исследования различных вопросов, связанных с запутыванием программ, нами разрабатывается интегрированная среда (ИС) для изучения запутывания программ Poirot [1]. Цель разработки – получить удобную и мощную ИС для исследования методов и алгоритмов преобразования программ. С её помощью можно быстро получить прототип нового запутывающего преобразования программ, проверить его устойчивость против разнообразных известных методов анализа, либо проверять устойчивость уже существующих алгоритмов запутывания программ. ИС Poirot уже содержит многие инструменты синтаксического, статического, динамического и статического анализа, с помощью которых можно анализировать методы запутывания программ и сами запутанные программы. Заметим, что такая ИС полезна и потому, что реализация методов анализа программ, описанных выше, намного более трудоёмка, чем их использование, а польза этих методов несомненна. Как было уже упомянуто выше, методы запутывания должны учитывать свойства человеческой психики и пытаться ставить в тупик человека, который управляет системой анализа программ. Можно отметить следующие свойства, которым должна удовлетворять запутанная программа:
На заключительном шаге, применив эквивалентные алгебраические преобразования и выявив принципы кодирования данных, программа была ещё более упрощена. Размер программы составил примерно 1.5 Кб, структура потока управления и структура данных, с которыми манипулировала программа, стали полностю очевидны. Текст распутанной программы приведен на рис. 6.
6. Заключение В данной работе мы рассмотрели запутывание программ с точки зрения устойчивости запутывающих преобразований к различным методам статического и динамического анализа программ. Мы сделали попытку определить, какие методы анализа программ могут использоваться для противодействия большинству из описанных методов запутывания программ. Для иллюстрации нашего подхода мы привели примеры распутывания программ, запутанных как вручную, так и с помощью специальных запутывателей. Поскольку теория запутывания программ сейчас находится в стадии активного формирования, кроме того, разрабатываются всё новые и новые эмпирические методы запутывания, существует потребность в среде как наборе инструментов, которая выступает в роли «испытательного стенда» для 35
Запутывание должно быть замаскированным. То, что к программе были применены запутывающие преобразования, не должно бросаться в глаза.
Запутывание не должно быть регулярным. Регулярная структура запутанной программы или её фрагмента позволяет человеку отделить запутанные части и даже идентифицировать алгоритм запутывания.
Применение стандартных синтаксических и статических методов анализа программ на начальном этапе её анализа не должно давать существенных результатов, так как быстрый результат может воодушевлять человека, а его отсутствие, наоборот, угнетать.
Нам не известно ни одного метода запутывания, который бы удовлетворял всем этим признакам. Разработка таких методов является одним из направлений дальнейших исследований. Другим направлением является разработка методов запутывания и методов анализа запутанных программ на уровне языка ассемблера (машинного кода). Хотя методы запутывания низкого уровня могут оказаться непереносимыми на другие архитектуры, в низкоуровневом запутывании скрыт большой потенциал.
36
Литература 1. А. В. Чернов. Интегрированная среда для исследования «обфускации» программ. Доклад на конференции, посвящённой 90-летию со дня рождения А.~А.~Ляпунова. Россия, Новосибирск, 8---11 октября 2001 года. http://res.tsu.ru/Lyapunov-2001/abstracts/2350.htm 2. B. Barak, O. Goldreich, R. Impagliazzo, S. Rudich, A. Sahai, S. Vadhan, K. Yang. On the (Im)possibility of Obfuscating Programs. LNCS, 2001, 2139, pp. 1–18. 3. S. Chow, Y. Gu, H. Johnson, V. Zakharov. An approach to the obfuscation of control-flow of sequential computer programs. LNCS, 2001, 2200, pp. 144–155. 4. C. Cifuentes, K. J. Gough. Decompilation of Binary Programs. Technical report FIT-TR-1994-03. Queensland University of Technology, 1994. http://www.fit.qut.edu.au/TR/techreports/FIT-TR-94-03.ps 5. C. Collberg, C. Thomborson, D. Low. A Taxonomy of Obfuscating Transformations. Department of Computer Science, The University of Auckland, 1997. http://www.cs.arizona.edu/~collberg/Research/Publications/CollbergThomborsonLow97a 6. C. Collberg, C. Thomborson, D. Low. Breaking Abstractions and Unstructuring Data Structures. In IEEE International Conference on Computer Languages, ICCL'98, Chicago, IL, May 1998. 7. C. Collberg, C. Thomborson, D. Low. Manufacturing Cheap, Resilient, and Stealthy Opaque Constructs. In Principles of Programming Languages 1998, POPL'98, San Diego, CA, January 1998. 8. C. Collberg, C. Thomborson. On the Limits of Software Watermarking. Technical Report #164. Department of Computer Science, The University of Auckland, 1998. http://www.cs.arizona.edu/~collberg/Research/Publications/CollbergThomborson98e 9. C. Collberg, C. Thomborson. Watermarking, Tamper-Proofing, and Obfuscation – Tools for Software Protection. Technical Report 2000–03. Department of Computer Science, University of Arizona, 2000. http://www.cs.arizona.edu/~collberg/Research/Publications/CollbergThomborson2000a 10. G. Hachez, C. Vasserot. State of the Art in Software Protection. Project FILIGRANE (Flexible IPR for Software Agent Reliance) deliverable/V2. http://www.dice.ucl.ac.be/crypto/filigrane/External/d21.pdf 11. M. Hind, A. Pioli. Which pointer analysis should I use? In ACM SIGSOFT International Symposium on Software Testing and Analysis, pp. 113–123, August 2000. 12. The International Obfuscated C Code Contest. http://www.ioccc.org 13. M. Jalali, G. Hachez, C. Vasserot. FILIGRANE (Flexible IPR for Software Agent Reliance). A security framework for trading of mobile code in Internet. In Autonomous Agent 2000 Workshop: Agents in Industry, 2000. 14. H. Lai. A comparative survey of Java obfuscators available on the Internet. http://www.cs.auckland.ac.nz/~cthombor/Students/hlai 15. D. Low. Java Control Flow Obfuscation. MSc Thesis. University of Auckland, 1998. http://www.cs.arizona.edu/~collberg/Research/Students/DouglasLow/thesis.ps 16. J. MacDonald. On Program Security and Obfuscation. 1998. http://www.xcf.berkeley.edu/~jmacd/cs261.pdf 17. M. Mambo, T. Murayama, E. Okamoto. A Tentative Approach to Constructing Tamper-Resistant Software. In ACM New Security Paradigms Workshop, Langdale, Cumbria UK, 1998. 18. A. von Mayrhauser, A. M. Vans. Program Understanding: Models and Experiments. In M. Yovits, M. Zelkowitz (eds.), Advances in Computers, Vol. 40, 1995. San Diego: Academic Press, pp. 1–38. 19. S. Muchnick. Advanced Compiler Design and Implementation. Morgan Kaufmann Publishers, 1997. 20. SourceAgain Java decompiler. http://www.ahpah.com 21. F. Tip. A survey of program slicing techniques. Journal of Programming Languages, 3(3): 121–189, September 1995.
22. E. Walle. Methodology and Applications of Program Code Obfuscation. Faculty of Computer and Electrical Engineering, University of Waterloo, 2001. http://walle.dyndns.org/morass/misc/wtr3b.doc 23. C. Wang. A Security Architecture for Survivability Mechanisms. PhD Thesis. Department of Computer Science, University of Virginia, 2000. http://www.cs.virginia.edu/~survive/pub/wangthesis.pdf 24. C. Wang, J. Davidson, J. Hill, J. Knight. Protection of Software-based Survivability Mechanisms. Department of Computer Science, University of Virginia, 2001. http://www.cs.virginia.edu/~jck/publications/dsn_distribute.pdf 25. Zelix KlassMaster Java Code Obfuscator and Obfuscation. http://www.zelix.com
38 37
Системы управления кластерами А.И. Аветисян, Д. А. Грушин, А.Г. Рыжов
1. Введение В данном обзоре мы рассмотрим одну из параллельных архитектур, называемую кластерной архитектурой. Согласно [10], кластер – это группа вычислительных машин (называемых узлами кластера), объединенных коммуникационной средой и работающих как единое целое. Каждый узел кластера работает под управлением своей операционной системы. Однородным кластером называется кластер, состоящий из узлов, имеющих одинаковую конфигурацию, и работающих под управлением одинаковых операционных систем. Далее мы будем рассматривать только однородные кластеры. В середине 60-х годов идея кластера была предложена компанией IBM как способ объединения мейнфреймов для построения относительно недорогого суперкомпьютера. Более широкое распространение кластерные архитектуры получили позднее, в 80-е годы, с появлением быстрых микропроцессоров, достаточно производительных каналов связи и средств написания параллельных приложений. В настоящее время конструирование кластеров сдвигается в сторону построения систем на основе стандартных компонент: обычных двух- или четырехпроцессорных рабочих станций (или персональных компьютеров) и коммуникационного оборудования (Myrinet, SCI, Fast Ethernet и др.). Кластеры применяются для решения различных классов задач: они используются и для выполнения большого массива последовательных программ, никак не связанных одна с другой, и для обработки данных распределенными программами, состоящими из большого числа независимых процессов, имеющих общие данные (coarse-grain parallelism), и для выполнения параллельных программ, когда выполнение отдельных операторов программы распределяется по узлам вычислительной сети, чтобы сократить время выполнения программы (fine-grain parallelism). Каждый из вышеперечисленных классов задач предъявляет свои требования к свойствам и параметрам кластера. Эти требования подробно изложены в [10]. Основное требование к кластеру и его системе управления состоит в том, что для классов программ, которые предполагается выполнять на кластере, он должен быть 39
масштабируем – при увеличении числа узлов кластера должна пропорционально возрастать его производительность. В простейшем случае, когда на кластере требуется выполнить только одну параллельную программу, вся функциональность, необходимая для запуска, выполнения и завершения программы, может обеспечиваться операционными системами на узлах и самой программой. В более сложных случаях, когда требуется выполнить несколько задач, как параллельных, так и последовательных, имеющих различные требования к системным ресурсам (например, параллельные программы, занимающие только часть узлов кластера), в дополнение к среде выполнения (т.е. операционной системе узла и, возможно, дополнительных библиотек, поддерживающих выполнение параллельных программ), необходима некоторая система, управляющая вычислительными ресурсами кластера в целом. В [17] кластер определяется как множество рабочих станций (узлов), связанных коммуникационной средой и способных работать как единое целое за счет дополнительного программного обеспечения, называемого системой управления кластером. Система управления кластером в очень ограниченном смысле может рассматриваться как распределенная операционная система для кластера. Она обеспечивает управление над выполняемыми заданиями, пользователями и ресурсами. Когда кластер применяется для выполнения большого числа последовательных программ, масштабируемость кластера во многом определяется его системой управления. Масштабируемость кластера, применяемого для выполнения распределенных программ, зависит от свойств аппаратуры, однако, если требуется одновременно выполнить несколько распределенных программ, каждая из которых занимает часть узлов кластера, масштабируемость снова начинает зависеть от его системы управления. Система управления должна распределять ресурсы кластера между поступающими от пользователей заданиями. Для этого система может иметь очередь или набор очередей, распределять (выделять необходимое количество узлов для запуска) каждого задания таким образом, чтобы максимально использовать вычислительные возможности кластера и, наконец, собирать полученные результаты. Более подробный список основных функций, обеспечиваемых системами управления, был впервые сформулирован в работе [17] и, позднее, в [5]. К настоящему времени разработано достаточно много систем управления. В данном обзоре мы постараемся рассмотреть некоторые наиболее интересные из существующих систем. В разделе 2 мы рассмотрим основные функции, обеспечиваемые системой управления кластером, в разделе 3 будут рассмотрены основные архитектурные особенности систем управления, в разделе 4 коротко опишем наиболее интересные системы управления кластером. В разделе 5 на основе 40
анализа систем, рассмотренных в разделе 4, сформулируем требования к системе управления кластером, разрабатываемой в ИСП РАН.
2. Основные функции, обеспечиваемые управления кластером
системой
2.1. Классы задач Во введении мы упомянули, что кластеры применяются для выполнения следующих классов программ:
массив последовательных программ, никак не связанных одна с другой;
распределенные программы, состоящие из независимых процессов, имеющих общие данные (coarse-grain parallelism);
параллельные программы, (fine-grain parallelism);
Распределенные программы – это основной тип задач, для которых создаются кластеры. Каждая такая программа состоит из конечного множества процессов, обменивающихся данными через коммуникационную среду кластера. В большинстве случаев во время выполнения таких программ часть узлов может простаивать. Рассмотрим пример. Предположим, в некоторый момент времени в очереди находится несколько распределенных программ готовых к запуску, и кластер имеет M свободных узлов. Пусть каждая такая программа требует для своей работы некоторое количество узлов K: 1
одной и той же базе данных. Каждый поиск выполняется независимо от других, тем самым, требуя только один узел. С другой стороны, чем больше свободных узлов, тем быстрее будет произведен поиск набора генов. В общем случае последовательные программы можно рассматривать как параллельные, требующие всего один узел. Если вернуться к формуле (1), то можно сказать, что, имея в очереди некоторое достаточное количество последовательных программ, можно полностью избавиться от простаивающих узлов. При запуске программы или группы программ пользователь должен указать некоторые параметры, например количество необходимых процессоров или узлов, количество памяти. Помимо этого каждая программа может использовать для своей работы некоторые файлы, например база данных или файл с описанием гена из предыдущего примера. Эти файлы должны быть переданы на конкретный узел до того, как программа будет запущена. Указывать каждый раз всю эту информацию при запуске задачи потребует много времени и может стать причиной множества неудобств и ошибок. Обрабатываемые системой задачи должны описываться заданием. Для системы управления оно определяет действия, которые необходимо совершить при запуске и выполнении задачи или группы задач, и при сборе результатов вычислений. Задание обычно представляется в виде отдельного текстового файла, содержащего описание необходимых действий и который затем интерпретируется системой управления. Файл с описанием задания, исполняемый файл задачи, а также все необходимые для работы файлы данных образуют пакет или пакетное задание. В процессе выполнения программа может создавать другие файлы, некоторые из которых содержат результат работы и должны быть возвращены пользователю. Более того, некоторые файлы могут являться входными данными для других задач, тем самым, создавая зависимость по результатам. Следует упомянуть, что система должна иметь возможность описывать такие зависимости в файле задания. Помимо создаваемых задачей файлов весь производимый в процессе выполнения стандартный вывод (печать на экран), а также стандартный поток сообщений об ошибках перенаправляются в специальные выходные файлы. Пользователь может также задать системе файл стандартного ввода, в таком случае система должна иметь возможность имитировать диалог с выполняющейся программой, аналогично возможностям программы expect1 [12]. Все указанные файлы создаются в рабочем каталоге задачи. Этот каталог может находиться на удаленном файловом сервере или непосредственно на узле, в таком случае система должна самостоятельно копировать все необходимые файлы. Следует отметить следующее. Если
1
Программа expect позволяет автоматизировать диалог с интерактивными программами, такими как telnet, ftp, passwd, fsck, rlogin, tip и многими другими. 42
система управления использует только сетевую файловую систему, то при большом количестве узлов (большие кластеры могут состоять из нескольких сотен узлов) и операций ввода-вывода может возникнуть большая загрузка передающей среды (обычно Ethernet) и работа системы может практически остановиться. Система управления кластером может позволять выполнять интерактивные задания. Ввод, вывод и сообщения об ошибках в таком случае перенаправляются на машину пользователя. Интерактивный режим в большинстве случаев необходим для отладки или тестирования программ, когда требуется постоянный анализ пользователем получаемых данных или когда описание имитации диалога объемно и требует слишком много времени. Сложность выполнения интерактивных задач заключается в необходимости поддерживать терминальное соединение с узлом на протяжении всего времени работы. Существуют различные способы, позволяющие сократить продолжительность соединения, например открытие по запросу и закрытие при отсутствии активности. После того, как пользователь отправляет любое задание на выполнение, он уже не может полностью им управлять так, как это возможно на обычной рабочей станции. Система управления должна предоставлять пользователю возможность проверки статуса выполняемой или выполненной задачи, т.е. завершена программа или еще выполняется, какое количество памяти, процессорного времени использует программа. Система может предоставить эту информацию пользователю, используя электронную почту, SMS сообщения или диалоговую программу. Пользователь должен иметь возможность непосредственно управлять ходом выполнения отправленной программы, например, остановить программу или запустить заново. Рассмотрим выполнение системой параллельных программ. Согласно [10], поддержку параллельных программ можно разделить на следующие критерии: 1. Система имеет фиксированную встроенную поддержку для одной или нескольких параллельных программных сред, например PVM или MPI. Такой подход требует перекомпоновки программы со специальной библиотекой. 2. Система предоставляет возможность выбора узлов для параллельной программы. Например, многие параллельные программные среды задают список конкретных узлов для запуска параллельной программы в командной строке, система может изменять этот список после того, как подходящие узлы будут выбраны. Основная проблема состоит в том, что порождение процессов происходит внутри параллельной программы и система не в состоянии отследить такие процессы. 3. Система управления и параллельная программная среда могут общаться, использую некий заранее определенный протокол. Такой подход был предложен инициативной группой psched [31]. Программный интерфейс psched должен поддерживаться как системой управления, так и 43
параллельной средой. В настоящее время наиболее популярные среды MPI и PVM не поддерживают интерфейс psched. Кластеры также могут использоваться для выполнения другого типа параллельных программ – SPMD, когда выполнение отдельных операторов программы распределяется по узлам кластера [4].
2.2. Балансировка нагрузки Система управления может отправлять задачи на выполнение на конкретные узлы в кластере не просто по мере их получения от пользователей, а, таким образом, чтобы каждый узел, входящий в состав кластера, выполнял работу пропорционально своей мощности и не простаивал. Балансировка может быть статической, до запуска задачи, и динамической, уже во время работы задачи. Статическая балансировка привлекательна тем, что не требует дополнительных затрат во время выполнения задачи, однако динамическая может быть более эффективна в случае изменения физической структуры кластера, добавления или удаления узлов во время работы. Рассмотрим оба эти способа. Для того чтобы эффективно производить статическую балансировку нагрузки, необходимо иметь некоторую информацию о каждой задаче, например, необходимое количество узлов, оперативной и дисковой памяти, предельное время работы. Вся эта информация является списком ресурсов, необходимых задаче, и описывается в файле задания. Используя указанные ресурсы, система способна выбирать из очереди готовых к выполнению задач наиболее подходящие в конкретной ситуации. Существует множество решений задачи статического распределения, которая для большинства вариантов принадлежит классу NP-полных задач, за исключением некоторых очень простых случаев [13, 32]. В целом следует сказать, что наиболее логичным способом распределения задач является минимизация количества простаивающих машин, в таком случае кластер используется наиболее эффективно. Динамическая балансировка позволяет перераспределять уже запущенные задачи. В таком случае используется миграция процессов. Миграция процессов позволяет перемещать процесс с одной машины на другую без необходимости перезапуска процесса с самого начала. Миграция процессов привлекательна тем, что поступление задач и ресурсов от пользователей не всегда известно заранее. Миграция в таком случае дает системе еще один шанс правильно распределить выполняемые задачи [3]. Миграция обычно используется вместе с контрольными точками, образ процесса сохраняется на одной машине и затем восстанавливается на другой. Более подробно мы расскажем о контрольных точках в разделе 2.3 и в разделе 4.2 при описании системы Condor. Если система разрешает пользователю для своей задачи указать используемые ресурсы, то нет никаких гарантий, что после запуска задачи эти данные будут 44
соответствовать действительности. Система должна контролировать соответствие между реальным использованием ресурсов и теми данными, которые указал для данной задачи пользователь, и в случае несоответствия завершить задачу или произвести заранее предусмотренное для такой ситуации действие. По нашему мнению, чем больше будет такой контроль, тем больше гарантий безопасности для системы и ее пользователей. Возможность ограничения ресурсов определяется операционной системой, использующейся на узлах кластера, и архитектурой самой системы управления.
2.3. Защита от сбоев Как мы сказали выше – система управления может рассматриваться как распределенная операционная система для кластера. Основным принципом любой распределенной системы является иллюзия целостности, когда совокупность независимых компьютеров представляется пользователю единой вычислительной машиной. Пользователь кластера отправляет задания на известный сетевой адрес. Этот адрес однозначно определяет некоторый узел в составе кластера, называемый управляющим узлом. Кластеры очень часто создаются с выделенным управляющим узлом, на котором не производятся вычисления, а работает основная часть системы управления: подсистема очередей, система сбора и обработки различной информации. Управляющий узел также может включать в себя файловый сервер и служить шлюзом, ограничивающим узлы кластера от внешней сети. Во время работы кластера один из узлов может отказать. В таком случае все вычисления, производимые на данном узле, теряются. Система должна, так или иначе, реагировать на такие отказы аппаратуры и, в случае необходимости перезапускать остановленные задачи. В простейшем случае каждая программа может быть запущена заново. Для программ, которые выполняются долгое время, перезапуск означает потерю большого количества вычислений и, следовательно, времени. Для сохранения текущего состояния программы, и затем ее восстановления используется механизм контрольных точек. В случае сбоя теряются только те вычисления, которые были произведены с момента последней контрольной точки. Контрольная точка процесса представляет собой образ адресного пространства процесса. Существует два метода создания контрольных точек: последовательное и неблокирующее сохранение. Последовательное сохранение сокращает объем необходимых для сохранения данных. Сохраняются только те страницы памяти, которые были изменены с момента последнего сохранения. Неблокирующее сохранение позволяет процессу продолжать свое выполнение во время сохранения. Страницы памяти процесса блокируются на запись, если же процесс пытается изменить страницу, она копируется, и процесс сохранения продолжается уже с копией страницы. Сохранение образа процесса 45
может существенно затормозить работу машины, выполняющей такое сохранение. Помимо вычислительных ресурсов процесс создания контрольных точек требует дополнительного дискового пространства. Для систем управления процесс создания контрольных точек сложен и зависит от реализации системы управления и возможностей операционной системы, используемой на узлах кластера. Все системы, поддерживающие контрольное сохранение, делают это с теми или иными ограничениями, о которых мы упомянем при более детальном рассмотрении конкретных систем. Сложной задачей является восстановление состояния параллельной программы. Применяются два метода (и их комбинация), основанные на промежуточной фиксации состояния либо ведении журнала выполняемых операций. Они различаются объемом запоминаемой информацией и временем, требуемым для восстановления. На рис. 1 показаны три процесса (X,Y,Z), взаимодействующие через сообщения. Вертикальные черточки показывают на временной оси моменты запоминания состояния процесса для восстановления в случае отказа. Стрелочки соответствуют сообщениям и показывают моменты их отправления и получения.
X
X1
X2
X3
M3
Y1
Y
t
M1
Y2 F1
t
M4
Z
Z1
Z2
M2
t
Рис. 1. Иллюстрация эффекта домино Если процесс Y аварийно завершился в точке F1 после отправки сообщения M1, посылка которого зафиксирована в точке x3 и не зафиксирована в y2. Сообщение М1 называется сообщением-сиротой, а состояния y2 и x3 являются несогласованными. В таком случае процесс X должен быть возвращен в состояние x2. Этот эффект известен как эффект домино. Множество контрольных точек должно быть консистентным. Для любой зафиксированной операции приема сообщения, соответствующая операция посылки должна быть также зафиксирована (нет сообщений-сирот). Система должна 46
осуществлять восстановление с использованием консистентного множества контрольных точек – повторить отправку тех сообщений, квитанции о получении которых стали недействительными в результате отката. В случае отказа управляющего узла, система должна иметь возможность либо продолжить работу, выбрав самостоятельно новый управляющий узел, либо восстановить свое состояние позднее, когда управляющая машина вернется в рабочее состояние.
Очень важная характеристика системы для конечных пользователей – это пользовательский интерфейс. Если пользователь не понимает систему из-за сложного или громоздкого интерфейса, система не будет использоваться. Система управления должна предоставлять пользователю возможность доступа через WEB – интерфейс. Такой доступ не требует установки на машины пользователя никакого дополнительного программного обеспечения. Задания для системы управления должны формулироваться с использованием некоторого языка. Этот язык не должен быть сложен при изучении и использовании. Для обеспечения эффективности система может потребовать, чтобы программа была заново перекомпилирована или собрана с использованием специальных библиотек. Такое требование обычно необходимо для обеспечения поддержки контрольных точек и миграции процессов, однако в большинстве случаев может быть неприемлемо для пользователей. Переносимости в общем случае нет.
обеспечивающая взаимодействие компонент распределенных программ, расположенных на разных узлах кластера, строится на основе коммуникационного оборудования Myrinet или SCI, а коммуникационная система для работы системы управления – на основе Ethernet или Fast Ethernet. Управляющая подсистема принимает задания от пользователей и отправляет их на вычислительные узлы. Кроме того, управляющая подсистема опрашивает периферийные подсистемы узлов кластера и собирает необходимую информацию, связанную с текущим состоянием каждого узла. Все управление кластером осуществляется его управляющей подсистемой, а периферийные подсистемы узлов кластера в общем случае только отвечают на запросы управляющей подсистемы. Такой подход позволяет избежать резких скачков сетевого трафика, которые могут быть порождены несогласованными действиями большого количества периферийных подсистем. Можно сказать, что система управления кластером работает по принципу клиент-сервер, клиентом является управляющая подсистема, а каждая периферийная подсистема – сервером. Как управляющая, так и периферийные подсистемы могут быть реализованы как прикладные программы для операционной системы узла. Однако некоторые требования к системе управления, связанные с механизмами защиты (сохранение состояния процесса, контроль над используемыми ресурсами и т.п.) не могут быть полностью реализованы на уровне прикладных программ. Для обеспечения указанных возможностей часть системы управления должна быть реализована на уровне ядра операционной системы (если операционной системой узла является Linux, то наиболее удобна реализация в виде модуля).
3. Особенности кластерами
4. Анализ программных кластерами
2.4. Пользовательский интерфейс
реализации
систем
управления
В общем случае система управления кластером имеет подсистему, работающую на управляющем узле кластера (управляющая подсистема) и подсистемы, работающие на каждом вычислительном узле (периферийные подсистемы). Управляющая и периферийные подсистемы взаимодействуют через коммуникационную систему кластера. Следует отметить, что кластер должен иметь две коммуникационных системы: одна используется для взаимодействия компонент распределенных программ, выполняемых на кластере, другая – для организации взаимодействия подсистем системы управления кластером. Это следует из того, что для эффективного использования сетей рабочих станций коммуникационная система должна обеспечивать максимальную пропускную способность и минимальные задержки при передаче данных [22], и дополнительная нагрузка со стороны системы управления может значительно снизить производительность кластера. В настоящее время коммуникационная система, 47
пакетов
управления
В этом разделе предлагается сравнительный анализ наиболее распространенных систем управления кластером. Будут рассмотрены системы управления Beowulf, EnFuzion, MOSIX, Condor, LoadLeveler, LSF, PBS и Cleo. Будет также рассмотрен планировщик заданий MAUI. Соответствие систем вышеупомянутым требованиям можно выразить следующей таблицей (табл. 1). Рассмотрим эти системы более подробно.
48
Beowulf EnFuzion MOSIX Condor Load Leveler LSF Cleo PBS Pro Пакетные задания Да
Да
Да
Да
Да
Да Да Да
Файл задания
Да
Да
Нет
Да
Да
Да Нет Да
Нет
Нет
Нет
Нет
Нет
Да Нет Нет
Статус задачи
Да
Да
Да
Да
Да
Да Да Да
Балансировка
Нет
Нет
Да
Нет
Да
Да Да Да
Задание ресурсов Да
Нет
Нет
Да
Да
Да Да Да
Задание приоритетов
Да
Да
Нет
Да
Да
Да Да Да
Интерактивные задачи
Нет
Нет
Да
Нет
Да
Да Нет Да
Ограничение ресурсов
Да
Нет
Нет
Нет
Да
Да Нет Да
Контрольные точки
Нет
Нет
Нет
Да
Да
Да Нет Нет
Миграция процессов
Нет
Нет
Да
Да
Да
Да Нет Нет
Зависимость задач результатам
по
4.1. Система Beowulf В 1994 г. научные сотрудники CESDIS2 Thomas Sterling и Don Becker собрали кластер, состоящий из 16 процессоров DX4, связанных сетью Ethernet [8]. Они назвали свой кластер Beowulf. Разработка быстро выросла в то, что сейчас называют проект Beowulf, который в настоящее время ведется компанией Scyld. Система управления для кластера Beowulf состоит из дополнений к ядру Linux, позволяющих создать единый образ системы, как для пользователей, так и для приложений. Пользователи запускают процессы на управляющем узле, система управления затем переносит (мигрирует) процессы на вычислительные узлы кластера. Кроме дополнений к ядру система включает в себя набор библиотек для распределенных программ и различные утилиты для администрирования кластера. Beowulf не поддерживает механизма контрольных точек и не имеет необходимой защиты от сбоев. В случае сбоя одного из узлов программа запускается заново. В будущих версиях разработчики планируют создать необходимую поддержку контрольных точек, также планируется возможность автоматического переключения на запасной мастер узел, что должно увеличить надежность работы всей системы [33]. Система специально создана для того, чтобы максимально использовать вычислительные возможности кластера. Улучшенные сетевые драйверы, специально оптимизированные библиотеки увеличивают производительность. Система может быть неэффективной для выполнения большого количества разнородных и небольших задач, так как имеет ограниченные возможности балансировки нагрузки. Тем не менее, систему можно отнести к наиболее эффективным для использования небольшим коллективом разработчиков.
4.2. Система Condor Перекомпиляция Да
Нет
Нет
Да
Нет
Нет Нет Нет
Защита от сбоев
Нет
Да
Да
Да
Да
Да Нет Да
Параллельные программы
Да
Нет
Нет
Да
Да
Да Да Да
Монопольный режим
Да
Да
Да
Да
Да
Да Да Да
Интерфейс
Да
Нет
Да
Нет
Да
Да Нет Да
Система Condor, в отличие от упомянутых выше систем, предназначена не для управления кластером, а для того, чтобы использовать простаивающие вычислительные ресурсы в сети рабочих станций. Основной принцип такой системы в том, что пользователь рабочей станции должен иметь возможность в любой момент времени использовать станцию в личных целях без каких-либо неудобств [9]. Система Condor имеет выделенный центральный узел, на котором работает управляющая часть системы. Специальный процесс постоянно контролирует активность на каждом из узлов, входящих в состав сети, и, в случае обнаружения активности пользователя, перемещает задачу на другую свободную машину. Condor периодически сохраняет задачу в
Таблица 1. Сравнение систем управления кластерами
2
49
CESDIS – The Center of Excellence in Space Data and Information Sciences 50
контрольных точках следующим образом. Программа пользователя собирается со специальной библиотекой. Система инициализирует процесс сохранения посредством посылки сигнала. Библиотека имеет обработчик такого сигнала, который производит сохранение всей информации в специальный файл, называющийся файлом контрольной точки. Этот файл содержит все необходимые данные для восстановления задачи и выполняемый код, осуществляющий такое восстановление. Таким образом, файл контрольной точки является обычной программой, которая при запуске восстанавливает исходный процесс. Система интересна как первая в своем классе. Такой тип систем может быть полезен для больших компаний, особенно в выходные дни, когда большинство рабочих станций свободно для использования [20]. Однако такой принцип использования вычислительных ресурсов подходит только для очень ограниченного класса задач: 1. Возможно сохранение только одиночных процессов. Все процессы, порожденные посредством вызовов FORK и EXEC, не сохраняются. 2. Сохраняемый процесс не может использовать сигналы. 3. Никакие механизмы межпроцессного обмена, включая работу с сокетами, не поддерживаются. 4. Все операции ввода-вывода должны быть односторонними, т.е. либо только чтение, либо запись. Следует отметить, что такая система должна обеспечивать максимальный контроль над запускаемыми программами с целью обеспечения защиты данных. Condor имеет очень серьезные недостатки с этой точки зрения. Так, практически отсутствует контроль над используемыми ресурсами, порождаемыми процессами и т.д.
одновременно выполняющихся задач на узле. Достаточно объемные задачи просто занимали весь кластер и большому количеству пользователей, имеющих маленькие задачи, приходилось ждать. Авторы решили проблему путем написания CRON-скрипта, который периодически приостанавливал долго выполняющиеся задачи и позволял коротким быстро выполниться. Другой проблемой является пропускная способность сети, накладывающая ограничения на производительность системы в целом. Система трудна для администрирования. Все проблемы с несовместимостью программного обеспечения на узлах кластера должен решать администратор.
4.4. Система MOSIX MOSIX – это набор дополнений к ядру Linux, позволяющий распределять процессы между узлами в составе кластера. MOSIX можно логически разделить на две части. Это миграция процессов и набор алгоритмов для эффективного динамического распределения ресурсов. Обе части реализованы на уровне ядра операционной системы и прозрачны для приложений. Миграция реализована следующим образом. Процесс разделяется на два контекста – контекст пользователя и системный контекст. Контекст пользователя содержит сегмент кода, стек, сегмент данных, содержимое регистров. Системный контекст содержит информацию об используемых ресурсах и стек ядра. Интерфейс между пользовательской и системной частями устанавливается на сетевом уровне. Пользовательская часть может мигрировать, системная часть жестко привязана к узлу, на котором был запущен процесс. Согласно [6, 7] время миграции состоит из двух компонент:
4.3. Система EnFuzion Система EnFuzion – это набор приложений для выполнения пакетных задач на кластерах с большим количеством узлов. EnFuzion была разработана а рамках исследовательского проекта Nimrod [1], выполнявшегося профессором Д. Абрамсоном в Австралийском университете Monash, в конце 90х годов. Затем система превратилась в коммерческий продукт и широко распространилась. Система способна распределять пакетные задания для кластеров, содержащих от нескольких десятков до нескольких сотен узлов [11]. Она имеет центральный выделенный узел, управляющий всеми остальными машинами в кластере. Система может функционировать как в пределах локальной сети, так и в масштабах Internet. Система достаточно масштабируема, однако не имеет возможностей балансировки. С этим была связана очень интересная проблема, которая была замечена в Австралийском университете Monash во время использования данной системы [18, 19]. Система позволяет ограничивать количество 51
фиксированная часть, когда происходит формирование нового образа процесса на удаленном узле;
изменяющаяся часть, пропорциональная количеству передаваемых в связи с миграцией страниц памяти.
Взаимодействие компонент схематично изображено на рис. 2 [6, 7].
52
Рис. 2 Миграция процесса в системе MOSIX MOSIX ориентирована на эффективное распределение ресурсов и обеспечение масштабируемости. Однако очевидным недостатком является большое количество накладных расходов, например, при обращении к сетевым ресурсам, осуществлении системных вызовов. Было даже предложено несколько интересных идей для уменьшения таких расходов. Например, мигрирующие сокеты и распределенные файловые системы [2], основную идею которых можно описать следующим образом. Если имеется процесс и некоторый ресурс, необходимый для данного процесса, то процесс может мигрировать на тот узел в кластере, на котором находится ресурс, вместо того, чтобы обращаться к нему удаленно. Однако такие способы могут лишь частично увеличить эффективность. Можно сказать, что MOSIX позволяет эффективно распределять ресурсы, но не позволяет их использовать максимально эффективно.
4.5. Система LoadLeveler Данная система является модифицированной версией системы Condor. В отличие от своего предшественника, система имеет много дополнительных возможностей. Система имеет встроенную подсистему статической балансировки, позволяющую разнообразными способами распределять поступающие от пользователя задачи. Система также имеет программный интерфейс пользователя (API), с помощью которого возможна реализация различных приложений, использующих возможности системы [14, 15]. Так, система 53
может использовать независимые внешние планировщики, широко используется планировщик MAUI, который будет описан позднее. По использованию ресурсов все задачи делятся на классы. Система имеет предопределенный набор классов, который может дополняться или изменяться администратором. Пользователь, в таком случае, при запуске задачи указывает только ее класс. Система имеет встроенную поддержку для пакетов MPI и PVM, для этого программы должны быть заново собраны со специальной библиотекой. Стоит отметить возможность системы осуществлять контрольное сохранение для параллельных задач. Преимущества системы по сравнению с Condor также заключаются в больших возможностях с точки зрения безопасности. Появилась концепция доверительных хостов, контроль за используемыми ресурсами. Очень хороший графический интерфейс, позволяющий контролировать все аспекты работы системы [14]. Планировщик позволяет эффективно распределять нагрузку в кластере. Имеет смысл использовать LoadLeveler не только для простаивающих рабочих станций, но и как систему управления для полноценного кластера. В сочетании с миграцией процессов и контрольными точками даже для параллельных задач систему можно назвать почти универсальной.
4.6. Система LSF Система LSF представляет собой набор приложений для администрирования и управления кластером рабочих станций. Система включает с себя подсистемы LSF Batch [24, 25, 26], LSF JobScheduler [29, 30], LSF MultiCluster, LSF Parallel [27], LSF Make, LSF Analyzer и основную подсистему LSF Base. Рассмотрим архитектуру системы. Базовая система состоит из менеджера загрузки, называемого LIM, и сервера удаленного выполнения RES (сокращение от Remote Execution Server). LSF Base взаимодействует с операционной системой на каждом из узлов кластера и предоставляет единый интерфейс (API) для всех остальных компонент системы управления и пользователя, называемый Load Sharing LIBrary (сокращенно LSLIB). Менеджер загрузки собирает всю необходимую информацию с узла и обменивается ею с менеджерами, работающими на других узлах. Один из таких менеджеров является центральным и имеет в распоряжении информацию, собранную со всех остальных узлов, что схематично изображено на рис. 3 [24 – 30]. Система поддерживает самые разнообразные ресурсы, которые классифицируются следующим образом (указанные классы ресурсов могут пересекаться): 1. Ресурсы, доступные на всех узлах. Число процессоров на узле, количество оперативной памяти, статус узла, доступное дисковое пространство и т.д. 54
2. Специальные ресурсы, доступные только на определенных узлах. Например, наличие принтера, сервер сетевой файловой системы, определенная версия библиотеки и т.д. 3. Динамические ресурсы, изменяющие свое значение в процессе выполнения задачи. 4. Статические ресурсы. 5. Настраиваемые ресурсы, определенные пользователем или администратором. 6. Встроенные ресурсы. 7. Распределенные ресурсы. Данный тип ресурсов применяется по отношению ко всему кластеру. Любое изменение ресурса на каком-либо из узлов, так или иначе, может повлиять на другие узлы в составе системы или некоторого подмножества узлов. В качестве примера можно назвать ресурсы «пропускная способность сети», «групповая лицензия для программного обеспечения» или «сетевая файловая система».
1. Сохранение на уровне ядра. В таком случае процесс сохранения прозрачен для приложений, все необходимые действия могут быть сделаны без каких либо изменений исполняемого кода программы. 2. Сохранение на уровне пользователя. Такой способ очень похож на уже описанный для систем CONDOR и LoadLeveler. 3. Сохранение на уровне приложения. В данном случае само приложение ответственно за свое сохранение и восстановление. Система только предоставляет необходимые функции из библиотеки. В случае сбоя все остановившиеся задачи перезапускаются с момента последней контрольной точки. Стоит отметить, что все части системы, являющиеся центральными, например главный менеджер загрузки, могут быть в момент сбоя заменены новыми. В случае с LIM любой из остальных, подчиненных менеджеров, занимает место главного после его отказа. Систему можно назвать универсальной. Мы указали только основную часть возможностей системы. Хочется отметить, что полный список гораздо шире. Например, система способна на работу в среде разнородных узлов, имеющих как различную архитектуру, так и под управление различных операционных систем. Впечатляет набор возможностей по указанию всевозможных ресурсов, в том числе и для параллельных программ. Подсистема LSF MultiCluster обеспечивает взаимодействие между несколькими кластерами в масштабах, например Internet. В этом случае используются различные методы защиты передаваемой информации, такие как шифровка, аутентификация и т.д. С другой стороны, все это делает систему гигантской как для установки, так и для поддержки. Одна документация для системного администратора занимает многие сотни страниц. Тем не менее, система может решить практически все требования конечного пользователя и использоваться в масштабах очень больших компаний.
4.7. Система Cleo Система управления очередями Cleo предназначена для управления прохождением задач на многопроцессорных вычислительных установках (в том числе кластерных) [23]. Система является отечественной разработкой, созданной в НИВЦ МГУ. Система позволяет автоматически распределять вычислительные ресурсы между программами, управлять порядком их запуска, временем работы, получать информацию о состоянии очередей. Система достаточно проста как с точки зрения реализации, так и с точки зрения использования. Система написана на языке Perl и допускает подключение независимых модулей, расширяющих функции системы [23].
Рис. 3 Взаимодействие компонент в системе LSF LSF JobScheduler в полной мере поддерживает все вышеперечисленные классы ресурсов при статическом и динамическом распределении нагрузки. Система способна осуществлять три вида сохранения процесса в контрольных точках. 55
56
4.8. Системы PBS и PBS Pro Системы PBS и PBS Pro [36] являются наследниками системы NQS [36], одной из первых систем управления кластерами. Иерархия этих систем управления схематично изображена на рис. 4.
Рис. 4 Иерархия систем управления Системы PBS и PBS Pro были созданы в подразделении американского космического агентства (NASA). Изначальным требованием к системе PBS помимо функциональности было удобство переноса системы на разные платформы. Система удовлетворяет стандарту POSIX 1003.2d и работает с такими операционными системами как Cray Unicos, SGI – IRIX, Sun – Solaris, Thinking Machines – CMOST, Intel – OSF/1-AD и другими. Система имеет возможность настройки политики распределения заданий, и может работать с большим количеством (до нескольких тысяч) узлов. Система может обслуживать несколько потоков очередей, разделенных как по архитектуре вычислительных узлов, так и по требуемым программе ресурсам. Определения ресурсов могут быть созданы администратором системы, и затем использоваться планировщиком при распределении заданий. Система обеспечивает взаимодействие между несколькими кластерами через глобальную сеть, имеет графический интерфейс пользователя и WEB интерфейс для отправки заданий. Доступны разнообразные способы указания зависимостей между запускаемыми программами. Системы PBS и PBS Pro могут быть удобны для организаций, имеющих разнородные кластеры с большим количеством узлов, в том числе суперкомпьютеров, и большое количество пользователей.
4.9. Планировщик MAUI Планировщик является одной из основных частей системы управления. Он осуществляет всю работу, связанную с распределением задач на конкретные узлы в составе кластера. Такое распределение основывается на информации о статусе задачи, используемых ею ресурсах, и информации о доступных 57
ресурсах в системе. Многие системы управления имеют собственные, встроенные, возможности для такого распределения. С другой стороны сами алгоритмы распределения могут быть реализованы за пределами конкретной системы управления, используя только необходимую информацию, которую, в таком случае, предоставляет сама система посредством набора библиотечных функций (API). В данном обзоре упомянем об одном из таких внешних планировщиков под названием MAUI. Планировщик MAUI был разработан в Maui High Performance Computing Center (MHPCC) в Мексике. Как говорилось выше, задача распределения ресурсов в общем случае принадлежит классу NPполных задач [13, 32]. При большом количестве различных, в общем случае, несогласованных друг с другом требований достаточно трудно сделать правильных выбор тех узлов, на которых будет запущена программа. Одно из решений состоит в обеспечении возможностей настраивать поведение планировщика согласно некоторой политике распределения. MAUI был создан как раз для того, чтобы удовлетворить такие требования для администраторов и пользователей [21]. Рассмотрим некоторые из них. 1. Предположим, программы распределяются согласно приоритетам. В таком случае, рано или поздно, возникнет ситуация, при которой выбранная программа не сможет быть запущена по причине недостатка требуемых ресурсов (ярким примером может служить система EnFuzion). При этом программы, имеющие более низкий приоритет и требующие незначительных ресурсов, должны ждать. Планировщик MAUI способен разрешать такие ситуации и выбирать из списка ожидающих программ те, которые могут успеть выполниться. Выбор тоже не тривиален и является, в свою очередь, нетривиальной задачей распределения, правда уже второго уровня. Рекурсия в данном случае не используется, для распределения применяются более простые способы, например, выбирается первый подходящий узел, имеющий наибольший приоритет, или тот, который требует максимальное количество ресурсов. Такая политика распределения задач называется Backfill и поддерживается также многими встроенными планировщиками. 2. Другой крайней точкой является наличие большого количества доступных узлов, при этом возникает проблема выбора не программы, а узла. Существует несколько простых способов. Выбираются узлы, имеющие минимальные подходящие ресурсы, в таком случае наиболее производительные, в силу заданных требований, узлы остаются свободными. Выбираются те узлы, которые имеют наименее загруженный процессор. И, наконец, те, которые простаивают наибольший период времени. 3. Другие способы позволяют принимать решения на основе информации о пользователе или группе пользователей. Например, обеспечивая наилучшее качество сервиса, программы, принадлежащие заданной группе 58
пользователей, будут иметь высокий приоритет. Или наоборот, ограничивая количество задач для данного пользователя или группы пользователей. Также предусмотрена интересная возможность резервирования узлов. При этом указанные администратором узлы не будут использоваться под распределение задач до некоторого периода времени, который может определяться как явно, так и являться зависимым от состояния системы. Возможности планировщика широки. Исходный код MAUI распространяется свободно, существует возможность внесения изменений и дополнений [21]. MAUI предоставляет возможность мониторинга системы: доступна различная информация о загрузке узлов, состояния задач, разнообразные статистики. Планировщик имеет независимый WEB интерфейс, созданы приложения, предоставляющие возможности администрирования и имеющие удобный графический интерфейс пользователя. Следует отметить, что в настоящее время MAUI может работать с системами LoadLeveler, PBS, PBSPro, Beowulf и другими.
5. Выводы Мы рассмотрели несколько различных систем управления, каждая из которых имеет свои особенности, как с точки зрения функциональности, так и с точки зрения архитектуры. Из всех рассмотренных систем наибольший интерес для нас представляют две системы –Beowulf и Condor. Система Beowulf (см. раздел 4.1) обычно устанавливается в университетах, научно-исследовательских институтах и небольших компаниях, нуждающихся в эффективном использовании кластера с относительно небольшим числом узлов для масштабируемых параллельных или распределенных вычислений. Beowulf-кластер на базе коммуникационной сети SCI установлен в НИВЦ МГУ3. Для управления кластером используется созданная в НИВЦ система управления заданиями Cleo, описанная в разделе 4.7. Кластер Beowulf установлен и в ИСП РАН4. Кластер насчитывает 16 узлов, соединенных оборудованием Myrinet и имеет выделенный управляющий узел. В дальнейшем предполагается увеличить число узлов кластера. Кластер планируется использовать для выполнения параллельных (SPMD), распределенных и последовательных программ. Для обеспечения возможности эффективной эксплуатации кластера в ИСП РАН разрабатывается система управления кластером (см. ниже). Система Condor обеспечивает наиболее простой способ использовать простаивающие рабочие станции. Такая система может принести пользу 3 4
Научно Исследовательский Вычислительный Центр Институт Системного Программирования РАН 59
многим организациям, например, при компиляции и сборке больших программных проектов или проведении редких, но трудоемких вычислений. Использование рабочих станций в качестве свободного ресурса должно контролироваться пользователем. Например, система может использовать узел только после того, как пользователь закончил сеанс работы (logout) и освобождать узел при начале пользователем нового сеанса (login), возможно, с некоторой задержкой, необходимой для корректного завершения выполняемой программы. При этом по желанию пользователя система обязательно должна предоставлять информацию об использовании машины в его отсутствие. Система подобная Condor нуждается в продуманном и надежном механизме защиты. Необходим максимальный контроль над запускаемыми задачами и использованием ресурсов. В случае сбоя или атаки под угрозой становится не только общая информация, относящаяся к выполняемым задачам, но и частная информация отдельных пользователей. Конечно, отмеченные системы уступают по набору возможностей системе LSF, которая обладает практически всеми необходимыми возможностями (см. раздел 4.6). С другой стороны, система LSF имеет слишком много функциональности, что сильно отражается на ее компактности и удобстве использования. Большая часть возможностей системы, как правило, остается невостребованной. Лишь немногие компании могут позволить себе использовать такую дорогую систему, как LSF. Сложной проблемой при выполнении параллельных программ является контроль над порождаемыми процессами. В большинстве случаев системы просто предоставляют необходимое количество узлов, действуя согласно пункту 2 указанного списка. Такой подход прост, но не эффективен. Программа не может динамически получать или освобождать узлы. В данной ситуации система MOSIX имеет явное преимущество, не делая различий между распределяемыми процессами. Также важной задачей является восстановление параллельной программы после сбоя одного из узлов. Для распределенных систем запоминание согласованного глобального состояния является серьезной теоретической и главное практической проблемой. Следует отметить необходимость реализации эффективных алгоритмов для передачи файлов. Многие системы используют либо сетевую файловую систему, либо стандартные приложения RSH, SSH и FTP. Рассмотрим пример. Пусть в некоторый момент времени необходимо отправить один файл на N узлов. Используя FTP, файл будет передан по сети N раз. Система может использовать альтернативные протоколы передачи файлов, способных передавать один файл многим получателям [34]. Согласно проекту система управления кластером, разрабатываемая в ИСП РАН, будет обеспечивать следующие возможности. Выполнение параллельных (распределенных и SPMD), последовательных и интерактивных задач. 60
Возможность указать в файле задания зависимость задач по результатам, управление через WEB интерфейс. Свойства системы можно выразить следующей таблицей (таб. 2). Миграция процессов Пакетные задания
Да Да
Файл задания Зависимость задач по результатам Статус задачи
Да Да
Балансировка
Да
Задание ресурсов Задание приоритетов Интерактивные задачи Ограничение ресурсов
Да Да Да Да
Контрольные точки Перекомпиляция Защита от сбоев
Нет Нет Нет
Параллельные программы
Да
Монопольный режим Интерфейс
Да Да
Да
Таблица 2. Свойства системы разрабатываемой в ИСП РАН В дальнейшем планируется поддержка защиты от сбоев для всех классов задач и миграция процессов. Литература 1. D. Abramson, R. Sosic, J. Giddy and B. Hall, "Nimrod: A Tool for Performing Parameterized Simulations using Distributed Workstations", The 4th IEEE Symposium on High Performance Distributed Computing, Virginia, August 1995. 2. L. Amar, A. Barak, A. Eizenberg, A. Shiloh, “The MOSIX Scalable Cluster File Systems for LINIX”. 3. Yair Amir, Baruch Awerbuch, A. Barak, R. Sean Borgstrom and Arie Keren, “An Opportunity Cost Approach for Job Assignment and Reassignment in a Scalable Computing Cluster”. 4. A. Avetisyan, S. Gaissaryan, O. Samovarov. “Extension of Java Environment by Facilities Supporting Development of SPMD Java-programs”. V. Malyshkin (Ed.): PaCT 2001, LNCS 2127, Springer-Verlag Berlin Heilderberg 2001, p. 175 – 180. 5. Mark A. Baker, Geoffrey C. Fox and Hon W. Yau, “Cluster Management Software”, Northeast Parallel Architectures Center 111 College Place Syracuse University New York 13244-4100 USA 16 November 1995. 6. A. Barak, O. La'adan, A. Shiloh, “Scalable Cluster Computing with MOSIX for LINUX”, the Hebrew University of Jerusalem.
61
7. A. Barak, O. La'adan, “The MOSIX Multicomputer Operating System for High performance Cluster Computing”. 8. The Beowulf project, www.beowulf.org. 9. A. Bricker, M. Litzkow, M. Livny, “Condor Technical Summary”, University of Wisconsin, 1991. 10. R. Buyya, “High performance cluster computing”, Volume 1, “Architectures and systems”, 1999. 11. EnFuzion 7.0 Administrator's Manual, Turbolinux. 12. Expect home page, http://expect.nist.gov/. 13. M.R. Garey, D.S. Johnson, “Computers and intractability: A guide to the theory of NPCompleteness”, 1979. 14. "IBM LoadLeveler User's Guide" IBM Corporation, 2001. 15. "IBM LoadLeveler Administration and Planning Guide" IBM Corporation, 2001. 16. S. Kannan, M. Roberts, P. Mayes, D. Brelsford, J.F Skovira, “Workload Management with LoadLeveler “, November 2001. 17. J.A. Kaplan, M.L. Nelson, “A comparison of Queuing, Cluster and Distributed Computing Systems”, NASA Langley Research Center June 1994. 18. C. Kopp, “Managing Cluster Computers. Parallelizing is the problem, flexibility is the key”, Dr. Dobb's Journal July 2000. 19. C. Kopp, “Supercomputing with TurboLinux enFuzion”. 20. M. Litzkow, M. Livny, “Experience With The Condor Distributed Batch System””, University of Wisconsin, 1991. 21. The Maui Scheduler, http://supercluster.org/maui. 22. R.P. Martin, A.M. Vahdat, D.E. Culler, and T.E. Anderson. Effects of Communication Latency, Overhead, and Bandwidth in a Cluster Architecture. //The 24th Annual International Symposium on Computer Architecture, 1997. http://now.cs.berkeley.edu/ 23. http://www.parallel.ru/cluster, система управления заданиями Cleo. 24. Platform Computing Corporation, “LSF Batch User's Guide”, 6th edition August 1998. 25. Platform Computing Corporation, “LSF Batch Programmers's Guide”, 4th edition August 1998. 26. Platform Computing Corporation, “LSF Batch Administrator's Guide”, 6th edition August 1998. 27. Platform Computing Corporation, “LSF Parallel User's Guide”, June 2001. 28. Platform Computing Corporation, “Using LSF with Condor Checkpointing”, 2001. 29. Platform Computing Corporation, “LSF JobScheduler User's Guide”, Fourth Edition, January 2000. 30. Platform Computing Corporation, “LSF JobScheduler Administrator's Guide”, Third Edition, January 2000. 31. Psched, API Standards for Parallel Job/Resource Scheduling, 1998. 32. H. El-Rewini, T.G. Lewis, H.H. Ali, “Task scheduling in parallel and distributed systems”, 1994. 33. Scyld Computing Corporation, Technology Brief, April 2001 document #3720-2-1. 34. M. Sola, M. Ohta, Y. Muraoka, T. Maeno, “Scalable and Reliable Multicast File Transfer Architecture”. 35. NASA Ames Research Center, “Portable Batch System”, Requirements Specification. 36. http://pbs.mrj.com/, Portable Batch System Pro.
62
Средства анализа параллельных SPMD программ Яковенко П.Н.
Аннотация. В этот статье представлен обзор задач, методов и средств анализа производительности и масштабируемости SPMD программ для параллельных вычислительных систем с распределенной памятью. Рассмотрены такие вопросы отладки параллельных программ как визуализация различных аспектов выполнения программы, моделирование параллельной программы при помощи алгебраических формул, зависящих от размерности задачи и числа процессоров, мониторинг (контролируемое выполнение). Обзор основан на большом количестве отладочных средств, разрабатываемых в рамках академических проектов, а также коммерческих продуктов. Ссылки на все эти средства анализа параллельных программ присутствуют в библиографии
разном порядке. Как следствие, программа на одних и тех же входных данных может давать разные результаты. Присутствие в программе межпроцессного взаимодействия неминуемо повлечет за собой возникновение коммуникационных ошибок (неправильное указание отправителя или получателя), блокировок процессов (ошибки синхронизации), длительных простоев процессов в ожидании событий в системе, которые потребуется устранять на стадии отладки программы. Все описанные выше проблемы требуют наличия у разработчика мощного набора инструментов, позволяющих с той или иной степенью автоматизации эффективно отлаживать параллельные программы. К таким средствам относятся стандартные отладчики, утилиты, позволяющие выполнять программу детерминировано, визуализировать ее выполнение и обмен сообщениями между процессами, оценивать ее масштабируемость при помощи соответствующих алгебраических моделей, устранять лишние временные задержки, возникающие в ходе выполнения программы. Более того, недетерминированность выполнения параллельной программы требует от разработчика глубокой внимательности при использовании каких-либо отладочных средств, т.к. любое вмешательство в выполнение исходной программы, влияющее на ее временные характеристики, может привести к тому, что программа в ходе отладки будет функционировать совсем не так, как до и после нее [5].
2. Масштабируемость параллельной вычислительной системы
1. Введение Достижения в области микропроцессорных и сетевых технологий позволили конструировать параллельные вычислительные системы с большим числом процессоров (например, Cray T3E, IBM SP2, Intel Paragon, кластеры на базе сети Marynet и SCI). К сожалению приложения, разработанные для традиционных последовательных компьютеров или даже для векторных суперкомпьютеров, не могут быть автоматически перенесены на подобные системы в силу архитектурных различий. Производительность параллельной программы зависит от большого числа параметров как непосредственно самой задачи, так и компьютерной системы, библиотеки поддержки выполнения и коммуникационной среды. Аккуратное предсказание влияния тех или иных стратегий на производительность программы, а также то, как скорость выполнения программы будет меняться при различных параметрах задачи и вычислительной системы, коренным образом может сократить время разработки эффективного программного обеспечения. Помимо описанных выше трудностей разработчик сталкивается с проблемой, заложенной в самой природе параллельной программы, недетерминированностью выполнения [4]. При каждом выполнении программы сообщения от взаимодействующих процессов могут приходить в 63
В этом разделе рассматриваются вопросы масштабируемости параллельных программ. Вводятся определения необходимых терминов, формулируются законы Амдала и Густафсона-Барсиса, устанавливаются необходимые условия создания высокопроизводительной масштабируемой параллельной программы.
2.1 Терминология Современные вычислительные системы с распределенной памятью состоят из вычислительных узлов, каждый из которых может содержать следующие устройства:
один или несколько центральных процессоров (обычно RISC);
локальную память (прямой доступ к памяти других узлов невозможен);
коммуникационный процессор или сетевой адаптер;
жесткие диски и/или другие устройства ввода/вывода.
Узлы связаны между собой через некоторую коммуникационную среду (высокоскоростная сеть, коммутатор и т.п.). 64
При проектировании параллельной программы основная задача состоит создании высокопроизводительной масштабируемой программы. В соответствии с толковым словарем русского языка производительность - есть эффективность трудовой деятельности, т.е. высокопроизводительная программа должна быть эффективной. Определение 1. Параллельная программа называется эффективной на данной параллельной вычислительной системе, если накладные расходы ограничены и достаточно малы. Определение 2. Параллельная вычислительная система называется масштабируемой, если производительность системы пропорциональна сумме производительностей всех узлов. Для каждой масштабируемой вычислительной системы существует максимальное число узлов (оно определяется архитектурой системы) такое, что при большем числе узлов система перестает быть масштабируемой. Определение 3. Параллельная программа называется масштабируемой, если скорость ее выполнения пропорциональна производительности вычислительной системы. Для масштабируемых вычислительных систем и параллельных программ коэффициент пропорциональности должен быть близок к единице. На практике, в силу накладных расходов, с увеличением числа процессоров, рост производительности замедляется. Для создания высокопроизводительной и масштабируемой параллельной программы необходимо выполнить ряд условий:
разработать эффективный алгоритм, обладающий высокой степенью параллелизма;
обеспечить равномерную вычислительную нагрузку всех узлов;
минимизировать накладные расходы на управление параллелизмом (синхронизация процессов и т.п.);
минимизировать накладные расходы, вызванные необходимостью коммуникационных обменов между процессами (пересылка сообщений, групповые операции и т.д.).
Масштабируемость является одной из наиболее важных характеристик программной системы, зачастую играющей большую роль, чем ее производительность. Более того, понятия "производительность" и "масштабируемость" тесно связаны между собой, и по масштабируемости системы можно судить о ее производительности [1].
2.2 Законы Амдала и Густафсона-Барсиса Производительность и масштабируемость параллельной программы легко описать при помощи кривых ускорения (speedup curves). Точка кривой ускорения вычисляется посредством деления времени, необходимого для решения данной задачи с использованием одного процессора, на длительность вычисления решения при использовании N процессоров. Закон Амдала, сформулированный в 1967 г., устанавливает зависимость между объемом последовательного (b,0<=b<=1) и параллельного кода в программе и ускорением S, достигаемым при использовании N процессоров вместо одного для решения данной задачи [2]: N N *b +1− b Полученная формула является серьезным сдерживающим фактором для программистов, склоняющихся к созданию параллельных программ, поскольку она обосновывает использование параллельных алгоритмов только для тех задач, у которых b является ничтожно малой величиной (например, задач линейной алгебры). Закон Амдала формулируется для общего случая параллельной программы, однако если рассматривать более узкий класс программ, то можно получить более оптимистичные оценки. Закон Густафсона-Барсиса исходит из того, что параметры b и N не являются независимыми [3]: S=
S = N − ( N − 1) * b
Действительно, запуск одной и той же программы на разном числе процессоров разумно производить только в академических целях. На практике, размерность задачи масштабируется при изменении количества доступных процессоров. Пользователи обычно имеют контроль над такими параметрами, как шаг сетки, длина временного интервала, величина погрешности и пр., и подбирают эти параметры, чтобы время выполнения программы было допустимым. Более верным будет считать, что время выполнения, а не размерность задачи является постоянным. При N = 32 и b=0.2 будет достигаться максимальное ускорение в 25.8 раз, что почти в 6 раз быстрее, чем ускорение, достигаемое в законе Амдала. Следует, конечно, помнить, что эта формула не учитывает коммуникационных задержек, накладных расходов операционной системы, таких как создание процесса, управление ресурсами памяти, буферизацию сообщений и т.д. Очевидно, что SIMD и SPMD формы параллелизма естественным образом укладываются в модель, предложенную Густафсоном и Барсисом. Весь спектр задач, возникающих при отладке, анализе и доводке распределенной масштабируемой SPMD программы, можно условно поделить следующим образом:
65
66
визуализация различных аспектов выполнения программы;
моделирование программы при помощи алгебраических формул, зависящих от размерности задачи и числа процессоров, для анализа производительности и масштабируемости;
отладка (мониторинг) параллельной программы.
В ходе решения этих задач возникают дополнительные подзадачи, связанные с обеспечением детерминированного выполнения программы, упорядочиванием событий произошедших в разных процессах, хранением собранной трассировочной информации и т.д.
3. Визуализация В этом разделе рассматриваются неотъемлемые свойства "полезной" системы визуализации, подзадачи, возникающие в ходе построения графического представления тех или иных аспектов выполнения параллельной программы, а также различные методы визуализации поведения программы и ее статистических характеристик. Текстовые представления данных, описывающие выполнение параллельной компьютерной системы, по своей природы последовательны и достаточно тяжелы для усваивания информации. В то же время, графические визуализации становятся все более мощными и удобными инструментами для понимания поведения системы и выполнения программы, а также для целей отладки и характеризации производительности системы.
3.1 Информативность визуализации Визуализация - двойственный метод, при правильном использовании он позволяет быстро ухватывать главные моменты, в противном случае может принести ужасающие результаты. К сожалению, нарисовать привлекательную картинку намного проще, чем полезную. Графическое изображение является более емким, чем текстовое представление данных. Информация может представляться посредством различных графических примитивов (линий, стрелок, окружностей и т.д.), трехмерных объектов, иконок и пр. Элементы одного типа различаются цветом, толщиной линий, формой линий (сплошная, прерывистая, двойная). Все эти средства дают возможность разместить на экране монитора огромный объем информации, позволяющий одним взглядом оценить различные характеристики параллельной программы. Неправильное их использование сделает систему визуализации совершенно неинформативной для пользователя или вообще сформирует у него некорректное представление об отлаживаемой программе. Статья [36] суммирует современные подходы по графическому отображению информации и предлагает технологический и теоретический базис для будущих исследований в области средств визуализации производительности параллельных программ. В статье [16] предлагается набор критериев, которым должна удовлетворять система визуализации. Наиболее важным следует 67
считать то, что такая система, прежде всего, должна направлять, информировать, а не иллюстрировать известные факты. Разница между информированием и иллюстрированием состоит в различии представления данных. Если система отображает полезные, информативные картинки без какого-либо серьезного вмешательства со стороны пользователя, то у нее есть хороший потенциал. Если же пользователю требуется на каждом шагу направлять систему для выборки и обработки данных, то можно сказать, что он имеет глубокое представление о структуре программы. Тем самым роль такой системы сводится к графическому представлению уже известной информации. Соответственно и полезность такой системы мала.
3.2 Задачи визуализации Исследования в области визуализации программ можно рассматривать на двух уровнях: с точки зрения выполняемой задачи и цели, поставленной разработчиками данного средства графического представления. При создании двух- или трехмерного образа системы решается целый ряд неотъемлемых задач, таких как сбор информации, анализ информации, сохранение исходных и обработанных данных и непосредственно графическое изображение. Целью графического представления может быть отладка программы или системы, вычисление и оптимизация производительности или своего рода "электронное документирование" посредством отображения структур данных, анимации алгоритма или других графических изображений, которые выражают знания о функционировании системы, программы или алгоритма. Цели визуализации программы могут налагать дополнительные ограничения на выполняемые задачи. Поэтому "цели" можно рассматривать как второй уровень характеризации визуализирующей системы.
3.3 Сбор данных Множество параметров и большой объем информации, обрабатываемый при параллельных вычислениях, требует, чтобы инструментирующий код для сбора данных использовался крайне избирательно и аккуратно. Инструментирование может происходить на аппаратном и на программном уровне. Код может вставляться для решения задач визуализации и затем удаляться из окончательной версии, существовать постоянно внутри системы и активироваться по требованию или быть динамически добавляемым, используя специализированные приемы отладчиков. Любое инструментирование в большей или меньшей степени влияет на производительность изучаемой системы. Степень такого влияния, или так называемый эффект зондирования (probe effect), определяется объемом собранной информации, оборудованием мониторинга, системным программным обеспечением, архитектурой системы и парадигмой программирования [18]. Этот эффект выражается в том, что дополнительные накладные расходы на выполнение инструментальной части программы могут 68
нарушить исходную последовательность событий в отлаживаемой программе. Как следствие, временные характеристики системы искажаются и ошибка, для поиска которой проводилось инструментирование, может не проявиться во время отладки. Эти факторы вкупе с целью визуализации определяют тип используемого инструментирования.
3.4 Инструментирование Информация, которая может быть получена при помощи аппаратуры, включает такие показатели как количество инструкций в секунду, количество операций с плавающей точкой в секунду, количество попаданий в кэш, пропускная способность памяти и коммутаторов, а также адреса инструкций и данных в памяти. В качестве примера можно привести микропроцессор мониторинга для параллельного процессора RP3 компании IBM [19]. Для визуализации алгоритма требуются более высокоуровневые данные. Они могут быть получены при помощи программного инструментирования. Программное инструментирование состоит в помещении небольших кусков кода внутрь операционной системы, системы поддержки выполнения или прикладной программы. Функция этого кода, называемого сенсором заключается в посылке некоторого значения компоненту, ответственному за суммирование всей информации. Уровень, на котором размещается сенсор, определяет тип собираемой информации. Инструментирование операционной системы может использоваться для фиксации данных об отправляемых и получаемых сообщениях, создании процессов, окончании программы, подкачке виртуальной памяти, переключении контекстов, доступе к памяти и системных вызовах. Инструментирование системы поддержки выполнения обеспечивает информацией о состоянии различных очередей, приобретение и освобождение захватов, вход и выход из критических секций, прибытие и проход через барьеры, вход и возврат из процедур. Подобная информация может быть использована для выявления простоев в программе применительно к конкретным участкам кода. Инструментирование прикладной программы дает доступ к абстрактным, высокоуровневым событиям и конструкциям внутри прикладной программы. Инструментирование на прикладном уровне обеспечивает достаточно детальную картину поведения программы для целей отладки, позволяя пользователю проверять как корректность всей программы, так и отдельных ее участков. Добавление сенсоров в программу может до определенной степени быть автоматизировано [37, 20]. Библиотека инструментирования Pablo [21] предоставляет возможность пользователю определять свои функции, причем информация о событиях в системе, прежде чем попасть в трассировочный файл, будет передана этим функциям. Данные в трассировочный файл 69
попадают в формате SDDF, содержащем описание записей непосредственно в самом файле. Это позволяет легко добавлять в файл записи нового типа. Система Paradyn [22] инструментирует программы динамически. Пользователь указывает интересующие его события уже во время выполнения программы. Разработчики нацеливают Paradyn на доводку очень больших систем, которые выполняются несколько дней и более. В таких ситуациях повторный запуск приложения является неприемлемым с точки зрения времени, необходимого для получения трассировочной информации. Другой подход состоит в инструментировании библиотек, реализующих функции параллельного программирования. Коммуникационная библиотека PICL [17] генерирует файл событий, таких как отправка и получение сообщения, а также статистика вычислений и коммуникационных операций. Трассировочный файл формата PICL используется многими системами визуализации производительности параллельных программ, например Paragraph [23].
3.5 Детерминированное выполнение Некорректная параллельная программа, вследствие недетерминированного выполнения, на одних и тех же входных данных может давать разные результаты. Для локализации ошибки требуется обеспечить идентичность выполнения последовательных запусков программы на одном наборе данных. Проблема воссоздания исходного поведения параллельной программы на данном наборе исходных данных при каждом последующем запуске решается с использованием механизма "записи и воспроизведения", который заключается в трассировке всех событий, влияющих на детерминированность выполнения. В распределенной параллельной программе недетерминированность порождается механизмом обмена сообщениями между процессами. Во время первого (эталонного) выполнения программы - стадии записи - отладочная система записывает в трассировочный файл всю необходимую информацию о передаваемых сообщениях. Данный файл может быть использован при последующих выполнениях программы - стадиях воспроизведения - для воссоздания поведения эквивалентного эталонному. Система CPDE [39] при компиляции генерирует две программы: одна содержит специальный код для записи всех событий в системе в трассировочный файл, другая версия - код для выполнения программы с сохранением последовательности событий. Статья [38] предлагает для программ на базе библиотек MPI и PVM сохранять минимум информации во внешний файл, тем самым, уменьшая его размер и влияние эффекта зондирования. В статье обосновывается механизм, позволяющий записывать только номер процесса отправителя в каждом событии получения сообщения, причем рассматриваются только те события, где сообщение принимается по маске, т.е. отправителем может быть любой процесс программы, или любой процесс в группе. 70
3.6 Анализ данных
3.7 Хранение данных
Анализ данных является неотъемлемой фазой при визуализации параллельной программы. Эта фаза включает в себя такие действия, как вычисление различных статистик, упорядочение событий, поступивших от разных процессоров, обнаружение в потоке более высокоуровневых событий. Наиболее общим для всех целей визуализации является упорядочивание событий в потоке. Поскольку время на разных процессорах, вообще говоря, не совпадает, то поток событий должен быть упорядочен некоторым образом. В противном случае может возникнуть ситуация, при которой сообщение было отправлено раньше, чем его получили. В распределенной системе в силу отсутствия глобального времени события упорядочиваются частично. Предложенный Лампортом механизм причинной упорядоченности [24] на основе отношения "произошло до" позволяет избежать казусов, когда сообщение принимается до его отправки. При таком механизме события, произошедшие на одном процессоре, упорядочиваются посредством физического времени данного процессора, т.е. в пределах одного процессора достигается полный порядок. События, произошедшие на разных процессорах, упорядочиваются через механизм сообщений. Событие отправки сообщения происходит непосредственно перед событием получения сообщения. Распространяя это правило транзитивно на все процессоры можно получить частичный порядок всех событий в системе. Такое упорядочивание также называют логическим временем. Существуют также другие механизмы упорядочивания событий. К ним можно отнести фазовое время [25], при котором события упорядочиваются только внутри своей фазы. Например, все события передачи сообщения 32 процессору объединяются в одну фазу, внутри которой они упорядочены. Статья [40] предлагает использовать виртуальное время. Виртуальное время организовано таким образом, что во время работы программы оно совпадает с физическим, а во время работы сенсора мониторинга - останавливается. Такой подход позволяет сохранить интервалы времени между событиями в инструментированной системе неизменными по отношению к ее исходному варианту. Следует сказать, что подход в упорядочивании событий сильно зависит от целей системы визуализации. В большинстве случаев достаточно лампортовского частичного порядка, в то время как в некоторых ситуациях, например, таких как анализ производительности параллельной программы, необходимо наличие физического времени. Разумеется, в распределенной системе организовать глобальное время практически невозможно, однако использование программных механизмов коррекции времени позволяет с высокой точностью приблизить логическое время к физическому.
Требования к хранению собранных в ходе мониторинга данных определяются большей частью предполагаемым будущим использованием сохраненной информации. Системы, предназначенные для визуализации исключительно поведения системы, например анимированного проигрывания событий в программе, предъявляют крайне слабые требования к входным данным. Чаще всего это обычный двоичный файл, возможно нерегулярной структуры, который грузится в память и по нему скользит окно, определяющее ту информацию, которая отображается на экране. Некоторые системы позволяют проводить достаточно сложные операции с трассировочными данными, такие как поиск, сортировка и другие формы анализа. Статья [41] представляет проект Prophesy - инфраструктуру из трех реляционных баз данных, позволяющую хранить информацию о выполнении различных версий программы, строить аналитические модели, изучать масштабируемость, влияние функций ввода/вывода на производительность системы и т.д. Среда SIEVE [26] - система визуализации масштабируемых параллельных программ - основана на электронных таблицах. Пользователь может вывести информацию в виде таблицы, где столбцы содержат показатели для строк процессоров вычислительной системы.
71
3.8 Графическое представление Графическое представление информации, особенно статистической, является областью исследований, ведущей свои истоки к началу двадцатого столетия. По аналогии с другими задачами, обсуждаемыми в этой работе, аспекты графического представления определяются нацеленностью конкретной системы визуализации. Отладчики отображают некоторым образом состояние программы. Часто, это узко специализированное представление некоторого аспекта вычислений, такого, как шаблон доступа к общей памяти, межпроцессные взаимодействия через некоторый канал или порядок вызова подпрограмм. Средства визуализации производительности системы обычно отображают стандартные метрики, среди которых загрузка процессора, использование виртуальной памяти, коммуникационная нагрузка и т.д. Системы визуализации поведения программы обеспечивают графическое представление структур данных на высоком уровне, операций, обновляющих эти структуры данных, а также некоторое абстрактное представление вычислений происходящих в программе и процент ее завершенности. Такие визуальные представления зачастую не только удобны в плане понимания вычислительной структуры программы, но и полезны с точки зрения отладки и вычисления производительности системы. Вычислительная структура отображается, как правило, в виде графов, в которых узлы представляют сущности программы, а дуги зависимости между 72
ними или временной порядок. Часто, такие отображения могут быть анимированными, выделяя текущую сущность. Такие визуализации могут быть полезными при анализе поведения программы, позволяя пользователю выявлять некорректные последовательности вызовов подпрограмм или невызываемые подпрограммы. Анимированный граф также раскрывает время присутствия в конкретной процедуре. Например, дисплей задач в Paragraph [23] может быть использован именно для решения таких задач. Среди других механизмов следует отметить карту параллельности [27] и граф причинности Макбилана [28]. HeNCE система [29] представляет собой интегрированную визуальную среду для создания, компиляции, выполнения и анализа PVM программ. Во время выполнения программы HeNCE может отображать ее анимированное поведение в виде графа. Узлы графа вычислений изменяют цвет для индикации возникновения различных трассировочных событий. Для графического представления коммуникационных взаимодействий между процессорами чаще всего используются различные графические примитивы, такие как прямоугольники, стрелки и линии. Прямоугольники обычно изображают процессоры, линии - соединения между ними, а стрелки коммуникационные события. В некоторых случаях расположение прямоугольников относительно друг друга раскрывает топологию процессоров в вычислительной системе. Такая информация может быть статически заложена в коммуникационной библиотеке, если вычислительная система имеет фиксированную топологию. Некоторые системы поддержки выполнения позволяют задавать топологию в программе логическим образом. Например, библиотека MPI содержит системные вызовы, устанавливающие логическое размещение процессоров в виде гиперкуба или произвольного графа. Статья [44] предлагает распознавать коммуникационную структуру вычислений посредством анализа трассировочного файла. Предложенная система построена на метрике, при помощи которой из ряда шаблонов выбирается граф, наиболее близкий к коммуникационному графу программы. Возможность просмотра коммуникационного графа, вершины которого размещены на экране в соответствии с логической топологией вычислений, позволяет выявлять аномалии в передаваемых сообщениях между процессорами (неправильный адрес назначения или отправителя), а также некорректное отображение процессов на процессоры вычислительной системы. Многие отладчики [30,25] имеют возможность строить диаграммы зависимости процессов (ось Y) от времени (ось X). Дуги на такой диаграмме характеризуют пересылку сообщений. Диаграмма Фейнмана из системы Paragraph [23] помимо коммуникационной информации предоставляет возможность оценить загрузку коммуникационных каналов. Системы, предназначенные для вычисления производительности программы, имеют механизмы вывода статистической информации в графическом виде. 73
Tapestry [31] поддерживает круговые диаграммы, гистограммы, индикаторные схемы, диаграммы Кивиата и матрицы. Paragraph [23] использует диаграммы Кивиата и гистограммы для изображения статистик использования процессора и матрицы для информирования об объеме трафика сообщений. Среда SIEVE [26], основанная на представлении производительности программы в виде электронных таблиц, может быть использована для создания XY схем. Колонки из электронной таблицы назначаются на оси X и Y. Линии и многоугольники соединяют точки на схеме. Цвет может быть использован для выделения некоторых атрибутов или идентификатора процессора.
4. Моделирование параллельной программы В этом разделе рассматриваются вопросы моделирования параллельной программы при помощи алгебраических формул, зависящих от размерности задачи и числа процессоров. Анализируются особенности строения таких моделей и универсальность средств моделирования. Параллельные программы создаются для вычислительных систем с большим количеством процессоров (десятки, сотни), однако тестирование и отладку обычно проводят на компьютерах, содержащих на порядки меньшее число вычислительных элементов (менее десяти). После того, как программа отлажена для эффективного выполнения на небольшом числе процессоров, разработчики встают перед дилеммой, как обеспечить эффективное выполнение на более мощной вычислительной системе и реальных размерах задачи: отлаживать программу непосредственно на мощном компьютере или моделировать выполнение программы на отладочном компьютере разработчика, используя различную трассировочную информацию. Первый вариант является малопривлекательным, поскольку требует монопольного использования в течение продолжительного периода времени мощной вычислительной системы, время работы которой достаточно дорогое. Также для разработчика остается скрытой производительность программы на еще более мощном компьютере, т.е. такой подход не затрагивает вопросы масштабируемости программы. Поэтому разработчикам необходимы инструменты, позволяющие моделировать параллельную программу с целью анализа времени ее выполнения и масштабируемости. Моделирование параллельной программы состоит в построении модели, дающей возможность вычислять (предсказывать) время выполнения программы T или ее масштабируемость как функцию, зависящую от размерности задачи N и числа процессоров P, т.е. необходимо найти такую функцию F, чтобы время выполнения T=F(N,P) вычислялось с наименьшей погрешностью. Как правило, N является векторной величиной, содержащей, например, для задач математической физики, шаг сетки по горизонтали и вертикали. При анализе масштабируемости обычно вычисляется асимптотическое ускорение, т.е. ускорение, в котором не накладывается ограничение на доступное число процессоров, или какая-либо другая метрика 74
масштабируемости [7]. Учитывая тесную связь между временем выполнения и масштабируемостью параллельной программы можно считать, что такие варианты моделирования решают единую задачу улучшения масштабируемости параллельной программы и сокращения временных потерь, возникающих в ходе ее выполнения.
4.1 Средства автоматизированного моделирования Средства моделирования программ используют один из следующих подходов:
алгебраический анализ;
абстрактная интерпретация;
симулирование выполнения.
Алгебраический анализ заключается в математическом моделировании вычислительных и коммуникационных операций в программе [8]. Абстрактная интерпретация заменяет алгебраическими выражениями только вычислительные блоки кода, симулируя все коммуникационные операции [9]. Подход симулирования выполнения эмулирует (интерпретирует) каждую инструкцию программы [10]. Несмотря на то, что последние два варианта в полном объеме не строят модели программы, абстрактная интерпретация и симулирование выполнения позволяют получать детальные трассировки поведения программы без ее непосредственного выполнения на большой вычислительной системе. Это является особенно важным, когда такая система не доступна программисту для проведения полной отладки и доводки параллельной программы. Важно заметить, что любая модель может рассматриваться только в контексте конкретной вычислительной системы. Если в самой модели не заложены механизмы независимости от конкретной архитектуры, то при переносе программы на другую компьютерную систему необходимо провести повторное моделирование. Получение информации о производительности моделируемой программы на данной вычислительной системе осуществляется, как правило, посредством прогонки специальных тестов (бенчмарков), одноразового выполнения инструментированной версии программы или при помощи каких-либо других специализированных механизмов, например, таких как бенчмапы [11]. Моделирование SPMD программ является более простой задачей, чем общий случай параллельной программы для MIMD архитектуры. Для SPMD программ возможно применение более грубых подходов в моделировании с использованием алгебраических абстракций приложения и компьютерной системы без значительных потерь в точности [12]. Статья Сарукая [13] описывает методологию и набор средств (MK-toolkit) для автоматизированного анализа масштабируемости параллельных программ, основанных на парадигме передачи сообщений. Суть примененного метода 75
состоит в получении информации о программе посредством анализа исходных текстов и построения дерева синтаксического разбора, а также анализа трассировочных файлов, получаемых в ходе выполнения инструментированной версии программы и иллюстрирующих коммуникационные взаимодействия процессов. Для каждого элемента дерева, представляющего собой цикл, условие ветвления, подпрограмму, вызов функции или коммуникационную операцию МК определяет формулу вычисляющую время выполнения блока программы и принимающую в качестве параметров размерность задачи N и количество процессоров P. Такие формулы, где это удается МК, определяются автоматически, используя трассировочные файлы от последовательных запусков программы на разных исходных данных. В противном случае, пользователь должен вручную задавать зависимость времени выполнения данного блока программы от параметров N и P. Таким образом, программа делится на набор блоков, для каждого из которых формулируется своя функция зависимости времени выполнения от параметров N и P. МК-toolkit учитывает то, что коммуникационные операции могут происходить параллельно с вычислениями посредством так называемых коммуникационных шаблонов. Авторы проверяли работоспособность модели на разных прикладных задачах, таких как решение трехдиагональных систем, симулирование атмосферных явлений и обнаружили, что погрешность моделирования составляет в среднем 15%. Брехм и другие ставили перед собой несколько иную задачу [14], - оценить точность алгебраических моделей параллельной программы на примере PSTSWM программы [42]. В статье поставлена задача оценить при помощи моделирования, какие варианты распараллеливания наиболее эффективны для данной задачи, насколько детальными должны быть модели, чтобы точность моделирования была достаточно высокой, и как будет меняться погрешность моделирования при изменении параметров задачи и числа доступных процессоров. По аналогии с MK-toolkit, предложенная утилита PerPreT описывает исходную SPMD программу как набор формул характеризующих вычислительную (арифметические выражения) и коммуникационную (вызовы библиотеки межпроцессных взаимодействий) части программы. Особенность PerPreT заключается в том, что модель программы и вычислительной системы отделены друг от друга, тем самым при переносе программы на компьютер другой архитектуры не требуется изменять модель программы. Авторы моделировали PSTSWM программу с разность степенью детализации модели и охарактеризовали те аспекты выполнения данной программы, которые должна учитывать модель для достижения достаточно малой (менее 5%) погрешности моделирования.
76
4.2 Анализ эффективности
Рассматривая средства моделирования времени выполнения и масштабируемости параллельных программ можно выделить общие аспекты для всех таких утилит:
требуется серьезное вмешательство разработчика в процесс построения модели, поскольку большая часть задач, возникающих при построении модели, не может решаться автоматически;
средства моделирования разрабатываются и тестируются для решения конкретного спектра задач (например, прогнозирование погоды), поэтому на других задачах они намного менее эффективны. Это связано с тем, что в одних задачах могут преобладать циклы с постоянными границами, в других - с переменными, или границы вложенного цикла зависят от номера итерации объемлющего цикла;
все модели пренебрегают теми или иными аспектами выполнения параллельной программы. Например, Брехм исходит из того, что все процессы SPMD программы выполняются синхронно. Такое предположение может привести к большой погрешности моделирования при анализе многих задач. Такие предположения также влияют на стабильность погрешности моделирования. При различных входных данных (размерность задачи и число процессоров) погрешность варьируется от 0 до 27 процентов.
вся программа делится на две крупных части: вычислительная и коммуникационная, которые фактически моделируются отдельно;
вычислительная часть разбивается на элементарные блоки (ветвления, циклы, подпрограммы и пр.), которые моделируются, как правило, отдельно;
каждый блок описывается в модели алгебраическим выражением, зависящим от нескольких параметров. В их число входят как статические параметры, такие как размерность задачи и число процессоров, так и динамические - вероятность ветвления, число итераций в цикле, размер передаваемого сообщения;
вид алгебраического выражения вычисляется либо в ходе тестовых прогонов инструментированной программы на разных исходных данных, либо эмпирически задается пользователем на основе досконального знания данной предметной области и моделируемой программы;
динамические параметры (вероятность ветвления, число итераций в цикле, размер передаваемого сообщения) в алгебраических выражениях модели вычисляются в ходе тестовых прогонов инструментированной версии программы; алгебраические выражения для коммуникационной части чаще всего зависят от размера и режима передачи сообщений: синхронный, асинхронный, широковещательная рассылка, операции редукции, синхронизация и т.д. Режимы передачи определяются в ходе синтаксического анализа исходных текстов программы.
Коммуникационную подсистему можно достаточно точно моделировать независимо от конкретного приложения [15]. Все коммуникационные операции можно определить через время подготовки, время передачи одного байта, размер сообщения, количество процессов, участвующих в групповой операции, скорость обмена данными в памяти (для буферизованных операций) и некоторых других. Полученные формулы можно использовать в средствах моделирования без необходимости производить большое количество тестов для определения быстродействия коммуникационной операции в отлаживаемой программе. Средства моделирования программ дают возможность оценить ее время выполнения и масштабируемость при разных размерностях задачи, без необходимости прогонов на вычислительной системе. С другой стороны они обладают рядом существенных недостатков: 77
На сегодняшний день средства моделирования времени выполнения и масштабируемости параллельных программ не являются универсальными. Все утилиты проектировались для анализа конкретного спектра задач, как правило, моделей физических явлений. Как результат, применение данных средств к другим классам задач приводит к увеличению погрешности моделирования.
5. Отладка (мониторинг) параллельной программы В этом разделе рассматриваются вопросы отладки (мониторинга) параллельной программы как контролируемого выполнения. На базе черновой версии стандарта анализируются отличительные особенности параллельного отладчика. Дается обзор таких отладчиков как P2D2, разрабатываемый в лабораториях NASA, Prism, входящих в пакет SUN HPC ClusterTools, и TotalView компании Etnus. SPMD программа отлаживается в два этапа - как последовательная и как параллельная. На первом этапе используется традиционный отладчик, под контролем которого выполняется одна копия программы (SPMD программа должна корректно функционировать в однопроцессном варианте). На втором этапе программа (запущено несколько копий) отлаживается с использованием параллельного отладчика. На сегодняшний день функциональные возможности параллельного отладчика не стандартизированы, доступен только черновой вариант стандарта [6]. Отладчик - это средство, дающее пользователю возможность наблюдать и контролировать выполнение отлаживаемой программы, так называемой целевой программы. Параллельный отладчик выполняет данную функцию для параллельной программы. 78
5.1 Особенности архитектуры параллельного отладчика Параллельная программа состоит из одного или более процессов, каждых из которых ассоциирован с исполняемым файлом (в SPMD модели единым для всех процессов). Каждый процесс, в свою очередь, как правило, стоит из нескольких нитей. Таким образом, целевая программа представляет собой множество нитей и/или взаимодействующих процессов. Важным для отладчика является поддержка отладки в терминах исходного языка, т.е. отладчик должен не только контролировать выполнение целевой программы на уровне регистров и памяти, но и поддерживать высокоуровневый интерфейс в терминах исходного языка, выраженный в использовании переменных и функций целевой программы. Пользователь взаимодействует с отладчиком посредством команд, которые для удобства могут быть обрамлены графическим интерфейсом. Несмотря на то, что отладчик может поддерживать как последовательную (следующая команда может выполняться только после того, как закончена предыдущая), так и параллельную (допускается параллельное выполнение некоторых команд) модель команд, существенным является конкретизация момента, когда команда считается оконченной. Для некоторых команд (вывести на экран значения элементов массива) такой момент очевиден. Для других команд (пошаговое выполнение) момент окончания не является интуитивно понятным. Если процесс на данном шаге выполняет функцию принятия сообщения, то это может потребовать у него перехода в режим ожидания, пока сообщение не придет от другого процесса. Можно ли считать шаг завершенным, если процесс ожидает приема сообщения? Таким образом, для каждой команды отладчика следует явно указывать условия, когда данная команда может быть выполнена (предусловие), и момент ее окончания. Управление выполнением последовательной программы не составляет трудностей, поскольку целевая программа может находиться либо в состоянии останова, либо в состоянии выполнения. Если в отлаживаемой программе возникает событие, то программа останавливается. Пользователь может исследовать ее состояние, адресное пространство и потом продолжить выполнение. Управление параллельной программой существенно более сложное, поскольку каждая нить целевой программы имеет свое уникальное состояние выполнения. При возникновении события следует решить, что должно быть сделано с другими нитями. Многие параллельные отладчики (P2D2 [32], TotalView [33], CXdb [34], Prism [35]) поддерживают выполнение операций с группами процессов (нитей). Группировка процессов (нитей) позволяет адресовать команду одному, многим или всеми процессам (нитям) в целевой программе. Аналогично, реакция на событие, возникшее в некотором процессе (нити) группы может распространяться на все объекты группы. Например, если в некоторой нити была достигнута точка останова (breakpoint), то будут остановлены все нити, входящие в данную группу. 79
Следующий вопрос, в котором проявляются особенности параллельного отладчика, заключается в том, какая модель "старт-стопа" реализована в отладчике. В последовательных отладчиках, поддерживающих отладку многонитиевых (multithreaded) целевых программ, как правило, используется модель "остановить всех" (stop-the-world). При возникновении события в одной нити, останавливаются все нити процесса. Для клиент-серверных приложений такая модель не применима: возникновение события в клиенте не должно останавливать сервер. Поэтому для многопроцессных программ используется модель "не трогать остальных" (leave-others-alone). В общем случае параллельный отладчик должен использовать смешанную модель: "остановить всех" для нитей одного процесса и "не трогать остальных" для процессов. Возникновение события в нити приводит к останову всех нитей данного процесса, но не влияет на другие процессы целевой программы. Модель возобновления выполнения целевой программы является зеркальным отражением модели останова. По умолчанию возобновляют выполнение только остановленные нити, попавшие в одно множество (все остановленные нити в результате возникновения события). Параллельный отладчик может также поддерживать возможность возобновления отдельных нитей (например, группы нитей) или всех нитей целевой программы.
5.2 Обзор реализаций Параллельный отладчик может проектироваться двумя способами:
независимо (с нуля) (TotalView [33], Prism [35], CXdb [34]);
надстройка для последовательного отладчика (P2D2 [32], GUARD [43]).
Во втором случае для каждого процесса целевой программы запускается отдельная копия многонитиевого отладчика. В качестве последовательного отладчика, как правило, используется GDB. Основной причиной для этого является наличие реализаций GDB для большого количества вычислительных платформ, что позволяет легко переносить отладчик на другие платформы, а также создавать отладчик для гетерогенных вычислительных систем.
5.3 P2D2 (Portable Parallel / Distributed Debugger) В статье [32] описан параллельный отладчик для гетерогенных вычислительных систем P2D2, построенный по технологии клиент-сервер. Отладочная часть сервера основывается на свободно распространяемом отладчике GDB. Основной задачей при создании P2D2 являлась задача построения масштабируемого параллельного отладчика для гетерогенных вычислительных систем. Под масштабируемостью в данном контексте понимается масштабируемость пользовательского интерфейса, т.е. способность отладчика 80
предоставлять пользователю возможность исследовать состояние целевой программы (просматривать значения переменных, исходные тексты, стэк и другие компоненты вычислительного состояния программы) и контролировать ее выполнение (останавливать выполнение в заданных точках и возобновлять его после исследования состояния программы) вне зависимости от числа выполняющихся в ней процессов. Отладка гетерогенных вычислительных систем достигается за счет использования отладчика GDB, доступного для многих вычислительных платформ, а также клиент-серверного строения P2D2, позволяющего достаточно легко адаптировать отладчик для новой платформы. Масштабируемость пользовательского интерфейса отладчика обеспечивается иерархичностью информации, предоставляемой пользователю. P2D2 определяет три уровня абстракции для просмотра состояния целевой программы:
решетка процессов (process grid) - верхний уровень;
выделенная группа (focus group) - средний уровень;
выделенный процесс (focus process) - нижний уровень.
Решетка процессов представляет собой область экрана, в которой представлены все процессы целевой программы в виде иконок. Пользователь имеет возможность программировать внешний вид процессов в решетке (тип иконки), в зависимости от состояния целевой программы. Такая настройка достигается посредством определения пользователем набора предикатов, проверяемых в каждом отлаживаемом процессе. В зависимости от значения предикатов процесс отображается соответствующей иконкой в решетке процессов. Например, выполняющиеся процессы имеют по умолчанию зеленую иконку, а остановленные - красную. В области экрана, отвечающей выделенной группе, отображается более детальная информация о процессах, сгруппированных пользователем. Эта область представляет собой список, в котором каждому процессу отведена одна строка. Для каждого процесса группы отображается такая информация как идентификатор процесса, наименование машины, на которой выполняется процесс, статус процесса (выполняется, остановлен и т.п.), расшифровка статуса (адрес, по которому остановлен процесс) и т.д. Область для просмотра состояния выделенного процесса предназначена для показа исходных текстов целевой программы.
5.4 TotalView Параллельный отладчик TotalView [33] выделяется тем, что он поддерживается руководством ускоренной суперкомпьютерной инициативы (ASCI). По своим функциональным возможностям TotalView во многом аналогичен P2D2, но имеет ряд особенностей: 81
автоматическое подключение к отладочной среде параллельных процессов целевой программы (TotalView отслеживает создание процессов);
автоматическое добавление новых процессов и нитей в соответствующие группы;
позволяет отлаживать программы, не содержащие отладочной информации. В такой ситуации программа представляется пользователю на уровне машинных кодов;
TotalView позволяет изменять исходные тексты программы (на некоторых платформах даже машинные коды) во время отладки. Это сокращает время поиска и исправления ошибок;
TotalView имеет встроенный интерфейс командной строки (CLI) для тех случаев, когда нет возможности использовать графический интерфейс пользователя;
поддерживает исследование core файла, полученного в результате аварийного останова программы;
пользователь может изменять реакцию отладчика на сигналы в целевой программе;
встроенный визуализатор данных (Visualizer) позволяет графически отображать массив данных, динамический граф вызова процедур в программе, граф передачи сообщений между нитями.
В то же время, в отличие от P2D2, TotalView не поддерживать отладку гетерогенных программ. Все узлы вычислительной системы, на которых выполняется целевая программа, должны иметь одинаковую архитектуру и операционную систему. Основная информация в TotalView о целевой программе представляется на трех экранах:
корневое окно (Root window);
окно процессов (Process window);
окно переменных (Variable window).
Корневое окно представляет общую информацию о целевой программе. Окно состоит из нескольких закладок, на которые выводится информация об отлаживаемых процессах и нитях, процессах, к которым TotalView может подключиться для отладки, группах, сформированных пользователем для управления процессами и нитями, а также журнал всех действий пользователя за время отладочной сессии. Окно процессов является основным окном отладочной сессии. В нем отображается подробная информация о процессе и нитях внутри этого 82
процесса. Панели внутри окна отображают трассу стэка, фрейм стэка, исходный текст для выделенной нити, информацию о контрольных точках и т.д. Окно переменных предоставляет информацию об адресе, типе и значении локальных, а также глобальных переменных. В этом окне также можно посмотреть значения ячеек памяти в заданном диапазоне адресов. TotalView предоставляет широкие возможности по спецификации контрольных точек в целевой программе. Помимо стандартных возможностей, таких как точки останова по адресу, барьерные точки останова (аналогично MPI_Barrier), условных точек останова (нить останавливается, если выполнено выражение), точки останова при изменении значения переменной, TotalView позволяет вставлять во время сеанса отладки фрагменты кода по заданному адресу. Такие вставки могут содержать любые операторы, включая вызовы функций, определенных в данном процессе. Пользователь может писать их на C, C++, Фортране и ассемблере. TotalView на лету компилирует данный фрагмент кода и учитывает его во время отладки. Благодаря этой возможности пользователь может динамически исправлять ошибки в программе во время отладки, без необходимости изменять код, компилировать программу и начинать новую отладочную сессию.
6. Заключение Анализ распределенной параллельной программы является одной из наиболее актуальных задач в области современного параллельного программирования. Природа параллельной программы требует наличия развитых средств отладки, таких как программы мониторинга, утилиты, позволяющие выполнять программу детерминировано, визуализировать ее выполнение и обмен сообщениями между процессами, оценивать ее масштабируемость при помощи соответствующих алгебраических моделей, устранять лишние временные задержки, возникающие в ходе выполнения программы. В этой работе представлен обзор задач, возникающих при отладке, анализе и доводке распределенной масштабируемой SPMD программы, а также методов и средств решения поставленных задач. К сожалению, на сегодняшний день не существует средств в полной мере решающих все стоящие задачи. Однако повышенный интерес коммерческих компаний и исследовательских центров, а также большое число академических проектов и развитие отраслевых и индустриальных стандартов свидетельствуют о том, что в ближайшем будущем данная проблема будет решена. Литература 1. Xian-Xe Sun, The Relation of Scalability and Execution Time, Proc. of the 10th Int Parallel Processing Symposium, 1996.
83
2. Amdahl, G.M., Validity of the single-processor approach to achieving large scale computing capabilities. In AFIPS Conference Proc. vol. 30 (Atlantic City, N.J., Apr. 18-20). AFIPS Press, Reston, Va., 1967, pp. 483-485. 3. Gustafson, J.L., Reevaluating Amdahl's Law, CACM, Vol. 31, No. 5, 1988. pp. 532-533. 4. Damodaran-Kamal S.K., Francioni J.M., Nondeterminacy: Testing and Debugging in Message Passing Parallel Programs, Proc. of the ACM/ONR Workshop on Parallel and Distributed Debugging, ACM SIGPLAN Notices, Vol.28, No.12, Dec. 1993, pp.118-128. 5. Sarukkai S.R., Malony A., Perturbation analysis of high level instrumentation for SPMD programs, 4th ACM SIGPLAN Symposium on Principles and Practices of Parallel Programming, 1993, pp. 4453. 6. www.ptools.org/hpdf/draft. 7. Sahni, S., Thanvantri, V., Performance Metrics: Keeping the Focus on Runtime, IEEE Parallel and Distributed Technology, Vol. 4, No. 1, 1996, pp., 43-56. 8. Sarukkai, S.R., Scalability Analysis Tools for SPMD Message-Passing Parallel Programs, Proc. of the Second Int Workshop on Modeling, Analysis and Simulation of Computer and Telecommunication Systems (Mascots'94), CS Press, 1994, pp. 180-186. 9. Mehra, P., Gower, M., Bass, M., Automated Modeling of Message-Passing Programs, Proc. of the Second Int Workshop on Modeling, Analysis and Simulation of Computer and Telecommunication Systems (Mascots'94) , CS Press, 1994, pp. 187-192. 10. Mehra, P., Schulbach, C., Yan, J., A Comparison of Two Model-Based Performance-Prediction Techniques for Message-Passing Parallel Programs, Sigmetrics Conference on Measurement and Modeling of Computer Systems, ACM Press, New York, 1994, pp. 181-190. 11. Toledo, S., Performance Prediction with Benchmaps, Proc. ot the 10th Int Parallel Processing Symposium, 1996. 12. Brehm J., Dowdy L., Madhukar M., Smirni E., PerPreT - a performance prediction tool, in Quantitative Evaluation of Computing and Communication Systems, Lecture Notes in Computer Science 977, Springer, Heidelberg, 1995. 13. Sarukkai S.R., Mehra P., Block R. J., Automated scalability analysis of message-passing parallel programs, IEEE Parallel and Distributed Technology, 3 (1995), pp. 21-32. 14. Brehm J., Worley P.H., Performance Prediction for Complex Parallel Applications, Proc. of the 11th Parallel Processing Symposium, 1997. 15. Rauber T., Runger G., Modeling the Runtime of Scientific Programs on Parallel Computers, Proc. ot the 2000 Int Workshops on Parallel Processing, 2000. 16. Miller B.P., What to Draw? When to Draw? An Essay on Parallel Program Visualization. Journal of Parallel and Distributed Computing, Vol. 18, No. 2, June 1993, pp. 265-269. 17. Geist G.A., Heath M.T., Peyton B.W., Worley P.H., PICL: A portable instrumented communication library, C reference manual, ORNL Technical Report ORNL/TM-11130, July 1990. 18. Malony A.D., Reed D.A., Arendt J.W., Aydt R.A., Grabas D., Totty B.K. An integral performance data collection, analysis and visualization system. Proc. of the Fourth Conference on Hypercube Concurrent Computers and Applications. Monterey, CA, Mar. 1989. 19. Kimelman D., Ngo T., Program Visualization for RP3: An overview. Technical report, IBM Research, Division. T.J.Watson Research Center, 1990. 20. Sarukkai S.R., Performance visualization and prediction of parallel supercomputer programs: An interim report. Technical report 318. Indiana University, Bloomington, IN, Nov. 1990. 21. Reed D.A., Aydt R.A., Madhyastha T.M., Noe R.J., Shields K.A., Schwartz B.W. The Pablo Performance Analysis Environment. Technical report, University of Illinois at Urbana, Champaign, Department of Computer Science, Nov. 1992. 22. Miller B.P., Callaghan M.D., Cargille J.M., Hollingsworth J.K., Irvin B.R., Karavanic K.L., Kunchithapadam K., Newhall T. The Paradyn Performance Measurement Tool, Computer, Vol. 28, No. 11, Nov. 1995, pp. 37-46. 23. Health M.T., Etheridge J.A., Visualizing the Performance of Parallel Programs, IEEE Software Vol. 8, No. 5, Sept. 1991, pp. 29-39. 24. Lamport, L. Time, clocks, and the ordering of events in a distributed system. Communications of the ACM., Vol. 21, No. 7, July 1978, pp. 558-565.
84
25. LeBlanc T.J., Mellor-Crummey J.M., Fowler R.J. Analyzing parallel program execution using multiple views. Journal of Parallel and Distributed Computing, Vol. 9, No. 2, June 1990, pp. 203217. 26. Sarukkai S.R., Gannon D. Performance visualization of Parallel Programs using SIEVE.1. Proc. of the 1992 Int Conference on Supercomputing. Washington. D.C., July 1992, pp. 157-166. 27. Stone J.M., A graphical representation of concurrent processes, SIGPLAN Notices, Vol. 24, No. 1, Jan. 1989, pp. 226-235 [Proc. of the Workshop on Parallel and Distributed Debugging, Madison, WI, May 1988]. 28. Zernick D., Rudolf L., Animating work and time for debugging parallel programs - Foundations and Experience. SIGPLAN Notices, Vol. 26, No. 12, Dec. 1991, pp. 46-56 [Proc. of the SIGPLAN/SIGOPS Workshop on Parallel and Distributed Debugging, Santa Cruz, CA, May 1991]. 29. Beguelin A., Dongarra J.J, Geist G., Manchek R., Sunderam V., Graphical development tools for network-based concurrent supercomoputing. Proc. of Supercomputing'91. Albuquerque, New Mexico, Nov. 1991, pp. 431-444. 30. Harter P.K., Heimbigner D.M., King R. IDD: An interactive distributed debugger, Proc. of the Fifth Int Conference on Distributed Computing Systems, May 1985, pp.498-506. 31. Malony A.D., Reed D.A. Visualizing parallel computer system performance. In Simmons M., Koskela R., Bucher I., (Eds.) Parallel Computer Systems: Performance Instrumentation and Visualization. Association for Computer Machinery, New York, 1990. 32. Hood R., The p2d2 Project: Building a Portable Distributed Debugger, Proc. of the SIGMETRICS Symposium on Parallel and Distributed Tools, May 1996. 33. Etnus, Inc. The TotalView Multiprocess Debugger, www.etnus.com/products/totalview 34. Convex Computer Corporation, Convex CXdb User's Guide, Second Edition, October 1993, DSW473. 35. docs.sun.com/ab2/coll.514.2/PRISMUG. 36. Heath M.T., Malony A.D., Rover D.T., Parallel Performance Visualization: From Practice to Theory, IEEE Parallel and Distributed Technology, Vol.3, No.4, 1995, pp. 44-60. 37. Bakic A.M., Mutka M.W., Rover D.T., BRISK: A Portable and Flexible Distributed Instrumentation System, Michigan State University, Technical Report. 38. Zheng Q., Chen G., Huang L., Optimal Record and Replay for Debugging of Nondeterministic MPI/PVM Programs, Proc. of the Fourth Int Conference /Exhibition on High Performance Computing in Asia-Pacific Region, 2000. 39. Paik E.H., Chung Y.S., Lee B.S., Yoo C.-W., A Concurrent Program Debugging Environment using Real-Time Replay, Proc. of the 1997 Int Conference on Parallel and Distributed Systems. 40. Zhang K., Sun C., Li K.-C., Dynamically Instrumenting Message-Passing Programs Using Virtual Clocks, Macquarie University, Technical Report. 41. Taylor V.E., Wu X., Geisler J., Li X., Lan Z., Stevens R., Hereld M., Judson I.R., Prophesy: An Infrastructure for Analyzing and Modeling the Performance of Parallel and Distributed Applications, Proc. of the 9th IEEE Int Symposium on High Performance Distributed Computing. 42. Worley P.H.,Toonen B., A users' guide to PSTSWM, Tech. Report ORNL/TM-- 12779, Oak Ridge National Laboratory, Oak Ridge, TN, July 1995. 43. Abramson D.A., Sosic R., A Debugging and Testing Tool for Supporting Software Evolution, Journal of Automated Software Engineering, 1996, Vol.3, pp. 369 – 390. 44. Huband S., McDonald C., Debugging parallel programs using incomplete information, Proc. of the 1st IEEE Computer Society Workshop on Cluster Computing, pp. 278 - 286, IEEE Computer Society Press, 1999.
85
86
Объектная модель JSCALA. О.И. Самоваров, И.В. Арапов, В.В. Бабкова Аннотация. В статье рассматривается объектная модель JSCALA, реализующая пакет прикладных программ SCALAPACK в среде ParJava. Эта модель позволяет создавать переносимые, масштабируемые, параллельные программы решения задач линейной алгебры в среде ParJava. На примере модели JSCALA показано, как следует включать в среду ParJava высокоуровневые модели параллельного программирования.
1. Введение. В последнее время широкое распространение получили параллельные архитектуры с распределенной памятью – кластерные системы. Одной из трудностей, препятствующих эффективному использованию таких систем, является отсутствие языковых средств высокого уровня, поддерживающих разработку эффективных параллельных программ. Сегодня существует достаточно много различных подходов к созданию параллельных программ, но наиболее популярным, ставшим фактически стандартом является интерфейс MPI (Massage Passing Interface) [1]. Модель данных, поддерживаемая MPI, предоставляет программисту средства, позволяющие организовать межпроцессорные взаимодействия. Обычно это примитивы, реализующие синхронные или асинхронные пересылки данных между процессами виртуальной сети. Пересылки могут быть организованы как между отдельной парой процессов, так и между всеми членами группы процессов, объединенных общим контекстом. Параллельная программа, использующая MPI, разрабатывается в терминах модели передачи сообщений. Это значит, что программисту необходимо решить ряд задач, связанных с распределением данных и распределением вычислений, необходимо также организовать доступ, к удаленным данным, используя примитивы MPI. Однако существует ряд моделей параллелизма, позволяющих рассматривать параллельную программу в определениях той прикладной области, задачи которой она решает. Среди таких моделей можно выделить DVM [2], DPJ [3] и др. Эти модели позволяют разрабатывать параллельные программы в естественной для прикладной области системе обозначений. Например, для DVM, модели предназначенной для решения сеточных задач линейной алгебры – это распределенные массивы, параллельные циклы, решетки процессоров. Для DPJ, 87
модели параллельной обработки объектов – это распределенные контейнеры, итераторы и параллельные алгоритмы. В ИСП РАН разрабатывается интегрированная система ParJava [4], поддерживающая разработку масштабируемых параллельных программ на языке Java [5] с использованием интерфейса MPI. Среда ParJava включает в себя графический редактор, отладчик, профилировщик, визуализатор трасс и другие средства отладки и анализа параллельных Java-программ. Также реализованы и интегрированы в среду ParJava модели высокого уровня DPJ и Java-DVM. Используя соответствующие библиотеки классов, программист имеет возможность разрабатывать параллельные программы, используя различные модели параллелизма. Дальнейшая разработка и реализация моделей высокого уровня параллелизма является одним из важных направлений развития среды ParJava. Наиболее широкое применение высокопроизводительные системы и параллельное программирование находят при решении вычислительных задач. Среди существующих средств разработки параллельных программ ориентированных на решение задач линейной алгебры можно выделить пакет прикладных программ SCALAPACK [6] созданный на факультете «Computer Science» Университета штата Теннеси под руководством Жака Донгарра. В статье рассматривается объектная модель JSCALA, которая представляет собой объектную реализацию пакета программ SCALAPACK и предназначена для решения задач линейной алгебры. При разработке модели JSCALA требовалось сохранить оригинальную архитектуру SCALAPACK, но в то же время использовать объектную концепцию проектирования, заложенную в Java. В разделе 2 представлен краткий обзор пакета SCALAPACK, рассматриваются его архитектура и функциональности. Раздел 3 посвящен описанию объектной реализации пакета SCALAPACK на языке Java – JSCALA. В заключении (раздел 4) делаются выводы относительно перспектив развития среды ParJava.
2. SCALAPACK – пакет программ для решения задач линейной алгебры. Аббревиатура SCALAPACK расшифровывается как «Scalable Linear Algebra Package», что можно перевести на русский язык как «пакет масштабируемых программ, предназначенный для решения задач линейной алгебры». Функционально пакет SCALAPACK разделен на несколько компонент: LAPACK (Linear Algebra PACKage) – набор подпрограмм, предназначенный для решения систем линейных уравнений, уравнений метода наименьших квадратов, задач на собственные значения, задач с сингулярными или плохо обусловленными матрицами. Высокая эффективность этих подпрограмм обусловлена тем, что в них используются алгоритмы, которые базируются на вызовах функций библиотеки BLAS. Кроме того, каждая подпрограмма пакета имеет один или несколько платформо-зависимых параметров, влияющих на эффективность ее выполнения. 88
Эти параметры определяются в процессе инсталляции пакета и используются во время выполнения программы.
BLAS (Basic Linear Algebra Subprograms) – содержит базовые функции линейной алгебры, такие как вычисление скалярного произведения, умножение матрицы на вектор, умножение матриц и т.п. Как известно эффективность выполнения операций с матрицами, в особенности умножение матриц, в большой степени зависит от особенностей архитектуры вычислительного комплекса. Функции BLAS настраиваются таким образом, чтобы учитывать эти особенности. BLAS можно рассматривать как уровень, обеспечивающий эффективность и переносимость SCALAPACK-алгоритмов.
PBLAS (Parallel Basic Linear Algebra Subprograms) – библиотека функций, обеспечивающих параллельное выполнение базовых алгоритмов линейной алгебры.
BLACS (Basic Linear Algebra Communication Subprograms) – библиотека подпрограмм межпроцессорного взаимодействия, ориентированная на решение задач линейной алгебры. Вычислительная модель этой библиотеки базируется на предположении о том, что части массивов и векторов распределяются на одно- или двумерную решетку процессов. Решетка процессов создается и может модифицироваться использованием функций BLACS. Кроме того, BLACS содержит функции, позволяющие организовать синхронные взаимодействия между процессами.
В алгоритмах пакета SCALAPACK часто требуется организовать широковещательный обмен данными или выполнить редукционную операцию на части процессах решетки. В BLACS решетка процессов может быть разделена на подмножества, присвоением каждому процессу своего контекста. Основной задачей BLACS можно считать обеспечение переносимости коммуникационного уровня пакета SCALAPACK. На рис. 1. представлена иерархия программных модулей пакета SCALAPACK. Компоненты (программные модули), расположенные в области «Local», реализуют последовательные алгоритмы и используют только локальные данные. Модули, расположенные в области «Global», реализуют параллельные алгоритмы решения задач линейной алгебры. Для их выполнения данные (матрицы и векторы) должны быть предварительно распределены на процессоры вычислительной сети.
89
Рис. 1. Иерархия программных модулей пакета SCALAPACK.
3. Объектная модель JSCALA. Существует два способа переноса SCALAPACK на Java. Первый заключается в создании Java-интерфейса для пакета SCALAPACK и реализации этого интерфейса, используя вызовы подпрограмм, входящих в состав SCALAPACK. Второй способ предполагает проектирование новой объектной модели, но в этом случае возникнет необходимость реализации всех подпрограмм SCALAPACK в виде Java-классов и их методов. В данной статье рассматривается второй способ. Объектная модель JSCALA структурно повторяет SCALAPACK и также разделена на уровни, как показано на рис. 1. Методы решения задач линейной алгебры собраны в двух классах:
90
JSCALADriverRoutines – класс «ведущих» методов, каждый из которых решает законченную задачу, например решение системы линейных уравнений или вычисление собственных значений симметричных матриц. Более подробно методы этого класса описаны в разделе 3.4.1.
JSCALAComputationalRoutines – класс «вычислительных» методов, предназначенных для решения специальных задач, таких как LUфакторизация, приведение вещественных, симметричных матриц к трехдиагональному виду и т.п. Необходимо отметить, что методы этого
класса используются в тех случаях, когда пользователю для решения его задачи не достаточно стандартных «ведущих» методов, он может сконструировать свой собственный «ведущий» метод, используя «вычислительные» методы. В разделе 3.4.2. содержится подробное описание методов этого класса. Оба класса наследуются от класса JSCALA, содержащего общие поля и методы, такие как:
jbcomm – объект класса JLAComm, реализует коммуникационный уровень модели и содержит методы межпроцессорного взаимодействия ориентированные на решение задач линейной алгебры.
jbpla – объект класса JPBaseLA, реализует базовые методы линейной алгебры.
Описанные выше классы являются отображением уровня SCALAPACK в иерархии программных модулей (см. рис. 1). Коммуникационный уровень модели JSCALA поддерживается классами JLACommPVM и JLACommMPI. В первом случае в качестве базовой среды межпроцессорного взаимодействия используется пакет PVM (Parallel Virtual Machine) [6], во втором – MPI. Оба класса реализуют интерфейс JLACommI описывающий методы коммуникационной подсистемы специализированной на решение задач линейной алгебры. Пользователь имеет возможность, реализовав этот интерфейс самостоятельно, создать собственную версию коммуникационного уровня рассчитанного на иную базовую коммуникационную подсистему. Классы JLACommPVM и JLACommMPI являются отображением BLACS в иерархии программных модулей SCALAPACK(см. рис. 1). Кроме того, необходимо заметить, что коммуникационный уровень JSCALA привязан к классам ParJava реализующим примитивы MPI и PVM. Важной частью модели являются классы JPBaseLALevel1, JPBaseLALevel2, JPBaseLALevel3, реализующие базовые методы линейной алгебры. Методы этих классов поддерживают параллельной выполнение алгоритмов и в качестве параметров используют распределенные данные. Подробное описание классов базовой линейной алгебры можно найти в разделе 3.3. Данный уровень объектной модели является отображением BLAS и PBLAS в иерархии программных модулей SCALAPACK (см. рис. 1). Распределенные данные – матрицы и векторы, объектной модели JSCALA реализуются целым набором классов. Поскольку в Java нет удовлетворительной поддержки многомерных массивов, предлагается использовать специальные классы, которые позволяют создавать двухмерные матрицы различных типов: IntegerArray, FloatArray, DoubleArray. Реализовав интерфейс ArrayI, пользователь может создать матрицу собственного типа данных. Для того чтобы определить распределенную матрицу или вектор, пользователь должен создать экземпляр класса IntegerDArray, FloatDArray, DoubleDArray, передав в 91
качестве параметра ссылку на соответствующий скалярный объект. Класс Descriptor содержит описание способа распределения матрицы, размер распределенной части матрицы, контекст и другую информацию необходимую, для обеспечения доступа к элементам распределенных данных. На рис. 3. представлена диаграмма классов модели JSCALA.
3.1. Распределенные данные. Модель JSCALA предполагает, что данные (векторы и матрицы) должны быть распределены на процессоры. Распределенные данные в JSCALA представляются специальными классами IntegerDArray, FloatDArray, DoubleDArray. Используя эти классы, пользователь может создать распределенный массив или вектор нужного типа данных. Эти классы реализуют интерфейс ArrayI, используя который пользователь может создать распределенный массив или вектор специального типа. Общие данные отображаются на локальную память узлов в соответствии с определенным способом распределения. В JSCALA существует три вида распределения данных: блочное, циклическое и блочно циклическое. Блочное распределения данных предполагает, что матрица или вектор делятся пропорционально количеству процессоров, образуя равные блоки которые хранятся в локальной памяти. Циклическое распределение описывает тот случай, когда элементы матрицы распределяются циклически на узлы, образуя смешенные блоки данных каждый из которых хранится на локальном узле. Блочно-циклическое распределение представляет собой совокупность первого и второго способа. В этом случае пользователь может задать размер блоков матрицы, которые будут распределяться циклически на узлы.
3.2. Коммуникационный уровень модели JSCALA. Коммуникационный уровень модели JSCALA представлен классами JLAComm, JLACommPVM, JLACommMPI, а также интерфейсом JLACommI. Этот уровень модели реализует систему межпроцессорных обменов, необходимую для выполнения параллельных алгоритмов линейной алгебры. Кроме того, интерфейсы коммуникационного уровня позволяют использовать эффективные, низкоуровневые средства межпроцессорных обменов и обеспечивают переносимость программ модели JSCALA между широким спектром вычислительных платформ с распределенной памятью. Система межпроцессорных обменов модели JSCALA основа на следующих ключевых идеях: Стандартный интерфейс. Одной из основных идей коммуникационного уровня JSCALA является то что, классы и методы, используемые для обеспечения межпроцессорных обменов, могут быть использованы на любой из поддерживаемых платформ. 92
Интерфейсом между платформами в описываемой модели является JLACommI. Текущая версия JSCALA содержат два класса JLACommPVM и JLACommMPI, реализующих интерфейс JLACommI. Эти классы обеспечивают взаимодействие с двумя широко распространенными коммуникационными подсистемами – PVM и MPI. Решетка процессов. Вычислительная модель JSCALA базируется на предположении о том, что части массивов и векторов распределяются на одно или 2-х мерную решетку процессов. Решетку процессов в данном случае можно представить как абстрактный параллельный компьютер процессоры которого пронумерованы как 0,1,..., P − 1 . Тогда решетка процессоров будет иметь Pτ строк процессоров и Pc столбцов процессоров, где Pτ * Pc = P . Каждый процессор может быть проиндексирован координатами Pτ , Pc , где 0 ≤ pτ < Pτ и 0 ≤ p c < Pc . Пример такого представления процессов приведен на рис. 2, где Pτ = 2 и Pc = 4 .
getIx() FloatArray getIy() swapRow() swapColumns() transposeArray() getWidth() DoubleArray getHeight() getX0() getY0()
На рис. 2 процессы отображаются на решетку процессоров, используя строковый порядок, иначе говоря, нумерация процессов в решетке производится по строкам слева направо. Существует еще один порядок нумерации процессоров в решетке – нумерация сверху вниз, такой способ отображения называется «по столбцам». Метод gridint() класса JLAComm реализует отображение процессов на решетку процессов. По умолчанию используется «строковый» метод отображения. Метод gridmap класса JLAComm является общим и позволяет пользователю более гибко задавать отображения процессов. Области операций обменов. В JSCALA поддерживаются так называемые «области» операций обменов. Это значит, что система, использующая линейный массив процессов, имеет область действия операции обмена, равную всем процессам. Однако, если используется двухмерная решетка процессов, существует три области действия операций обмена: Row – все процессы в строке; Column – все процессы в столбце; All – все процессы в решетке. 93
Array ix[] iy[] x0 y0 width height
0..n IntegerDArray
DArray
FloatDArray 0..1
JLAComm my_p_num : Integer n_procs : Integer i_contxt : Integer np_row : Integer n_col : Integer my_row : Integer my_col : Integer p_row : Integer p_col : Integer val : Integer
setup() get() set() gridInit() pInfo() gredMap() freeBuff() gredExit() abort() exit() gredInfo() pNum() barrier() pCoord() gSnd2d() tSnd2d() gRcv2d() tRcv2d() gbSnd2d() tbSnd2d() gbRcv2d() tbRcv2d() gSum2d() gMax2d() gMin2d()
JLACommMPI
parjava.mpi
1..1
0..1
getType() getCtxt() getRow() getCol() getRowb() getColb() getRsrc() getCsrc() getLld()
JLACommI
JLACommPVM
0..n
DoubleDArray
Descriptor type ctxt row col rowb colb rSrc cSrc lld
Рис. 2. Восемь процессов, представленных в виде решетки размерностью 2× 4 .
IntegerArray
ArrayI
0..1 1..1 JScaLA jbcomm : JLAComm1..1 jbpla : JPBaseLA
1..1
JPBaseLA jcommla : JCommLA
JBaseLA Level3
0..1 JScaLAComputationalRoutines psdbtrf() psdbtrs() psdbtrsv() psdttrf() psdttrs() psdttrsv() psgbtrf() psgbtrs() psgebrd() psgecon() psgeequ() psgehrd() psgelqf() psgeqlf() psgeqpf() psgeqrf() psgerfs() psgerqf()
JPBaseLALevel2 JScaLADriverRoutines psdbsv() psdtsv() psgbsv() psgels() psgesv() psgesvd() pspbsv() psposv() psptsv() pssyev() pssyevd() psgesvx() psposvx() pssyevx() pssygvx()
JPBaseLALevel1 JBasePLALevel3I JBasePLALevel2I JBasePLALevel1I swap() scal() copy() axpy() dot() dotu() dotc() nrm2() asum() amax()
gemv() hemv() symv() trmv() trsv() ger() geru() gerc() her() her2() syr() syr2()
gemm() symm() hemm() syrk() herk() syr2k() her2k()
Рис. 3. Диаграмма классов объектной модели JSCALA.
Контекст. В модели JSCALA каждая решетка процессов имеет свой контекст. Также контекст ассоциируется с каждой распределенной матрицей. Использование контекста обеспечивает возможность разделить пространство обмена сообщениями. Это означает, что решетка процессоров обеспечивает безопасные коммуникации даже в то время, когда другая решетка процессоров так же производит операции с 94
обменом сообщениями. Похожая концепция разделения коммуникационных пространств используется и в MPI. Используемая в JSCALA концепция контекстов позволяет следующее:
double amax(a) – найти максимальный элемент распределенного вектора a и его индекс
Создавать случайные группы процессов
3.3.2. Базовые методы второго уровня класса JPBaseLALevel2.
Создавать неопределенное число перекрывающихся процессорных решеток
Разделять решетку процессов так чтобы, обмены производимые между узлами решетки не пересекались.
Методы данного класса реализуют базовые параллельные алгоритмы линейной алгебры второго уровня:
Класс JLAComm модели JSCALA имеет два метода gridinit() и gridmap(), позволяющие создавать решетки процессоров в собственном контексте. Эти методы возвращают контекст, который представляет собой простое целое число, определяемое внутри класса. Контекст является расходуемым ресурсом, поэтому он должен быть освобожден, после того как в нем пропала необходимость. Освобождение ресурса осуществляется вызовом метода gridexit().
Y = aA*X + bY, где a и b – скаляры, X и Y – распределенные векторы, A – распределенная матрица.
Модель JSCALA содержит классы, описывающие функции линейной алгебры. Такие как скалярное произведение, умножение матрицы и вектора, умножение матриц. Методы этих классов обеспечивают параллельное выполнения базовых алгоритмов линейной алгебры. Классы этого уровня модели разделены на три типа. Первый обеспечивает операции вида вектор-вектор, второй реализует операции вектор-матрица и третий поддерживает операции матрица-матрица. Ниже перечислены методы классов всех трех типов реализованных в данной версии JSCALA.
Методы данного класса реализуют базовые параллельные алгоритмы линейной алгебры первого уровня:
void swap(a,b) – поменять два распределенных вектора,
double SCAl(a,c) – умножить элементы вещественного, распределенного вектора a на скаляр c,
void copy(a,b) – скопировать один распределенный вектор в другой,
void axpy(a,b) – добавить один распределенный вектор к другому,
double dot(a,b) – внутреннее произведение двух распределенных векторов
double nrm2(a) – вычислить вторую норму распределенного вектора
double asum(a) – вернуть сумму абсолютных значений распределенного вектора
double symv(a,b,A,X) – выполнить операцию:
Y = aA*X + bY, где a и b – скаляры, X и Y – распределенные векторы, A – распределенная симметричная матрица.
double trmv(A) – выполнить операцию:
X = A*X где X – распределенный вектор, A – распределенная верхняя или нижняя треугольная матрица.
3.3.1. Базовые методы первого уровня класса JPBaseLALevel1.
double hemv(a,b,A,X) – выполнить операцию:
Y = aA*X + bY, где a и b – скаляры, X и Y – распределенные векторы, A – распределенная Эрмитова матрица.
3.3. Классы базовых методов линейной алгебры.
double gemv(a,b,A,X) – выполнить операцию:
double trsv(A, X) – решить систему уравнений:
A*X=b, где b и X – распределенные векторы, A – распределенная верхняя или нижняя треугольная матрица.
double ger(a, X,Y) – выполнить операцию:
A = aX*Y+A, где a – скаляр, X и Y – распределенные векторы, A – распределенная матрица.
double her(a,X) – выполнить Эрмитову операцию первого порядка:
A = a*X*conjg(X)+A, где а – вещественный скаляр, X – распределенный вектор, A – распределенная Эрмитова матрица.
double her2(a,A,X,Y) – выполнить Эрмитову операцию второго порядка: A = a*X*conjg(Y)+conjq(a)*Y*conjg(X)+A
95
96
где а – вещественный скаляр, X и Y – распределенные векторы, A – распределенная Эрмитова матрица.
double syr(a,X) – выполнить симметричную операцию первого порядка:
A = a*X*X’+A, где а – вещественный скаляр, X – распределенный вектор, A – распределенная симметричная матрица.
3.3.3. Базовые методы третьего уровня класса JPBaseLALevel3. Методы данного класса реализуют следующие базовые параллельные алгоритмы линейной алгебры: double gemm(a,b,A,B) – выполнить операцию:
C=a*op(A)*op(B)+b*C, где a и b – скаляры, A, B, C – распределенные матрицы.
double symm(a,b,A,B) – выполнить операцию:
C=a*A*B+b*C, где a и b скаляры, A – симметричная, распределенная матрица, B, распределенные матрицы.
C –
double hemm(a,b,A,B) – выполнить операцию:
double syrk(a,b,A) – выполнить симметричную операцию порядка k:
C=a*A*A’+b*C, где a и b – скаляры, С – симметричная, распределенная матрица, A распределенная матрица.
double herk(a,b,A) – выполнить Эрмитову операцию порядка k:
C=a*A*A’+b*C, где a и b скаляры, С – Эрмитова распределенная матрица, A – распределенная матрица.
C=a*A*conjg(B’)+conjg(a)*B*conjg(A’)+b*C, где a и b скаляры, С – Эрмитова распределенная матрица, A и B – распределенные матрицы.
3.4. Классы, реализующие параллельные алгоритмы линейной алгебры. В модели JSCALA описываются два класса методов реализующих алгоритмы, предназначенные для решения задач линейной алгебры. Первый – JSCALADriverRoutines, – содержит методы решения законченных задач, таких как решение системы линейных уравнений, вычисление собственных значений симметричных матриц и др. Этот класс базируется на методы класса JSCALAComputationalRoutines. В классе собраны методы, предназначенные для выполнения специальных операций, например LU факторизация, приведение вещественных, симметричных матриц к трехдиагональному виду и пр. Рассмотрим описываемые классы JSCALADriverRoutines и JSCALAComputationalRoutines более подробно.
3.4.1. Класс «ведущих» методов JSCALADriverRoutines.
C=a*A*B+b*C, где a и b скаляры, A – Эрмитова, распределенная матрица, B, C – распределенные матрицы.
double her2k(a,b,A,B) – выполнить Эрмитову операцию порядка 2k:
double syr2(a,X,Y) – выполнить симметричную операцию второго порядка:
A = a*X*Y' + aY*X'+A, где а – вещественный скаляр, X и Y – распределенные векторы, A – распределенная симметричная матрица.
где a и b скаляры, С – симметричная распределенная матрица, A и B – распределенные матрицы.
Здесь и далее методы этого класса мы будем называть «ведущими». Методы класса JSCALADriverRoutines решают общие задачи линейной алгебры: Решение систем линейных уравнений. В классе реализовано два метода решения систем линейных уравнений. Простой метод psgesv() – решает систему AX=B факторизацией A и переписыванием B c решением X. И расширенный метод psgesvx(), который выполняет некоторые или все из следующих функций: решение AT X = B или A H X = B (за исключением случаев, когда A симметричная или Эрмитова матрица); улучшение решения и вычисление передних и задних границ ошибок. Вычисление собственных значений. Решение задачи нахождения собственных значений сводится к нахождению характеристических чисел λ и соответствующих собственных векторов z != 0, таких что (2) Az = λ z , A = AT где A – вещественная матрица. Для Эрмитовых матриц мы имеем: Az = λ , A = AT
double syr2k(a,b,A,B) – выполнить симметричную операцию порядка 2k: C=a*A*B’+aB*A’+b*C, 97
98
(3)
В обоих случаях λ это вещественные значения. Когда будут найдены все характеристические числа и векторы, можно записать (4) A = ZΛZ T где Λ – диагональная матрица элементы, которой характеристические числа, а Z – ортогональная матрица, столбцы которой – характеристические векторы. В классе JSCALADriverRoutines есть метод pssyev(), который предназначен для вычисления всех характеристических чисел и векторов семеричной или Эрмитовой матрицы. Сингулярное разложение. Такое разложение матрицы размерностью m на n можно представить выражением: (5) A = UΣV T A = UΣV T
где U и V ортогональны, Σ - это вещественная диагональная матрица размерностью m на n элементы, которой вещественные числа. При этом должно выполняться условие: (7) σ 1 ≥ σ 2 Kσ min( m,n ) ≥ 0
σ i это сингулярные значения матрица A, а первая колонка min(m,n) матриц U и V – это левый и правый сингулярные векторы матрицы A. Метод psgesvd() реализует разложение по сингулярным числам общих не симметричных матриц. Обобщенная симметричная задача собственных значений. Класс содержит метод который обеспечивает вычисление всех собственных значений и характеристических векторов следующих типов задач: Az = λ Bz (8) (9)
BAz = λ z
(10)
(14) Z T B −1 Z = I в случае задачи (9). Когда A и B Эрмитовы, матрица вычисленных собственных значений Z удовлетворяет условиям: (15) Z H AZ = Λ в случае задач (8) и (10) и условию (16) Z −1 AZ − H = I в случае задачи (9), где Λ это диагональная матрица собственных значений на диагонали. Z также удовлетворяет условию: (17) Z H BZ = I в случае задач (8) и (10) и условию
(6)
ABz = λ z
в случае задач (8) и (10) и условию
где A и B это симметричная или Эрмитова матрица и B положительно определена. Для всех задач характеристические значения λ вещественные. Когда матрицы A и B семеричные матрица Z вычисленных собственных значений удовлетворяет условию: (11) Z T AZ = Λ в случае задач (8) и (10) и условию (12) Z −1 AZ −T = I в случае задачи (9), где Λ это диагональная матрица собственных значений на диагонали. Z также удовлетворяет условию: (13) Z T BZ = I 99
Z H B −1 Z = I в случае задачи (9).
(18)
3.4.2. Класс «вычислительных» методов JSCALAComputationalRoutines. Методы этого класса мы будем называть «вычислительными», поскольку здесь решаются общие задачи линейной алгебры. Необходимо заметить, что с использованием этих методов реализован класс JSCALADriverRoutines. Линейные уравнения. Систему связанных линейных уравнений можно записать как: (19) Λx = b где A это матрица коэффициентов, b это правая часть, а X решения. В (19) A определено как квадратная матрица порядка n, однако есть методы, в которых A может быть прямоугольной матрицей. Ортогональная факторизация и линейные задачи наименьших квадратов. JSCALA предлагает методы факторизации прямоугольной матрицы A общего вида размерностью m на n, а также ортогональных матриц и треугольных матриц. QR факторизация. Наиболее общий и широко известный способ факторизации – это QR факторизация представленная выражением ⎛R⎞ A = Q⎜ ⎟, if m ≥ n, ⎝O⎠
где R это треугольная матрица размерностью n на n и Q это ортогональная матрица размерностью m на m. Метод класса psgeqrf() реализует вычисления QR факторизации. 100
LQ факторизация. LQ факторизация определяется выражением: ⎛Q A = ( L 0)Q = ( L 0) ⎜⎜ 1 ⎝ Q2
⎞ ⎟⎟ = LQ1, if m ≤ n, ⎠
где L это треугольная матрица размерностью m на m, Q это ортогональная матрица размерностью n на n. Q1 содержит первые m строк матрицы Q, Q2 содержит остальные n-m строки. Этот вид факторизации поддерживается методом psgelqf().
Другие факторизации. QL и RQ факторизации можно записать как: ⎛0⎞ A = Q⎜ ⎟ , if m ≥ n, ⎝L⎠
и
3. В.П. Иванников, С.С. Гайсарян, М.В. Домрачев, В.Ф. Еч, Н.Н. Шталтовная. “Расширение языка Java средствами разработки параллельных программ с распределением по данным с помощью библиотеки классов DPJ” Вопросы Кибернетики. Приложения системного программирования. Выпуск 4. 1998. 4. А.И. Аветисян, И.В. Арапов, С.С. Гайсарян, В.А. Падарян. “Среда ParJava для разработки SPMDпрограмм для однородных и неоднородных сетей JavaVM.” Труды Института Системного Программирования Российской Академии Наук. Том 2, 2000. 5. JAVA 2 SDK, Standard Edition http://java.sun.com/products/jdk/1.2/ 6. SCALAPACK: SCAlable Linear Algebra PACKage. http://www.netlib.org/SCAlapack/
7. С.С. Гайсарян, М.В. Домрачёв, В.Ф. Еч, О.И.Самоваров, А.И. Аветисян. “Параллельное программирование в среде Java для систем с распределенной памятью. Объектные модели параллельного выполнения.” Труды Института Системного Программирования Российской Академии Наук, Том 1, 1999. 8. Rajkumar Buyya (ed.) "High Performance Cluster Computing": Programming and Applications. Vol. 2. Prentice Hall PTR, New Jersey, 1999.
A = (0 R )Q, if m ≤ n.
Эти типы факторизаций вычисляются методами psgeqle() и psgerqf(). Эти виды факторизаций не так часто используются как рассмотренные выше QR и LQ но могут оказаться полезными при разработке приложений, например для вычисления QR факторизации. В текущей версии JSCALA реализована лишь часть методов предлагаемых моделью SCALAPACK. Полная версия этих классов – задача дальнейшего развития системы.
4. Заключение. В данной статье была рассмотрена объектная модель параллельного программирования JSCALA. Модель JSCALA расширяет среду ParJava интегрированную среду, предназначенную для разработки высокоэффективных параллельных программ. Приводится описание архитектуры модели, описывается диаграмма классов модели. Объектные модели параллелизма представляют собой технологию, способную обеспечить все этапы разработки эффективных параллельных программ: проектирование, реализацию и отладку. Объектные модели можно рассматривать как средства высокого уровня, которые способны дать ряд преимуществ при разработке параллельных программ. Литература: 1. MPI: Message Passing Interface Standard, Message Passing Interface Forum, 2000; http://www.mcs.anl.gov/mpi/index.html 2. В.П. Иванников, С.С. Гайсарян, М.В. Домрачев, О.И. Самоваров. “Объектная модель DVM и ее реализация на языке Java.” Вопросы Кибернетики. Приложения системного программирования. Выпуск 4. 1998.
101
102
Система информационной поддержки врачебных решений, основанная на модифицированном методе динамического кластерного анализа. Юдин В.Н. Аннотация. В статье описывается система «Спутник Врача», предназначенная для информационной поддержки врачебных решений в медицине с использованием современных информационных технологий, в частности, методов распознавания образов. В системе используется общая методика принятия решений с использованием дифференциального ряда и метода аналогий. В отличие от применяемых в медицине экспертных систем, «Спутник Врача» помогает принимать решения в условиях неоднозначной классификации при неполном наборе показателей пациента. Метод аналогий позволяет врачу предвидеть случаи, когда заболевание выходит за пределы своей обычной симптоматики, проявляясь через симптомы другого заболевания. В качестве математического аппарата используется метод кластерного анализа, модифицированный для работы с переменным набором признаков. Приводится формальная постановка предлагаемого метода классификации. На основе метода создан Универсальный Количественный Классификатор, который может использоваться как классифицирующий модуль в системах для оценки состояний, прогнозирования и принятия решений. Приводится пример классификации объекта, у которого часть признаков отсутствует.
1. Введение В настоящее время в нашей стране и за рубежом разработан ряд систем для информационной поддержки врачебных решений. В них используются различные методы: распознавание образов, искусственный интеллект, многозначная (неклассическая логика), прикладная математическая статистика, теория экспертных систем и т. д. Так, в Центре сердечно-сосудистой хирургии имени А.Н. Бакулева создан программный комплекс АЙБОЛИТ [4] для информационной поддержки 103
диагностики, классификации и коррекции терапии острых расстройств кровообращения у детей. Используется имитационное моделирование. Система ЭКСАПРАС [5] представляет собой инструментальное средство для создания прикладных экспертных систем в областях, связанных с принятием решений, в частности, для решения задач диагностического, лечебнотактического, прогностического характера. В основу системы положен тезис о принципиальной возможности сведения процедуры принятия решений к задачам распознавания образов с обучением. Решение задач распознавания осуществляется логико-комбинаторными методами, базирующимися на эквивалентных и оптимизирующих преобразованиях в пространстве признаков, с использованием некоторых разделов дискретной математики и теории надежности. В ИПС РАН (г. Переславль-Залесский) разработан SIMER+MIR [6] инструментарий для экспертных систем, ориентированный на извлечение знаний из экспертов через моделирование рассуждений типа аргументации и вычисления над базой знаний (решатель или интерпретатор базы знаний). SIMER+MIR решает задачи автоматизированной диагностики, включая методику определения оптимальной тактики лечения больных, выводит заключение о диагнозе с указанием его вероятности, рекомендации по дальнейшему обследованию больного; описание клинической картины диагностированных заболеваний; текст решения (комментарии к правилу); описание принципов лечения, диспансеризации, прогноза, путей наследования заболеваний. В Московском НИИ педиатрии и детской хирургии была создана система по наследственным болезням у детей ДИАГЕН [8,9,10], ориентированная на выделение узкого круга синдромов из большого числа заболеваний детского возраста до проведения специальных дорогостоящих исследований, позволяющих окончательно уточнить диагноз. В системе предусмотрена работа в условиях неопределенных и неточных исходных данных. Используется механизм продукций с коэффициентами доверия для вычисления функций близости (схема Шортлифа). Можно изменить "вес" любого из отмеченных у ребенка симптомов в соответствии с гипотезой об их диагностической ценности, что позволяет использовать опыт и интуицию врача-генетика. По результатам работы системы врач получает перечень возможных диагнозов (дифференциальный ряд), упорядоченный по степени вероятности. Информационная Классифицирующая Система (ИКС) [11], разработанная в МНИИЦРР, предназначена для решения задач распознавания образов, или классификации, как в медицине, так и в других областях. Применялась для выявления групп риска среди лиц, принимавших участие в ликвидации последствий аварии на Чернобыльской АЭС. Близка по идеологии к 104
описываемой системе. Основана на кластерном анализе и теории алгоритмов вычисления оценок в распознавании образов [3]. Система мониторинга пациентов Guardian [16] была создана в 1996 в Стэндфордском университете. Ориентирована на сбор и интерпретацию показателей пациентов, диагностику, выбор лечения, предупреждение об осложнениях. Допускает подключение медицинских приборов. Guardian имеет ряд отличительных черт: множественность способов рассуждения, конфигурирование доступных знаний и методов в зависимости от контекста, адаптация доступной базы знаний к вычислительным ресурсам. Iliad 4.5 [17] - медицинская экспертная система, разработанная в Университете штата Юта. Для построения дифференциального диагноза, выбора дальнейших исследований и лечения используется Байесова логика и искусственный интеллект. Имеет три режима работы: консультация, моделирование и справка. Особенности Iliad 4.5 - исчерпывающая диагностика и база данных, сформированная известными в США специалистами на более чем 500,000 клинических случаях из многих областей медицины и поддержанная примерно 1400 цифровыми фотографиями, доступными через гипертекст. Методы распознавания образов находят практическое применение в различных областях человеческой деятельности, где одной из основных задач является классификация объектов или явлений. В медицине такими областями являются дифференциальная диагностика, оценка состояния пациента, выбор методики, прогноз лечения и осложнений. Методы распознавания образов имеют несомненное родство с ними по содержанию и понятийному аппарату. Они опираются на косвенную и весьма разнородную информацию об объектах исследования и их принадлежности к заданным классам. Существо методов состоит в том, что классификация объекта производится на основе анализа структуры описания этого объекта. В данной статье описывается система «Спутник Врача», основанная на методах распознавания образов и предназначенная для информационной поддержки врачебных решений. Она позволяет врачу аккумулировать собственный опыт путем накопления и интерпретации данных о пациентах (показатели, заболевание, лечение и исход). Система может оказаться полезной в работе домашнего врача. Отдельная ее часть, под названием “Универсальный Диспетчер Поликлиники”, может применяться в регистратуре и кабинете доврачебного наблюдения для назначения дополнительных исследований и принятия решения, к какому специалисту направить больного. Систему обучают на конкретных или моделируемых пациентах. Как и экспертные системы, она может поддерживать процесс постановки диагноза. Но отличительная черта системы – метод аналогий. В медицине наибольшую сложность представляют случаи, когда заболевание выходит за пределы своей обычной симптоматики. Так, пневмония в некоторых случаях дает симптомы “острого живота”, что может привести к ложному диагнозу и потере времени 105
на неадекватное лечение, ставя под угрозу жизнь больного. Хорошо обученная система может и не дать определенного диагноза, но подскажет на примере пациентов возможность пневмонии при схожих симптомах. Аналогии интересны еще и тем, что из системы можно выбрать лечение и исход этих пациентов. В системе применяется систематизированный подход ко всем этапам лечения с применением дифференциального ряда и аналогий. В качестве математического аппарата используется метод кластерного анализа, модифицированный для работы с переменным набором признаков. Система реализована в среде Borland C++ Builder. В разделе 1 вводятся основные понятия кластерного анализа, дается формальная постановка предлагаемого метода классификации при нефиксированном наборе признаков. В разделе 2 описывается Универсальный Количественный Классификатор, который используется как классифицирующий модуль, или ядро системы. Приводится пример классификации объекта, у которого часть признаков отсутствует. Вводится понятие аналогов. Раздел 3 посвящен системе Спутник Врача, как оболочке, назначение которой информационная поддержка процесса лечения. Описывается общая методика принятия решений на основе дифференциального ряда.
2. Формальная модель метода. Типичная задача распознавания образов состоит в классификации объектов, которые мы будем называть объектами исследования. Объект исследования представляется своим формализованным описанием - вектором значений признаков. В медицинских приложениях объектами исследования могут быть пациенты, а признаками, например, показатели пациента (лабораторные данные, данные инструментальных исследований, жалобы и т.д.) Значение признака – количественная характеристика, которая задает степень выраженности качества, описываемого этим признаком. Объекты разбиваются на группы, называемые классами. Класс - это множество объектов, объединенных по некоторым общим свойствам. Примером классов могут служить заболевания. Если признаки объектов представить действительными числами, эти объекты можно рассматривать в пространстве признаков. Метод кластерного анализа заключается в выявлении скоплений объектов каждого класса (кластеров) в этом пространстве. Построение систем распознавания, основанных на реализации данного принципа, определяется взаимным пространственным расположением отдельных кластеров. Отнесение распознаваемого объекта к одному из классов делается либо на основе попадания объекта в кластер, либо 106
вычисления эвклидова расстояния (или другой метрики) от этого объекта до всех кластеров. В данной работе используется метод динамического кластерного анализа [1], решающий задачу классификации с предварительным обучением. Обучающая выборка - множество объектов, для каждого из которых распознающей системе известна информация о принадлежности к классу. Задача распознавания образов состоит в том, чтобы на основании информации, заключенной в обучающей выборке, установить принадлежность к классу заданного объекта исследования, представленного своим формализованным описанием. Решение этой задачи предполагает выполнение двух основных этапов:
обучение;
классификация, или оценка принадлежности объекта к классу.
Обучение является, по сути, настройкой распознающей системы на предметную область. Процедура обучения состоит из двух фаз: 1. Формирование системы признаков и обучающей выборки на ее базе. Обучающая выборка формируется из объектов, которые являются наиболее типичными представителями каждого из заданных классов. Принадлежность объектов обучающей выборки к классам сообщается распознающей системе. 2. Определение границ классов для объектов обучающей выборки. Кластер как совокупность объектов, объединенных похожими свойствами, а значит, и «похожими» значениями признаков, в пространстве признаков ограничивается замкнутой поверхностью. В ИКС [11] кластеры описываются гиперсферами. Вместо сфер можно использовать эллипсы. В представленной системе выбран способ оконтуривания многомерным параллелепипедом. Этап принятия решений, или классификации, заключается в определении степени принадлежности объекта тому или иному классу. Оценка производится через сопоставление признаков объекта исследования и границ кластеров, полученных на этапе обучения. Попадание точки объекта внутрь кластера означает, что объект относится к соответствующему классу. Если кластеры, соответствующие различным классам, разнесены друг от друга, задача классификации решается сравнительно легко. Если кластеры в большой степени перекрываются, задача усложняется. Перекрытие кластеров является результатом неполноценной информации. Степень перекрытия часто удается уменьшить, увеличивая количество и качество измерений. Например, если два заболевания, как два класса, на выбранном наборе показателей образуют перекрывающиеся кластеры, добавлением нового исследования (т.е. увеличением размерности признакового пространства) можно добиться разделения кластеров и дифференцирования заболеваний друг от друга. 107
В задачах распознавания образов предполагается, что в основе описаний объектов лежит набор признаков, общий для объектов всех классов. Иными словами, объекты обучающей выборки и исследуемые объекты имеют одинаковую размерность и располагаются в едином признаковом пространстве. В реальных приложениях это условие не выполняется. В медицине разные наборы симптомов могут быть не только у разных заболеваний, но и в разных случаях одного и того же заболевания, вводимых в систему для обучения. И, наконец, исследуемый случай может иметь набор показателей, не совпадающий с наборами показателей заболеваний, которые введены в систему. Эту проблему помогает решить предлагаемый модифицированный метод кластерного анализа, который опирается на описание кластера как многомерного параллелепипеда. Этот метод позволяет классифицировать объекты с неполным числом признаков без переобучения системы. Рассмотрим формальную модель метода. Объекты исследования задаются значениями признаков x j , j=1, … , n. Назовем этот набор общим набором признаков. Совокупность значений признаков определяет описание объекта. Допускается, что некоторые из значений признаков объекта могут быть «пустыми», т.е. отсутствовать. Множество объектов разбито на конечное число классов Ωp , p=1,…,m. Обучающая выборка состоит из объектов ω k , k ∈ 1, k p , представляющих все классы. В пределах класса Ωp объекты представлены набором значений
признаков x p1 , …, x pn где p1 , …, p n ∈ 1, n . Такую совокупность признаков j будем называть набором признаков класса. Обозначим через a pk , (j ∈ 1, n ,
k ∈ 1, k p , p ∈ 1, m ) значение j-го признака на k-м объекте p-го класса
обучающей выборки. При исследовании обучающей выборки запоминаются границы признаков в каждом классе, иными словами, для каждого класса p и признака j составляются пары j
j { min k [ a pk ], max k [ a pk ]}
(геометрическая интерпретация такого способа описания – многомерный параллелепипед, который располагается в подпространстве общего признакового пространства).
108
Необходимо определить принадлежность классу Ωp предъявленного для распознавания объекта ω , который представлен значениями признаков xω1 ,…, xω n , где ω1 ,…, ω n ∈ 1, n . Объект считается принадлежащим классу Ωp , если для любого непустого признака xω j , входящего в набор признаков класса Ωp (т.е. для всех
ωj∈
p1 , p n )
j j min k [ a pk ] ≤ xω j ≤ max k [ a pk ]
(отсюда, в частности, следует, что значения признаков предъявленного для распознавания объекта, не входящих в набор признаков класса, не влияют на принадлежность объекта к классу). Признакам могут присваиваться веса, отражающие влияние этого признака на принадлежность объекта к классу. Значение веса признака зависит от класса. Дело в том, что в медицине важно не только наличие симптома, но и степень его «выраженности», т.е. значимости для того или иного заболевания. Обозначим через λ p1 , …, λ pn веса признаков x p1 , …, x pn соответственно. Хотя веса определяются на этапе ввода обучающей информации, их корректировка в дальнейшем не запрещена. Переобучение при этом не требуется. Предположим, что предъявленный для распознавания объект попадает в класс Ωp . Тогда в терминах введенного нами определения принадлежности классу определим степень принадлежности объекта ω классу Ωp как отношение суммы весов признаков объекта, входящих в набор признаков класса, и суммы весов всех признаков этого класса. Г( ω , Ωp ) =( λω1 +…+ λω n )/( λ p1 +…+ λ pn ), где ω j ∈ p1 , p n )
объекты, но самое главное - свой набор признаков. Описание признаков вводится при создании приложения. Следующий этап – ввод обучающей выборки. Определяются классы, вводятся объекты с указанием их принадлежности классам, значения признаков объектов (объекты в пределах одного класса могут иметь разные наборы признаков). Определяются веса признаков в классах (по аналогии с медициной, в системе используется 10-бальная система весов). Когда наступает момент исследовать обучающую информацию, запоминаются диапазоны изменений признаков для каждого класса. По сути, описание класса – это совокупность диапазонов встречающихся в нем признаков. Этот способ оптимально подходит, когда классы имеют разные наборы признаков. Так, при добавлении объекта обучающей выборки нет нужды заново вычислять описывающую поверхность. Процедура переобучения сводится лишь к корректировке соответствующих диапазонов. Объект, предъявляемый системе для классификации, представлен признаками, которые могут не совпадать с наборами признаков существующих классов. По отношению к полному набору признаков приложения, некоторые признаки могут отсутствовать (в среде Borland C++ Builder - это пустые поля). Оценка происходит по схеме, описанной в формальной постановке задачи. Каждый непустой признак исследуемого объекта, если он входит в набор признаков класса, сравнивается с соответствующим диапазоном в описании класса, вне связи с другими признаками. Классификация осложняется, когда объект попадает в область перекрытия кластеров. Об истинном перекрытии кластеров с разными наборами признаков можно говорить, когда существует общее подпространство, где они пересекаются. Такой случай часто возникает из-за недостатка информации при обучении. Например, два кластера, которые в трехмерном изображении располагаются отдельно, в двумерном могут быть видны как пересекающиеся. Отнесение исследуемого объекта к нескольким кластерам может возникать, когда у этого объекта часть признаков отсутствует. Проиллюстрируем такой случай на простом примере.
3. Универсальный Количественный Классификатор. Для решения задачи классификации объектов, был разработан Универсальный Количественный Классификатор. Это система, которая может эксплуатироваться как в совокупности с другими системами, использующими ее в качестве распознающего модуля, так и независимо, как инструмент исследователя в различных областях, где требуется применять распознавание образов для классификации объектов. В системе предусмотрена работа со многими независимыми приложениями. Каждое приложение имеет свои классы, обучающую выборку, исследуемые 109
110
X2
X2
A
Класс A
Х
Класс B
1,5
B
X1
X1
Два класса, A и B, описаны в пространстве признаков {X1, X2}. Их кластеры не пересекаются. Объект исследования представлен одним признаком X1=1,5, признак X2 отсутствует. Согласно нашему определению, объект попадает в оба кластера. Для более точной оценки нужно добавить контрольному объекту значение признака X2 (так же поступают и в медицинской практике: если имеющихся показателей не хватает для дифференцирования заболеваний, только дополнительное исследование позволит сделать окончательный вывод). На практике это часто бывает невозможно, поэтому приходится вводить веса признаков. О степени принадлежности объекта к классу мы теперь сможем судить по суммарному весу признаков объекта, попавших в его кластер, в отношении к суммарному весу признаков класса. Присвоим веса признакам X1 и X2: в классе A – соответственно 1 и 2, в классе B – 2 и 1. Суммарные веса признаков в классе A и в классе B равны 3. Степень принадлежности объекта к классу A будет равна 1/3, к классу B – 2/3. Если объект попадает в область пересечения кластеров, можно выделить из обучающей выборки аналоги – объекты соответствующих классов, которые в признаковом подпространстве исходного объекта попадают в ту же область пересечения. Предположим, что исследуемый объект Х, представленный двумя признаками X1 и X2, при классификации попадает в область пересечения кластеров A и B.
111
Для дифференцирования нужно добавить объекту один или несколько новых признаков, так, чтобы в новом пространстве кластеры разделились. Аналогами исследуемого объекта будут три объекта из кластера A и два – из кластера B. Их рассмотрение может дать полезную информацию о том, на каких из оставшихся признаков нужно сосредоточиться. Универсальный Количественный Классификатор может являться ядром для медицинских, технических, экономических, экологических и других сложных систем, где можно применять методы распознавания образов для оценки состояний, прогнозирования и принятия решений. Ядро – классифицирующая часть системы. Для решения задач в прикладной сфере нужна оболочка, работающая с объектами этой сферы, и преобразующая их в объекты распознавания образов при передаче на классификацию в ядро.
4. Система «Спутник Врача». Спутник Врача представляет собой оболочку, назначение которой информационная поддержка процесса лечения. Основными этапами в лечении больного, с момента его направления в лечебное учреждение, являются: 1. Выбор специалиста (доклиническая оценка показателей больного, назначение дополнительных исследований), 2. Постановка диагноза. 3. Оценка стадии заболевания. 4. Выбор стратегии лечения, прогноз результата. 5. Контроль состояния больного на этапе лечения. 6. Оценка эффективности/адекватности лечения. 112
Покажем более подробно взаимодействие оболочки с ядром на примере первых трех этапов. Все они осуществляются по общей схеме. Основные понятия, которые здесь используются - заболевание, пациент и показатель. В ядре им соответствуют понятия класс, объект и признак. Показатели пациентов представляют собой данные исследований, а также субъективные оценки, в число которых входят жалобы и т.д. Каждый пациент представлен своим набором показателей. Когда показатель выходит за пределы нормы, это может говорить о наличии заболевания. Каждое заболевание характеризуется своим набором симптомов, или показателей заболевания. Симптом заболевания – это показатель, принимающий характерные для данного заболевания значения, например, Белок_в_моче>0,033ммоль/л, Нарушение_оксигенации=”Истина”. Все показатели больного за время наблюдения отражаются в системе как в амбулаторной карте. Чтобы система могла успешно работать, ее предварительно обучают. Врач вводит реальных или моделируемых больных, их показатели и заболевания. Система формирует в ядре “образы” заболеваний. Начиная с некоторого момента, она готова к эксплуатации. Дообучаться можно и в дальнейшем, если врач сочтет нужным добавить какой-либо случай заболевания как эталон. По аналогии с признаками в кластерном анализе, рассмотрим проблему достаточности показателей. В западной медицине для каждого заболевания существует необходимый минимум исследований, который должен быть завершен до момента постановки диагноза. В отечественной школе часто ставят диагноз, когда не все показатели известны. Обычно исследования проводятся в направлении от простых - к сложным, начиная с жалоб, внешнего осмотра, и заканчивая лабораторными и инструментальными исследованиями. С одной стороны, если диагноз ясен уже на первых стадиях исследования больного, нет нужды делать дорогостоящие анализы. С другой стороны, иногда можно отложить окончательное решение на более поздний срок, когда будут сделаны дополнительные исследования, или появятся новые симптомы. Если набор симптомов оставляет выбор между несколькими заболеваниями, последние помещаются в так называемый дифференциальный ряд. Работа происходит по следующей схеме:
Формирование дифференциального ряда.
Оценка достаточности набора имеющихся показателей пациента для выбранного дифференциального ряда.
Передача дифференциального ряда в ядро для установления степени достоверности элементов ряда.
Перед тем, как приступить к классификации, нужно, с одной стороны, оценить набор показателей пациента на достаточность, с другой – отобрать из показателей только имеющие отношение к дифференциальному ряду. Чтобы не отягощать оценку лишними признаками, система сама отберет нужные 113
показатели, если набор окажется неполным - покажет недостающие показатели. Решение, передавать на оценку неполный набор или проводить дополнительные исследования, принимает врач. В ядре вычисляется степень достоверности для заболеваний из дифференциального ряда. В ряд может быть добавлено новое заболевание, а часть заболеваний может не подтвердиться. Тем самым система помогает врачу принять решение «за» или «против» того или иного диагноза, не навязывая своего выбора. На рис. 1 - сформированное в ядре приложение для диагностики. Видны три класса, полученные в результате обучения на эталонных выборках из показателей пациентов. Исследуемые пациенты условно сгруппированы в отдельный класс.
Рис. 1.
114
Рис. 2.
На рис. 2 виден набор показателей исследуемого пациента и сформированный на основе этого набора дифференциальный ряд. Показатели, имеющие отношение к этому ряду, отмечены системой. В правом нижнем блоке система указала недостающие показатели.
115
Рис. 3.
После передачи в ядро объект с номером карты 123 распознается как принадлежащий в разной степени двум классам: «Ателектатические бронхоэктазы» и «Смешанные бронхоэктазы» (рис. 3).
116
Рис. 4.
Результат оценки возвращается в оболочку. После нормирования по сумме баллов к 100% получаем степень достоверности заболеваний дифференциального ряда (рис. 4). Изучение аналогов может оказать существенную помощь при исследовании больного. Согласно определению из предыдущего раздела, в признаковом пространстве исследуемого пациента аналоги должны образовывать такой же дифференциальный ряд.
117
Рис. 5. На рис. 5 видно, как система отобрала аналоги для исследуемого пациента. Ими послужили моделируемые пациенты из обучающей выборки как примеры конкретных заболеваний, отсюда - дифференциальный ряд, состоящий из одного заболевания и отсутствие недостающих показателей.
5. Заключение. Система Спутник Врача предназначена для информационной поддержки всех этапов лечения. Она позволяет хранить и анализировать большие объемы данных, упростить принятие решения при постановке диагноза и выборе лечения, систематизировать сам процесс лечения. Система не диктует врачу, какое решение принимать, а лишь помогает ему сделать выбор. Врач с ее помощью может накапливать опыт, запоминая больных, интерпретируя их показатели, лечение и исход. Опытный врач может ставить диагноз по небольшому количеству симптомов, учитывая скрытые связи между показателями, при отсутствии специфических проявлений идентифицируемых заболеваний. Именно этот опыт и призвана сохранить система. Благодаря используемому математическому аппарату, она позволяет использовать ассоциирующие симптомы, в неявном виде учитываемые врачом. В сочетании с механизмом поиска аналогов, система может стать для врача одновременно и записной книжкой, и справочником, и 118
экспертной системой, помогающей ориентироваться в сложных и неоднозначных ситуациях. В отличие от большинства информационных систем, применяемых в медицине, данная система не ограничена отдельной областью медицины. Она может быть использована врачами специализированных лечебных учреждений, поликлиник, а также для обучения студентов и практикантов. Литература. 1. Дюран Б., Оделл П. Кластерный анализ. М. Статистика, 1977. 2. Дж. Ту, Р. Гонсалес. Принципы распознавания образов. М. Мир, 1978. 3. А.Л. Горелик, И.Б. Гуревич, В.А. Скрипкин, Современное состояние проблемы распознавания. М. Радио и Связь, 1985. 4. Бураковский В.И., Лищук В.А., Газизова Д.Ш. "Айболит" - новая технология для классификации, диагностики и интенсивного индивидуального лечения: Препринт ИССХ им.А.Н.Бакулева. -М. -1991. -63С. 5. Янковская А.Е., Гедике А.И. Построение прикладных интеллектуальных систем на базе системы ЭКСАПРАС.- Сб. научных трудов III конференции по искусственному интеллекту (КИИ-92).- Тверь, Ассоциация искусственного интеллекта, 1992.- Т. 2, с. 82 - 85. 6. Беляев А.Б., Годовников М.Н., Голубев С.А., Загоровский Н.М., Комаров С.И., Куршев Е.П., Осипов Г.С., Сазонова Л.И. Технология создания распределенных интеллектуальных систем. // Переславль-Залесский, 1997. 7. Кобринский Б.А. Системы искусственного интеллекта в медицине: Состояние, проблемы и перспективы // Новости искусственного Интеллекта. 1995;2:65-79. 8. Кобринский Б.А Автоматизированные диагностические и информационно-аналитические системы в педиатрии. Русский Медицинский Журнал, Том 7, № 4, 1999 9. Диаген: http://www.aha.ru/~softbk/sbk_diagen.htm 10. Медицинский центр новых информационных технологий (МЦНИТ):http://pediatr.mtunet.ru/News/mcnit-30.html 11. Ставицкий Р.В., Гуслистый В.П., Беридзе А.Д. Медицинская диагностика и динамика кластерного анализа: алгоритмы кластеризации, некоторые их свойства и возможности применения. // "Проблемы окружающей среды и природных ресурсов", 1997, № 2 12. Журавлев Ю.И. Об алгебраическом подходе к решению задач распознавания или классификации. В сб. "Проблемы кибернетики", Вып. 33, М., Наука, 1978. 13. Булыгин В.П., Шумский В.И, Портнов Л.Н. и др., Компьютерные медицинские экспертные системы как средство повышения эффективности диагностики и лечения // Альманах клинической медицины – М., 1999. Т II. C 366-376. 14. Expert Systems in Medicine: http://amplatz.uokhsc.edu/acc95-expert-systems.html 15. AIM (Artificial Intelligence in Medicine): http://www.gretmar.com/ailist/list-main.html 16. 16.Larsson, J. E. and Hayes-Roth, B. Guardian: An Intelligent Autonomous Agent for Medical Monitoring and Diagnosis, IEEE Intelligent Systems and their Applications, 13(1), 58-64, 1998. 17. Iliad 4.5 Course Description. http://www.medinfosource.com/catalog/670info.html
119
120
Применение нечеткой логики в имитационной системе автономного адаптивного управления А.А. Жданов, М.В. Караваев
Аннотация. Работа посвящена исследованию возможностей объединения в одной управляющей системе двух технологий управления – нечеткой логики и метода «автономного адаптивного управления». Обладая своими достоинствами и ограничениями, оба метода при объединении дают новое качество, наделяя систему управления новыми свойствами: 1) системы нечеткой логики оказываются удобными для представления в адаптивной системе управления априорной информации о свойствах объекта управления. Это позволяет в процессе работы системы существенно сократить фазу обучения и быстрее достигать высокого качества управления; 2) использование подсистемы нечеткой логики в адаптивной системе управления повышает как качество управления, так и эффективность метода ААУ в целом. Основной результат данной работы состоит в том, что впервые показана возможность использования нечеткой логики для адаптивного управления, т.е. для управления, автоматически изменяющего свои свойства непосредственно в процессе управления, в то время как обычным использованием нечеткой логики является построение систем управления на основе знаний эксперта.
1. Введение В рамках направления, называемого системами «искусственного интеллекта» (ИИ), был разработан ряд известных методов, позволяющих решать различные задачи управления без использования математических моделей управляемых объектов. Именно, возможность обходиться без сложной и дорогостоящей разработки математических моделей составляет основное достоинство этих методов. Это достоинство тем более существенно, что для подавляющего большинства как искусственных, так и естественных объектов, которыми нам хотелось бы управлять, построение точных математических моделей практически невозможно, такие объекты называют плохо формализуемыми. Ситуация еще усложняется, если свойства объектов изменяются в процессе управления. Сам термин «искусственный интеллект» в последнее время не пользуется популярностью. Причин тому несколько: прежде всего, так и не определилось 121
однозначно само понятие «интеллекта», который интуитивно ассоциируется со свойствами человеческого мозга; слишком очевидны отличия любой системы ИИ от свойств мозга; принципы построения систем ИИ крайне редко связываются с принципами работы мозга, а сами эти принципы еще не поддаются пониманию. Однако свойства систем ИИ позволяют применять их для решения прикладных задач, самая распространенная из которых – построение систем управления бытовой электроникой, узлами автомобилей, промышленными роботами. Особенно преуспели в этом направлении японские корпорации Matsushita, Hitachi, Mitsubishi, Nissan. Большинство этих разработок носят закрытый характер. К тому же они обычно не претендуют на решение фундаментальных проблем в области ИИ. Более того, можно видеть, что отдельные системы ИИ имитируют лишь разные подзадачи, решаемые естественными нервными системами или мозгом, и не способны к решению этих подзадач всех вместе. Тенденции объединения различных направлений ИИ начинают появляться в разработках последних лет. Эти тенденции естественным образом возникают в каждом из направлений ИИ тогда, когда их разработчики пытаются анализировать работу нервных систем и приближать к ним свои системы. Тем самым, нервные системы являются «центром притяжения» всех направлений ИИ. Поэтому объединение идей различных систем ИИ является естественным и в перспективе ведет к получению не только систем ИИ, имеющих новые свойства, но и к решению фундаментальных проблем ИИ. В настоящей работе представлены результаты, полученные авторами при объединении двух технологий ИИ. Одна из этих технологий хорошо известна, это системы нечеткой логики, другая технология – это метод автономного адаптивного управления (ААУ), развиваемый в ИСП РАН. Метод ААУ [1-4] разработан на основе развиваемой авторами концептуальной модели нервной системы. Управляющая система (УС), построенная по методу ААУ, представляет собой единый самообучающийся распознающийуправляющий комплекс. Управляющая система ААУ строится с учетом следующих четырех исходных условий: 1. Условие дискретности, означающее, что система строится из конечного числа элементов (нейронов, связей между ними, блоков памяти и т.д.); 2. Условие автономности, означающее, что УС является подсистемой ОУ и получает знания только на основании своего эмпирического опыта; 3. Условие минимума исходных знаний означает, что на момент начала процесса управления УС располагает минимумом знаний о свойствах объекта и среды его обитания; 4. Условие максимальной начальной приспособленности говорит о возможности системы управления обеспечивать минимальное качество управления на начальном этапе жизнедеятельности объекта управления. В УС взаимно согласованно решаются несколько задач, именно: задача 122
автоматической кластеризации (задача формирования образов), задача распознавания образов, задача получения знаний о свойствах объекта управления на основе эмпирического опыта (задача поиска закономерностей в предыстории системы), представление знаний, задача принятия решений и некоторые другие. Для решения этих задач в УС предусмотрены соответствующие подсистемы. Роль «учителя» в самообучаемой управляющей системе, построенной по методу ААУ, играет подсистема, названная «аппаратом эмоций». Эта подсистема выполняет в УС несколько функций, в частности, она вырабатывает некоторый функционал, который УС пытается оптимизировать. Текущее значение этого функционала должно по возможности объективно отражать соответствие состояния объекта управления и заданных целевых функций. Множество целевых функций образует определенную иерархическую систему, вершиной которой являются две цели «выживание объекта управления» и «накопление знаний управляющей системой». Принцип работы УС ААУ в целом состоит в следующем. Объект управления (ОУ) несет на себе множество датчиков, регистрирующих параметры, обеспечивающие наблюдаемость и управляемость ОУ, а также состояние исполнителей. В потоке входной информации, поступающей от датчиков, автоматически обнаруживаются пространственно-временные регулярности определенного вида - образы. Те образы, прообразы которых включают в себя причинно-следственные связи состояний ОУ и внешних воздействий УС, являются эмпирическими знаниями, отражающими функциональные свойства ОУ. Эти знания сохраняются в разделе памяти УС, названном базой знаний. Статистическими способами находятся зависимости различимых состояний ОУ и упомянутого выше функционала, отражающего состояние ОУ. При принятии решений УС распознает образы, описывающие текущее состояние ОУ, обращается к базе знаний и находит в ней такие действия, которые максимизируют функционал. Исполнителям дается команда на совершение принятого решения. Адаптивные свойства УС ААУ состоят в том, что в реальном времени управления а) формируются новые образы, в том числе – образы над образами (образы большего порядка); б) для новых образов находятся их оценки качества, что тем самым расширяет размерность максимизируемого функционала; в) находятся новые знания; г) происходит агрегирование знаний при обнаружении знаний для образов большего порядка. Эти подзадачи решаются подсистемами УС, каждая из которых использует некоторый метод автоматического поиска закономерностей, например, статистических закономерностей. Одна из основных проблем метода ААУ состоит в том, что для подсистем УС, реализующих тот или иной автоматический метод поиска закономерности в своих входных данных, необходимо заранее указать классы таких закономерностей, либо правила, по которым эти классы следует формировать, т.е. задать гипотезы, которые будет проверять УС. Известно, что задание априорной информации требуется для любой самообучаемой системы 123
распознавания. Причина этого состоит в том, что для описания всякой входной информации можно использовать огромное число различных базисов. Также можно искать закономерности в заданных данных в многочисленных базисах. Построить такую распознающую систему, которая проверяла бы все мыслимые гипотезы во всех мыслимых базисах, невозможно. Подобно тому, как всякий живой организм приспособлен к работе в определенном для него ограниченном базисе знаний, УС ААУ требует априорного задания такого базиса. В живой природе такой базис находится и уточняется в процессе эволюционного отбора. В системе ААУ требуется разработка специальной методики представления в БЗ имеющейся априорной информации об ОУ, среде и т.д. Одну из возможностей представления априорной информации в УС ААУ мы увидели в нечеткой логике (fuzzy logic), в последнее время часто использующейся для построения управляющих систем. Сегодня нечеткая логика применяется лишь для формальной записи знаний эксперта, что позволяет использовать их для автоматического принятия решений, аналогичных решениям эксперта. Мы не нашли в литературе упоминаний об управляющих системах на основе нечеткой логики, которые допускали бы самообучение в процессе управления. Управляющая система, которую мы представляем в данной работе, является самообучаемой. Таким образом, мы предлагаем способ использования нечеткой логики для задания априорной информации в самообучаемой (адаптивной) системе управления.
2. Основы теории нечеткой логики Нечеткая логика(НЛ) появилась в 1965 в работах [5,6] Лотфи А. Задэ, профессора технических наук Калифорнийского университета в Беркли. Нечеткая логика является многозначной логикой. В отличие от традиционной математики, требующей на каждом шаге моделирования точных и однозначных формулировок закономерностей, нечеткая логика предлагает совершенно иной уровень мышления, при котором творческий процесс моделирования происходит на наивысшем уровне абстракции, и постулируется лишь минимальный набор закономерностей. Значения нечетких переменных, получаемые в результате неточных измерений, во многом аналогичны распределениям теории вероятностей, но свободны от присущих последним недостатков. К таким недостаткам относятся: малое количество пригодных к анализу функций распределения, необходимость их принудительной нормализации, соблюдение требований аддитивности, а также трудность обоснования адекватности математической абстракции для описания поведения фактических величин. В пределе, при возрастании точности, нечеткая логика переходит в Булеву алгебру. По сравнению с вероятностным методом, нечеткий метод позволяет резко сократить объем производимых вычислений, что приводит к увеличению быстродействия нечетких систем. 124
Нечеткая логика первоначально использовалась как наиболее удобный способ построения систем управления метрополитенами и сложными технологическими процессами, в дальнейшем она нашла применение в бытовой электронике, различных системах управления, диагностических и других экспертных системах.
2.1 Нечеткие множества и основные операции над ними Пусть E - универсальное множество, x - элемент E. Нечеткое подмножество A универсального множества E определяется как множество упорядоченных пар A = {μA(х)/х}, где μA(х) - характеристическая функция принадлежности (или просто функция принадлежности), принимающая значения в некотором вполне упорядоченном множестве M (например, M = [0,1]). Функция принадлежности указывает степень (или уровень) принадлежности элемента x подмножеству A. Пример графического представления функции принадлежности представлен на рис. 1. Множество M иногда называют множеством принадлежностей. Если M = {0,1}, то нечеткое подмножество A может рассматриваться как обычное или четкое множество.
μ
μA ( x)
5. Дизъюнктивная сумма: A⊕B = (A – B)∪(B – A) = (A ∩ ^B)∪(^A ∩ B) с функцией принадлежности ∀x∈E μA⊕B(x) = max {min(μA(x), 1–μB(x)); min(1–μA(x), μB(x))}. 6. Алгебраическое произведение: A⋅B с функцией принадлежности ∀x∈E μA⋅B(x) = μA(x) ⋅ μB(x). 7. Алгебраическая сумма: A+B с функцией принадлежности ∀x∈E μA+B(x) = μA(x) + μB(x) – μA(x)⋅μB(x). 8. Возведение в степень: Aα с функцией принадлежности ∀x∈E μAα(x) = μαA(x). Частные случаи возведения в степень: CON(A) = A2 - операция концентрирования, DIL(A) = A0,5 - операция растяжения.
2.2 Нечеткие и лингвистические переменные. алгоритм нечеткого управления
Общий
Нечеткая переменная характеризуется тройкой <α, X, A>, где α – наименование переменной, X – универсальное множество (область определения α), A – нечеткое множество на X, описывающее ограничения (т.е. μA(x)) на значения нечеткой переменной α. Лингвистической переменной называется набор <β,T,X,G,M>, где
μ B ( x)
β – наименование лингвистической переменной;
Т – множество ее значений (терм-множество), представляющих собой наименования нечетких переменных, областью определения каждой из которых является множество X. Множество T называется базовым терммножеством лингвистической переменной;
G – синтаксическая процедура, позволяющая оперировать элементами терм-множества T, в частности, генерировать новые термы (значения). Множество T∪G(T), где G(T) – множество сгенерированных термов, называется расширенным терм-множеством лингвистической переменной;
x
М – семантическая процедура, позволяющая превратить каждое новое значение лингвистической переменной, образуемое процедурой G, в нечеткую переменную, то есть сформировать соответствующее нечеткое множество.
Рис. 1. Примеры функций принадлежности. Над нечеткими множествами определены следующие основные операции. 1. Дополнение: пусть M=[0,1], A и B – нечеткие множества, заданные на E. A и B дополняют друг друга, если ∀x∈E μA(x) = 1–μB(x). Обозначается “A=^B” или “B=^A”. 2. Пересечение: A∩B – наибольшее нечеткое подмножество, содержащееся одновременно в A и B. ∀x∈E μA∩B(x) = min (μA(x), μB(x)). 3. Объединение: A∪B – наименьшее нечеткое подмножество, включающее как A, так и B с функцией принадлежности ∀x∈E μA∪B(x) = max (μA(x), μB(x)). 4. Разность: A – B = A∩^B с функцией принадлежности ∀x∈E μA-B(x) = μA∩^B(x) = min (μA(x), 1–μB(x)). 125
Примером лингвистической переменной может служить слово “вода”, которая включает в себя следующие нечеткие переменные (множество значений): “холодная”, “теплая” и “горячая”. Из лингвистических переменных строятся нечеткие высказывания. Они часто представляют собой предложения естественного языка, что позволяет, например, использовать знания экспертов для построения систем управления на основе нечеткой логики. Подмножество нечетких высказываний, используемое в системах нечеткого управления, называется правилами, которые обычно представляют собой 126
выражения вида “Если … то …”. В левой части правил находятся входные лингвистические переменные, сопоставленные с их возможными значениями, а в правой – выходные. В правилах нечеткие переменные могут объединяться при помощи связок “и”, “или” и “не”, которые соответствуют операциям пересечения, объединения и дополнения над нечеткими множествами. Например, простейший набор правил для управления температурой и давлением воды в смесителе может выглядеть следующим образом: 1. Если вода горячая и давление слабое, то повернуть синий вентиль вправо; 2. Если вода горячая и давление сильное, то повернуть красный вентиль влево; 3. Если вода теплая, то не вращать вентили; 4. Если вода холодная и давление слабое, то повернуть красный вентиль вправо; 5. Если вода холодная и давление сильное, то повернуть синий вентиль влево; где “вода” и “давление” – лингвистические переменные, а “холодная”, “теплая”, “горячая” и “слабое”, “сильное” – их значения – нечеткие переменные, задаваемые нечеткими множествами. Процесс преобразования входных физических величин в значения лингвистических переменных при помощи функций принадлежности называется фаззификацией. Например, сопоставление физического значения температуры воды с множествами значений лингвистической переменной “вода” можно произвести следующим образом (рис. 2): степень принадлежности физического значения температуры к нечеткой переменной (нечеткому множеству) “холодная” равна 0,10, к нечеткой переменной “теплая” – 0,65, а к переменной “горячая” – 0,00. При обработке нечетких правил применяются стандартные операции над нечеткими множествами, в результате чего происходит вычисление степеней принадлежности выходных физических воздействий к значениям лингвистических переменных. Если для рассмотренного выше примера из пяти правил принять значения лингвистической переменной “давление”: “слабое” – 0,35, а “сильное” – 0,78, то результаты работы правил буду выглядеть следующим образом: 1. Повернуть синий вентиль вправо: min(0,00; 0,35) = 0,00; 2. Повернуть красный вентиль влево: min(0,00; 0,78) = 0,00; 3. Не вращать вентили: 0,65 = 0,65; 4. Повернуть красный вентиль вправо: min(0,10; 0,35) = 0,10; 5. Повернуть синий вентиль влево: min(0,10; 0,78) = 0,10. Процесс вычисления значений выходных физических переменных называется дефаззификацией и происходит обычно в соответствии с формулами нахождения средневзвешенной величины. Дефаззификация так же часто производится нахождением центра масс фигуры, образованной линиями функций принадлежности соответствующих нечетких переменных выходной лингвистической переменной, осью абсцисс и прямыми, параллельными оси 127
абсцисс и отстоящими от нее на величину степени принадлежности физической переменной соответствующей нечеткой переменной.
μ
холодная
теплая
горячая
1,00
0,65 0,50
0,10
t1
t
Рис. 2. Сопоставление физического значения температуры t с нечеткими переменными “холодная“, “теплая“, “горячая“. Для рассмотренного выше примера допустим, что термин “повернуть вправо” означает поворот вентиля на 30 градусов, а “повернуть влево” – на –30 градусов. Тогда значение угла поворота для красного вентиля будет равно: (0,65⋅0 + 0,1⋅30)/(0,65 + 0,1) = 4 градуса. Аналогичное значение получится и для синего вентиля. Таким образом, общий алгоритм нечеткого управления состоит из следующих взаимосвязанных процедур: 1. преобразование входных физических величин, получаемых от измерительных датчиков с объекта управления, в значения лингвистических переменных – фаззификация; 2. обработка нечетких высказываний относительно входных и выходных лингвистических переменных системы; 3. преобразование выходных лингвистических переменных системы в физические управляющие переменные – дефаззификация. Недостатками нечетких систем являются: 1. отсутствие стандартной методики конструирования нечетких систем; 2. невозможность математического анализа нечетких систем существующими методами; 3. применение нечеткого подхода, в отличие от вероятностного подхода, не приводит к повышению точности вычислений; 4. отсутствие адаптивности в реальном времени. 128
3. Построение подсистемы системы ААУ
начальной
адаптации
Рассмотрим возможности применения нечеткой логики в управляющих системах, построенных по методу ААУ. Для обеспечения одного из основных начальных условий системы, построенной по методу ААУ – максимальной начальной приспособленности, необходим некоторый аппарат, заменяющий действие механизмов естественного отбора и передачи наследственной информации, присущих живым организмам. Одним из возможных вариантов обеспечения этого свойства в моделируемых нервных системах является введение некой подсистемы с заранее заложенными в нее базовыми правилами управления, основанными на общих знаниях об объекте и предполагаемой среде его функционирования. Эта подсистема должна определять основные принципы управления на начальном этапе жизнедеятельности объекта, когда основная система управления еще не успела накопить достаточно информации о закономерных связях между воздействиями на внешнюю среду и состояниями, в которые объект переходит в результате этих воздействий, необходимой для “осмысленного” управления объектом. Очевидно, что управление на основе только априорных знаний будет менее качественным, чем управление, осуществляемое системой ААУ с эмпирически заполненной базой знаний. Напомним, что нас интересуют плохо формализуемые объекты управления. Основная цель вводимой подсистемы – обеспечение минимального качества управления объектом на начальном этапе. В качестве подсистемы нечетких исходных знаний (так был назван новый блок системы ААУ, дополняющий ее априорными знаниями) выбрана система управления, построенная на основе нечеткой логики. На примере прикладной системы – прототипа системы стабилизации углового движения автоматического космического аппарата “Пилот”, рассмотрим работу подсистемы нечетких исходных знаний. Подробно система “Пилот” описана в [1]. Общая структура системы ААУ с модулем нечетких исходных знаний изображена на рис. 3. Входными данными для него являются физические значения текущего углового отклонения f(t) и угловой скорости df/dt, которые отображаются в нечеткие множества (переменные) входных лингвистических переменных “угловое отклонение” и “угловая скорость”. Выходные данные модуля – усредненное значение номера управляющего воздействия, полученное из выходной лингвистической переменной “управляющее воздействие”. 129
Среда в строгом смысле Среда в узком смысле
Объект управления Управляющая система Блок оценки состояния ОУ
Ot Блок датчиков
f, df/dt
Блок ФРО
Ot
E(Ot) Система нечетких исходных знаний
Среда в широком смысле
База знаний правил нечеткой логики
Блок корректировки правил
База знаний ААУ
At Исполняющий орган
Mt
Блок выбора действия
At
Рис. 3. Структура системы ААУ с подсистемой нечетких исходных знаний. База знаний модуля состоит из набора правил, заданных при помощи нечетких переменных F1…FN, dF1…dFM, и A1…AN*M, минуя уровень лингвистических переменных “угловое отклонение”, “угловая скорость” и “выходное воздействие”: F1 & dF1 → A1 F1 & dF2 → A2 . . . . . . . F1 & dFM → A M F2 & dF1 → A M+1 F2 & dF2 → A M+2 . . . . . . . . . Fi & dFj → A(i-1)*M+j . . . . . . . . . FN & dFM–1 → AN*M–1 FN & dFM → AN*M , где N и M – количества нечетких множеств, на которые разбит диапазон наблюдаемых значений угла отклонения и угловой скорости космического аппарата. Результаты сработавших правил используются для вычисления номера A управляющего воздействия по следующей формуле: 130
A=
∑ ( Ak ⋅ Pk ) , ∑ Pk
(1)
(5)
где Ak – номера сработавших правил, Pk – выходные значения соответствующих правил. Также возможен вариант управления, при котором среди сработавших правил выбирается одно правило с максимальным результатом и выполняется действие, заданное только этим правилом. В смоделированной системе управления на основе НЛ используются 3 вида функций принадлежности p(x) входной переменной x одному из нечетких множеств наблюдаемых переменных: линейная (треугольная), квадратичная и обратная квадратичной. Здесь i – номер интервала (i ∈ [0..N]), x – безразмерная величина, полученная из значения входной физической переменной по следующей формуле: x=
f + FMAX ⋅ N, 2 ⋅ FMAX
(2)
где f – значение входной физической переменной, FMAX – максимальное абсолютное значение входной физической переменной (считается, что f может принимать любое значение в диапазоне [–FMAX ; FMAX]), N – количество нечетких множеств, на которые разбит диапазон наблюдаемых значений физической входной переменной. Причем N может не совпадать с числом интервалов для системы ААУ. Для системы НЛ лучше поделить диапазон наблюдаемых значений на меньшее число более широких интервалов с целью сокращения количества правил в БЗ. Используемые функции принадлежности: 1. Линейная (треугольная) функция: ⎧ x − i + 1, i − 1 ≤ x < i pi ( x ) = ⎨ ⎩i − x + 1, i ≤ x < i + 1
где К – априорно задаваемый коэффициент, определяющий площадь графика функции принадлежности (чем больше К, тем меньше площадь). Рассмотрим общий алгоритм работы такой системы ААУ. Изначально база знаний системы ААУ пуста, а в базе знаний системы НЛ находятся правила, полученные на основании априорной информации о движении космического аппарата. По этим правилам и производится управление на начальном этапе. Во время полета космического аппарата база знаний системы ААУ постепенно заполняется и управление плавно переходит к системе ААУ: если ОУ попадает в какое-то состояние, оптимальное управляющее воздействия для которого не содержится в БЗ ААУ (не заполнен столбец БЗ), то право выбора управляющего момента на этом такте передается системе НЛ, а информация об этом состоянии и выбранном системой НЛ воздействии заносится в БЗ системы ААУ. При этом система ААУ получает право выбора действия в каждом состоянии только в том случае, если она может предложить действие, которое переведет ОУ в состояние со значением оценочной функции не ниже некоторого порога Emin, задающегося априорно. При длительном управлении в БЗ системы ААУ накапливаются более достоверные знания, чем те, которые заложены априорно в БЗ нечетких правил, в результате чего возникает необходимость корректировки этих правил системы начальной адаптации для использования их в следующих сеансах работы. Корректировка происходит путем вычисления по БЗ ААУ суммарного выходного управляющего воздействия для каждого нечёткого правила и замещения вычисленной величиной старого значения. При этом непрерывное значение индекса F углового интервала фазовой плоскости системы НЛ получается из соответствующего индекса образа на фазовой плоскости ААУ по следующей формуле:
(3)
⎡ [( X − 1) ⋅ 2 − X max + 1] ⎤ Fmax F =⎢ + 1⎥ ⋅ , X max − 2 2 ⎣ ⎦
2. Квадратичная функция: pi ( x ) = 1 − ( x − i ) 2 , i − 1 ≤ x < i + 1
где X – номер углового интервала фазовой плоскости ААУ; Xmax – количество угловых интервалов фазовой плоскости ААУ (включая открытые); Fmax – количество нечетких переменных входной лингвистической переменной системы НЛ.
(4)
3. Функция, обратная квадратичной pi ( x ) =
1 1 + K ⋅ ( x − i) 2
, i − 1 ≤ x < i + 1,
(6)
131
132
4. Результаты работы программной модели За основу программной модели комплекса взята упомянутая выше программа “Пилот”. Добавленный к программе модуль, моделирующий подсистему нечетких исходных знаний, может содержать от 4 до 10000 правил, входные лингвистические переменные которых содержат соответственно от 2 до 100 нечетких переменных. На рис. 4 приведены два графика, отображающие временные интервалы значений углового отклонения и угловой скорости на начальном этапе управления: с использованием системы нечетких исходных знаний и без нее. По графикам можно видеть, что разработанная подсистема на основе нечеткой логики обеспечивает приемлемое качество управления в начале работы и позволяет избежать полного перебора при самообучении системы ААУ. Рис. 5 содержит пример заполнения базы знаний нечеткими правилами при разбиении входных лингвистических переменных на 9 нечетких переменных. На пересечении столбца f и строки df находится номер действия, выполняемого при попадании входных физических переменных f и df в нечеткое множество соответствующей входной нечеткой переменной.
б) Рис. 4. Сравнение качества управления на начальном этапе работы адаптивной системы управления с использованием подсистемы нечетких исходных знаний (б) и без нее (а). При тестовых запусках программы было определено, что а) оптимальным количеством является 25 правил при сопоставлении входных физических переменных f и df 5-ти нечетким переменным; б) оптимальной является линейная (треугольная) функция принадлежности входных физических переменных нечетким множествам, к тому же, она наиболее проста для вычисления; в) оптимальный вариант объединения выходных результатов сработавших правил – объединение по среднему значению.
а) 133
134
5. Заключение В данной работе предложен один из возможных путей совмещения двух технологий управления в одной системе: метода нечеткой логики и метода «автономного адаптивного управления», развиваемого в ИСП РАН. Подсистема нечетких исходных знаний на основе fuzzy logic была встроена в систему управления ААУ, дополняя ее на начальной фазе работы адаптивной системы управления априорными правилами управления, полученными на основе знаний экспертов. Для испытания разработанной технологии было выбрано одно из практических приложений – программа «Пилот», прототип адаптивной системы управления угловым движением космического аппарата. Этот прототип удобен для сравнения различных вариантов систем управления тем, что наглядно демонстрирует их достоинства и недостатки. Основные результаты и выводы, полученные в результате компьютерных испытаний разработанной системы заключаются в следующем:
Рис. 5. База знаний, состоящая из 81 нечеткого правила, сформированного по заполненной базе знаний системы ААУ. При сравнении разработанного варианта управляющей системы ААУ с исходным вариантом (без системы нечетких исходных знаний) были выявлены следующие преимущества: 1. длительность фазы начальной стабилизации объекта управления сократилась в среднем с 7000 до 500 тактов; чем более точно подобраны правила, тем меньше время начальной стабилизации (точность правил ограничивается, естественно, тем, что в нашем случае объект управления плохо формализуем); 2. качество управления повысилось в среднем с 2,0 до 2,9 за первые 2000 тактов и с 2,5 до 3,0 за первые 10000 тактов управления, где количественная оценка, использовавшаяся в программе «Пилот», вычисляется по следующей формуле: 13
S
i
∑ t ⋅10 i+ 10 ⋅ 2
(7)
i =0
Здесь t – номер текущего такта, Si – количество попаданий объекта в состояние с оценкой равной i, за все время работы системы. Оценка i =1,2,...,6 тем больше, чем дальше от центра фазовой плоскости находится текущее состояние ОУ. 135
применение подсистемы нечетких исходных знаний в системе ААУ повышает среднее качество управления за счет ускорения фазы обучения адаптивной системы управления (в прикладном примере высокое качество управления космическим аппаратом достигалось быстрее);
при заполнении обычной для системы ААУ базы знаний эмпирической информацией блок корректировки нечетких правил формирует адекватные нечеткие правила управления объектом, которые затем могут использоваться экспертами для изучения поведения данного объекта;
подсистему нечетких исходных знаний нельзя применить к объектам управления, законы управления которыми эксперты не могут сформулировать хотя бы в самом общем, приближенном виде.
6. Направления дальнейших исследований При дальнейшей разработке данной темы следует рассмотреть также иные возможные пути применения нечеткой логики в системах ААУ, а именно: 1) использовать теорию нечетких множеств в процессе формирования и распознавания образов, заполнения БЗ, и на этапе принятия решений; 2) разработать БЗ, построенную не на основе нейроноподобной сети (как в базовой версии), а из нечетких правил, формирующихся в процессе управления. При использовании рассмотренного пути применения нечеткой логики в системах ААУ с более развитым, нежели у системы “Пилот”, аппаратом ФРО (который распознает образы более продолжительных во времени прообразов) 136
может возникнуть следующая проблема: распознавание образов аппаратом ФРО – дискретный процесс (на выходе дискретные величины: образ распознан или не распознан), а входные переменные системы НЛ должны иметь непрерывный характер. В противном случае БЗ системы НЛ вырождается в набор элементарных продукционных правил, теряя все преимущества нечеткого управления. При исследовании возможности построения основной БЗ системы ААУ на основе правил нечеткой логики понятие “образ” может претерпеть некоторые изменения, потеряв, в частности, свои дискретные черты: его может заменить множество значений входных физических переменных или их степень принадлежности одной из входных нечетких переменных. Система управления на основе нечеткой логики также может найти применение в качестве основной управляющей системы, функционирующей за пределами диапазона значений входных переменных, в котором работает система ААУ. Литература 1. Труды института системного программирования, 1999. Том 1. Под ред. Иванникова В. П. М.: Биоинформсервис, 2000. – 124 с. 2. Zhdanov A. A. About an Autonomous Adaptive Control Methodology. ISIC/CIRA/(ISAS'98), NIST, Gaithersburg, Maryland. September 14-17, 1998, pp. 227-232. 3. Zhdanov A. A., Vinokurov A. N., Emotions Simulation in Methodology of Autonomous Adaptive Control, Proceedings of ISIC’99/ISAS’99, 1999. 4. Информация с сайта http://www.ispras.ru/~zhdanov/. 5. Zadeh, Lotfi. Fuzzy Sets / Information and Control, 8(3), June 1965, pp.338-53. 6. Заде Л.А. Понятие лингвистической переменной и его применение к принятию приближенных решений. М.:Мир, 1976. 7. Пивкин В.Я., Бакулин Е.П., Кореньков Д.И. Нечеткие множества в системах управления, http://idisys.iae.nsk.su/fuzzy_book/. 8. Баер П., Новак С., Винклер Р. Введение в нечеткую логику и системы нечеткого управления, http://softlab.od.ua/algo/neuro/fuzzy-intro/
137
138
конкретные выводы производительности.
и
рекомендации,
в
том
числе
по
вопросам
2. Реляционная и объектная модели. Соответствие между ними
Способы отображения объектов в реляционных базах данных∗
2.1. Объектная модель ODMG
В.В. Рубанов Аннотация. В статье рассматриваются различные способы обеспечения объектнореляционного отображения. Проводится анализ соответствия объектной и реляционных моделей. Рассматриваются, в частности, вопросы представления в реляционных таблицах отдельных атрибутов объектов, массивов и коллекций, связей и объектных ссылок. Далее исследуются вопросы организации отображения отдельных классов и целых иерархий наследования. Также описываются тонкости объектно-реляционного отображения, связанные с обеспечением максимальной производительности. Приводятся конкретные выводы и рекомендации.
Данный подраздел содержит описание терминологии, необходимой для дальнейшего изложения. В нем определяется объектная модель, принятая ODMG в качестве стандарта (см. [10]). В этой модели специфицируются конструкции, которые должны поддерживаться любой ООСУБД:
1. Введение В данной статье рассматривается современное состояние дел в области средств хранения данных объектно-ориентированных программ в реляционных базах данных. Центральное место занимает исследование различных способов организации так называемого объектно-реляционного отображения (objectrelational mapping). Обеспечение такого механизма является основной задачей при создании систем долговременного хранения объектов в реляционных базах данных. Трудности возникают из-за сильного несоответствия понятий объектной и реляционных моделей. Данная статья ставит своей целью описать специфику каждой из этих моделей и затем на основе этой информации сделать выводы о возможных путях решения исходной задачи путем комбинации классических и современных решений. В первом разделе приводится собственно описание объектной модели, специфицированной ODMG (см. [10]), и описание понятий классической реляционной модели Кодда. Затем дается общий обзор соответствия элементов данных моделей. Второй раздел посвящен описанию конкретных решений, обеспечивающих отображение объектных понятий в реляционные, приводятся
Базовыми примитивами являются объект и литерал. Каждый объект обладает уникальным идентификатором (OID). Литерал не имеет идентификатора.
Состояние объекта определяется как совокупность значений его свойств. Свойства делятся на атрибуты и связи.
Поведение объекта определяется набором методов этого объекта.
Тип объединяет два понятия интерфейса и класса.
Интерфейс специфицирует только абстрактное поведение своих экземпляров (без описания состояния).
Класс специфицирует абстрактное поведение и абстрактное состояние своих экземпляров.
В БД хранятся объекты, доступ к которым, вообще говоря, может осуществляться из нескольких различных приложений. БД основывается на схеме классов, которая определяется на этапе разработки приложения и описывается на одном из специальных языков (ODL - Object Definition Language, Java, UML). В стандарте ODMG специфицируется язык ODL. БД содержит экземпляры типов (объекты), описанных в данной схеме. Тип обладает внешней спецификацией и одной или несколькими реализациями. Спецификация определяет внешние характеристики типа, т.е. характеристики, которые видны пользователям этого типа, а именно:
∗
Статья основывается на результатах проектов, выполнявшихся в ИСП РАН по грантам РФФИ № 96-07-89591 и № 97-01-00142. 139
140
Атрибуты или переменные состояния (если они есть), значения которых можно получить для каждого экземпляра типа;
Методы, присущие экземплярам типа;
Исключения (exceptions), которые могут возникнуть в процессе работы методов типа.
Реализация содержит внутренние детали и аспекты механизмов работы экземпляров типа. Реализация включает в себя представление и набор методов. Представление – это структура данных, получаемая из определения абстрактного типа на ODL после отображения в конкретный язык программирования (ODL to program language mapping). Каждому атрибуту абстрактного типа соответствует переменная в его представлении. Методы – это тела операций (процедур), объявленных в спецификации данного типа, написанные с использованием классов, предоставляемых ОО средой. Тип может обладать несколькими реализациями, хотя обычно только одна из них используется в конкретной программе. Разделение спецификации и реализации типа, при которой спецификация описывается независимым от языка программирования образом, обеспечивает прозрачный доступ к объекту в неоднородном компьютерном окружении (соответствующий интерфейс на стороне клиента генерируется специальной утилитой – ODL/IDL компилятором).
}; class EmployeePerson extends Person : Employee { attribute Date hireDate; attribute Currency payRate; relationship Manager boss inverse Manager::subordinates; };
Extends-связь выражает наследование поведения и состояния типа и не допускает множественного наследования.
2.1.4. Агрегированные типы Объектная модель ODMG поддерживает различные типы объектов-агрегатов, которые состоят из элементов литеральных или объектных (возможно, агрегированных) типов. Среди прочих поддерживаются следующие агрегированные типы (t означает тип элементов агрегата): Bag List
2.1.1. Виды наследования
Array
Зависимости между типами в ОМ представляются двумя видами наследования, которые отражают соответственно наследование поведения и наследование состояния.
Интерфейсы всех этих типов наследуются от типа Collection, который является абстрактным интерфейсом, объединяющим общие характеристики. Семантически Bag является неупорядоченным мультимножеством (с возможностью содержания одинаковых элементов). List это упорядоченное множество элементов. Array соответствует обычному понятию массива с возможностью динамического изменения размера. Соответствующие реализации этих типов в Java имеют такие же имена, но с префиксом D, то есть DBag, DList, и т.д.
2.1.2. Наследование поведения Данному типу наследования соответствует понятие ISA-связи (is–a– relationship) – связи наследования между типами выражающей обобщение, когда супертип наследует поведение подтипа. Объектная модель поддерживает множественные ISA-связи наследования между интерфейсами. На языке ODL ISA-связи представляются двоеточием: interface interface interface interface
Person{…}; Employee {...}; Professor : Employee {...}, Person {…}; Associate_Professor : Professor {...};
2.1.3. Наследование состояния ODMG определяет также связь наследования (extends), когда наследуется состояние объекта класса: class Person { attribute string name; attribute Date birthDate;
141
2.1.5. Атрибуты Как уже упоминалось выше, состояние объекта определяется набором значений его свойств, подразделяющихся на атрибуты и связи. Атрибуты составляют внутреннее состояние объекта. Они могут быть элементарных и агрегированных литеральных или объектных типов. При этом последние выделяются особо, так как включение одним объектом в свой состав других объектов является типичным для ОО программирования. Они называются атрибутами-ссылками. Реально объекты ссылаются друг на друга посредством своих идентификаторов (объектной ссылки OID или идентификатора хранимого объекта POID), поэтому значение атрибутассылки – это объектная ссылка OID некоторого объекта в приложении или POID в БД. При отображении хранимых объектов из БД в приложение 142
идентификаторы хранимых объектов соответствующим образом преобразуются в объектные ссылки. Заметим, что атрибуты являются абстрактным описанием состояния объекта и не обязаны соответствовать структурам данных при отображении в конкретный язык программирования. Они могут реализовываться и как набор соответствующих методов.
2.1.6. Связи Тогда как свойства-атрибуты объекта характеризуют его внутреннее состояние, свойства-связи используются для задания семантических связей (отличных от связей наследования) с другими типами в спроектированной пользователем Объектной Модели Задачи (ОМЗ). Отметим, что связи – не то же самое, что атрибуты-ссылки. Атрибуты-ссылки характеризуют состояние объекта, которому принадлежат, путем задания ссылки на объекты, входящие в состав данного и являются однонаправленными (отражают семантику объектаагрегата), тогда как связи характеризуют не состояние объекта, а участие его в том или ином отношении к другим объектам в смысле семантических связей задачи и всегда являются двунаправленными. Хотя синтаксическая реализация этих свойств в конкретных языках программирования и совпадает. Связи могут существовать между экземплярами только двух типов (которые могут совпадать), т. е. связи являются бинарными. Свойства-связи, так же, как и атрибуты, объявляются в ODL-схеме БД. В объявление связей входит обязательное указание на обратное направление связи. Ниже приведен пример связи двух классов Professor и Course. interface Professor { relationship set teaches inverse Course:: is_taught_by; }; interface Course { relationship Professor is_taught_by inverse Professor:: teaches; };
Как и атрибуты-ссылки, связи реализуются с помощью объектных ссылок (POID и OID) на связанный объект, массива ссылок, либо объектов-агрегатов таких ссылок. Связи по типу бывают упорядоченные и неупорядоченные, в зависимости от типа агрегата, представляющего связь. Например, Bag – неупорядоченная связь, тогда как List<Member> – упорядоченная. Заметим, что упорядоченные связи практически мало применяются для моделирования реальных прикладных задач. Связи также различаются по мощности (количеству элементов с обеих сторон) и бывают следующих типов: 1:1, 1:n, n:m. 143
Механизм связей ODMG – двунаправленный. При этом СУБД обязана отслеживать корректность взаимных ссылок хранящихся в БД объектов согласно имеющейся схеме и обеспечивать целостность данных, в частности, удаление связей на объект со стороны других объектов при его удалении из БД (обеспечение целостности для атрибутов-ссылок не гарантируется). Описанные в формате ODL связи – лишь абстрактная спецификация. Для организации нормальной работы и использования связей необходимо описать некоторые классы и методы, позволяющие устанавливать, видоизменять и удалять эти связи. Так ODL - описание связи (1:1): relationship X inverse Z;
преобразуется в эквивалентное IDL – описание: attribute X, Y; void form_Y(in X target); void drop_Y(in X target);
Аналогично, для связей мощности 1:n имеем: relationship set<X> Y inverse Z;
На IDL: attribute void void void void
set<X> Y; form_Y (in X target) raises(lntegrityError); drop_Y (in X target); add_Y (in X target) raises(lntegrityError); remove_Y (in X target);
2.1.7. Хранимые объекты (Persistent Objects) Для приложения ООС является хранилищем перманентных (хранимых) объектов. При этом в конкретный момент времени такой объект может и не существовать в памяти программы. Но ООС знает уникальный идентификатор (POID) хранимого объекта и знает, как этот объект “синтезировать”, так что по запросу пользователя (т.е. по вызову соответствующих методов ООС) он может быть создан в памяти приложения и проинициализирован состоянием, хранимым в БД. Следует разделить понятия уникального Объектного Идентификатора в прикладной программе (OID) и уникального Объектного Идентификатора Хранимого Объекта в БД (POID). OID и POID представляют, вообще говоря, независимые пространства идентификаторов. При этом один и тот же хранимый объект, имеющий свой постоянный POID в БД, может обладать различными OID в прикладной программе при её запуске в разные моменты времени. 144
2.1.8. Экстент ОБД
2.2.1. Тип данных
Экстентом объектного типа называется совокупность всех хранимых объектов данного типа в рамках определенной БД. Если тип B наследуется от типа A, то экстент типа B является подмножеством экстента типа A. Экстенты имеют большое значение для работы целевого приложения. Они являются той базовой единицей, по которой производятся OQL запросы на получение хранимых в БД объектов.
Понятие тип данных в реляционной модели данных полностью адекватно понятию типа данных в языках программирования. Обычно в современных реляционных БД допускается хранение символьных, числовых данных, битовых строк, специализированных числовых данных (таких как "деньги"). А также специальных "темпоральных" данных (дата, время, временной интервал). Как уже замечалось, в объектно-реляционных системах активно развивается подход к расширению возможностей реляционных систем абстрактными типами данных. В нашем примере мы имеем дело с данными трех типов: строки символов, целые числа и "деньги".
2.1.9. Фундаментальные свойства объектно-ориентированной технологии Заметим, что описанные понятия объектной модели отражают фундаментальные свойства объектно-ориентированной технологии:
Инкапсуляция
Наследование
Полиморфизм
Данные свойства являются отличительными для ОО модели, и именно их отражение в реляционную модель вызывает наиболее принципиальные проблемы.
2.2. Реляционная модель Рассмотрим теперь реляционную модель данных, предложенную изначально Коддом. Это необходимо для дальнейшего изложения. При рассмотрении используется информация и примеры из [2]. Основными понятиями реляционных баз данных являются тип данных, домен, атрибут, кортеж, первичные и внешние ключи и отношение. Для начала покажем смысл этих понятий на примере отношения СОТРУДНИКИ, содержащего информацию о сотрудниках некоторой организации:
2.2.2. Домен Понятие домена более специфично для баз данных, хотя и имеет некоторые аналогии с подтипами в некоторых языках программирования. В самом общем виде домен определяется заданием некоторого базового типа данных, к которому относятся элементы домена, и произвольного логического выражения, применяемого к элементу типа данных. Если вычисление этого логического выражения дает результат "истина", то элемент данных является элементом домена. Наиболее правильной интуитивной трактовкой понятия домена является понимание домена как допустимого потенциального множества значений данного типа. Например, домен "Имена" в нашем примере определен на базовом типе строк символов, но в число его значений могут входить только те строки, которые могут изображать имя (в частности, такие строки не могут начинаться с мягкого знака). Следует отметить также семантическую нагрузку понятия домена: данные считаются сравнимыми только в том случае, когда они относятся к одному домену. В нашем примере значения доменов "Номера пропусков" и "Номера групп" относятся к типу целых чисел, но не являются сравнимыми. Заметим, что в большинстве реляционных СУБД понятие домена не используется, хотя в Oracle начиная с V.7 оно уже поддерживается.
2.2.3. Схема отношения, схема базы данных Схема отношения - это именованное множество пар {имя атрибута, имя домена (или типа, если понятие домена не поддерживается)}. Степень или "арность" схемы отношения - мощность этого множества. Степень отношения СОТРУДНИКИ равна четырем, то есть оно является 4-арным. Если все атрибуты одного отношения определены на разных доменах, осмысленно использовать для именования атрибутов имена соответствующих доменов (не забывая, конечно, о том, что это является всего лишь удобным способом именования и не устраняет различия между понятиями домена и атрибута). Схема РСУБД (в структурном смысле) - это набор именованных схем отношений. 145
146
2.2.4. Кортеж, отношение Кортеж, соответствующий данной схеме отношения, - это множество пар {имя атрибута, значение}, которое содержит одно вхождение каждого имени атрибута, принадлежащего схеме отношения. "Значение" является допустимым значением домена данного атрибута (или типа данных, если понятие домена не поддерживается). Тем самым, степень или "арность" кортежа, то есть число элементов в нем, совпадает с "арностью" соответствующей схемы отношения. Попросту говоря, кортеж - это набор именованных значений заданного типа. Отношение - это множество кортежей, соответствующих одной схеме отношения. Иногда, чтобы не путаться, говорят "отношение-схема" и "отношение-экземпляр", иногда схему отношения называют заголовком отношения, а отношение как набор кортежей - телом отношения. На самом деле, понятие схемы отношения ближе всего к понятию структурного типа данных в языках программирования. Было бы вполне логично разрешать отдельно определять схему отношения, а затем одно или несколько отношений с данной схемой. Однако в реляционных базах данных это не принято. Имя схемы отношения в таких базах данных всегда совпадает с именем соответствующего отношения-экземпляра. В классических реляционных базах данных после определения схемы базы данных изменяются только отношенияэкземпляры. В них могут появляться новые и удаляться или модифицироваться существующие кортежи. Однако во многих реализациях допускается и изменение схемы базы данных: определение новых и изменение существующих схем отношения. Это принято называть эволюцией схемы базы данных.
2.2.5. Ключи В реляционной терминологии используются два вида ключей – первичные (primary key) и внешние (foreign key). Первичный ключ отношения это один или несколько элементов схемы данного отношения (атрибутов), совокупность значений которых уникальна для всех кортежей отношений. Иными словами это идентификатор кортежа. Внешний ключ задает ограничение на значения атрибутов в одном отношении в соответствии со значениями первичного ключа другого отношения. То есть, если для отношения описан внешний ключ для некоторого атрибута, то в этом отношении не может быть кортежей со значением данного атрибута, если в исходной таблице нет кортежей с соответствующим значением первичного ключа.
отношения является таблица, заголовком которой является схема отношения, а строками - кортежи отношения-экземпляра; в этом случае имена атрибутов именуют столбцы этой таблицы. Поэтому иногда говорят "столбец таблицы", имея в виду "атрибут отношения". Этой терминологии придерживаются в большинстве коммерческих реляционных СУБД. Реляционная база данных на языке введенных определений это набор отношений, имена которых совпадают с именами схем отношений в схеме БД.
2.3. Соответствие основных понятий Рассмотрим, как принципиально возможно привести описанные выше объектную и реляционную модели к соответствию. Здесь описываются только общие правила и трудности, более подробный анализ различных вариантов отображения элементов объектной модели на реляционную схему проводится в данной главе ниже в соответствующих пунктах. Первым ключевым моментом является представление данных конкретного экземпляра объекта в виде кортежа(ей) реляционного отношения(ий). То есть состояние объекта запоминается в виде набора значений атрибутов в одной или нескольких реляционных таблицах. Экстент объектной модели это заполненная таблица в РСУБД. Такая таблица в дальнейшем будет часто называться таблицей класса. При этом каждому атрибуту базового типа в описании класса соответствует один атрибут реляционной таблице. Ситуация осложняется для агрегатных типов. В данном случае можно, например, использовать отдельную таблицу для хранения элементов данного агрегата. Каждый объект идентифицируется своим уникальным объектным идентификатором (OID). Реально для этой цели можно использовать столбец типа длинного целого числа. Данный столбец будет выполнять функции первичного ключа в соответствующей таблице класса. А связи между объектами выражаются в виде реляционных внешних ключей, так же как и простые односторонние ссылки на другие объекты. Таким образом, с использованием приведенных выше приемы становится возможным организовать хранение объектных данных в реляционной базе данных. Однако существует принципиальное несоответствие парадигм. Рассмотрим, как отражаются в РСУБД фундаментальные свойства объектноориентированной технологии:
2.2.6. Общепринятая реляционная терминология Описанные выше понятия соответствуют названиям из реляционной теории, где они определяются абсолютно формально и точно. Однако в литературе часто используется другая терминология, имеющая очень простую интуитивную интерпретацию. Обычным житейским представлением 147
148
Инкапсуляция. Реляционные БД не поддерживают средств ограничения доступа, кроме паролей и пользователей. Однако прикладная программа обычно выполняется на одном уровне привилегий и поэтому все скрытые данные, хранящиеся в БД, становятся общедоступными, а попытки обеспечить инкапсуляцию путем введения различных представлений (views) с определенными правами доступа ведут к существенному усложнению схемы БД и программного кода.
Наследование. Так как в РБД хранятся только данные объектов, то для корректного их воссоздания нужно хранить еще и информацию об их классах. Иерархия классов может быть представлена путем создания отдельных таблиц (таблиц класса) для каждого элемента этой иерархии. При этом для обеспечения корректного выполнения запросов данные объекта необходимо хранить и во всех таблицах родительских классов, и при обработке данного объекта надо следить за правильным использованием всех этих родительских таблиц.
Полиморфизм. Поддержка данного механизма в РБД полностью отсутствует, и чтобы обеспечить корректную реконструкцию объектов, нужно следить за наличием достаточной дополнительной информации в таблицах класса. При этом большая нагрузка ложится на прикладного программиста по обеспечению правильного использования этой информации.
Итак, мы видим, что обеспечение базовых механизмов ОО модели в реляционной базе данных связано с рядом трудностей, связанных, прежде всего, с необходимостью решать задачу корректного отображения объектной иерархии на реляционную структуру, что при построении сложных информационных систем является очень нетривиальным делом. Далее проводится описание и анализ конкретных элементов решения данной задачи.
3. Приемы объектно-реляционного отображения 3.1. Идентификация объектов Ключевым моментом в организации работы с объектами является их идентификация; при этом в рамках системы долговременного хранения различаются два вида идентификаторов: Объектные Идентификаторы в прикладной программе (OID) и Уникальные Объектные Идентификаторы Хранимых Объектов в БД (POID). Как уже отмечалось, каждый хранимый объект имеет свой постоянный POID в БД, который не совпадает с различными OID в прикладной программе при её запуске в разные моменты времени. С точки зрения рассматриваемой системы наибольший интерес представляет POID. Реализация этого элемента в приложении представляет собой отдельный класс, а в базе данных в виде набора столбцов таблицы класса. Целью POID является обеспечение возможности однозначно идентифицировать объект в приложении и его данные в реляционной таблице. При этом в реляционных таблицах POID играет роль не только ключа в таблице данного класса; он также используется в качестве ссылки на эти объекты со стороны других классов в соответствующих таблицах. Существует несколько подходов к организации объектных идентификаторов. Главным выводом при анализе этих методик является то, что POID не должен быть связан с семантикой конкретного класса. Это ограничение связано с тем, 149
что содержание или структура бизнес-смысла может меняться, что влечет большую работу по переделке существующей схемы и содержащихся в ней данных. Кроме того, наличие унифицированной структуры POID для всех классов позволяет упростить код, обрабатывающий объектно-реляционное отображение. В настоящее время общепризнанным подходом является организация POID в виде пары целых чисел – идентификатора класса объекта и идентификатора экземпляра самого объекта в рамках данного класса. То есть класс POID реализуется в следующем виде: class POID { private int cid; // идентификатор класса private int poid; // идентификатор объекта внутри класса // методы доступа . . . }
Такой подход позволяет вместе с решением основной задачи по уникальной идентификации объекта решать еще одну важную задачу – определять класс объекта. Среди схожих подходов можно упомянуть урезанные варианты данного подхода, а именно, когда уникальность идентификатора сохраняется только внутри класса (отсутствует поле cid), и когда идентификаторы уникальны внутри иерархии наследуемых классов (вместо cid имеется iid). Однако эти подходы усложняют работу программистов по отслеживанию класса объекта и, кроме того, при изменении дерева наследования приходится существенно переделывать уже существующие прикладные программы. Следующим важнейшим вопросом организации объектной идентификации является генерация новых идентификаторов во время сохранения новых объектов в БД. В следующих пунктах описываются различные стратегии решения данной задачи. При этом главным аспектом является определение именно части poid, так как информация о cid является статической (по крайней мере, на время сеанса работы приложения), и проблем с ее получением не возникает.
3.1.1. Использование SQL функции MAX Простейшим вариантом определения следующего значения объектного идентификатора является использование SQL функции MAX() на столбце poid соответствующей таблицы класса. То есть фактически при добавлении записи в таблицу класса вычисляется максимальное значение poid среди уже существующих записей и столбцу poid новой присваивается значение на единицу больше полученного. Проблема с использованием этого способа заключается в том, что возникает необходимость в выполнении большого количества SQL запросов, каждый из которых вызывает блокирование таблицы 150
и при наличии большого количества записей сам по себе занимает много времени на вычисление максимального значения идентификатора.
3.1.2. Использование служебной таблицы Для устранения последнего недостатка предыдущего способа можно определить специальную служебную таблицу, имеющую два столбца – идентификатор класса и идентификатор объекта. В этой таблице должны храниться последние значения используемых идентификаторов. Сценарий присвоения нового идентификатора в данном случае выглядит следующим образом: 1. Таблица блокируется для доступа других пользователей 2. Выбирается значение идентификатора для соответствующего класса 3. Вместо него записывается значение на единицу большее 4. Блокировка снимается Использование этого механизма обеспечивает независимость присвоения идентификатора от количества записей в таблице конкретного класса. Также унифицируется процедура получения этого идентификатора, нет необходимости в построении динамических SQL запросов с различными именами таблиц. Однако при интенсивной работе различных пользователей доступ к этой системной таблице может стать узким местом в производительности, из-за наличия блокировки во время выполнения транзакции присвоения нового poid.
3.1.3. Использование механизмов автоматических счетчиков РСУБД Большинство существующих реляционных СУБД поддерживают так называемые автоматические счетчики для значений столбцов. Реализация этих механизмов может быть различной. Например, в MS Access для поля существует отдельный тип – Counter, а в Oracle для этой цели используется отдельный механизм Sequence, и значения в целевую таблицу прописываются с помощью триггеров. То есть для приложения отпадает необходимость в отслеживании уникальности poid, эта задача полностью ложится на сервер. Однако данный подход имеет существенный недостаток. Механизм присвоения идентификаторов зависит от конкретного производителя РСУБД. Вследствие этого создаваемое приложение, во-первых, становится привязанным к конкретной РСУБД, а во-вторых, процесс присвоения идентификаторов находится вне контроля прикладного программиста, в частности, возникают проблемы с получением только что занесенного значения идентификатора, что необходимо для полноценной реализации сервиса генерации Poid (то есть объектов класса POID).
151
3.1.4. Использование хеш-функций Принципиальным моментом описываемых далее методов является увеличение производительности за счет минимизации общения с сервером и переноса алгоритмов генерации poid на сторону клиента. Наиболее естественным решением является использование специальных хэшфункций, которые используют уникальные аппаратные идентификаторы конкретной машины в комбинации с текущим временем. Такие технологии реализованы, например, в GUID компании Microsoft и в UUID компании Digital. Таким образом, уникальность значений достигается на стороне клиента, однако за это приходится расплачиваться потенциальной емкостью пространства идентификаторов. В этом случае для poid нельзя использовать простой int, нужно число гораздо большей емкости (в существующих реализациях используются 128-битные значения), так как между двумя последовательно создаваемыми идентификаторами имеется достаточно большой неиспользуемый промежуток. Кроме того, использование аппаратных данных не всегда доступно на различных платформах и создаваемые приложения нельзя сделать полностью переносимыми.
3.1.5. Метод сложного двухкомпонентного идентификатора Главной идеей данного метода является разбиение poid на две логические части: серверную и клиентскую. Серверная часть идентификатора (HIGH value) поддерживается с помощью одного из изложенных методов. В качестве оптимального способа можно использовать специальную служебную таблицу. Как уже отмечалось, недостаток этого способа состоит в частом обращении к серверу для извлечения следующего значения, что может снижать производительность. В предлагаемом методе обращение к серверу происходит очень редко, так как необходимость в извлечении серверного значения возникает только при переполнении клиентского счетчика, а фактически только при начальной загрузке приложения. Для генерации же очередного клиентского значения используется тривиальный последовательный счетчик с поддержкой соответствующей переменной в памяти приложения. Таким образом, работа метода выглядит следующим образом: 1. При загрузке приложения с сервера извлекается текущее серверное значение в соответствии с алгоритмом использования служебной таблице (см. выше). Данное значение записывается в соответствующей переменной в памяти приложения. 2. Для получения очередного идентификатора poid полученное серверное значение просто объединяется с получаемым локально клиентским значением (изначально оно равно 0). Эта процедура занимает минимум времени, но при этом обеспечивает гарантированную уникальность получаемого poid. 152
3. При переполнении клиентского счетчика (что происходит только при очень интенсивном создании новых объектов в текущем сеансе работы) с сервера извлекается новое значение серверной части и процедура сохраняется. Среди рассмотренных здесь методов генерации объектных идентификаторов метод сложного двухкомпонентного идентификатора является наилучшим с точки зрения обеспечения производительности, при этом сохраняется простота реализации. Другие способы организации объектной идентификации можно посмотреть в [12] - [14].
В новой версии виртуальной машины Java появились специальные возможности по предоставлению при определенных условиях доступа к частным и защищенным атрибутам со стороны других классов, в нашем случае со стороны сервиса долговременного хранения. Данная технология носит названия Reflection. Подробнее о ней см. документацию по Java [16]. Таким образом, для всех приводимых ниже способов при описании отображения для каждого атрибута существует альтернатива выбора между двумя методами доступа (первый применим только для общедоступных (public) атрибутов в Java 1.1 и любых в Java 2):
3.2. Хранение отдельного атрибута В данном пункте рассматривается задача хранения отдельного атрибута класса в реляционной таблице. Выше упоминалось, что базовой концепцией в решении данной проблемы является хранение значений атрибутов класса в столбцах реляционных таблиц. Ниже проводится обзор различных способов организации отображения атрибута на реляционный столбец, вернее, ситуации, которые описывает прикладной программист при задании объектнореляционного отображения атрибутов. Описание некоторых приведенных здесь методик и их реализация хорошо представлены в продукте TopLink for Java компании ObjectPeople (см. [15]). Выбор конкретного способа индивидуален для каждого атрибута и зависит от семантики и характера используемых объектных и реляционных структур.
3.2.1. Доступ к значениям атрибутов Чтобы сохранять и восстанавливать значения атрибутов конкретного экземпляра класса, необходимо обеспечить доступ к данным этих атрибутов для сервиса долговременного хранения объектов. Трудность заключается в инкапсуляции данных объекта, что запрещает доступ к частным (private) и защищенным (protected) атрибутам со стороны других непривилегированных классов приложения. Поэтому для виртуальных машин Java до версии 1.2 существовал единственный способ обеспечить доступ к нужным данным: посредством описания специальных методов доступа (accessor methods): class Person { private String name; // методы доступа public String getName() { return name; } public void setName(String name) { this.name = name; }
Прямой доступ к атрибутам
Доступ с помощью специальных методов
Выбор нужного остается на усмотрение прикладного программиста и зависит от принятых в компании соглашений по написанию кода или от структуры уже существующих Java классов.
3.2.2. Прямое отображение атрибутов элементарных типов Простейшим способом организации отображения атрибутов объекта на реляционные столбцы является отношение один к одному (direct to field mapping). То есть для хранения данных определенного атрибута класса используется определенный столбец реляционной таблицы, при этом не требуется никаких преобразований, и данные передаются непосредственными вызовами соответствующих методов JDBC. Основным моментом в этом случае является установление соответствия типов данных атрибутов приложения и типов данных реляционных столбцов. Здесь имеются в виду атрибуты элементарных типов, атрибуты-коллекции и атрибуты-ссылки на другие объекты рассматриваются в следующих пунктах. В рамках разрабатываемой нами системы ODESTOR соответствие базовых типов выглядит следующим образом: ODL Базовые типы Short Unsigned short Long Unsigned long Float Double Char Boolean Octet
};
153
154
Таблица соответствия типов данных ODL, Java и SQL разных производителей Java ANSI SQL Oracle Short Int Int Long Float Double Char Boolean Byte
SMALLINT INTEGER INTEGER DECIMAL(19) REAL DOUBLE PRECISION CHAR(1) DECIMAL(1) SMALLINT
Sybase
NUMBER(5) NUMBER(10) NUMBER(10) NUMBER(19) NUMBER NUMBER
SMALLINT INTEGER INTEGER BIGINT FLOAT DOUBLE
CHAR(1) NUMBER(1) NUMBER(3)
CHAR(1) BIT TINYINT
ODL String Date
Таблица соответствия типов данных ODL, Java и SQL разных производителей Java ANSI SQL Oracle Sybase String VARCHAR VARCHAR2(255) VARCHAR(25 5) java.sql. DATE DATE DATE Date java.sql. TIME DATE TIME Time java.sql. TIMESTAMP DATE TIMESTAMP TimeStamp
объектов). Этот бинарный поток сохраняется в столбце типа VARBINARY или LONGVARBINARY.
3.2.6. Отображение со сложной трансформацией
Прямое отображение атрибута в столбец реляционной таблицы может быть невозможно ввиду отличия типов данных (в терминах приведенной выше таблицы). В таком случае описывается отображение с преобразованием типа. То есть при указании правил отображения таких атрибутов указывается исходный и целевой тип в терминах типов Java. И тогда при работе с этими атрибутами происходит двухэтапное преобразование: к прямому отображению добавляется обычное преобразование типов Java.
Сложная трансформация данных используется в наиболее общих случаях, когда пользователь сам хочет описать собственные преобразующие методы. При этом происходит преобразование одного или нескольких атрибутов класса в несколько столбцов таблицы. В частном случае несколько столбцов реляционной таблицы используются для хранения данных одного атрибута класса. Например, в старых базах данных дата и время часто хранятся в отдельных столбцах, и необходимо использовать сложную трансформацию для отображения этих столбцов в атрибут типа java.util.Date. То есть можно использовать атрибуты не только базовых типов. Технически при описании данного типа отображения прикладной программист задает два набора методов класса. Один из них отвечает за чтение данных из предоставляемой в качестве параметра реляционной строки (JDBC ResultSet); каждому атрибуту соответствует отдельный метод. Другой набор методов служит для извлечения данных из атрибутов класса, и число этих методов соответствует числу реляционных столбцов, участвующих в отображении.
3.2.4. Отображение с конечным набором значений
3.3. Хранение коллекций и массивов
Данный тип отображения используется для преобразования типов, имеющих семантически фиксированное число значений. Дело в том, что в реляционных таблицах для экономии памяти во многих случаях используются специальные сокращения. Например, для обозначения пола человека в реляционной таблице может быть определен столбец типа char(1), в котором бывают только два значения: “M”(Male) и “F”(Female). В классе же при этом имеется атрибут типа String, в котором должны быть полные значения: “Male” и “Female”. В данном случае программист описывает специальную таблицу преобразования, по которой система долговременного хранения затем автоматически строит нужные методы трансформации.
В предыдущем пункте рассматривались аспекты работы с атрибутами базовых типов (за исключением общего случая отображения со сложной трансформацией и отображения с преобразованием в бинарный поток), но описанные способы не подходят для хранения атрибутов, содержащих коллекции этих базовых типов, то есть массивов и специальных коллекционных типов, описанных в объектной модели. Коллекции объектов рассматриваются отдельно в пункте Хранение связей между объектами. Здесь же анализируется организация хранения массивов примитивных типов.
3.2.5. Преобразование в бинарный поток
Для массивов, если в запросах к БД не используется навигация по конкретным элементам, вполне целесообразно использовать уже рассмотренный выше способ преобразования в двоичный поток с помощью технологии сериализации в Java. Этот способ в особеннсости рекомендуется для массивов byte[].
Time Timestamp
3.2.3. Отображение с преобразованием типа
Этот тип целесообразно использовать для сохранения атрибутов, содержащих данные сложной структуры и/или имеющих большой размер. Но при этом это внутреннее содержание не должно использоваться на детальном уровне в запросах к объектно-реляционной прослойке, то есть данный способ подходит для хранения «листовых» атрибутов, не участвующих в OQL запросах. Физически данное преобразование осуществляется с использованием механизма преобразования данных объекта в бинарный поток (serialization, подробнее см. выше в разделе описания подходов к хранению данных 155
3.3.1. Преобразование в бинарный поток
3.3.2. Использование таблиц для каждого типа Использование преобразования в двоичный поток невозможно, если внутренние данные массива используются для запросов к базе данных. Кроме того, этот способ неудобен для коллекций элементарных типов, в связи с 156
необходимостью преобразования в объект, поддерживающий интерфейс Serializable. Решением для хранения внутренних элементов массива может служить образование отдельных таблиц для массивов каждого из элементарных типов. То есть массивы элементарных типов рассматриваются как отдельные типы данных, и для каждого из них производится соответствующее построение нужной реляционной таблицы следующего вида: CID
POID
AID
SEQ
VALUE
Здесь CID и POID идентифицируют исходный объект, к которому относятся данные, AID обозначает идентификатор атрибута-массива в исходном классе (статическая информация), SEQ используется для идентификации порядка элементов в упорядоченных массивах. Столбец VALUE имеет тип, соответствующий базовому типу Java, для которого предназначается данная таблица. Недостаток данного способа состоит в перегруженности таблицы для систем с большим количеством данных, что отрицательно сказывается на производительности. Но зато в базе данных имеется малое количество таблиц, не загромождается схема РСУБД.
3.3.3. Использование таблиц для каждого атрибута Для устранения узкого места в таблицах коллекционных типов можно использовать отдельные таблицы для каждого атрибута массива. При этом экономится место на исключении столбца AID, и таблица для хранения данных атрибута-массива выглядит следующим образом: CID
POID
SEQ
VALUE
Однако использование данного способа при наличии сложной схемы базы данных может существенно загромождать структуру БД, усложнять семантику обработки рассматриваемых атрибутов. Реально прикладной программист должен иметь возможность выбора типа отображения конкретного атрибутамассива при задании общего объектно-реляционного отображения. Для больших по объему данных атрибутов рекомендуется использовать отдельные таблицы, а все мелкие атрибуты хранить в соответствующей таблице типа.
подходит. Во-вторых, для связей нужно обеспечить двустороннюю способность к навигации. Преобразование в двоичный поток может использоваться только тогда, когда программист уверен, что объектная ссылка является листовой и не участвует в OQL запросах. Данная ситуация, например, возникает, когда атрибут описывается как структура (struct), то есть формально в Java он является объектной ссылкой, но семантически данная структура не является объектом, у нее нет объектного идентификатора, и на нее ссылается только один объект. Данный вид отношения называется агрегацией. При этом, кроме способа преобразования в двоичный поток, такой атрибут можно хранить с помощью метода отображения с агрегацией. При таком способе внутренние данные атрибута-структуры разворачиваются и хранятся в таблице исходного класса таким образом, как если бы они были обычными атрибутами этого класса. Как уже отмечалось, ключевым моментом в отображении объектов на реляционные таблицы является представление ссылки на любой объект в виде пары целых чисел (CID, POID). Таким образом, атрибут, являющийся односторонней ссылкой, представляется в РСУБД парой столбцов, в которые записываются соответствующие идентификаторы нужного объекта-цели. Это так называемый метод внедренного ключа. Аналогично решается проблема отображения связей один-к-одному и один-комногим. В этом случае атрибуты-ссылки помещаются в обоих классах, участвующих в связи. В случае связи один-ко-многим и наличии упорядочивания элементов связи, кроме столбцов-идентификаторов в классе, отвечающем за множественную часть связи, вводится дополнительный атрибут SEQ, в котором хранится информация о порядке элементов. В результате система долговременного хранения в состоянии однозначно определить все стороны связи. Для связей один-к-одному также рекомендуется способ объединения в одну таблицу, когда данные двух классов хранятся в одной таблице, это обеспечивает очень высокую производительность. Ситуация осложняется в случае связей много-ко-многим. Здесь простым включением столбцов-идентификаторов в таблицу класса обойтись нельзя. Выходом из положения является создание для каждой связи отдельной таблицы связи. В данной таблице содержится в общем случае 6 столбцов: CID1
POID1
SEQ1
CID2
POID2
SEQ2
3.4. Хранение ссылок и связей между объектами До сих пор шла речь в основном о хранении атрибутов, содержащих данные элементарных базовых типов. При отображении объектных ссылок и связей (в терминах описанной объектной модели) возникают определенные нюансы. Вопервых, сама парадигма объектной базы данных обеспечивает навигацию по внутренней структуре объектов, поэтому простейший рассмотренный тип преобразования в двоичный поток для хранения связей и объектных ссылок не 157
Четыре из этих столбцов (CID1, POID1, CID2, POID2) отвечают за идентификацию объектов, участвующих в связи, а два (SEQ1, SEQ2) служат для определения порядка элементов в случае наличия упорядоченности на одной или двух сторонах связи. Заметим, что для общности можно использовать способ отдельных таблиц также и для связей типа один-к-одному и один-ко-многим. При этом в случае один-к-одному каждая пара столбцов 158
(CID, POID) является уникальной, а в случае один-ко-многим уникальной будет пара столбцов, соответствующих единичной стороне связи. Общим правилом для отражения связей межу объектами в реляционной базе данных является описание реляционных внешних ключей (foreign key) для обеспечения целостности данных. Поэтому поля (CID, POID) в каждой из таблиц связи и атрибуты-связи в таблицах класса объявляются внешними ключами по отношению к соответствующим таблицам класса-цели.
3.5. Отображение одного класса Итак, в предыдущих пунктах описаны способы организации хранения отдельных атрибутов различных типов. Теперь рассмотрим, как в РСУБД обеспечивается хранение объектов класса. В соответствии с общими принципами объектно-реляционного отображения понятию класса в объектной модели соответствует понятие таблицы (отношения) в реляционной модели. Однако здесь существуют устоявшиеся стратегии отражения класса на реляционные таблицы. Простейшим способом организации этого отражения является классическое табличное отображение (TABLE mapping). В этом случае все (в том числе и все унаследованные) атрибуты одного класса хранятся в отдельной таблице, и в этой таблице нет лишних атрибутов, не относящихся к рассматриваемому классу. Следующим типом отображения класса является метод подмножества (SUBSET mapping). Здесь для хранения данных определенного класса в реляционной таблице используется только часть полей (но при этом в таблице по-прежнему содержатся поля для хранения всех атрибутов данного класса). То есть в таблице имеется избыточное число полей, и для конкретного класса используется их подмножество. Такая ситуация часто возникает при работе с унаследованными базами данных. И, наконец, последний случай -- это метод надмножества (SUPERSET mapping). В отдельной таблице СУБД хранится только часть необходимой для данного класса информации, то есть данные класса хранятся в нескольких таблицах. Для скрытия этой внутренней структуры таблиц часто используются реляционные представления (view). Подробнее использование приведенной классификации рассматривается ниже, также см. [11], [17] - [25].
3.6. Отображение дерева наследования Основным моментом в построении схемы объектно-реляционного отображения является выбор стратегии отражения в РСУБД иерархии наследования классов. Проблема состоит в решении, в каких таблицах хранить атрибуты каждого класса иерархии. В различных реализациях существующих продуктов используется три варианта указанной стратегии. 159
3.6.1. Отображение с фильтрацией (filtered mapping) Метод заключается в поддержке одной глобальной таблицы (таблица иерархии) для каждой иерархии классов. Таблица строится следующим образом. Сначала в таблицу попадают все атрибуты корневого класса, а затем для каждого нового наследника в таблицу добавляются специфические для этого наследника атрибуты. Так продолжается до классов-листьев. В результате в таблице иерархии содержатся все возможные атрибуты для хранения экземпляра любого класса, принадлежащего этой иерархии. Это частный случай метода подмножества. Данный вид отображения обеспечивает максимальную производительность работы приложений и простоту реализации сервиса долговременного хранения объектов. При изменении роли объекта в иерархии нет необходимости в изменении структуры таблиц, полиморфизм обеспечивается автоматически, запросы по извлечению данных также очень простые. К недостаткам относится необходимость изменять таблицу при добавлении новых атрибутов в любом из классов потомков, такая же проблема при добавлении новых потомков. Кроме того, за счет того, что каждый из объектов в таблице иерархии использует только часть столбцов для хранения своих данных, происходит излишняя трата дискового пространства.
3.6.2. Горизонтальное отображение (horizontal mapping) Для устранения проблемы неиспользуемого пространства можно разбить глобальную таблицу иерархии на несколько таблиц. При этом в каждой такой таблице будут находиться все атрибуты конкретного класса, в том числе и унаследованные. Данная ситуация соответствует методу табличного отображения. Использование метода горизонтального отображения обеспечивает высокую эффективность и простоту запросов по манипулированию данными. Однако ему присущи определенные недостатки. Во-первых, при модификации класса, у которого есть потомки, необходимо модифицировать и все таблицы этих потомков. Во-вторых, изменение места объекта в иерархии классов влечет за собой необходимость в перемещении данных из одной таблицы в другую и присваивании нового объектного идентификатора. В-третьих, поддержка полиморфизма затруднительна ввиду необходимости обработки нескольких таблиц для извлечения нужных данных.
3.6.3. Вертикальное отображение (vertical mapping) Данный вид отображения отражает иерархию наследования наиболее естественным образом. Как и при горизонтальном отображении для каждого класса определяется отдельная таблица, но в этой таблице хранятся только атрибуты, специфические для данного класса. Между таблицами в иерархии 160
определяются внешние ключи, по которым в дальнейшем происходит соединение таблиц (join) для манипулирования содержащимися в них данными. Рассматриваемый способ является частным случаем метода надмножества. Вертикальное отображение очень хорошо поддерживает механизм полиморфизма. При извлечении данных определенного типа в таблице хранятся данные не только данного класса, но и всех потомков. Модификация классов, входящих в иерархию, теперь происходит с наименьшими усилиями – нужно изменить только одну таблицу. К недостаткам способа относится сложные механизмы манипулирования данными объектов. Для чтения всех требуемых данных необходимо обращаться к нескольким таблицам вплоть до корневой. Проблему можно частично решить введением реляционных представлений для имитации таблиц класса, как при использовании горизонтального отображения.
3.7. Замечания по производительности и оптимизации Одним из основных требований практически к любому приложению является высокая производительность. Поэтому вопросы оптимизации работы сервиса долговременного хранения является важнейшим аспектом при построении таких систем. В данном пункте приводятся основные выводы и фундаментальные принципы обеспечения высокой скорости объектнореляционных преобразований. Способы повышения производительности можно разделить на два принципиальных раздела. Первый это повышение эффективности работы реляционного сервера, а второй это оптимизация запросов к серверу со стороны клиента, в частности, минимизация сетевого трафика и числа обращений к серверу (подробнее см. [26]).
3.7.1. Оптимизация сервера Одним из самых важных вопросов является правильный выбор одного из вариантов объектно-реляционного отображения в каждом конкретном случае, понимая, что всегда существует баланс между производительностью и гибкостью способа. В этом случае можно дать следующие рекомендации. При отображении дерева наследования наиболее производительными являются варианты отображения с фильтрацией и горизонтального отображения. Метод вертикального отображения, будучи наиболее очевидным и соответствующим реальной объектной схеме, может значительно снижать скорость работы. По возможности нужно скрывать нормализованные реляционные таблицы с помощью использования реляционных представлений. При отображении связей нужно избегать отдельных таблиц связи и применять их только для связей типа много-ко-многим, когда они необходимы. Для связей один-к-одному настоятельно рекомендуется способ объединения в одну 161
таблицу. Для связей один-ко-многим следует применять метод внедренных ключей. При проектировании схемы базы данных необходимо также выполнить классические настройки сервера. Основным аспектом здесь является определение индексов и внешних ключей. Индексы следует определять для столбцов, которые часто используются в условиях запросов, и для столбцов внешних ключей. Для достижения максимального результата можно использовать особенности конкретного реляционного сервера, в частности, хранимые процедуры, асинхронные и пакетные запросы. Также нужно размещать таблицы, к которым осуществляется одновременный доступ, на физически разных пластинах жесткого диска или вообще на разных дисках.
3.7.2. Оптимизация клиента При оптимизации клиентской части сервиса долговременного хранения главной задачей является минимизация числа обращений к серверу. Достигается это с помощью механизма кэширования. То есть при чтении данных объекта они сохраняются на стороне клиента в специальной системной области сервиса долговременного хранения и при повторных обращениях данные берутся уже из этой области. Кроме того, существенным моментом в организации кэширования является создание в памяти приложения виртуального образа часто используемых объектов. При этом кардинальное ускорение происходит не только при чтении отдельного объекта, но и при выполнении навигации по объектным связям. Авторы [26] провели исследование производительности различных ОО приложений, использующих в качестве хранилища РСУБД. По результатам этих тестов утверждается, что хорошо спроектированное ОО приложение, использующее объектно-реляционное отображение, обладая всеми преимуществами использования ООБД, не только не уступает в производительности приложению, написанному на основе API конкретной РСУБД, но и превосходит его за счет использования клиентского кэширования объектных данных. Поэтому вопросы оптимизации объектно-реляционных сервисов являются действительно очень важными.
4. Заключение Результаты проведенных в данной работе исследований были успешно применены при работе над проектом реализации системы ODESTOR. Данная система относится к классу объектно-реляционных систем, предоставляющих интерфейс чисто объектной БД над реляционной СУБД. Такие системы попрежнему очень актуальны по причине наличия большого числа унаследованных реляционных баз данных. Разными коллективами производились исследования производительности систем этого класса. В итоге основным выводом стало, что главным аспектом обеспечения высокой 162
производительности является именно грамотная и настроенная под конкретные нужды архитектура объектно-реляционного отображения. При этом хорошо спроектированные системы ОР отображения обеспечивают даже бόльшую производительность, чем классические решения на основе использования библиотек низкого уровня. Этот вывод строится на двух аспектах, во-первых, хорошо построенная система ОР отображения проигрывает лишь при непосредственных операциях обмена данными, а во-вторых, за счет использования централизованного управления кэшированием объектных данных на уровне приложения во многих случаях получается порядковый рост производительности. В итоге в реальных условиях система ОР отображения показывает в среднем бόльшую производительность. В связи с этим проведенные в данной работе исследования оптимальной схемы ОР отображения приобретают дополнительную актуальность. Литература 1. “Object-Oriented Design”. G. Booch. 1992, Behjamin/Cummings Publishing Company. 2. “Основы современных баз данных”. Кузнецов С.Д. Информационно-аналитические материалы Центра Информационных Технологий. www.citforum.ru. 3. “Тенденции в мире систем управления базами данных”. Кузнецов С.Д. Информационноаналитические материалы Центра Информационных Технологий. www.citforum.ru. 4. “Object Database vs. Object-Relational Databases”. Steve McClure. IDC Bulletin. www.cai.com/products/jasmine/analyst/idc/14821E.htm. 5. “Extending Relational Databases”. Martin Rennhackkamp. DBMS Magazine, December 1997 6. “Why Use an ODBMS?”. Poet Software White Paper. www.poet.com/products/oss/white_papers/rel_vs_obj/rel_vs_obj.html 7. “Объектно-ориентированные базы данных: среда разработки программ плюс хранилище объектов”. Андреев А.М., Березкин Д.В., Кантонистов Ю.А. www.inteltec.ru/publish/russian/articles/objtech/oodbms_o.htm 8. “Манифест систем объектно-ориентированных баз данных”. М. Аткинсон, Ф. Бансилон, Д. ДеВитт, К. Диттрих, Д. Майер, С. Здоник. Системы Управления Базами Данных, # 4/95, стр. 142-155. 9. .“The Object Database Standard: ODMG 2.0”. R.G.G. Cattell, Douglas K. Barry, 1997. www.odmg.org 10. .“The Object Data Standard: ODMG 3.0”. R.G.G. Cattell, Douglas K. Barry, 2000. www.odmg.org 11. “Mapping Objects To Relational Databases”. Scott W. Ambler. AmbySoft White Paper. http://www.ambysoft.com/mappingObjects.pdf 12. “Object Identity”. Khoshafian S. , Copeland G. OOPSLA Conference, 1986 13. “A rigorous model of object reference, identity and existence”. Kent W. JOOP June, 1991. 14. “Storage management for persistent complex objects”. Khoshafian, Franklin, Carey. Information Systems, vol.15, no.3, p. 303-320, 1990. 15. “TopLink for Java Documentation”. Object People. www.objectpeople.com/toplink/java/java.htm 16. “Java 2 Platform, Standard Edition Documentation”. java.sun.com/docs/index.html 17. “Object Relational Mapping Strategies”. Visual Business Sight Framework documentation. Mapping tool guide. www.objectmatter.com 18. “Foundations of Object Relational Mapping”. Mark L. Fussell. www.chimu.com 19. “Object-relational Access Layers – a Roadmap, Missing Links and More Patterns”. Wolfgang Keller. EPLoP’98.
163
20. “Patterns for Object/Relational Access Layers”. Wolfgang Keller. www.objectarchitects.de/ObjectArchitects/orpatterns/index.htm 21. “Integrating Objects with RDBMs”. GemStone White Paper. www.gemstone.com/products/s/papers_integrate.html 22. “Crossing Chasms: A Pattern Language for Object-RDBMS Integration (The Static Patterns)”. Kyle Brown and Bruce G. Whitenack. www.ksccary.com/Articles/ObjectRDBMSPattern/ObjectRDBMSPattern.htm 23. “A Pattern Language for Relational Databases and Smalltalk”. Kyle Brown and Bruce Whitenack. www.ksccary.com/Articles/PatternLangForRelationalDB/PatternLangForRelationalDB.htm 24. “Baker’s dozen”. Tim Ottinger. www.oma.com/ottinger/BakersDozen.html 25. “Cetus Links on Objects and Components: Object Relational”. WWW links collection. http://www.cetus-links.org/oo_db_systems_3.html 26. “Architecting Object Applications for High Performance with Relational Databases”. S. Agarwal, C. Keene, A.M. Keller. Persistence Software White Paper. 27. “Проблемы организации объектно-ориентированного доступа к реляционным базам данных”. К.В. Антипин, В.В. Рубанов. Труды Института Системного Программирования (сдано в печать), вып. 2. 2000 г. 28. “Объектно-ориентированное окружение, обеспечивающее доступ к реляционным СУБД”. В.П. Иванников, С.С. Гайсарян, К.В. Антипин, В.В. Рубанов. Труды Института Системного Программирования (сдано в печать), вып. 2. 2000 г.
164
Ядро объектно-реляционной системы ODESTOR В.В. Рубанов, М.А. Миткевич, Д.А. Марковцев, А.И. Гриневич Аннотация. В статье рассматриваются детали реализации ядра системы объектнореляционного отображения ODESTOR. Описание основывается на схеме работы с системой, рассматриваются аспекты работы с каждым модулем для случаев прямого и обратного проектирования (forward и reverse engineering).
1. Введение В данной статье рассматривается технология работы с системами объектнореляционных трансформаций на примере ядра программной среды ODESTOR (Object Database Engine System on the Top Of Relations). Актуальность использования таких систем обусловлена, с одной стороны, наличием на рынке развитых объектно-ориентированных технологий, причем в области долговременного хранения информации все большее признание находят объектно-ориентированные базы данных (ООБД), а с другой стороны, существованием в мире массы унаследованных систем на основе реляционных систем (РСУБД). В результате возникает необходимость в построении системы объектно-реляционной трансляции, которая представляет собой комплекс утилит и библиотек, обеспечивающего полный цикл разработки прикладного программного обеспечения и эмулирующего для прикладного программиста работу ООБД, но использующего в качестве реального хранилища данных реляционную базу данных. Центральным моментом статьи является описание объектно-реляционного ядра такой системы. Рассматриваются задачи как прямого (Forward engineering), так и обратного (Reverse Engineering) проектирования. В первом случае проект пишется с чистого листа, и реляционные таблицы могут быть сформированы системой автоматически. Во втором случае уже имеется заданная структура реляционных таблиц, и нужно с помощью специальных средств описать, как объектные данные будут отражаться в данных таблицах. В настоящее время в рамках проекта ODESTOR реализованы основные модули объектно-реляционной трансляции исходных метаданных и модули базового интерфейса ООБД. Планируются работы по расширению возможностей этого интерфейса и повышению удобства использования системы со стороны прикладного программиста 165
(графические утилиты). Работа поддерживалась грантами РФФИ и выполняется в рамках внутреннего проекта ИСП РАН. Начальные сведения о проекте можно найти в [27] – [28]. В первом разделе статьи описываются методы создания объектной модели целевой задачи. Следующий этап, обсуждаемый во втором разделе, -- это описание реляционной схемы прикладной системы. Наконец, в третьем разделе рассматривается процесс собственно определения способов объектнореляционного отображения, заданных на первых этапах. Описываются различные аспекты работы системы, как с точки зрения пользователя, так и с точки зрения программиста. В заключение приводится общая схема работы с системой, отражающая полный цикл создания приложений, в которых для хранения объектных данных используются возможности рассматриваемой системы.
2. Создание объектной модели В процессе разработки прикладной части, использующей возможности рассматриваемой системы, в первую очередь возникает задача описания схемы классов целевого приложения. На основании данной схемы формируются описания метаданных в реляционной базе данных, которые будут затем использоваться при работе приложения (run-time). Кроме того, данные об объектной схеме используются системой для формирования шаблонов классов на целевом языке разработки (в данном случае Java). Также при решении задачи прямого проектирования метаданные используются для формирования SQL-скриптов для создания таблиц, в которых будут храниться данные сохраняемых объектов. Общая схема работы по обработке объектной схемы классов приложения выглядит следующим образом: Файлы конфигурации
Метаданные
OdlCompiler. cfg
dbtypes. map
odl.cfg
Программа Файлы
Файлы исходной информации
Map Builder
Java файлы ODL файл
166
UML файл
DDL SQL
Файлы Java JDBC Driver целевой РСУБД
РСУБД
3.1 Файлы конфигурации Программа Map Builder использует в своей работе ряд конфигурационных файлов, которые играют центральную роль в работе системы. В файле odl.cfg указываются опции по формированию скелетонов Java классов на основе описания объектной схемы на языке ODL или UML. Пример содержания файла имеет следующий вид: [Edit] parenthnewline = false parenthspace = true identspaces = 4 tokendelimeter = space [Class] classmemberorder = standard constructors = true attrmodifier = package opermodifier = public structmodifier = public exceptmodifier = public interfmodifier = package
Параметры в разделе Edit задают форматирующие опции, указывающие, в частности, следует ли переносить открывающую фигурную скобку на новую строку, использовать пробел для отделения фигурных скобок от текста, количество пробелов в табуляции и тип разделителя лексем. В разделе Class указываются опции, связанные с семантикой генерации кода. Эти опции указывают, нужно ли генерировать скелеты конструкторов, менять порядок следования членов класса в соответствии со стандартной организацией, задают модификаторы для различных элементов языка Java. Графический интерфейс по настройке этого файла выглядит следующим образом:
167
Файл dbtypes.map определяет соответствие элементарных типов данных, используемых в объектной модели, «родным» реляционным типам данных конкретных РСУБД (в данное время поддерживаются Oracle, Sybase, MS SQL Server и MS Access (для быстрого тестирования)). Эта информация используется при генерации SQL файлов описания реляционной схемы при решении задачи прямого проектирования. Пример раздела файла для Oracle выглядит следующим образом: [Oracle] short = NUMBER(5) ushort = NUMBER(10) long = NUMBER(10) ulong = NUMBER(19) float = NUMBER double = NUMBER char = CHAR(1) boolean = NUMBER(1) octet = NUMBER(3) string = VARCHAR2(255) date = DATE time = DATE timestamp = DATE
168
Наконец, в файле ODLCompiler.cfg хранятся настройки параметров самой программы Map Builder, в частности, тип целевой РСУБД, строка соединения JDBC для нее, имя пользователя и пароль. Содержимое файлов odl.cfg и ODLCompiler.cfg можно редактировать как с помощью текстовых редакторов, так и с помощью визуальных настроек внутри программы Map Builder (см. рисунки):
3.2 Файлы исходной информации Исходная информация о структуре объектной схемы приложения может быть предоставлена системе одним из трех способов:
Описание на языке Object Definition Language (ODL)
Описание в виде файлов Java
Описание в виде диаграмм UML
Первый способ рекомендован ODMG и описан в соответствующем стандарте. Пример кода на этом языке в окне программы имеет следующий вид:
169
То есть на данном языке описываются все элементы объектной модели с использованием текстовой нотации, аналогичной языку Java. Более подробно об ODL см. [9] - [10]. Второй и третий способы продиктованы практическими потребностями. В частности, регенерация файлов Java необходима для обеспечения цикличности разработки, то есть схема разработки может выглядеть следующим образом. Начальная объектная схема описывается на ODL, затем с использованием Map Builder формируются файлы Java. После этого они редактируются программистом и, чтобы отразить внесенные изменения в объектной схеме БД, опять подаются на вход Map Builder. Так обеспечивается цикличность использования Java кода. Наряду с файлами Java, в соответствии с последней версией стандарта [10], используются специальные дополнительные конфигурационные файлы для отражения информации объектной модели ODMG, которая отсутствует в Java. В частности, конфигурационный файл содержит такую информацию о свойствах хранимых классов, как собственно свойство хранимости (persistency), описания локальных (transient) атрибутов и расширенную информацию о связях.
170
// ERROR } catch(SQLException e){ // ERROR }
3.3 Структура метаданных объектной модели В результате обработки объектной модели соответствующим компилятором (ODL, Java или UML) в системе формируется описание объектной схемы приложения. Для внутреннего представления используются специальные классы. Эта система классов с префиксом Sch предназначена для представления структуры классов приложения, описанной в исходных файлах (метаданные приложения). Корневым классом является SchemaDcl. Объект данного класса можно создать на основе объекта спецификации или загрузить из указанной базы данных. Первый способ задействуется в модуле компиляции программы Map Builder и для модуля ODL выглядит следующим образом:
Или: try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); Connection conn = DriverManager.getConnection( “jdbc:odbc:ODESTOR”, “userID”, “passwd”); SchemaDcl schema = new SchemaDcl(conn, schID); } catch(ClassNotFoundException e){ // ERROR } catch(SQLException eSql){ // ERROR }
import RU.ISPRAS.ODLComp.*; // инициализация входного потока ODLParser parser; try { parser = new ODLParser(new FileReader(“inputfile.odl”)); } catch (java.io.FileNotFoundException e) { // ERROR }
Созданный объект используется как классами самой системы ODESTOR, так и прикладными классами для доступа к метаданным приложения на основе навигации по атрибутам. Структура и связи данных классов представлены на рисунке:
// генерация ODL спецификации входного файла ODLSpec odlSpecification; try { odlSpecification = parser.getSpecification(); } catch (ParseException e) { // ERROR } // инициализация схемы классов SchemaDcl schema = new SchemaDcl(odlSpecification);
Эти метаданные сохраняются в РСУБД в виде системных таблиц с префиксом Sch. Такие таблицы формируются программой Map Builder при первичной обработке информации об объектной схеме с помощью прямого соединения с базой данных.
cName
Другой способ используется во время выполнения прикладных программ. При этом данные загружаются из специальных таблиц РСУБД. Для данного способа существуют два конструктора – задание явной строки соединения JDBC, пользователя и пароля, либо использование уже созданного соединения JDBC (до вызова конструктора нужный JDBC драйвер должен быть загружен):
String typeStruct
SchStructDcl
String
attrName
cAttrs
sStructs
SchAttributeDcl SchemaDcl
cAttrs
cStructs
attrType SchBaseType
sClasses
try {
SchClassDcl
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); SchemaDcl schema = new SchemaDcl(“jdbc:odbc:ODESTOR”, “userID”, “passwd”, schID); } catch(ClassNotFoundException e){
cName
String
cExtends
String
typeSubType
relName cRelations
171
172
SchRelationDcl
typeConst
int
typeName cName
String
typeArraySizes String
relCollType
int
relTarget
String
relInvAttr cName
String
int
3.4 Таблицы для хранения данных объектов При решении задачи прямого проектирования программа Map Builder генерирует объектно-реляционное отображение на основании собственных правил, обеспечивающих максимальную производительность прикладных программ. Результатом такого отображения является набор SQL файлов, в которых содержатся описательные операторы DDL. Используя эти файлы, можно с помощью средств РСУБД (например SQL*Plus для Oracle) создать все необходимые таблицы для хранения данных прикладных объектов.
3. Создание реляционной модели Следующим этапом в работе по созданию приложения пользователя при решении задачи обратного проектирования является создание реляционной схемы, в которой должны храниться данные объектов. Пользователь должен описать таблицы в существующей реляционной базе данных, чтобы система могла построить соответствующий программный код для трансляции объектных данных в реляционные поля. Схема работы с данным модулем выглядит следующим образом: И зменения в схеме Реляционная схема
Map Builder
Ручные данные
Проектиров щик
SQL
Данные об имеющейся схеме РСУБД РСУБД
Существует два основных способа задать данные о реляционной схеме – вручную или автоматически из РСУБД. Также можно применять комбинации этих способов.
предоставляется интерфейс, аналогичный визуальным средствам описания таблиц РСУБД (Enterprise Manager для Oracle, Sybase Central для Sybase SQL Anywhere). В нем пользователь задает имя для каждой таблицы и все ее столбцы с указанием типов данных. Также задается информация о первичных ключах. На основании этих данных система может сформировать файлы на языке SQL, в которых содержатся все необходимые операторы для создания описанных таблиц в конкретной базе данных. Данный способ можно использовать также для собственного описания реляционной схемы при решении задачи прямого проектирования, когда программист сам хочет описать структуру реляционных таблиц для хранения объектных данных, то есть когда его не устраивает автоматическое отображение, генерируемое программой Map Builder.
3.2 Автоматическое создание из существующей базы Для первоначального построения реляционной схемы можно использовать автоматическую загрузку данных непосредственно из работающей РСУБД. Для этого программист задает источник данных, то есть строку соединения и, возможно, некоторые параметры фильтра. После этого система связывается с указанной РСУБД и извлекает существующую схему таблиц. Пользователь имеет возможность отредактировать эту схему с помощью той же программы, что и при ручном вводе. На основании созданной схемы можно сформировать файлы SQL, с помощью которых можно внести все необходимые изменения в РСУБД, в частности, добавить в таблицы столбцы для хранения объектных идентификаторов. При выполнении этой процедуры возникает отдельная задача заполнения столбцов значениями, уже существующими в базе данных. Эта проблема решается с помощью специальной полуавтоматической процедуры.
4. Описание объектно-реляционного отображения Итак, после завершения предыдущих этапов при решении задачи обратного проектирования в системе формируются две схемы данных: объектная и реляционная. С одной стороны, формально описана внутренняя структура и взаимосвязи прикладных объектов, и, с другой стороны, описаны реляционные таблицы с указанием столбцов, в которых должны храниться эти объектные данные. Теперь нужно описать объектно-реляционное отображение между этими схемами. Загруженные данные отображаются в главном окне программы отображения модуля Map Builder подобно окну, используемому в продукте TopLink for Java:
3.1 Описание вручную При отсутствии непосредственного доступа к работающей РСУБД реляционную схему можно описать полностью вручную. Для этого в системе 173
174
Результатом работы по созданию объектно-реляционного отображения является набор специальных данных – объектно-реляционные метаданные. Эти данные должны в конечном итоге сохраняться в служебных таблицах РСУБД. Определения этих таблиц в базе данных могут быть сформированы непосредственно из программы Map Builder или записаны в отдельные файлы SQL для последующей обработки средствами РСУБД. После создания полного объектно-реляционного отображения система формирует SQL файлы для заполнения этих служебных таблиц конкретными данными. В дальнейшем информация об этих ОР метаданных будет загружаться во время работы приложения, и использоваться как средствами сервиса долговременного хранения объектов, так и прикладными программистами для обеспечения возможности создавать оптимизированные программы. Кроме ОР метаданных, Map Builder формирует Java код, который собственно и будет обеспечивать объектно-реляционную трансляцию данных во время работы прикладных программ. После создания этого кода программист имеет возможность изменить некоторые функции для обеспечения оптимизации работы приложений.
Далее программист последовательно выбирает класс в левом списке и таблицу (таблицы) в правом. Для каждой такой комбинации открывается окно, где уже для каждого из атрибутов класса описываются правила, по которым записываются и считываются данные из РСУБД. Возможные способы отображения рассматриваются в [27]-[28]. Структура работы выглядит следующим образом: Проектиров щик
Прав ила объектно -реляционного отображения
5. Общая схема работы Итак, сценарий и основные принципы работы с описываемой системой обеспечения объектно-реляционной трансляции ODESTOR выглядят следующим образом: 1. Нужно описать структуру классов приложения, в котором будет использоваться долговременное хранение объектов. Вообще это можно сделать тремя способами: описать объектную схему в соответствии со стандартом ODMG на специальном декларативном языке ODL
Java файлы
Map Builder
SQL
Реляционная схема
описать схему, используя технологии объектного моделирования (UML)
описать собственно Java файлы приложения
Важное значение имеет здесь возможность подачи на вход системы набора файлов целевого языка программирования Java в качестве описания объектной схемы. Это обеспечивает возможность циклической работы с системой. Например, можно описать объектную схему на ODL, затем система сгенерирует на основе этой схемы файлы Java, их можно поправить и подать обратно на вход системы для учета внесенных изменений. В любом случае, результатом работы этого этапа является представленная во внутреннем формате информация об объектной схеме прикладной задачи. На основе этих данных в дальнейшем в РСУБД формируются специальные служебные
ОР Метаданные
Объектная схема
РСУБД
175
176
таблицы, куда записываются метаданные для последующего извлечения на этапе работы приложения. 2. Вторым этапом является создание реляционной модели. Эта модель формируется или вручную, или на основе имеющейся РСУБД, или на основе комбинации этих способов. Результатом этапа является информация о структуре реляционных таблиц, в которых в дальнейшем должны сохраняться данные объектов. Существует возможность сформировать SQL файлы для отражения этой информации в РСУБД (создание и модификация таблиц). 3. Наконец, третьим этапом является описание правил, по которым будут транслироваться объектные данные в реляционные таблицы и обратно. Это так называемая информация об объектно-реляционном отображении. Третий этап является ключевым в процессе описания данных приложения. Ему следует уделить особое внимание для обеспечения оптимальной производительности системы. Фактически, на основе описанной на данном этапе информации будет сформирован программный код, обеспечивающий непосредственную загрузку и запись данных в РСУБД. 4. После того, как на предыдущих трех этапах описана вся необходимая информация, система готова сформировать все необходимые компоненты для обеспечения дальнейшей работы приложения. Сценарий в этом случае такой: Система генерирует файлы Java, в которых отражена структурная информация о классах (описаны все атрибуты, связи и методы) и реализован код объектно-реляционной трансляции.
Затем программист кодирует собственно семантику работы программы. При этом для хранения своих объектных данных он пользуется предоставленной библиотекой. То есть он имеет возможность писать код таким образом, как если бы при разработке использовалась чисто объектная база данных. При этом, как уже отмечалось, существует возможность циклического возврата к первым этапам и последовательной отладки кода.
После завершения кодирования Java файлы окончательно компилируются стандартным компилятором Java и полученные class файлы являются конечным программным продуктом, который можно использовать по назначению.
6. Заключение Данная статья описывает существующую на данный момент схему работы с системой ODESTOR. Обсуждаются некоторые основные модули системы, отвечающие за начальную компиляцию, представление метаданных приложения и описание объектно-реляционного отображения. Первый модуль отвечает за компиляцию информации об объектной схеме задачи. В настоящее 177
время реализованы компиляторы ODL и Java. В дальнейшем планируется добавить входные форматы UML и XML. Второй модуль обеспечивает обработку данных о реляционной схеме. И, наконец, третий модуль предоставляет возможности по описанию правил объектно-реляционного отображения. Все эти модули объединены в единую интегрированную среду разработки Map Builder. Также разработаны отдельные модули проекта, выходящие за рамки данной статьи и относящиеся ко всему проекту в целом: реализация ODMG коллекций и OQL выборок над ними, диспетчер объектных транзакций и другие модули, обеспечивающие ODMG интерфейс ООБД. Детально проработана полная архитектура и алгоритмы взаимодействия модулей и действия всей системы в целом. В дальнейшем планируется доработать систему для работы в распределенной среде с наличием большого количества пользователей с применением технологий CORBA и XML. Также планируется разработка улучшенных графических средств администрирования и работы с системой. Система ODESTOR является свободно распространяемой (freeware), и она обеспечивает полный интерфейс ООБД, специфицированный ODMG. Литература 1. “Object-Oriented Design”. G. Booch. 1992, Behjamin/Cummings Publishing Company. 2. “Основы современных баз данных”. Кузнецов С.Д. Информационно-аналитические материалы Центра Информационных Технологий. www.citforum.ru 3. “Тенденции в мире систем управления базами данных”. Кузнецов С.Д. Информационноаналитические материалы Центра Информационных Технологий. www.citforum.ru 4. “Object Database vs. Object-Relational Databases”. Steve McClure. IDC Bulletin. www.cai.com/products/jasmine/analyst/idc/14821E.htm 5. “Extending Relational Databases”. Martin Rennhackkamp. DBMS Magazine, December 1997 6. “Why Use an ODBMS?”. Poet Software White Paper. www.poet.com/products/oss/white_papers/rel_vs_obj/rel_vs_obj.html 7. “Объектно-ориентированные базы данных: среда разработки программ плюс хранилище объектов”. Андреев А.М., Березкин Д.В., Кантонистов Ю.А. www.inteltec.ru/publish/russian/articles/objtech/oodbms_o.htm 8. “Манифест систем объектно-ориентированных баз данных”. М. Аткинсон, Ф. Бансилон, Д. ДеВитт, К. Диттрих, Д. Майер, С. Здоник. Системы Управления Базами Данных, # 4/95, стр. 142-155. 9. .“The Object Database Standard: ODMG 2.0”. R.G.G. Cattell, Douglas K. Barry, 1997. www.odmg.org 10. .“The Object Data Standard: ODMG 3.0”. R.G.G. Cattell, Douglas K. Barry, 2000. www.odmg.org 11. “Mapping Objects To Relational Databases”. Scott W. Ambler. AmbySoft White Paper. http://www.ambysoft.com/mappingObjects.pdf 12. “Object Identity”. Khoshafian S. , Copeland G. OOPSLA Conference, 1986 13. “A rigorous model of object reference, identity and existence”. Kent W. JOOP June, 1991 14. “Storage management for persistent complex objects”. Khoshafian, Franklin, Carey. Information Systems, vol.15, no.3, p. 303-320, 1990 15. “TopLink for Java Documentation”. Object People. www.objectpeople.com/toplink/java/java.htm 16. “Java 2 Platform, Standard Edition Documentation”. java.sun.com/docs/index.html 17. “Object Relational Mapping Strategies”. Visual Business Sight Framework documentation. Mapping tool guide. www.objectmatter.com 18. “Foundations of Object Relational Mapping”. Mark L. Fussell. www.chimu.com
178
19. “Object-relational Access Layers – a Roadmap, Missing Links and More Patterns”. Wolfgang Keller. EPLoP’98. 20. “Patterns for Object/Relational Access Layers”. Wolfgang Keller. www.objectarchitects.de/ObjectArchitects/orpatterns/index.htm 21. “Integrating Objects with RDBMs”. GemStone White Paper. www.gemstone.com/products/s/papers_integrate.html 22. “Crossing Chasms: A Pattern Language for Object-RDBMS Integration (The Static Patterns)”. Kyle Brown and Bruce G. Whitenack. www.ksccary.com/Articles/ObjectRDBMSPattern/ObjectRDBMSPattern.htm 23. “A Pattern Language for Relational Databases and Smalltalk”. Kyle Brown and Bruce Whitenack. www.ksccary.com/Articles/PatternLangForRelationalDB/PatternLangForRelationalDB.htm 24. “Baker’s dozen”. Tim Ottinger. www.oma.com/ottinger/BakersDozen.html 25. “Cetus Links on Objects and Components: Object Relational”. WWW links collection. http://www.cetus-links.org/oo_db_systems_3.html 26. “Architecting Object Applications for High Performance with Relational Databases”. S. Agarwal, C. Keene, A.M. Keller. Persistence Software White Paper. 27. “Проблемы организации объектно-ориентированного доступа к реляционным базам данных”. К.В. Антипин, В.В. Рубанов. Труды Института Системного Программирования, вып. 2. 2000 г. 28. “Объектно-ориентированное окружение, обеспечивающее доступ к реляционным СУБД”. В.П. Иванников, С.С. Гайсарян, К.В. Антипин, В.В. Рубанов. Труды Института Системного Программирования, том. 2. 2000 г.
179
180