Fortran`forall`或`do concurrent`中的临时变量

时间:2013-09-11 10:41:43

标签: arrays loops for-loop fortran

我有一个使用forall制定的数组赋值。一个数组的索引必须从循环变量计算,所以我尝试以类似ii的方式使用临时变量这个简单的例子:

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

gfortran警告我“带有索引'i'的FORALL未在(1)的赋值左侧使用,因此可能会导致对该对象的多次赋值”,确实输出不是我想要的:

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

ifort甚至会抛出错误。)我意识到每次出现时我都可以重复ii的定义,但这在现实生活中会变得乏味。

Q1:有没有办法使用forall正确地执行此类操作?


现在,正如您可以阅读某些other questions,似乎不再推荐forall,而且直do甚至可能更快。

但是,在这种情况下,我对forall的使用与可读性和速度一样重要。在我的真实程序中,我有类似forall (i=1:10, j=1:10, i<=j)的东西,我喜欢forall标题允许我在没有嵌套循环的情况下指定它的方式。

我还在这些问题中了解了do concurrent,这似乎解决了问题,即用forall替换上面的do concurrent,输出变为:

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

我仍然可以使用forall标题。

Q2:是否使用了本地变量,该变量在forall中不起作用,由标准保证在do concurrent中工作? (而不是它在gfortran中起作用的侥幸。)

不幸的是,目前我无法使用do concurrent,因为稍早版本的ifortgfortran版本不支持它。

3 个答案:

答案 0 :(得分:3)

对于你问题中的情况我根本不认为需要标量ii,你可以替换

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

     b(i) = a(ii)
  end forall

  forall (i=1:20)
     b(i) = a((i+1)/2)
  end forall
在这种情况下,

会产生您想要的结果。至于你的问题:

A1:是的,如图所示。

A2: do concurrent是Fortran 2008中的标准功能,因此可以保证正常运行;保证,如果您有一个最新的编译器,并且该编译器已正确实现。到目前为止,我只使用do concurrent和最新版本的英特尔Fortran编译器,并且没有发现任何非标准行为。

在Vladimir F的评论之后

是的,重写的forall结构可能需要幕后的临时数组,但我相信我所写的内容在语法上是正确的,并且在语义上等同于OP的原始代码。关于do concurrent构造的合法性,以下

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

编译并执行得很好。同样,我认为这是符合标准的代码和行为。

答案 1 :(得分:2)

您似乎正在使用forall我自然会使用do循环:

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

forall语句对于要将掩码应用于整个数组赋值的条件单行特别有用,但即便如此,我个人更喜欢do循环。

forall (i=1:10, j=1:10, i<=10)的例子也没有多大意义,因为最后的表达式不会掩盖任何值。

forall和其他全数组赋值背后的想法是编译器会知道如何更好地优化整个事物,但在实践中(好吧,至少从上次我亲自检查过),它通常会导致代码较慢。

编辑:

只是想提一下,直接将计算出的整数分配给它可能会更好 b中的数组值,而不是通过使用:

再次从内存中获取相同的内容
do i=1,20
  b(i)=(i+1)/2
end do

但我希望这只是因为这个例子过于简化了? :)

答案 2 :(得分:2)

正如Mark指出的那样,在ii构造中定义forall是不必要的,因为您可以在定义中使用a((i+1)/2)。作为另一种选择,如果您完全坚持使用forall,则可以使用pure function(或elemental function)来设置索引:

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

将此与a=(/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)一起使用,我得到输出

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