1

I have an array assignment that I formulated using forall. The indices to one array have to be computed from the loop variables, so I tried to use temporary variables in a way analogous to ii this simple example:

program test
  integer :: i, ii, a(10), b(20)
  a=(/(i, i=1,10)/)

  forall (i=1:20)
     ii = (i+1)/2

     b(i) = a(ii)
  end forall

  print '(20I3)', b
end program test

But gfortran warns me that “The FORALL with index 'i' is not used on the left side of the assignment at (1) and so might cause multiple assignment to this object”, and indeed the output is not what I wanted:

10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10

(ifort even throws an error.) I realize I could just repeat the definition of ii every time it appears, but that can get tedious in real-life cases.

Q1: Is there a way to do this sort of thing correctly using forall?


Now, as you can read in some other questions, forall no longer seems to be recommended, and a straight do may even be faster.

However, my use of forall in this instance was as much about readability as about speed. In my real program, I have something like forall (i=1:10, j=1:10, i<=j), and I like the way the forall header lets me specify this without nested loops.

I also learned about do concurrent in those questions, and that seems to solve the problem, i.e. replacing forall above with do concurrent, the output becomes:

1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10

and I still get to use a forall header.

Q2: Is this use of a local variable, which does not work in forall, guaranteed by the standard to work in do concurrent? (Rather than it being a fluke that it worked in gfortran.)

Unfortunately, for now I cannot use do concurrent anyway, because slightly older ifort and gfortran versions do not support it.

Community
  • 1
  • 1
xebtl
  • 450
  • 4
  • 11
  • A local, temporary variable is fine inside a DO CONCURRENT construct, since concurrency, in Fortran, actually implies arbitrary ordering of iterations of the loop, and not parallelism. – physkets Nov 12 '18 at 06:51

3 Answers3

3

For the case in your question I don't see the need for the scalar ii at all, you could replace

  forall (i=1:20)
     ii = (i+1)/2

     b(i) = a(ii)
  end forall

with

  forall (i=1:20)
     b(i) = a((i+1)/2)
  end forall

which produces the result you want, in this case. As to your questions:

A1: Yes, as illustrated.

A2: do concurrent is a standard feature in Fortran 2008 so it is guaranteed to work; guaranteed if you have an up-to-date compiler and that compiler has been correctly implemented. So far I've only used do concurrent with a recent edition of the Intel Fortran compiler and have found no non-standard behaviour.

After Vladimir F's comment:

Yes, the rewritten forall construct may require a temporary array behind the scenes but I believe that what I wrote is syntactically correct and semantically equivalent to OP's original code. As to the legality of a do concurrent construct, the following

do concurrent (i=1:20)
   b(i) = a((i+1)/2)
end do

compiles and executes just fine. Again, I think this is standard-conforming code and behaviour.

High Performance Mark
  • 77,191
  • 7
  • 105
  • 161
  • Remark:this `forall` solution will require an array temporary, but it is probably not a problem. For `do concurrent` it would be probably illegal. But otherwise +1 – Vladimir F Героям слава Sep 11 '13 at 12:19
  • @Mark Ad 1: This is what I was trying to avoid. Of course it is just a matter of convenience, and readability. Ad 2: I edited the question to be more explicit about this point. (I take it your answer would still be ‘yes’). – xebtl Sep 11 '13 at 12:45
  • @VladimirF: Why would that solution be illegal in `do concurrent`? – xebtl Sep 11 '13 at 12:46
  • @VladimirF Why do you think forall will require an array temporary here? This doesn't strike me as a case where compilers should struggle. The presented do concurrent seems legal (and note it is legal with the ii temporary variable approach too, which is a neat aspect of do concurrent). – IanH Sep 11 '13 at 12:48
  • 2
    You seem to be wedded to the idea of using `ii` inside your `forall` or `do concurrent` construct. I don't see the need, so I suspect that you have asked a simplified form of your real question. And I see that IanH has now properly answered part of your question which I did not. – High Performance Mark Sep 11 '13 at 12:51
  • Sorry, my bad. A and b are distinct. – Vladimir F Героям слава Sep 11 '13 at 12:58
2

You seem to be using forall where I would naturally use a do loop:

do i=1,20
  ii=(i+1)/2
  b(i)=a(ii)
end do

The forall statement is especially useful for conditional one-liners where you want to apply a mask to a whole-array assignment, but even then I personally prefer do loops.

Your example of forall (i=1:10, j=1:10, i<=10) also doesn't make much sense, as the final expression doesn't mask any values.

The idea behind forall and other whole-array assignments is that the compiler would know how to optimize the whole thing better, but in practice (well, at least since last I checked personally), it usually leads to slower code.

EDIT:

just wanted to mention that it's probably better to directly assign your computed integer to the array value in b, instead of fetching the same thing from memory again through the use of a:

do i=1,20
  b(i)=(i+1)/2
end do

but I hope this is just because the example is over-simplified? :)

steabert
  • 6,540
  • 2
  • 26
  • 32
  • As for the mask, I meant to write `forall (i=1:10, j=1:10, i<=j)`; I corrected this in the question. In this and the example program, I tried to boil down my point to a short, simple example -- sorry if I oversimplified. As for the do loop: I know, but I consider the `foreach` more elegant (see original question). – xebtl Sep 11 '13 at 12:36
  • 1
    Until today, I have never thought that anyone would think a `forall` construct to be "more elegant" than a `do` loop. – Kyle Kanos Sep 11 '13 at 15:15
  • @KyleKanos: Well, you can see I'm no real Fortran programmer :-). The point to me is that the `forall` can express two (or more) nested loops in a single line. – xebtl Sep 12 '13 at 07:47
  • 1
    @xebtl I understand that, but in a lot of cases where the loop body becomes more complex and you want to optimize it, you'll need to use do loops anyway and it may look even worse than a simple nested do loop :) But I get your point, unfortunately from experience, I found out that performance is usually reversely proportional to elegance. – steabert Sep 12 '13 at 08:11
  • that should be 'inversely', not 'reversely' I guess – steabert Sep 12 '13 at 16:01
2

As Mark points out, defining ii inside the forall construct is unnecessary as you can use a((i+1)/2) in your definition. As yet another alternative, if you are completely wedded to using the forall, you can use pure function (or an elemental function) to set the indices:

program forall_test
   ...
  forall(i=1:20)
     b(i) = a(set(i))
  end forall 

contains
  pure function set(i)
    integer, intent(in) :: i
    integer :: set
    set = (i+1)/2
  end function set
end program

Using this with a=(/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/), I get as output

1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10
Kyle Kanos
  • 3,257
  • 2
  • 23
  • 37
  • 1
    upvoted, most hilarious expansion of making something simple complicated :) Of course, in a more real example this might be a nice touch – steabert Sep 11 '13 at 16:00
  • @steabert: if someone wants to *intentionally* restrict their ability to write good code by using a "more elegant" work-around, then making something complex is necessary :) --Truth be told, if the indices were a bit more complex (say involving `if`s), this is probably a really good way to do it (assuming, of course, that you're still restricting yourself to `forall`). – Kyle Kanos Sep 11 '13 at 16:20
  • Thanks, this is an actual answer to my question :-) – xebtl Sep 12 '13 at 07:42