Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Avoid aliasing assumption in intrinsic assignment

Sebastien_B_
Beginner
1,069 Views

Dear Ifort team, in the following program, I ask for 2 assignments. One is using the targets themselves, the other is using pointers instead: program test real(kind=4), target :: a(2),b(2) real(kind=4), pointer :: pa(:),pb(:) ! b = a pa => a pb => b pb = pa ! end program test Quoting this document: https://software.intel.com/en-us/articles/fortran-array-data-and-arguments-and-vectorization "Potential overlap of pointer based variables on the left-hand side (LHS) expression and the right-hand side (RHS) will cause array temporaries to be created to hold the RHS expression as it's evaluated prior to storing to the LHS." There is a temporary array involved in the second (pointer to pointer) assignment. This affects clearly the execution time when using large arrays and/or repeating many times the copy. While the standard says more or less that "you'll get what you deserve" when aliasing actual arguments passed to a procedure, on the other hand it ensures that the assignment works "as expected" with the following rule (Fortran 2003, 7.4.1.3): "The execution of the assignment shall have the same effect as if the evaluation of all operations in expr and variable occurred before any portion of variable is defined by the assignment." So, here is where we stand: 1) the standard says the assignment must work in any case, 2) the LHS and RHS may be aliased when pointers are involved, 3) the compiler introduces a temporary array for safety. Am I correct up to now? So, here is the question: is it possible to avoid this temporary array? I looked at the alias-related compilation options, but they seem all focused on argument aliasing. Or, is there any hope that ifort relaxes the 3rd point above, and that it checks if LHS and RHS actually overlap or not? I would say this is very similar to what is done regarding arrays, contiguity, strides, etc in the document I pointed above. I can also add that in my real use-case: - I need those pointers for other reasons / I can not switch to allocatables, - I always keep in mind efficiency when working with arrays (contiguity, etc), - I would accept to take the responsibility of ensuring non-aliasing of LHS and RHS if the temporary array could be disabled by force e.g. with an option. Thanks, Sebastien

0 Kudos
13 Replies
Sebastien_B_
Beginner
1,069 Views
(Problem with rich-text! Same post again) Dear Ifort team, in the following program, I ask for 2 assignments. One is using the targets themselves, the other is using pointers instead: program test real(kind=4), target :: a(2),b(2) real(kind=4), pointer :: pa(:),pb(:) ! b = a pa => a pb => b pb = pa ! end program test Quoting this document: https://software.intel.com/en-us/articles/fortran-array-data-and-arguments-and-vectorization "Potential overlap of pointer based variables on the left-hand side (LHS) expression and the right-hand side (RHS) will cause array temporaries to be created to hold the RHS expression as it's evaluated prior to storing to the LHS." There is a temporary array involved in the second (pointer to pointer) assignement. This affects clearly the execution time when using large arrays and/or repeating many times the copy. While the standard says more or less that "you'll get what you deserve" when aliasing actual arguments passed to a procedure, on the other hand it ensures that the assignment works "as expected" with the following rule (Fortran 2003, 7.4.1.3): "The execution of the assignment shall have the same effect as if the evaluation of all operations in expr and variable occurred before any portion of variable is defined by the assignment." So, here is where we stand: 1) the standard says the assignment must work in any case, 2) the LHS and RHS may be aliased when pointers are involved, 3) the compiler introduces a temporary array for safety. Am I correct up to now? So, here is the question: is it possible to avoid this temporary array? I looked at the alias-related compilation options, but they seem all focused on argument aliasing. Or, is there any hope that ifort relaxes the 3rd point above, and that it checks if LHS and RHS actually overlap or not? I would say this is very similar to what is done regarding arrays, contiguity, strides, etc in the document I pointed above. I can also add that in my real use-case: - I need those pointers for other reasons / I can not switch to allocatables, - I always keep in mind efficiency when working with arrays (contiguity, etc), - I would accept to take the responsibility of ensuring non-aliasing of LHS and RHS if the temporary array could be disabled by force e.g. with an option. Thanks, Sebastien
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,069 Views

>>While the standard says more or less that "you'll get what you deserve" when
aliasing actual arguments passed to a procedure

ergo create an inline subroutine to perform the copy (and hope the optimizer does not thwart your intentions).

Jim Dempsey

0 Kudos
TimP
Honored Contributor III
1,069 Views

!dir$ simd sometimes suppresses creation of a temporary array.  ifort 15.0 took care of a few cases where this was needed on a DO loop.

0 Kudos
Sebastien_B_
Beginner
1,069 Views
jimdempseyatthecove wrote:
ergo create an inline subroutine to perform the copy (and hope the optimizer does not thwart your intentions).
Indeed, that is one of the options I tested. But this means that if I want to keep my code efficient I have to replace all my assignments involving pointers by a copy subroutine. This is just crazy!
Tim Prince wrote:
!dir$ simd sometimes suppresses creation of a temporary array. ifort 15.0 took care of a few cases where this was needed on a DO loop.
This is a compiler specific syntax... I am willing to find either a Fortran-standard solution, or a per-compiler global solution (option). Both of your suggestions imply editing all of my assignments involving pointers and use something specific. This seems hard to maintain for me and my current and future co-workers. I was looking more for a general solution from the compiler itself.
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,069 Views

