Функция на Fortran за претоварване на умножението между производни типове с разпределяеми компоненти

Предговор

За да съхранявам лентови матрици, чиито пълни двойници могат да имат както редове, така и колони, индексирани от индекси, различни от 1, дефинирах производен тип данни като

TYPE CDS
  REAL, DIMENSION(:,:), ALLOCATABLE :: matrix
  INTEGER, DIMENSION(2) :: lb, ub
  INTEGER :: ld, ud
END TYPE CDS

където CDS означава компресирано диагонално съхранение. Предвид декларацията TYPE(CDS) :: A,

  1. Компонентът с ранг 2 matrix трябва да съдържа като колони диагоналите на действителната пълна матрица (като тук, освен че съхранявам диагоналите като колони, а не като редове).
  2. Предполага се, че компонентите ld и ud съдържат съответно броя на долните и горните диагонали, тоест -lbound(A%matrix,2) и +ubound(A%matrix,2).
  3. Предполага се, че 2-елементните компоненти lb и ub съдържат долните граници и горните граници на действителната пълна матрица по двете измерения. По-специално lb(1) и ub(1) трябва да са същите като lbound(A%matrix,1) и lbound(A%matrix,2).

Както можете да видите в точки 2. и 3., производният тип съдържа някаква излишна информация, но не ме интересува, тъй като те са само 3 двойки цели числа. Освен това, в кода, който пиша, информацията за границите и лентата на действителната пълна матрица е известна преди матрицата да бъде попълнена. Така че първо присвоих стойностите на компонентите ld, ud, lb и ub и след това използвах тези компоненти за ALLOCATE компонента matrix (тогава мога да го попълня правилно).

Проблемът

Трябва да извърша умножение на матрици между такива редки матрици, така че написах FUNCTION за изпълнение на такъв продукт и го използвах, за да претоваря оператора *.

В момента функцията е следната,

FUNCTION CDS_mat_x_CDS_mat(A, B)
IMPLICIT NONE
TYPE(CDS), INTENT(IN) :: A, B
TYPE(CDS) :: cds_mat_x_cds_mat

! determine the lower and upper bounds and band of the result based on those of the operands
CDS_mat_x_CDS_mat%lb(1) = A%lb(1)
CDS_mat_x_CDS_mat%ub(1) = A%ub(1)
CDS_mat_x_CDS_mat%lb(2) = B%lb(2)
CDS_mat_x_CDS_mat%ub(2) = B%ub(2)
CDS_mat_x_CDS_mat%ld = A%ld + B%ld
CDS_mat_x_CDS_mat%ud = A%ud + B%ud

! allocate the matrix component
ALLOCATE(CDS_mat_x_CDS_mat%matrix(CDS_mat_x_CDS_mat%lb(1):CDS_mat_x_CDS_mat%ub(1),&
                               & -CDS_mat_x_CDS_mat%ld:+CDS_mat_x_CDS_mat%ud))

! perform the product
:
:

END FUNCTION

Това означава, че ако трябва да направя продукта няколко пъти, разпределението се извършва многократно вътре във функцията. Мисля, че това не е добре от гледна точка на ефективността.

Моля за предложения как да изпълня задачата за лентова разредена матрица, умножена по лентова рядка матрица. Бих искал да използвам типа, който дефинирах, защото имам нужда да бъде толкова общ, по отношение на границите, колкото е в момента. Но мога да променя процедурата за изпълнение на продукта (от FUNCTION на SUBROUTINE, ако е необходимо).

Идеи

Бих могъл да пренапиша процедурата като SUBROUTINE, за да декларирам CDS_mat_x_CDS_mat с INTENT(INOUT) да направя присвояването на компоненти, различни от matrix, както и разпределението извън SUBROUTINE. Недостатъкът би бил, че не можех да претоваря оператора *.

Забелязах, че присъщата функция MATMUL може да работи с всеки операнд от ранг 2, независимо от горната и долната граница на двете измерения. Това означава, че разпределението се извършва вътре във функцията. Предполагам, че е ефективно (тъй като е присъщо). Разликата по отношение на моята функция е, че тя приема масиви от ранг 2 с всякаква форма, докато моята приема обекти с произволни типове данни с компонент на масив от ранг 2 с всякаква форма.


