 |
forums.silverfrost.com Welcome to the Silverfrost forums
|
View previous topic :: View next topic |
Author |
Message |
KL
Joined: 16 Nov 2009 Posts: 155
|
Posted: Tue Nov 17, 2009 6:43 pm Post subject: Problem: �Problem Array Section with /Checkmate� |
|
|
The attached program looks terrible. However, it reflects the structure of a �real� program, where I found the problem. The problem occurs when running the program with the /Checkmate option, which I almost always use:
ftn95 Problem_Array_Section.f95 /Checkmate /underflow /Link >> comp.lis
sdbg Problem_Array_Section.exe
The error message is
�Attempt to call a routine with argument number two containing too few array elements�
The reason why I chose this form is because in the real problem the indices of array x (1,2,3..) in subroutine SubB correspond to indices 2,3,4 of array Data in subroutine SubA. For this transformation I need the array section as argument.
Is anything wrong with the program? Can anybody comment on this problem?
Many thanks,
Klaus Lassmann
[ Winapp
Program Problem_Array_Section
Implicit None
Integer :: i, n
Real , Dimension (400) :: Result
n = 3
! Subroutine SubA calculates with the help of SubB the field Result
Call SubA ( Result, n )
! #########
Write (*,*) ' Result (1,n)= ', ( Result (i), i=1,n )
Contains
Subroutine SubA (Data, m)
Implicit None
Integer :: i
Integer , Intent (in ) :: m
Real , Dimension (m), Intent (out) :: Data
! --- This call leads to an error when compiled with /Checkmate:
Call SubB ( m, Data (1:m) )
! --- This call seems to be OK
! Call SubB ( m, Data )
Write (*,*) ' Data (1,m) = ', ( Data (i), i=1,m )
End Subroutine SubA
End Program Problem_Array_Section
! --------------------------------------------------------------------
Subroutine SubB ( k,x )
Implicit None
Integer , Intent (in ) :: k
Real , Dimension (k), Intent (out) :: x
Integer :: i
Do i = 1, k
x(i) = i**2
End Do
End Subroutine SubB
] |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Tue Nov 17, 2009 11:58 pm Post subject: |
|
|
Paul,
This second example shows that errors associated with array sections in subroutine arguments when compiled with /check are not limited to automatic arrays. The program works ok with /debug.
As I have found before, if I use array sections, I can not debug using /check.
Can this be investigated ?
Thanks John |
|
Back to top |
|
 |
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 8210 Location: Salford, UK
|
Posted: Wed Nov 18, 2009 9:39 am Post subject: |
|
|
Yes this looks like a problem I need to look at but I may not be able to give you a quick response at the moment. |
|
Back to top |
|
 |
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 8210 Location: Salford, UK
|
Posted: Thu Nov 19, 2009 9:02 am Post subject: |
|
|
I have identified the problem and expect to be able to fix it in one way or another.
The only short term solution is to use /inhibit_check 5 on the command line.
You could put subB in a separate file in order to apply this only to the subroutine. |
|
Back to top |
|
 |
KL
Joined: 16 Nov 2009 Posts: 155
|
Posted: Thu Nov 19, 2009 9:50 am Post subject: |
|
|
Paul,
thank you very much for your quick reaction. For me it is easy to circumvent this problem. I guess that this problem will be solved with the next update and I will wait for it.
Best wishes,
Klaus |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Fri Dec 04, 2009 12:15 am Post subject: |
|
|
Paul,
Any update on this, as this bug has limited my use of both /check, SDBG and array sections for a number of years.
It would be a good one to clean up.
John |
|
Back to top |
|
 |
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 8210 Location: Salford, UK
|
Posted: Fri Dec 04, 2009 9:10 am Post subject: |
|
|
I have been working on this but it is one of those tricky ones.
I am reasonably hopeful that it will be fixed for the next release. |
|
Back to top |
|
 |
