Оптимизирующий компилятор инженерных расчетов,
использующий платформу .NET
Пётр Иванков
Увеличение скорости
вычислений (производительности) является важной задачей программирования. По
мнению многих это самая важная задача. Однако люди знакомые с профессиональным
программированием знают, что, как правило, гибкость программного обеспечения
является гораздо более важной, чем производительность. В данной статье
рассмотрен подход, позволяющий обеспечить гибкость и высокую
производительность. Примеры кода использующего данный подход расположены в
проекте AstroFrame https://sourceforge.net/project/showfiles.php?group_id=159404 . Они присутствуют в любом файле загруженном
после 26.07.2008.
Данная статья была бы
малоэффективной, если бы в ней не обсуждались возражения к применению данного
подхода.
К числу возражений следует
отнести:
- данный поход несовместим с
разработанным ранее программным обеспечением, связанным с инженерными
вычислениями;
- данный поход ограничивается
платформой .NET;
- специализированное
программное обеспечение эффективнее универсального;
- затраты времени на освоение
.NET слишком велики и
лучше использовать устаревшие информационные технологии.
В данной статье приведены
ответы на все эти возражения. А теперь рассмотрим суть оптимизации
производительности.
Технология .NET поддерживает компиляцию исходного
кода во время выполнения. Здесь уместно привести цитату из Википедии http://ru.wikipedia.org/wiki/Base_Class_Library#cite_note-0
:
============ Цитата
=================
System.CodeDom
Обеспечивает возможность
создавать код и запускать его.
============Конец
цитаты ===========
Теперь покажем
как System.CodeDom используется для оптимизации инженерных
вычислений в проекте AstroFrame. Данный проект содержит продвинутый редактор формул имеющий возможность
манипулировать с большим числом математических структур. Например на следующем
рисунке изображено произведение матриц a и b.
Произведению соответствует формула на коричневом фоне. То, что данная
формула представляет собой произведение матриц обусловлено типами переменных.
Переменная а представляет собой массив Double[4, 5], а
переменная b имеет тип Double[5, 6] (Типы отображаются в левом нижнем
углу рисунка). В соответствии с типами формула интерпретируется как произведение
матриц. В соответствие с формулами
генерируется следующий код:
var_2[0, 0] = var_0[0, 0] * var_1[0, 0] + var_0[0, 1] * var_1[1, 0] + var_0[0, 2] * var_1[2, 0] + var_0[0, 3] * var_1[3, 0] + var_0[0, 4] * var_1[4, 0];
var_2[0, 1]
= var_0[0, 0] * var_1[0, 1] + var_0[0, 1] * var_1[1, 1] + var_0[0, 2] *
var_1[2, 1] + var_0[0, 3] * var_1[3, 1] + var_0[0, 4] * var_1[4, 1];
var_2[0, 2] = var_0[0, 0] *
var_1[0, 2] + var_0[0, 1] * var_1[1, 2] + var_0[0, 2] * var_1[2, 2] + var_0[0,
3] * var_1[3, 2] + var_0[0, 4] * var_1[4, 2];
var_2[0, 3] = var_0[0, 0] *
var_1[0, 3] + var_0[0, 1] * var_1[1, 3] + var_0[0, 2] * var_1[2, 3] + var_0[0,
3] * var_1[3, 3] + var_0[0, 4] * var_1[4, 3];
var_2[0, 4] = var_0[0, 0] *
var_1[0, 4] + var_0[0, 1] * var_1[1, 4] + var_0[0, 2] * var_1[2, 4] + var_0[0,
3] * var_1[3, 4] + var_0[0, 4] * var_1[4, 4];
var_2[0, 5] = var_0[0, 0] *
var_1[0, 5] + var_0[0, 1] * var_1[1, 5] + var_0[0, 2] * var_1[2, 5] + var_0[0,
3] * var_1[3, 5] + var_0[0, 4] * var_1[4, 5];
var_2[1, 0] = var_0[1, 0] *
var_1[0, 0] + var_0[1, 1] * var_1[1, 0] + var_0[1, 2] * var_1[2, 0] + var_0[1,
3] * var_1[3, 0] + var_0[1, 4] * var_1[4, 0];
var_2[1, 1] = var_0[1, 0] * var_1[0, 1] +
var_0[1, 1] * var_1[1, 1] + var_0[1, 2] * var_1[2, 1] + var_0[1, 3] * var_1[3,
1] + var_0[1, 4] * var_1[4, 1];
var_2[1, 2] = var_0[1, 0] *
var_1[0, 2] + var_0[1, 1] * var_1[1, 2] + var_0[1, 2] * var_1[2, 2] + var_0[1,
3] * var_1[3, 2] + var_0[1, 4] * var_1[4, 2];
var_2[1, 3] = var_0[1, 0] *
var_1[0, 3] + var_0[1, 1] * var_1[1, 3] + var_0[1, 2] * var_1[2, 3] + var_0[1,
3] * var_1[3, 3] + var_0[1, 4] * var_1[4, 3];
var_2[1, 4] = var_0[1, 0] *
var_1[0, 4] + var_0[1, 1] * var_1[1, 4] + var_0[1, 2] * var_1[2, 4] + var_0[1,
3] * var_1[3, 4] + var_0[1, 4] * var_1[4, 4];
var_2[1, 5] = var_0[1, 0] *
var_1[0, 5] + var_0[1, 1] * var_1[1, 5] + var_0[1, 2] * var_1[2, 5] + var_0[1,
3] * var_1[3, 5] + var_0[1, 4] * var_1[4, 5];
var_2[2, 0] = var_0[2, 0] *
var_1[0, 0] + var_0[2, 1] * var_1[1, 0] + var_0[2, 2] * var_1[2, 0] + var_0[2,
3] * var_1[3, 0] + var_0[2, 4] * var_1[4, 0];
var_2[2, 1] = var_0[2, 0] * var_1[0,
1] + var_0[2, 1] * var_1[1, 1] + var_0[2, 2] * var_1[2, 1] + var_0[2, 3] *
var_1[3, 1] + var_0[2, 4] * var_1[4, 1];
var_2[2, 2] = var_0[2, 0] *
var_1[0, 2] + var_0[2, 1] * var_1[1, 2] + var_0[2, 2] * var_1[2, 2] + var_0[2,
3] * var_1[3, 2] + var_0[2, 4] * var_1[4, 2];
var_2[2, 3] = var_0[2, 0] *
var_1[0, 3] + var_0[2, 1] * var_1[1, 3] + var_0[2, 2] * var_1[2, 3] + var_0[2,
3] * var_1[3, 3] + var_0[2, 4] * var_1[4, 3];
var_2[2, 4] = var_0[2, 0] *
var_1[0, 4] + var_0[2, 1] * var_1[1, 4] + var_0[2, 2] * var_1[2, 4] + var_0[2,
3] * var_1[3, 4] + var_0[2, 4] * var_1[4, 4];
var_2[2, 5] = var_0[2, 0] *
var_1[0, 5] + var_0[2, 1] * var_1[1, 5] + var_0[2, 2] * var_1[2, 5] + var_0[2,
3] * var_1[3, 5] + var_0[2, 4] * var_1[4, 5];
var_2[3, 0] = var_0[3, 0] *
var_1[0, 0] + var_0[3, 1] * var_1[1, 0] + var_0[3, 2] * var_1[2, 0] + var_0[3,
3] * var_1[3, 0] + var_0[3, 4] * var_1[4, 0];
var_2[3, 1] = var_0[3, 0] *
var_1[0, 1] + var_0[3, 1] * var_1[1, 1] + var_0[3, 2] * var_1[2, 1] + var_0[3,
3] * var_1[3, 1] + var_0[3, 4] * var_1[4, 1];
var_2[3, 2] = var_0[3, 0] *
var_1[0, 2] + var_0[3, 1] * var_1[1, 2] + var_0[3, 2] * var_1[2, 2] + var_0[3,
3] * var_1[3, 2] + var_0[3, 4] * var_1[4, 2];
var_2[3, 3] = var_0[3, 0] *
var_1[0, 3] + var_0[3, 1] * var_1[1, 3] + var_0[3, 2] * var_1[2, 3] + var_0[3,
3] * var_1[3, 3] + var_0[3, 4] * var_1[4, 3];
var_2[3, 4] = var_0[3, 0] *
var_1[0, 4] + var_0[3, 1] * var_1[1, 4] + var_0[3, 2] * var_1[2, 4] + var_0[3,
3] * var_1[3, 4] + var_0[3, 4] * var_1[4, 4];
var_2[3, 5] = var_0[3, 0] *
var_1[0, 5] + var_0[3, 1] * var_1[1, 5] + var_0[3, 2] * var_1[2, 5] + var_0[3,
3] * var_1[3, 5] + var_0[3, 4] * var_1[4, 5];
В данном коде переменой a соответствует идентификатор var_0, а
переменной b – var_1. Идентификатор var_2 соответствует выходной формуле. Мы видим, что сгенерированный код не
имеет циклов. Он компилируется во время выполнения и затем исполняется.
Отсутствие циклов обеспечивает высокую производительность. Приведём ещё один
пример.
Формула следующего вида
Транслируется в следующий программный код.
var_5[0] = Math.Sin((double)var_4[0]);
var_5[1] = Math.Sin((double)var_4[1]);
var_5[2] = Math.Sin((double)var_4[2]);
var_5[3] = Math.Sin((double)var_4[3]);
var_5[4] = Math.Sin((double)var_4[4]);
var_6[0] = (var_3) * ((double)var_5[0]);
var_6[1] = (var_3) * ((double)var_5[1]);
var_6[2] = (var_3) * ((double)var_5[2]);
var_6[3] = (var_3) * ((double)var_5[3]);
var_6[4] = (var_3) * ((double)var_5[4]);
var_7 = Math.Cos(var_3);
var_8[0] = ((double)var_6[0]) + (var_7);
var_8[1] = ((double)var_6[1]) + (var_7);
var_8[2] = ((double)var_6[2]) + (var_7);
var_8[3] = ((double)var_6[3]) + (var_7);
var_8[4] = ((double)var_6[4]) + (var_7);
var_10[0] = Math.Tan((double)var_9[0]);
var_10[1] = Math.Tan((double)var_9[1]);
var_10[2] = Math.Tan((double)var_9[2]);
var_10[3] = Math.Tan((double)var_9[3]);
var_10[4] = Math.Tan((double)var_9[4]);
var_11[0] = ((double)var_8[0]) + ((double)var_10[0]);
var_11[1] = ((double)var_8[1]) + ((double)var_10[1]);
var_11[2] = ((double)var_8[2]) + ((double)var_10[2]);
var_11[3] = ((double)var_8[3]) + ((double)var_10[3]);
var_11[4] = ((double)var_8[4]) + ((double)var_10[4]);
var_12[0] = (var_2) * ((double)var_11[0]);
var_12[1] = (var_2) * ((double)var_11[1]);
var_12[2] = (var_2) * ((double)var_11[2]);
var_12[3] = (var_2) * ((double)var_11[3]);
var_12[4] = (var_2) * ((double)var_11[4]);
var_13[0] = Math.Exp((double)var_12[0]);
var_13[1] = Math.Exp((double)var_12[1]);
var_13[2] = Math.Exp((double)var_12[2]);
var_13[3] = Math.Exp((double)var_12[3]);
var_13[4] = Math.Exp((double)var_12[4]);
Видно, что данный код не содержит циклов и повторных операций. Список
примеров можно продолжить. Хотя важнее привести программную документацию
проекта AstroFrame, которая в
настоящее время готовится. А в последующем тексте будут приведены ответы на
взражения.
Если бы данная статья была бы адресована только
профессиональным программистам, то данный раздел можно было бы не писать.
Профессиональные программисты всегда обеспечивают совместимость. О том, как обеспечивается совместимость
проекта AstroFrame написано
в статье посвященной определению орбит космических аппаратов:
http://www.codeproject.com/KB/cs/UniversalEnggFrmwork6.aspx
http://www.mathframe.com/articles/usef/orbitdetermination/index.html
http://sourcecode.aivietnam.net/SourceCode/tabid/54/GroupDetailId/50/ContentId/358/Default.aspx
В данной статье приводится описание экспорта в проект AstroFrame библиотек связанных с расчётом
гравитационного поля Земли и динамической атмосферы. Как любой профессионально
выполненный проект AstroFrame поддерживает импорт и экспорт из/в программное обеспечение совместимое с .NET. Класс совместимых с .NET программ очень широк. Это различные
библиотеки COM объекты.
Кроме того имеются широкие возможности конвертирования несовместимого с .NET программного обеспечения в
совместимое.
Для многих .NET является экзотикой.
Однако все ведущие производители программного обеспечения давно уже эту платформу
используют. Есть версии .NET совместимые с UNIX системами и с MAC/OS. Компании Oracle и IBM уже давно используют .NET для баз данных. На самом деле
основные компоненты .NET такие как C# и CLI имеют сертификат ISO.
Мнение о том, что специализированное программное
обеспечение лучше универсального имеет глубокие корни, связанные с техникой. Например,
в авиации невозможно нагрузить самолёт большим числом функций. Истребитель не
может быть одновременно стратегическим бомбардировщиком. Однако этот довод
неприменим к программному обеспечению. Колоссальные объёмы памяти современных
компьютеров позволяют виртуально создавать совершенно немыслимые вещи. Один раз
автор статьи показал авиатору, как виртуальный бомбардировщик летит с
перегрузкой 2000g. «Не
надо!!!» - воскликнул авиатор. Этот
пример наглядно показывает неприменимость технических аналогий к программному
обеспечению.
С другой стороны универсальное программное
обеспечение даёт много дивидендов. Например, если Вы исследуете морские
ресурсы, то можно казалось бы можно ограничиться специализированными
программами, с принятыми в Вашей отрасли базами данных. Однако, если Вам
придётся совместить Ваше ПО с метеорологической базой данных, то скорее всего Ваше
ПО будет нуждаться в 90 процентной ревизии. Упор на специализацию – характерный
признак ограниченности мышления и недальновидности.
Если технологии существенно увеличивают производительность
труда то, очевидно, что их нужно изучать. Хотя если Вы работаете в монопольной
организации, где не требуется снижение трудозатрат, то Вам не надо изучать и
внедрять эффективные технологии. По этой причине для внедрения прогрессивных
технологий нужны эффективные антимонопольные мероприятия включающие
законодательство и действенные механизмы его выполнения. Мы пока очень далеки от
этого.
В настоящее время технология .NET не нашла должного объёма применения среди
инженеров. При проведении научных исследований применение .NET ещё большая редкость. Сейчас чаще среди исследователей можно
встретить Fortran, Pascal или Basic. Автор надеется, что данная статья
привлечёт внимание к .NET
продвинутых инженеров и исследователей.