|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
2006 г.
Разработка системной поддержки вызова программ,
С.С. Гайсарян, К.Н. Долгова, Труды Института системного программирования РАН
|
| Тип данных языка Java | Требуемый объем памяти | Тип данных языка Fortran |
|---|---|---|
| Int | 4 байт | INTEGER |
| Short | 2 байт | INTEGER*2 |
| Long | 8 байт | INTEGER*8 |
| Byte | 1 байт | CHARACTER |
| Float | 4 байт | REAL |
| Double | 8 байт | DOUBLE PRECISION |
| Char | 2 байт | CHARACTER |
| Boolean | 1 байт | LOGICAL*1 |
Массив языка Java можно отобразить на такое представление данных языка Fortran как массив. Отображение массива языка Java на массив языка Fortran можно сделать через прямой буфер, средства работы с которым предоставлены в пакете "java.nio".
Данные в Fortran-программах могут быть представлены в виде констант или имен переменных (или идентификаторов).
Основные типы языка Fortran и соответствующие им типы языка Java представлены в таблице 2. Данные для таблиц взяты из литературы [2] и [4]
Таблица 2. Отображение примитивных типов языка Fortran в типы языка Java.
| Тип данных языка Fortran | Требуемый объем памяти | Тип данных языка Java |
|---|---|---|
| INTEGER*2 | 2 байт | short |
| INTEGER INTEGER*4 | 4 байт 4 байт | int int |
| REAL REAL*4 DOUBLE PRECISION REAL*8 REAL*16 | 4 байт 4 байт 8 байт 8 байт 16 байт | float float double double double double |
| COMPLEX COMPLEX*8 COMPLEX*16 COMPLEX*32 | 8 байт 8 байт 16 байт 32 байт | float float float float double double |
| LOGICAL*1 LOGICAL LOGICAL*4 | 1 байт 4 байт 4 байт | byte int int |
| CHARACTER CHARACTER*L | 1 байт L байт | byte string |
Для отображения данных, определенных в общем блоке, в окружении Java следует использовать прямой байт буфер. Такое отображение легко организовать, потому что общий блок представляет собой некоторую область памяти, хранящую неоднородные данные. Прямой байт буфер, доступный в Java окружении также представляет собой область памяти, которая может хранить неоднородные данные.
Для каждого как именованного, так и неименованного общего блока можно использовать по одному буферу.
В языке Fortran массивом называется упорядоченная последовательность данных, занимающая непрерывную область памяти, к которой можно обращаться по имени [2]. Массивы характеризуются типом значений их элементов и граничными парами - диапазоном индексов по каждому измерению.
Несмотря на то, что Fortran массивы могут быть как одномерными, так и многомерными, в памяти они располагаются как одномерный массив. Причем элементы многомерного массива располагаются в памяти таким образом, что значение первого индексного выражения возрастает быстрее второго, значение второго - быстрее третьего и т. д. [2].
Следовательно, приведенный индекс многомерного массива можно рассчитать по ниже приведенной формуле, а именно: пусть имеется многомерный массив arr[N, M, K], тогда приведенный индекс элемента arr[i, j, k] рассчитывается следующим образом:
(i-1)+(j-1)*N+(k-1)*N*M
Массив языка Fortran следует отображать на прямой байт буфер, доступный в Java среде. Такое представление выгодно, потому что многомерный Fortran-массив в памяти располагается как одномерный массив. Для получения данных из прямого буфера соответствующих элементу многомерного Fortran-массива в Java окружении реализуется специальный класс.
Многомерный массив не может иметь больше 7 измерений [5], то всегда можно автоматически получить данные из прямого буфера, соответствующие элементу многомерного Fortran-массива в Java окружении. При этом следует обойтись без транспонирования самого Fortran-массива.
Для ссылки на элемент массива задается индексированная переменная; на массив в целом ссылаются по его имени. Начиная со стандарта Fortran 90, в языке есть возможность непосредственно работать с частями массива - вырезками и сечениями. Вырезка из массива представляет собой подмассив вида
<имя_массива>(< нижняя граница - верхняя граница),
Элементы вырезки из массива могут быть взяты с шагом, отличным от единицы. В этом случае вырезка по соответствующему измерению задается уже не граничной парой, а триплетом.
<имя_массива>(< нижняя граница - верхняя граница, шаг >,…)
Если по какому-то измерению опущены обе границы, то говорят о сечении массива. Вырезку из массива можно также задать с помощью векторного индекса[5].
Для отображения вырезки или сечения массива на объекты Java среды также можно использовать байт буфер. Такое отображение можно выполнить следующим образом. Весь массив отображается на буфер, а дальше в Java среде организуется специальный класс, содержащий методы получения и записи элементов вырезки и сечения массива посредством пересчета с учетом шага.
Таким образом, любой массив языка Fortran можно отобразить на прямой байтовый буфер языка Java. Если массив размещен на общем блоке, он автоматически отобразится в Java окружение при отображении общего блока. Если массив определен посредством использования оператора DIMENSION, то для него надо создать прямой буфер, расположенный на том участке памяти, который компилятор языка Fortran выделил для хранения данного массива. Что касается вырезки и сечения массивов, так это представление данных можно отобразить через указатели на соответствующие элементы.
При вызове Fortran-подпрограмм из Java среды необходимо учитывать особенности чтения данных в Java- и Fortran-средах.
Java-машина читает байты, в которые записано одно число, слева направо (прямое чтение), а в C и Fortran - программах порядок байт в записи чисел зависит от архитектуры. То есть на некоторых платформах используется чтение справа налево (так называемое инвертированное чтение). Следовательно, на некоторых платформах для корректной работы, данные, записанные Fortran-подпрограммой, нужно подвергнуть дополнительному преобразованию в формат языка Java, чтобы Java-программа прочитала их корректно. И наоборот, данные, записанные Java-программой, тоже надо подвергать обратному преобразованию в формат языка Fortran, чтобы подпрограмма, реализованная на языке Fortran, смогла прочитать именно то, что было помещено в Java-коде. В выше упомянутом преобразовании предполагается менять местами соответствующие записи в ячейках. Такое преобразование необходимо осуществлять каждый раз, когда обработка данных, расположенных в памяти, общей и для Java-окружения, и для среды Fortran, передается от Java машины Fortran-среде и обратно.
Такое преобразование предполагается целесообразным выполнять при каждой записи виртуальной машиной Java данных в общую память и при каждом считывании данных из общей памяти Java машиной. Следовательно, среда Fortran всегда будет обрабатывать данные, записанные в формате языка Fortran, а Java машина всегда будет работать с данными, записанными в формате языка Java.
Прототипная реализация выполнена посредством связывания вызова подпрограммы, реализованной на языке Fortran, из Java-программы через язык С (JNI). В настоящее время окружение Java не предоставляет возможности вызывать напрямую подпрограммы, реализованные на языке Fortran.
Реализация выполнена для GNU компилятора Fortran (g77), GNU компилятора С (gcc) версии 3.3.4 и JDK версии 1.4.2_03.
Компилятор g77 основан на стандарте ANSI Fortran 77, но он включает в себя многие особенности, определенные в стандартах Fotran 90 и Fortran 95 [6].
JDK версии 1.4.2_03 содержит пакет java.nio, который предоставляет возможность использования новых средств ввода-вывода, таких, как прямые буферы и JNI (Java Native Interface).
Как уже отмечалось в пункте 2, прежде чем выполнить вызов подпрограммы, реализованной на языке Fortran, из Java среды, необходимо выделить область памяти, которая была бы доступна как из Java окружения, так и из среды Fortran.
Для этого нужно:
Байт-буферы расположены непосредственно в том же участке памяти, что соответствующие им общие блоки, следовательно, все данные, которые записываются в прямой буфер в Java-коде, автоматически становятся доступными из общего блока в коде, реализованном на языке Fortran. И наоборот: все, что помещено в общий блок в Fortran-подпрограмме, автоматически становится доступно из прямого буфера в Java-программе. Такое расположение данных полностью решает поставленную в пункте 1 задачу о совместном размещении данных Java окружения и среды Fortran на одном участке памяти.
Чтобы выполнить вызов Fortran-подпрограммы из Java-среды, нужно:
В предложенной реализации накладные расходы возникают при вызове метода инициализации прямых Java буферов, но эти накладные расходы возникают только один раз за все время работы программы, поэтому время, которое на них тратится, не существенно влияет на общую производительность программного продукта.
Накладные расходы возникают при преобразовании данных из формата языка Java формат языка Fortran. Однако полное преобразование данных из одного формата в другой есть необходимость выполнять только дважды за работу всего приложения: в начале, после инициализации, и в конце, перед тем, как вывести окончательный результат работы приложения. Следовательно, эти накладные расходы тоже считаются разовыми и не существенно влияют на время выполнения программного продукта.
Однако возникают еще накладные расходы, когда данные обрабатываются не только в Fortran-подпрограммах, но и в основной программе, написанной на языке Java. В этом случае при каждом переключении есть необходимость преобразовать данные из одного формата в другой. Но, как правило, объем данных, обрабатываемый сразу и в Java-коде и в коде, реализованном на языке Fortran, не очень велик. Следовательно, не следует преобразовывать сразу все данные, которые рассчитываются в приложении, а нужно преобразовать только тот их фрагмент, который нужен для обработки. Такой подход позволит сократить накладные расходы на преобразование данных. Именно эти накладные расходы следует учитывать при оценке времени работы программного приложения.
Чтобы убедиться в корректности работы реализации, была взята программа расчета динамики взрыва сверх новой звезды, реализованная на языке Fortran. [7]. Основная функция main, которая управляет расчетами, была переписана на язык Java. Остальные подпрограммы оставлены на языке Fortran.
Результаты работы исходной программы, реализованной только на языке Fortran, и программы, основная часть которой реализована на языке Java, а подпрограммы выполнены на языке Fortran, одинаковые.
Для сравнения времени работы полученного приложения, реализованного на языке Java с использованием Fortran-подпрограмм, было произведено сравнение с точно таким же приложением, но реализованным целиком на языке Fortran и на языке Java. Приложение можно представить в виде следующей схемы, представленной на рисунке 1.
Рисунок 1. Схема приложения.
В приложении, реализованном на языках Java+Fortran, инициализация данных и запись данных в файлы выполняется в Java-окружении, а счет выполняется в Fortran-среде.
Для сравнения времени выполнения были выполнены замеры, как скорости работы всего программного приложения, так и отдельных его частей, в соответствии с рисунком 1. Замеры проводились на персональном компьютере. Размер оперативной памяти 512 MB, частота процессора 1700 MHz. Характеристики кэш-памяти процессора следующие:
CPU L1 Cache: 64K (64 byte/line), CPU L2 Cache: 526K (64 byte/line)
Сравнение времени работы представлено в таблице 3 и на рисунке 2.
Таблица 3. Сравнительная производительность.
| полное приложение (ms) | инициализация (ms) | счет (ms) | запись (ms) | |
| Fortran | 261559 | 42826 | 218450 | 283 |
| Java + Fortran | 266223 | 43540 | 221623 | 1060 |
| Java | 337225 | 69874 | 265727 | 1624 |
(а)
(б)
(в)
(г)
Рисунок 2. Время выполнения.
Как видно из таблицы 3 и на рисунке 2(а), реализация приложения на языках Java+Fortran не значительно проигрывает по времени выполнения приложению, реализованному только на языке Fortran. Это достигается за счет того, что в приложении, реализованном на Java+Fortran, вычисления полностью выполняются в Fortran-среде.
Однако приложение, реализованное на языках Java+Fortran, работает значительно быстрее, нежели приложение, реализованное на языке Java. Как видно на рисунке 2(б), 2(в), 2(г), в приложении, реализованном только на языке Java, не только вычисление занимает больше времени, нежели в приложении, реализованном на языках Java+Fortran, но и инициализация и запись данных в файл. Потеря времени происходит за счет того, что большая часть инициируемых данных берется из файла. В приложении, реализованном только на языке Java, данные из файла записываются в массивы языка Java, а в приложении, реализованном на языках Java+Fortran, - в прямые буферы.
Fortran-подпрограммы, которые вызываются из Java-среды, не должны содержать символа "подчеркивание" в своем имени. В противном случае разделяемая библиотека не сможет сопоставить реализованные в ней методы с теми, которые вызываются. Это вызовет падение работы всего приложения.
Компилятор GNU g77, разрешает использование переменных без их явного описания. Однако, если явно не определить тип переменной в подпрограмме, которая вызывается из Java-окружения, вероятен случай, что виртуальная Java-машина получит внешний сигнал. Этот сигнал, номер которого 11, сообщает виртуальной машине о некорректном обращении к памяти за пределами ее работы. Аналогичная ситуация может возникнуть и с функциями. При описании функций стандартом предусмотрено описывать явно тип возвращаемого значения. Однако, если этого не сделать, то компилятор сам подберет соответствующий тип, исходя из типа возвращаемого выражения. Если такую функцию вызывать из программы, реализованной только на языке Fortran, то все будет работать стабильно. Но как только объектный модуль с такой функцией участвует в формировании разделяемой библиотеки и подобного рода функция вызывается подпрограммой, которая, в свою очередь, вызывается виртуальной машиной Java, выполнение основной программы прекращается по причине получения виртуальной Java-машиной сигнала номер 11.
Данные ограничения в дальнейшем развитии работы будут сняты посредством автоматического добавления в код Fortran-программы недостающих описаний.
Разработана организация взаимодействия среды Java и подпрограмм, реализованных на языке Fortran. Была выполнена прототипная реализация. Прототипная реализация показала, что описанная методика вызова подпрограмм, реализованных на языке Fortran, из окружения Java, реализуется с минимальными накладными расходами, а, следовательно, эффективно.
Дальнейшее развитие предполагает разработку методики рефакторинга Fortran-программ с целью преобразования их в такой вид, какой было бы удобно автоматически транслировать на язык Java.
| 1.обратно | Б.Керниган, Д.Ритчи. Язык программирования Си. Санкт-Петербург, 2001 |
| 2.обратно | Фортран 77 ЕС ЭВМ. Справочное издание. Москва "Финансы и статистика", 1989 |
| 3.обратно | Фортран. Программированное учебное пособие. Киев "Вища школа", 1980 |
| 4.обратно | У.Савитч. Язык Java. Курс программирования. Москва - Санкт-Петербург - Киев "Вильямс", 2002 |
| 5.обратно | Ю.И. Рыжиков. Совремнный фортран. Санкт-Петербург "Корона принт", 2004 |
| 6.обратно | Артур Гриффитс. GCC. Полное руководство. Москва - Санкт-Петербург - Киев DiaSoft, 2004 |
| 7.обратно | С. Д. Устюгов, В. М. Чечеткин. Взрыв сверхновой при крупномасштабной конвективной неустойчивости вращающейся протонейтронной звезды. // Астрономический журнал, 1999, том 76, №11, с. 816-824. |
|
CITForum © 1997–2025