IanLambley
Joined: 17 Dec 2006 Posts: 506 Location: Sunderland
|
Posted: Sat Dec 05, 2009 1:22 pm Post subject: |
|
|
I'm not sure why you ever need to use the form
Call SubB ( m, Data (1:m) )
If you are passing m as a variable, then the range (1:m) is redundant.
If you argue that you may want to pass another section of the array such as Data(i:j) then this can be done as:
i=5
j=10
m=j-i+1
call SubB(m,data(i))
for example if i=5 and j=10, then the following elements are required in the subroutine: 5, 6, 7, 8, 9, 10 which is six and m=10 - 5 + 1 = 6. This statement then passes 6 elements of array "data" starting with the 5th.
All you are passing for an array is the address of the start element and if that is the very first element, i.e. data(1) or data(1,1) then you only need to pass "data" and this you have done in the part of your example that worked. Even the shape of the array can be different from one program segment to another, but care is required if it is of rank 2 or higher.
The only reason that you might want to pass data(1:m) is if you also do not pass m and use a dummy dimension and the size finction to determine the passed size. See below:
Code: |
winapp
integer*4 data1(10), i
do i=1,10
data1(i) = i
enddo
call sub1(data1(2:4))
end
subroutine sub1(idata)
integer*4 idata(*),isize
isize = size(idata)
print *,'array contains ',isize,' elements'
print *,(idata(i),i=1,isize)
end
|
When compiling this with only /debug, the size of the array in the subroutine seems to be 538976289, which is ridiculous.
By adding /check, it partially works. I would have thought that it should work without the need to use any specific compiler switch. This might be in the documentation somewhere, but if not, it should be.
What happens is that the "size" function returns 9, which IS the size of the array starting at position2 to the end, i.e. position 10. I would have thought that it should have been of size 3.
I seems to be safer to go back to the old fashioned method as shown in sub2
Code: |
winapp
integer*4 data1(10), i, m
do i=1,10
data1(i) = i
enddo
m=4 - 2 + 1
call sub2(data1(2),m)
end
subroutine sub2(idata,m)
integer*4 idata(m),isize
isize = size(idata)
print *,'array contains ',isize,' elements'
print *,(idata(i),i=1,isize)
end
|
Comments please!!
Ian |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Sat Dec 05, 2009 11:15 pm Post subject: |
|
|
Ian,
I have code which is of the form
Code: | call ends (data(6:7,n))
...
subroutine ends (end_data)
integer*4 end_data(2)
... |
If I compile this with /debug, I think there is no problem, but if I compile it with /check, I get an error that the size of array is wrong. I can always overcome it by changing the code with a temporary array or not using /check. Unfortunately it is in a development project/hobby which at present I don't have much time to complete, so will have to wait till the bug goes. I've been trying to clean out old code patches and be more Fortran 95+ consistent.
John |
|
Back to top |
|
 |
IanLambley
Joined: 17 Dec 2006 Posts: 506 Location: Sunderland
|
Posted: Sun Dec 06, 2009 12:00 am Post subject: |
|
|
Hi John,
I assume that data is a two dimensional array and that your intent is that the subroutine deals with the two elements in positions 6 & 7 on row n.
I think that it is only necessary to pass data(6,n) to the subroutine and as it is dimensioned to two in the subroutine, then it is treated within as a one dimensional array of two elements.
I have never understood the need for the array range format described whilst passing the info. I only use the start:end format for the dimensioning of arrays where the default start element is not 1.
But of course I'm an old fashioned guy and I might have missed the introduction of this method.
Multi dimensional arrays can be tricky to pass by the method described if one is hoping to refer to a small rectangular section which does not start at (1,1,1...) That is
Code: |
consider a matrix containing values thus:
i----->
j a b c d e f
| g h i j k l
| m n o p q r
v
This is stored in memory as a linear block of memory containing:
a b c d e f g h i j k l m n o p q r
|
Trying to pass the sub-array
i j and
o p using
call sub(data(3,2))
with
subroutine sub(data)
dimension data(2,2)
will just end up with the data in the subroutine containing:
i j
k l
That is, the call references the start element and the dimension of 2,2 takes the next 4 elements.
Using the start:end format for two-dim arrays would only make sense if one could pass:
data(3:4,2:3)
but then the compiler would have to extract the appropriate elements where it knows the original shape of the array, and reform these into separate linear storage and pass the pointer to this new grouping to the routine. On return, it would then have to reverse the process and set any new values which may have been changed in the subroutine. This is very messy, and I just don't think that Fortran does that. The time overhead would be excessive for large multi dimensional arrays.
Please confirm
Regards
Ian |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Sun Dec 06, 2009 1:57 am Post subject: |
|
|
Ian,
My understanding is that when providing "data(3:4,2:3)" as an argument, then the compiler will provide a temporary array(2,2) and update it when it returns. I tested this on a recent post (see general>use of type arrays) by changing the argument, while the original array did not change, but was updated on return.
It's always difficult to stop using old Fortran, when it worked so well.
John[/quote] |
|
Back to top |
|
 |
