Стеклянная луковица

Обо мне

Подписаться!Ленты RSS

Последние посты

 
 
воскресенье, мая 25, 2008

.NET + Unmanaged Fortran

0 комментариев

Так уж вышло, что у меня возникла необходимость прикрутить к дотнетовскому фронт-энду математический модуль, писанный на Фортране. Задача для меня не особо тривиальная, потому как раньше с Фортраном я не сталкивался.

В целом, задача моя состояла в следующем: взять код нескольких готовых функций на Фортране, несколько написать самому, скомпилировать получившееся в DLL, нежно обернуть библиотеку управляемым кодом, и вызвать из своей гуишки. Будем исходить из того, что получить необходимо 32-разрядное приложение.

Попытаюсь проследить весь процесс от начала и до конца :)

Писать/править код можно, в принципе, и в блокноте, а можно использовать неплохие бесплатные текстовые редакторы, подсвечивающие фортрановский синтаксис: к примеру, PSPad или Crimson Editor (первый мне понравился больше). Хотя, честно говоря, до UltraEdit’a им далековато. Он не бесплатен, и подсветка синтаксиса для Фортрана по умолчанию вместе с ним не идет – правила подсветки можно скачать с официального сайта программы. Но удобство редактора и набор возможностей позволяют закрыть глаза на эти мелочи.

Следующий шаг – выбор компилятора. Первым делом я ухватился за опенсорсовый вариант – G95. Мал, шустер и прост. Впрочем, на этом этапе у меня к компилятору было только одно требование – компилировать.

Итак, забрасываю все исходники в одну папку, решаю назвать свою библиотеку test.dll и вызываю команду:


g95 -s -shared -mrtd -o test.dll test.def *.f

Если верить FAQ’у, то здесь параметр -mrtd отвечает за соглашение передачи параметров STDCALL. В файле test.def разбираемся с декорированием имен: пусть экспортируем функцию testfcn, тогда, чтобы избавиться от знака подчеркивания при экспорте, пишем в нем:

EXPORTS testfcn=testfcn_

Итак, библиотека получена, теперь задача – обернуть ее в какой-нибудь класс на C#.

Пусть наша функция принимает параметрами xr, xi – вещественные массивы двойной точности, целые числа n и ord, и еще один вещественный массив y. Тогда метод, импортирующий функцию из нашей библиотеки, следует объявить следующим образом:

[DllImport( "test.dll", EntryPoint = "testfcn", SetLastError = true,
CharSet = CharSet.Ansi, ExactSpelling = true,
CallingConvention = CallingConvention.Stdcall )]
public static extern void testfcn( ref double xr,
ref double xi,
ref int n,
ref int ord,
ref double y );


При вызове функции, передавая массивы, даем ссылку на их первый элемент:

testfcn( ref xr[ 0 ], ref xi[ 0 ], ref n, ref o, ref y[ 0 ] );

Если работа выполняется на 64-разрядной машине, не забываем синхронизировать разрядность в настройках дотнетовского проекта.

В целом, на этом изыскания можно было бы и закончить, если бы не одно «но»: быстродействие полученного модуля меня абсолютно не устраивало. Поэтому мой взгляд упал на Intel Fortran Compiler, а именно – десятую его версию.

Он вполне неплохо проинтегрировался в Visual Studio 2005, поэтому нужда во внешних редакторах сразу же отпала. Порадовал широкий выбор опций, связанных с оптимизацией.

Вместо def-файла ему достаточно подсунуть такую директиву:

!DEC$ ATTRIBUTES DLLEXPORT, ALIAS: 'testfcn' :: testfcn

По советам Гугла пытался впихнуть туда же атрибут STDCALL, равно как и проставить соответствующую опцию в параметрах компилятора, но он упорно выдавал библиотеку с соглашением CDECL, поэтому C#-обвертку пришлось немного изменить, выставив для DLLImport’a CallingConvention = CallingConvention.Cdecl.

Еще одна проблема возникла с массивами – по умолчанию они передавались через стек, и даже при небольших объемах данных сыпались исключения переполнения стека. Лечится это добавлением ключика /heap-arrays в параметры командной строки компилятора.

Итак, выставив настройки оптимизации по своему вкусу (включая специфические для процессоров Intel), компилируюсь, запускаю программу на тест, и – получаю прирост производительности на 50% (!!!).

Единственный минус – при развертывании приложения придется тащить за собой целый ворох вспомогательных библиотек, от которых теперь, оказывается, зависит наш модуль. Их не так уж и много, они не такие уж и большие, но все равно вполне неприятно. При использовании G95 такой необходимости не было. Впрочем, в таких случаях я сразу вспоминаю псевдокомпилятор MATLAB’a со 150-мегабайтным установочником рантаймов, и на душе сразу становится легче :)

Comments
0 comments

Отправить комментарий