Accessing data with SafeArrayAccessData
A way to avoid the overhead associated with locking and unlocking a Safe Array is to use f90VB’s procedure SafeArrayAccessData [13]. This procedure puts a lock in the Safe Array and then maps the data-block of a Safe Array into a Fortran array, so your program accesses the Safe Array data in the same way it would do with a native Fortran array. Changes you make in the mapped Fortran array are automatically reflected in the Safe Array, because both objects are actually using the same memory locations. When your application has finished using the data-block of the Safe Array, it must call SafeArrayUnAccessData, which unlocks the data and destroys the mapping. The are two catches with this mechanism. First, your Fortran array only contains meaningful information from the moment you issue the call to SafeArrayAccessData to the moment you call SafeArrayUnAccessData. Remember that once a Safe Array is unlocked, the OS may relocate the memory location of the Safe Array’s data-block. Second, you must not explicitly DEALLOCATE the mapped Fortran arrays, even after you have called SafeArrayUnAccessData. The reason for this is that some Fortran compilers may attempt to release the memory of the mapped Fortran array to the operating system, which could result in an Access Violation error if the mapped Fortran array is sharing memory with a Safe Array that was allocated outside your Fortran program or subroutine (for example, an array created in Visual Basic and passed to a Fortran subroutine).
Example 2.5 illustrates the use of the two mechanisms to access Safe Array data explained in the previous sections. This example also shows a rough comparison of the performance of both mechanisms.
Example 2.5
program Example25
!This program compares the performance of
!accessing Safe Array data using the
!traditional method (SafeArrayGetElement)
!and SafeArrayAccessData
use f90VBDefs
use f90VBSafeArrays
implicit none
integer(SAFEARRAY_KIND)::SAHndl
integer(LONG_KIND)::i,j
integer(HRESULT_KIND)::iRet
real(REAL_KIND)::rValue
real(REAL_KIND),pointer::R4MappedMatrix(:,:)
integer,dimension(8)::StartTime,EndTime
!Allocate a Safe Array (100x100)
Call SafeArrayCreate(SAHndl,VT_R4, 1, 1000, 1, 1000)
!Set the values of the safe array to random numbers
call random_seed
print *, 'Filling a Safe Array using SafeArrayPutElement'
!Start timing for assignment loop
call date_and_time(values=StartTime)
do i=1,1000
do j=1,1000
call random_number(rValue)
call SafeArrayPutElement(SAHndl,rValue,i,j,iRet)
enddo
enddo
!Stop timing
call date_and_time(values=EndTime)
!print the elapsed time
print *,'Assignment loop:'
write (*,'(a15,i2.2,a1,i2.2,a1,i2.2,a1,i3.3)') &
'Starting time:',StartTime(5),':',StartTime(6),':', &
StartTime(7),'.',StartTime(8)
write (*,'(a15,i2.2,a1,i2.2,a1,i2.2,a1,i3.3)') &
'Ending time: ',EndTime(5),':',EndTime(6),':', &
EndTime(7),'.',EndTime(8)
print *,''
print *, 'Filling a Safe Array using SafeArrayPutAccessData'
!Start timing for mapping operation
call date_and_time(values=StartTime)
!Map matrix to Safe Array data-block
call SafeArrayAccessData(SAHndl,R4MappedMatrix,iRet)
!Stop timing
call date_and_time(values=EndTime)
!print the elapsed time
print *,'Mapping:'
write (*,'(a15,i2.2,a1,i2.2,a1,i2.2,a1,i3.3)') &
'Starting time:',StartTime(5),':',StartTime(6),':', &
StartTime(7),'.',StartTime(8)
write (*,'(a15,i2.2,a1,i2.2,a1,i2.2,a1,i3.3)') &
'Ending time: ',EndTime(5),':',EndTime(6),':', &
EndTime(7),'.',EndTime(8)
!Start timing for assignment loop
call date_and_time(values=StartTime)
do i=1,1000
do j=1,1000
call random_number(rValue)
R4MappedMatrix(i,j)=rValue
enddo
enddo
!Stop timing
call date_and_time(values=EndTime)
!print the elapsed time
print *,'Assignment loop:'
write (*,'(a15,i2.2,a1,i2.2,a1,i2.2,a1,i3.3)') &
'Starting time:',StartTime(5),':',StartTime(6),':', &
StartTime(7),'.',StartTime(8)
write (*,'(a15,i2.2,a1,i2.2,a1,i2.2,a1,i3.3)') &
'Ending time: ',EndTime(5),':',EndTime(6),':', &
EndTime(7),'.',EndTime(8)
!Start timing for unmapping
call date_and_time(values=StartTime)
!Map matrix to Safe Array data-block
call SafeArrayUnAccessData(SAHndl,iRet)
!Stop timing
call date_and_time(values=EndTime)
!Stop timing
call date_and_time(values=EndTime)
!print the elapsed time
print *,'Unmapping:'
write (*,'(a15,i2.2,a1,i2.2,a1,i2.2,a1,i3.3)') &
'Starting time:',StartTime(5),':',StartTime(6),':', &
StartTime(7),'.',StartTime(8)
write (*,'(a15,i2.2,a1,i2.2,a1,i2.2,a1,i3.3)') &
'Ending time: ',EndTime(5),':',EndTime(6),':', &
EndTime(7),'.',EndTime(8)
!Release memory used by the Safe Array
call SafeArrayDestroy(SAHndl, iRet)
stop
end
Figure 2.1 shows the results printed by Example 2.5 on a computer with a Pentium II – 400 MHz with Windows NT 4.0. Note that using SafeArrayPutElement required a total of 1.118 seconds to fill the array with random numbers. On the other hand, filling the Safe Array through the mapped Fortran array took only 0.256 seconds. The timing taken to map and un-map the Fortran array into the data-block of the safe array was negligible. In other words, using a mapped Fortran array to access the Safe Array data-block is more than 4 times faster than using SafeArrayGetElement or SafeArrayPutElement. This difference in performance, as explained before, is due to the overhead of internal locking and unlocking of the Safe Array each time SafeArrayGetElement or SafeArrayPutElement are called.