IanLambley
Joined: 17 Dec 2006 Posts: 506 Location: Sunderland
|
Posted: Sun Dec 06, 2009 10:05 am Post subject: |
|
|
John,
Wow - that is useful, I shall now have to rewrite all my software even though it works, well some of it works.
Test program where the array elements contain the subscripts in the form i*1000+j and the test subroutine negates the value so you can see the modification to the main routine data.
Code: |
winapp
integer*4 i,data2(10,10),i1,i2,j1,j2,m12,n12
!set some data
do i=1,10
do j=1,10
data2(i,j) = i*1000 + j
enddo
enddo
!show the original contents
print *,'before call main data2 array contains '
do j=1,10
print '(10i5)',(data2(i,j),i=1,10)
enddo
!define the sub array and its dimensions
i1 = 3
i2 = 6
m12 = i2 - i1 + 1
j1 = 2
j2 = 8
n12 = j2 - j1 + 1
!call the test subroutine to negate the sub matrix values
call sub3(data2(i1:i2,j1:j2),m12,n12)
!display result of the action on the full matrix
print *,'after call main data2 array contains '
do j=1,10
print '(10i5)',(data2(i,j),i=1,10)
enddo
end
subroutine sub3(idata,m,n)
integer*4 idata(m,n),i,j,m,n
print *,'passed array contains '
do j=1,n
print *,(idata(i,j),i=1,m)
do i=1,m
idata(i,j) = -idata(i,j)
enddo
enddo
end
|
Thanks John!
Ian |
|
Back to top |
|
 |