comment
Профилирахте ли, за да определите (относителната) цена на това разпределение (което изглежда трябва да направите точно веднъж, независимо дали във функцията или преди да влезете в подпрограмата)? Но тогава може би не разбирам: предлагате повторно използване на разпределението при умножаване повече от веднъж. Но защо просто не кеширате резултата и сте го направили?   -  person francescalus    schedule 06.07.2016


Отговори (1)


Вътрешната функция MATMUL има еквивалента на автоматичен (F2008 5.2.2) резултат - формата на резултата се изразява по такъв начин, че да стане характеристика на функцията (F2008 12.3.3) - формата на резултата от функцията се определя в спецификационната част на функцията и (от гледна точка на изпълнение) компилаторът следователно знае как да изчисли формата на резултата от функцията преди действителното изпълнение на функцията.

Вследствие на това няма променливи ALLOCATABLE на езика Fortran, свързани с еквивалента на резултата от присъщата функция MATMUL. Това не е същото като да няма "разпределение на памет" - компилаторът може все още да трябва да разпредели памет зад кулисите за собствените си цели - за неща като временни изрази и други подобни.

(Казвам „еквивалент на“ по-горе, защото присъщите процедури са по своята същност специални – но се престорете за момент, че MATMUL е просто потребителска функция.)

Еквивалентът на този вид автоматичен резултат за вашия случай може да бъде постигнат чрез използване на параметри тип дължина. Това е характеристика на Fortran 2003 - същият базов езиков стандарт, който въведе разпределяеми компоненти - но не е тази, която все още е внедрена от всички активно поддържани компилатори.

MODULE xyz
  IMPLICIT NONE

  TYPE CDS(lb1, ub1, ld, ud)
    INTEGER, LEN :: lb1, ub1, ld, ud
    REAL :: matrix(lb1:ub1, ld:ud)
    INTEGER :: lb2, ub2
  END TYPE CDS

  INTERFACE OPERATOR(*)
    MODULE PROCEDURE CDS_mat_x_CDS_mat
  END INTERFACE OPERATOR(*)
CONTAINS
  FUNCTION CDS_mat_x_CDS_mat(A, B) RESULT(C)
    TYPE(CDS(*,*,*,*)), INTENT(IN) :: A, B
    TYPE(CDS(A%lb1, A%ub1, A%ld+B%ld, A%ud+B%ud)) :: C

    C%lb2 = B%lb2
    C%ub2 = B%ub2

    ! perform the product.
    ! :

  END FUNCTION CDS_mat_x_CDS_mat
END MODULE xyz

Теоретично това дава на компилатора повече възможности за оптимизация, тъй като има повече информация преди извикването на функцията за съхранението, необходимо за резултата от функцията. Дали това наистина води до по-добра производителност в реалния свят зависи от внедряването на компилатора и естеството на препратките към функциите.

person IanH    schedule 06.07.2016
comment
Изглежда точно това, което търсех. Само два въпроса за това: (1) Прочетох класически пример за параметризирани производни типове данни на Fortran95/2003 на Chapman, но не виждам LEN след декларацията на параметрите. има ли разлика (2) Не мога ли да декларирам 2-елементи от ранг-1 lb и ub и да използвам lb(1)use ub(1) в декларацията на компонента matrix? - person Enlico; 07.07.2016
comment
Kind параметрите за дефинирани от потребителя производни типове са или KIND параметри - които трябва да бъдат посочени от времеви константи за компилиране (и които след това могат да се използват в ситуации, изискващи времева константа за компилиране) - или LEN параметри, които могат да бъдат посочени по време на изпълнение. Параметрите на типа винаги са скаларни. - person IanH; 07.07.2016
comment
Това трябваше да са параметри на типа за дефинирани от потребителя... - person IanH; 07.07.2016
comment
Ако разбирам какво питаш - тогава не - не можеш. Спецификацията на масива за компонентен масив без възможност за разпределяне, без указател не може да зависи пряко от стойността на променлива - ето защо примерът в отговора използва параметър тип дължина. Параметрите на типа винаги са скаларни - те никога не са масиви. - person IanH; 07.07.2016
comment
О, да, погрешно прочетох последните ви думи Тип [Прочетох Вид] параметрите винаги са скаларни. Добре! Ще опитам с твоя отговор. - person Enlico; 07.07.2016