Цена ошибки
Продолжим. Наша текущая цель - на примере аттракторов достичь равенства результатов в SimInTech и ВКПа. Делать мы это будем приведением моделей к наиболее универсальной базе - используя языки программирования (ЯП). В ВКПа уже есть реализация на С++. Осталось создать ее в SimInTech. В таком виде они будут соответствовать друг другу. А в идеале, если языки одинаковые, даже просто совпасть. Все это должно способствовать равенству результатов. И на этом пути, кроме освоения внутреннего языка программирования SimInTech, особых препятствий не предвидится.
Блоки на внутреннем ЯП в SimInTech создаются на базе блока PL - блок библиотеки Динамические. Напомним реализацию модели аттрактора Лоренца на стандартных библиотечных блоках. Она приведена на рис. 1. Далее мы ее будем называть исходной схемой. Часть ее вместе с соответствующим кодом на языке программирования SimInTech (LangBlock22) представлена на рис. 2.
Три подобных блока формируют модель аттрактора. Однако, их соединение приводит к сообщению об алгебраических петлях. И это сразу вызывает вопросы, поскольку блоки содержат интеграторы, которые фактически выполняют и роль задержек (или элементов памяти). Но нам ни чего не остается делать, как, проделав стандартные манипуляции по устранению алгебраических петель, прийти к схеме на рис. 3. Результат ее последующего тестирования приведен на рис. 4.
Сравним работу созданной и исходной моделей (графики тестирования последней приведены на рис. 5). Их отличие достаточно очевидно. При этом, кроме формы сигналов, обращают на себя внимание моменты завершения колебательных процессов. Для созданной схемы они заканчиваются примерно на 4 сек позже (ср. рис.4 и рис.5).
Напомним, что в MATLAB тестирование аналогичной схемы (ее результаты представлены на рис. 6) совпадает в точности с результатами в SimInTech. Во многом это определяется идентичностью выбранных методов интегрирования. В SimInTech и MATLAB они представлены методом Эйлера, а в реализации на ЯП это метод трапеций (см. код процедуры Integr на рис. 2). Сразу скажем, что это совпадение, как мы еще убедимся, ни о чем не говорит.
В целях минимизации влияния ядра среды исполнения на работу модели сведем все блоки на ЯП в один блок. Код такого блока приведен на рис. 7, а соответствующая ему схема на рис. 8. Здесь уже не пришлось устранять алгебраические петли. Хотя они ни куда не делись, а просто переместились во внутрь блока. После прогона модели мы получили результат, который представлен на рис. 9. И это еще один вариант отличный от предыдущих. Не считаете ли вы, что при прочих равных условиях такого разнообразия уже многовато для одного (!) аттрактора?
Но, пойдем дальше. Введем буферизацию выходных сигналов, имитируя тем самым параллелизм вычисления уравнений. Такой код приведен на рис. 10, а результат его тестирования на рис. 11. Обратим внимание, что он полностью совпадает с тестом на рис. 4. Таким образом, эффект от буферизации аналогичен разрыву алгебраических петель. Запомним это.
Не столько потеряв всякий страх, а скорее ради любопытства, введем буферизацию в схему, с которой все начиналось - схему на рис. 3. Такая схема показана на рис. 12, а результаты ее работы на рис. 13. Введение подобного "буферного блока", копирующего сигналы со входов на выходы, так и не устранило проблему алгебраических петель. Их все равно пришлось "рвать". Правда, если элементы, разрывающие алгебраические петли, спрятать во внутрь блока, то, скорее всего, тогда не пришлось бы их пристраивать в саму схему.
Итак, что мы имеем на текущий момент? Результаты тестирования многоблочной реализации на рис. 12 полностью совпали с результатами схем на рис. 3 и рис. 8. И это несмотря на существенное их отличие друг от друга. Но, видимо, есть что-то, что делает их работу эквивалентной. И это, с одной стороны, элементы, устраняющие алгебраические цепи, а, с другой, - буферизация сигналов в явном виде.
Примечание. Эксперимент с заменой блоков интегрирования на блоки дифференцирования тут же привел к сообщению об алгебраических петлях. А это вызывает вопросы, т.к. аналогичная процедура, проведенная со схемой в MATLAB, к такому "эффекту" не привела.
Но вернемся к теме параллелизма, вокруг которой развернулись основные споры. Достаточно ли сортировки блоков или необходима инерционность, а в простейшем случае - буферизация? Существует ли доказательство того, что сортировка не эквивалентна параллельному исполнению блоков? С появлением реализаций блоков на ЯП, у нас теперь есть все, чтобы что-то доказать или это же опровергнуть.
На рис. 14 приведена схема, которая позволит провести эксперимент, который должен разрешить наши проблемы. Каждый блок схемы представляет вариант одноблочной последовательной реализации аттрактора Лоренца на ЯП, приведенный на рис. 7. В каждом из блоков были переставлены (можно сказать - отсортированы) вычисления уравнений в порядке, отраженном в названиях блоков.
Исходя из понятий обычного программирования, внесение подобных изменений никак не должно повлиять на результат (от перемены мест слагаемых, как известно, сумма не изменяется). Но, по виду полученных графиков, представленных на рис. 15, это далеко не так: средний график отличается от остальных. И это подтверждает наши опасения. Такого быть не должно, т.к. при вычислении уравнений, которые параллельны (а для системы уравнений, представляющих аттракторы, это именно так и есть), результаты не должны зависеть от последовательности записи/работы программных операторов.
Анализируя эксперимент, можно утверждать, что сортировка не позволяет получить результат, который был бы эквивалентен параллельному исполнению операторов. В нашем случае - результату на рис. 4 или на рис. 11. Например, момент выхода из колебательного процесса при любой "пересортице" операторов никак не совпадет с аналогичным моментом при их параллельной работе. В этом убеждает также сравнение графика на рис. 11 с графиками на рис. 15.
Главный итог проделанной работы, пожалуй, в том, что было показано, как работа схемы может не зависеть от порядка вычисления операторов. Для этого не нужен даже явный параллелизм, который еще нужно реализовать, используя то же множество ядер или потоков, а затем необходимо еще и синхронизировать их работу. Иногда достаточно простой буферизации. Хотя по большому счету она лишь часть более общего понятия - модели параллельных вычислений. Но об этом подробнее повествуется в предыдущих статьях, где буферная память названа теневой.
Ну, вот пока и все. Выводы может сделать каждый, основываясь на результатах экспериментов. Использовать ли специальные блоки, устраняющие алгебраические петли, реализовать ли прикладные блоки на ЯП и вводить или не вводить в них буферную память, применять ли какие-то другие приемы и методы - решать разработчику. Только он отвечает за правильность и надежность работы приложения.
Наша задача - предупредить о последствиях применения тех или иных подходов и показать, к чему, если что, это может привести. Реализовать последовательными методами изначально параллельные задачи - серьезная ошибка. Рассмотренный ранее RS-триггер и рассматриваемые аттракторы - практические примеры из этого ряда. Они своеобразные тесты на параллелизм. А без параллелизма современное проектирование невозможно. Отсюда и актуальность подобных тестов. И чем их больше, тем быстрее будут выявляться ошибки, связанные с параллелизмом. Тем меньше будут затраты на их поиск и устранение.
PS
Да, чуть не забыли главного. Во исполнение поставленной в начале статьи цели приведем результат работы аттрактора Лоренца в ВКПа. Он следующий (рис. 16):
Вплоть до 30-й секунды мы имеем полное совпадение, например, с рис. 13. По сравнению с тем, когда было только начато тестирование аттракторов, мы, можно сказать, существенно продвинулись к осуществлению поставленной цели. Но лишь частично, т.к. по-прежнему нет ответа на вопрос - а возможно ли полное совпадение?
Может, получить ответ поможет код аттрактора на С++ в ВКПа (см. рис. 17)?. В нем опущены детали, связанные с объектной реализацией, но приведена та часть, которая отвечает за вычисления. И ее уже можно сравнить с приведенной на рис. 7 реализацией в SimInTech. Имеются ли между ними отличия, которые в конечном счете приводят к разнице результатов?
Автор, если честно, принципиальной разницы не видит. Как следствие, не видит и причин для несовпадения результатов. Но результаты, ведь, разные? И знать о причинах этого просто необходимо. Хотя бы с точки зрения понимания источника возможных ошибок. Если они здесь есть, конечно. А если нет?...
Да, если что, тестирование с иными шагами интегрирования, хотя и порождает новый вид графиков, но, как ни странно, именно до 30-й секунды они совпадают. Прямо мистика какая-то?!