IanLambley
Joined: 17 Dec 2006 Posts: 506 Location: Sunderland
|
Posted: Sun Dec 06, 2009 10:50 am Post subject: |
|
|
But of course, I wan't happy about having to pass the dimensions, so using modules, interfaces and colons gives:
Code: |
winapp
!create a module with interfaces for subroutines where necessary
module interfaces
INTERFACE
SUBROUTINE SUB4(idata)
INTEGER idata(:,:)
END SUBROUTINE
END INTERFACE
end module interfaces
! add this statement wherever one of these subroutine is to be called
use interfaces
! create the test data
integer*4 i,j,data2(10,10),i1,i2,j1,j2
do i=1,10
data1(i) = i
do j=1,10
data2(i,j) = i*1000 + j
enddo
enddo
! setup the range of the sub matrix
i1 = 3
i2 = 6
j1 = 2
j2 = 8
! check the data content
print *,'before call main data2 array contains '
do j=1,10
print '(10i5)',(data2(i,j),i=1,10)
enddo
! call the subroutine
call sub4(data2(i1:i2,j1:j2))
! show the changesprint *,'after sub4 call main data2 array contains '
do j=1,10
print '(10i5)',(data2(i,j),i=1,10)
enddo
end
subroutine sub4(idata)
! use blank place holders to show passed array is rank 2
integer*4 idata(:,:),i,j,m,n
m=size(idata,1)
n=size(idata,2)
print *,'array dimensions are',m,n
print *,'passed array contains '
do j=1,n
! show the sub matrix as passed
print *,(idata(i,j),i=1,m)
! make some changes
do i=1,m
idata(i,j) = -idata(i,j)
enddo
enddo
end
|
Now I understand, what you tried to say to me!
And how I suffered for my sanity.
etc.
Sorry about the corrupted song lyrics
And the following is also possible:
Code: |
call sub4(data2(i1:i2,j1:j2:2))
|
Only alternate rows from data2 are passed, i.e. j1=start row, j2 = finish row and 2 is the row increment.
Regards
Ian |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Sun Dec 06, 2009 1:19 pm Post subject: |
|
|
Ian,
I changed your program, using a module to access the original data2 array. The changed program shows (to me) that there must be a copy of the sectioned array data2, being used in sub4. It would appear that all sections are placed on the stack or heap, so we should be careful when specifying sections of large arrays,
for example with dot_product (a(i1:i2), b(j1:j2)); this could be very inefficient, in comparison to vecsum (a(i1), b(j1), i2-i1+1), when i2-i1 is large. I have made this mistake in the past! and now realise why the newish code did not work well.
Code: | !create a module with interfaces for subroutines where necessary
module all_data
integer*4 data1(10), data2(10,10)
end module all_data
module interfaces
INTERFACE
SUBROUTINE SUB4(idata)
INTEGER idata(:,:)
END SUBROUTINE
END INTERFACE
end module interfaces
! add this statement wherever one of these subroutine is to be called
use all_data
use interfaces
! create the test data
integer*4 i,j
!
do i=1,10
data1(i) = i
do j=1,10
data2(i,j) = i*1000 + j
enddo
enddo
! check the data content
print *,'before call main data2 array contains '
do j=1,10
print '(10i6)',(data2(i,j),i=1,10)
enddo
! call the subroutine
call sub4 ( data2(3:6,2:8) )
! show the changesprint *,'after sub4 call main data2 array contains '
print *,'original array after return from sub4 contains '
do j=1,10
print '(10i6)',(data2(i,j),i=1,10)
enddo
end
subroutine sub4 (idata)
use all_data
! use blank place holders to show passed array is rank 2
integer*4 idata(:,:),i,j,m,n
m=size (idata,1)
n=size (idata,2)
print *,'array dimensions are',m,n
print *,'passed array contains '
do j=1,n
! show the sub matrix as passed
print *,(idata(i,j),i=1,m)
! make some changes
do i=1,m
idata(i,j) = -idata(i,j)
enddo
enddo
!
print *,'passed array after changes contains '
do j=1,n
! show the sub matrix as passed
print *,(idata(i,j),i=1,m)
enddo
!
print *,'original array after changes contains '
do j=1,10
print '(10i6)',(data2(i,j),i=1,10)
enddo
end
|
|
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Mon Dec 07, 2009 3:02 am Post subject: |
|
|
Ian,
I further changed the program to test both 2 dimension arrays and 1 dimension vectors. Based on the results, it may appear that the sub section is generated if the required array is not continuous in memory, as the 1 dimensional vector appears to change.
Code: | !create a module with interfaces for subroutines where necessary
module all_data
integer*4 data1(10), data2(10,10)
end module all_data
module interfaces
INTERFACE
SUBROUTINE SUB4(jdata, idata)
INTEGER jdata(:), idata(:,:)
END SUBROUTINE
END INTERFACE
end module interfaces
! add this statement wherever one of these subroutine is to be called
use all_data
use interfaces
! create the test data
integer*4 i,j
!
do i=1,10
data1(i) = i
do j=1,10
data2(i,j) = i*1000 + j
enddo
enddo
! check the data content
print *,'before call main 10x10 array contains '
do j=1,10
print '(10i6)',(data2(i,j),i=1,10)
enddo
! call the subroutine
call sub4 ( data1(3:6), data2(3:6,2:8) )
! show the changes
print *,'original 10x10 array after return from sub4 contains '
do j=1,10
print '(10i6)',(data2(i,j),i=1,10)
enddo
print *,'original vector data1 after return from sub4 contains '
print '(10i6)',(data1(i),i=1,10)
end
subroutine sub4 (jdata, idata)
use all_data
! use blank place holders to show passed array is rank 2
integer*4 jdata(:), idata(:,:),i,j,m,n
!
m=size (idata,1)
n=size (idata,2)
print *,'array idata dimensions are',m,n
print *,'passed array contains '
do j=1,n
print '(10i6)',(idata(i,j),i=1,m)
! make some changes
do i=1,m
idata(i,j) = -idata(i,j)
enddo
enddo
!
print *,'passed array after changes contains '
do j=1,n
print '(10i6)',(idata(i,j),i=1,m)
enddo
!
print *,'original 10x10 array after changes contains '
do j=1,10
print '(10i6)',(data2(i,j),i=1,10)
enddo
!
m=size (jdata)
print *,'vector jdata dimension is',m
print *,'passed vector contains '
print '(10i6)',(jdata(i),i=1,m)
! make some changes to vector
do i=1,m
jdata(i) = -jdata(i)
end do
!
print *,'passed vector after changes contains '
print '(10i6)',(jdata(i),i=1,m)
!
print *,'original vector after changes contains '
print '(10i6)',(data1(i),i=1,10)
print *,'NOTE ORIGINAL VECTOR HAS CHANGED, SO NO TEMPORARY VECTOR !!'
!
end
|
So my past experience with inefficiencies associated with sections of the form "data1(i1:i2)" are not due to temporary arrays.
John
ps : I also tried this program in SDBG, but got the asembler code window, rather than the source code to display. Don't know why. Linked with "ftn95 ian.f95 /check /link". I still can't drive SDBG !! |
|
Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|