Comments 13
все хорошо, все понравилось, хочу поделиться каламбуром, который навеяло:
мне кажется это все таки не тонкости, это толстости :) в основном.
Вроде для совместимости с Си в Фортране надо использовать type(c_ptr), а не pointer.
Но у меня другой вопрос - не проще было распараллелить фортрановскую программу, чем сишную? Тем более пользуетесь интеловским компилятором, который умеет автопараллелизацию.
Проблемы с такой реализацией начались с того, что компилятор начал ругаться на повторную деаллокацию массивов во внутренних функциях фортрана. Речь идет о таких массивах, которые объявляются, аллоцируются и деаллоцируются внутри какой-либо подпрограммы для совершенно локальных задач.
Как известно, в фортране есть атрибут save, который позволяет отвести для локального объекта постоянную область памяти. То есть, область видимости у него локальная, но память глобальная. Иногда это немного ускоряет работу (не надо заново выделять память при каждом вызове функции), а кроме того, этот атрибут гарантирует, что значение такого объекта сохраняется между вызовами. Например,
вот такой код будет вычислять число вызовов функции MyFunc.
Поясню для не-фортранистов, как это работает: COUNTER инициализируется один раз при старте программы, а затем при каждом входе в MyFunc увеличивается на 1:
function MyFunc(...)
integer, SAVE :: COUNTER =0
COUNTER=COUNTER+1
! Теперь при первом вызове функции COUNTER=1,
! при втором вызове COUNTER=2, и т.д.
! А вот что будет при распараллеливании, я не знаю
(...)
end
Возможно, в Вашем случае компилятор по какой-то причине решил создавать такой "сохраняемый" объект вместо tmp? Подозреваю, что есть некоторая логика в том, чтобы при оптимизации скорости вычислений локальные объекты большого размера создавались один раз (т.е. как бы в SAVE-режиме). А вот если включить оптимизацию памяти, то они будут заново выделяться и освобождаться при каждом вызове.
Но как на самом деле поступает в таких случаях интел-компилятор фортрана, я не знаю. У меня самого до сих пор компилятор 2008г, так как перейти на более новую версию
я не смог
легально это теперь нельзя, а обновиться "неофициально" я не сумел
я не смог
Так и бог с ним. Интелевский фортран, конечно, хорош (в основном, своим оптимизатором). Что касается параллельных вычислений, то спецификации OpenMP вполне достаточно, а её поддерживает и Интель- и ГНУ-фортран. Я сижу в IDE Simly Fortran, хотя раньше тоже использовал Интель. Численные задачки решаются вполне себе нормально, даже мухобойные типа электродинамических FDTD.
Автопараллелизация, помнится, всё же не очень предсказуема. В моём опыте получалось, что написанный самостоятельно OpenMP может и не побыстрее, но постабильнее. В простом расчётном коде, когда себя для контроля происходящего хватает уж точно. AutoParallelization иной раз вроде и создаёт все потоки в должном количестве, и ядра загружены, а ускорения особо не заметно — где‑то упирается в обмен данными и кирдык.
Надо бы провести количественное тестирование, наверное.
Да, конечно, параллелить только один язык проще. Но в данном конкретном случае параллелить из С++ было наиболее оптимально, да и интереснее)
далее мне необходимо передать информацию о месте памяти, где лежит все относящееся к данной ячейке. Логично сделать это через указатель, но такой код работает некорректно!
Тут не очень понятно: а зачем указатель-то? Фортран все равно по умолчанию передает в функцию адрес объекта. В отличие от некоторых других языков, ничего никуда не копируется ;-) Проще говоря, никаких накладных расходов, как при передаче параметров по значению, нет. Поэтому обычный call никаких трюков с указателями не требует (что очень удобно для всяких тупиц вроде меня, которые только на этом языке и могут что-то писать ;-) А указатели в фортран добавлены не для этого, а для более изощренных хитростей ;-).
Короче, если у Вас есть глобальный массив объектов axmes(N), то в фортране обычно этот самый массив напрямую в функцию и передается. Или его элемент. Как Вы и сделали чуть ниже:
call AxMeshComputation(axmeshes(iaxialmesh))
Это как бы стандарт
Для того, чтобы это разрешить, необходимо полностью прописать то, что мы хотим скопировать, а именно содержимое структур, т.е. (...) ...Сколько бы переменных не было, их нужно перечислить все.
приравнивание объекта структуры другому объекту структуры в фортране делает копирование указателей, а не содержимого структур
А вот это явно какой-то баг. Все мои олдскулы говорят, что должна копироваться структура. Больше того, я сам сплошь и рядом использую именно такое копирование, которое у Вас не работает. Ни разу нигде не писал все переменные (элементы подструктуры) полным списком. Да и наверно не смог бы: у меня местами присутствует 4-уровневая иерархия структур. Боюсь даже подумать, как бы я в такой ситуации копировал 2-3-й уровень Вашим методом. И все работает штатно (но у меня IVF 2008).
Правда, в справке я сейчас не нашел прямого обязательства компилятора, что так можно делать (копировать подструктуру в подструктуру). Честно говоря, я раньше этот вопрос в справке и не искал никогда , так как считал это само собой очевидным из "первых принципов". Что когда я пишу элемент структуры, то это физически он самый и есть (а не указатель какой-то).
У Вас точно структуры описаны "тупым" способом, без использования указателей на структуры? По идее, в фортране для всех перечисленных операций указатели вообще не нужны (напомню, это не язык для продвинутых программистов ;-). Поэтому если Вы по какой-то старой привычке эти указатели куда-нибудь запихнули, то компилятор действительно мог подумать, что Вам хочется странного, и тогда его поведение может оказаться не совсем ожидаемым.
Согласен, здесь неоткуда взяться указателям. Скорее всего, переменные описаны как-то криво.
Вот что пишет по этому поводу IBM в описании Фортрана 2008. Как и следовало ожидать.
A derived-type assignment statement is an intrinsic assignment statement if there is no accessible defined assignment for objects of this derived type. The derived type expression must be of the same declared type as the variable. Each kind type parameter of the variable must have the same value as the corresponding kind of expression. Each length type parameter of the variable must have the same value as the corresponding type parameter of expression unless the variable is allocatable, and its corresponding type parameter is deferred. See “Determining declared type for derived types” on page 70 for the rules that determine when two structures are of the same derived type. Assignment is performed as if each component of the expression is assigned to the corresponding component of the variable. Pointer assignment is executed for pointer components, defined assignment is performed for each nonpointer nonallocatable component of a type that has a defined assignment consistent with the component, and intrinsic assignment is performed for each other nonpointer nonallocatable component.
Причина оказалась в конфликте версий OpenMP: в компиляторе C++ в Visual Studio 2017 стоит версия OpenMP 2.0, в упомянутом компиляторе фортрана - OpenMP 5.0.
А почему тогда уж и C++ компилятор не использовать интеловский из той же Parallel Studio?
Речь идет о таких массивах, которые объявляются, аллоцируются и деаллоцируются внутри какой-либо подпрограммы для совершенно локальных задач.
По хорошему лучше при инциализации выделить для каждого потока память для всех временных массивов и во время счёта ничего не переаллоцировать.
Тонкости в распараллеливании с OpenMP программы, написанной одновременно на C++ и Fortran