3

현대 Fortran에서 서브 루틴이 인수로 전달 된 배열을 채우는 것과 동일한 성능을 가진 함수에서 배열을 반환 할 수 있습니까?Fortran 2008 : 함수 반환 값은 어떻게 반환됩니까?

예 : 간단한 예로서 여기에서 스택 리턴 된 배열을 폐기하기 전에, 함수 반환 값으로부터 값을 복사 할 광고 C = FUNC()

PROGRAM PRETURN 

    INTEGER :: C(5) 
    C = FUNC() 
    WRITE(*,*) C 
    CALL SUB(C) 
    WRITE(*,*) C 

CONTAINS 

    FUNCTION FUNC() RESULT(X) 
    INTEGER :: X(5) 
    X = [1,2,3,4,5] 
    END FUNCTION FUNC 

    SUBROUTINE SUB(X) 
    INTEGER :: X(5) 
    X = [1,2,3,4,5] 
    END SUBROUTINE SUB 

END PROGRAM PRETURN 

. 서브 루틴 버전 CALL SUB(C)C을 직접 채우므로 여분의 대처 단계와 임시 배열과 관련된 메모리 사용을 피할 수 있지만 SUM(FUNC())과 같은 표현에서 사용은 불가능합니다. 그러나 경우

컴파일러 구현은, 반환 값이 두 버전 사이에 동일한 성능의 결과로, C의 기본 포인터를 변경하여 간단하게 할당 할 수있는 힙의 모든 배열을 할당하기로 결정했습니다. *

등인가 일반적인 컴파일러에 의한 최적화 또는 성능 오버 헤드없이 기능적 의미를 얻기위한 다른 방법이 있습니까?


* 할당 가능한 배열이 더 명확 할 수도 있지만 컴파일러 지원 문제가 발생합니다. Intel Fortran은 기본적으로 다른 크기의 배열을 할당 할 때 배열을 (재) 할당하지 않지만 ALLOCATE(C, SOURCE=FUNC()) 문을 사용하여 동일한 효과를 허용합니다. Gfortran은 할당시 자동 할당을 수행하지만 모양은 SOURCE 인수에서 파생되는 ALLOCATE 문을 방지하는 버그가 있으며 수정 사항은 아직 이진 릴리스에 포함되지 않았습니다.

답변

7

Fortran 표준은 언어에서 거의 모든 것을 구현하기위한 실제 메커니즘에 대해 침묵합니다. 언어의 의미는 할당이 시작되기 전에 함수 결과가 완전히 평가된다는 것입니다. 출력으로 대상을 전달한 경우 함수가 일부 이유로 완료되지 않은 경우 변수가 부분적으로 수정 될 수 있습니다. 컴파일러는 이것을 최적화하기에 충분한 오버랩 분석을 할 수 있습니다. 나는 인텔 포트란이 이것을하지 않는다고 확신한다 - 의미 론적 제한이 중요하다.

예제는 장난감 프로그램입니다. 흥미로운 질문은 그러한 최적화가 적용 가능하고 보람있는 제작 응용 프로그램이있는 경우입니다.

인텔 포트란이 할당 가능한 배열에 대한 할당에 대한 기본 동작을 변경하여 버전 17에서 표준에 지정된대로 자동 재 할당이 발생한다고 설명합니다.

+0

언제든지 포트란에 대한 "이동 할당"의미 설정 시도를 알고 있습니까? 기본적으로 그렇게하는 것에 반대하여 좋은 점을 지적했지만, 함수/반환 값에 대한 명시 적 속성으로 구현 될 수있는 것처럼 보입니다. – kdb

+0

표준위원회에서 논의 된 내용을 들어 보지 못했습니다 (2008 년부터 회원으로 활동 해 왔습니다). 서브 루틴 호출로 원하는 것을 성취 할 수 있기 때문에 불필요한 복잡성으로 보입니다. –

0

나는 언젠가 같은 것을 가지고있다. 내가 잠시 멈추고 그것에 대해 생각할 때, 함수가 좋다는 것을 깨닫고 서브 루틴은 fortran에서와 마찬가지로 좋은 방식이라는 것을 알았습니다.

능력이있는 분을 상상해 우리는 다음과 같은 기능이 있습니다

function doThings(param) results(thing) 
    integer :: thing 
    integer, intent(in out) :: param 
    ! Local variables 
    integer :: genialUpdatedValue, onOfThePreviousResult 
    ! some other declarations 
    ! serious computation to do things 
    ! and compute genialUpdatedValue and onOfThePreviousResult 
    param = genialUpdatedValue 
    thing = onOfThePreviousResult 
end function doThings 

을 그리고 우리는 다음과 통화를 :

확실히 OK입니다
! some variables first 
integer, parameter :: N_THINGS = 50 ! just love 50 
integer :: myThing, myParam 
integer, dimension(N_THINGS) :: moreThings 
! 
! Reading initial param from somewhere 
! myParam now has a value 
! 
myThing = doThings(myParam) 

, 무엇에 대한

다음
! 
! Reading initial param from somewhere 
! myParam now has a value 
! 
moreThing = doThings(myParam) 

결과로 표시되는 내용은 무엇입니까? 그것은

integer :: i 
do i = 1, N_THINGS 
    moreThings(i) = doThings(myParam) 
end do 

을한다 또는이 하나

integer :: i, thing 
thing = doThings(myParam) 
do i = 1, N_THINGS 
    moreThings(i) = thing 
end do 

myParam 함수에 의해 변경됩니다 것을 기억해야한다. 이것은 단순한 경우라고 주장 할 수 있지만 결과는 정수가 아니라 큰 배열 구성원이있는 사용자 정의 유형이라는 이미지를 사용합니다.

생각해 보면 분명히 그런 문제가있을 것입니다. 물론 더 많은 제한이이 기능을 허용하기 위해 여기저기서 추가 될 수 있으며, 결국 우리는 충분한 수요가있을 때 필요한 제한으로 구현 될 것입니다. 도움이되기를 바랍니다.

+0

여기에서 문제를 고려해 보시기 바랍니다. 이 코드는 다소 혼란 스럽지만 문제는 SAVE 속성을 가진 변수를 반환하는 것이므로 데이터를 가리키는 포인터를 반환하고 복사하여 값을 반환하는 것입니다. – kdb

+0

여기에 save 속성이 없습니다. 문제는 반환 값이'param'과'param'에 따라 계산된다는 것입니다. 한 번 호출하고 동일한 값을 사용하여 배열의 모든 셀을 설정하는 것은 배열의 각 셀에 대해 함수를 호출하는 것과 완전히 다릅니다. 호출 할 때마다 함수에서'param '이 변경되어 반환 값이 달라집니다. 가장 단순한 예시는 함수가'param'을 증가시키고 리턴 값은'param'의 제곱이라는 것입니다. – innoSPG

+0

기본적으로 배열 *을 반환하지 않습니다. 나는 그 부분을 놓쳤다. 이것은 내가 말할 수있는 한 질문에 완전히 무관 한 대답을 만들 것입니다. 내 예제가 의도적으로 단순한 장난감 예제 였을지라도 (할당 테이블을 사용하지 않았기 때문에 지나치게 단순화되었을 수도 있음), 구체적으로 배열을 한 메모리 영역에서 다른 메모리 영역으로 복사하지 않아도 배열 값 함수의 결과를 효율적으로 반환하는 것입니다. – kdb