Where you have a pointer that is known to not alias, you could redefine the variable to be user defined type, containing only the pointer and an assignment operator. Then your source could contain.

  pa = pb

And behind the scenes you call the copy function.

However, this may come at the expense of requiring pa%p => targetedArray

Though this may also be addressed by use of a second assignment operator (one for like pointer, another for type of array it points to).

Jim Dempsey

0 Kudos
TimP
Honored Contributor III
1,069 Views

I have submitted some cases to Intel premier support where ifort requires the proprietary !dir$ simd to accomplish a result which gfortran accomplishes with !$omp simd.  For the latter (OpenMP 4 standardized) directive, you need newer compilers.  One of the objectives for OpenMP 4, which Intel supported, was to cut down on these differences among compilers.  Still, Intel compilers frequently rely on directives to achieve superior performance.

Global optimization flags aren't satisfactory if they imply risking failure of standard-compliant code.  Enough flak comes from customers whose code breaks due to violations of standard which weren't exposed before.

The improvement which I mentioned in the ifort 15 release indicates that Intel isn't necessarily insisting on continuing use of proprietary directives where they can be made redundant.  The situation is confused by the slogan about directive based vectorization in the publicity about the newer releases.

0 Kudos
Sebastien_B_
Beginner
1,069 Views

jimdempseyatthecove wrote:

Where you have a pointer that is known to not alias, you could redefine the variable to be user defined type, containing only the pointer and an assignment operator. Then your source could contain.

  pa = pb

And behind the scenes you call the copy function.

However, this may come at the expense of requiring pa%p => targetedArray

Though this may also be addressed by use of a second assignment operator (one for like pointer, another for type of array it points to).

Jim Dempsey

This seems an interesting path, but I think that accessing the actual data as pa%p will make things worse everywhere else. Or then I have to redefine all the other methods which I need. This seems costly and dangerous...

I also realized that Fortran 2008 allows me to provide a "defined assignment", but unfortunately I am not allowed to redefine the assignment for 2 arrays of same type, kind and rank. Too bad.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,069 Views

You also must be mindful that at some future time, you or your successor might be (forced into) using

pa => NonContiguousArraySection(From:To:Stride)

And then your efforts to avoid the temporary copy on pb = pa will come back and bite you (them).

If you use the user defined type with assignment operator(s), you could examine the array descriptor for stride. For stride of 1 you could then do the equivalent of the memcopy, for other strides use cell by cell copy.

Jim Dempsey

0 Kudos
Sebastien_B_
Beginner
1,069 Views
It seems that declaring the pointer as CONTIGUOUS solves the issue: real(kind=4), pointer, contiguous :: pa(:),pb(:) At first level I do not understand why, since 2 contiguous pointers can still alias each other. The only reason I can imagine is that the compiler (ifort 14.0.2) as enough details about the pointers to disable the temporary array. Can someone please confirm this?
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,069 Views

>> since 2 contiguous pointers can still alias each other

However, they (contiguous pointers) are also guaranteed to have a stride of 1. In the general case, pointers may have non-1 strides.

Jim Dempsey

0 Kudos
Sebastien_B_
Beginner
1,069 Views
jimdempseyatthecove wrote:
>> since 2 contiguous pointers can still alias each other However, they (contiguous pointers) are also guaranteed to have a stride of 1. In the general case, pointers may have non-1 strides.
I do not see the point. Here is where we stand: - while contiguity does not ensure non-aliasing, the compiler checks if it worth adding a temporary array in between CONTIGUOUS pointers, - but on the other hand, non-aliased non-CONTIGUOUS pointers do not benefit this optimization. As far as I understand, if stride is known to be 1 at compilation time, the temporary array may be disabled (non-aliasing still must be checked at runtime!), but if stride-1 is not known at compilation time, the optimization is just dropped off. Is it a problem to check for stride-1 at run time, and fall back to the same optimizations as the CONTIGUOUS pointers case?
0 Kudos
Steven_L_Intel1
Employee
1,069 Views

I don't think that we do a run-time check for assignments. Currently we do this only when passing assumed-shape arguments to a routine that wants a contiguous array.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,069 Views

Sebastien,

While I agree with you that code could (should) be added to test to see if a temporary can be avoided (when beneficial), this is apparently an optimization request that is low on the list of feature requests (list ranking made by compiler developers).

I would like to point out that a lot of optimization effort went in to generating code to auto-test and perform peel and loop remainder operations to enhance vectorization. It should be easier to perform overlap and/or contiguity tests and then choose the best of several copy (construct) strategies.

Jim Dempsey

0 Kudos
Reply