# \`target\` attribute seemingly affecting performances

**URL:** <https://fortran-lang.discourse.group/t/target-attribute-seemingly-affecting-performances/9137>\
**Category:** GNU\
**Created:** [January 28, 2025, 10:27am UTC](https://fortran-lang.discourse.group/t/target-attribute-seemingly-affecting-performances/9137 "2025-01-28T10:27:20Z")\
**Posts on this page:** 1\
**Showing post:** 25

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [January 29, 2025, 10:23pm UTC](https://fortran-lang.discourse.group/t/target-attribute-seemingly-affecting-performances/9137/25 "2025-01-29T22:23:10Z")

</div>

I noticed this recently for the Level 2 BLAS [`SGER`](https://netlib.org/lapack/explore-html/d8/d75/group__ger_ga95baec6bb0a84393d7bc67212b566ab0.html)/[`DGER`](https://netlib.org/lapack/explore-html/d8/d75/group__ger_gaef5d248da0fdfb62bccb259725935cb8.html) operation which computes the rank-1 update:

A = A + \alpha x y^T

The matrix A is m \times n, the vector x is m \times 1 and the vector y is n \times 1. In Fortran there are multiple ways to perform this (assuming you don’t just use BLAS):

```fortran
program rank1_update
implicit none
real :: A(3,4)
real :: x(3), y(4)
real :: alpha
integer :: i

alpha = 1.0
x = [1,2,3]
y = [1,2,3,4]

A = 0
call sger_dc(alpha,A,x,y)
print '(4(F8.3))', (A(i,:), i=1,3)
print '()'

A = 0
call sger_arr(alpha,A,x,y)
print '(4(F8.3))', (A(i,:), i=1,3)
print '()'

A = 0
call sger_mm(alpha,A,x,y)
print '(4(F8.3))', (A(i,:), i=1,3)
print '()'

A = 0
call sger_reshape(alpha,A,x,y)
print '(4(F8.3))', (A(i,:), i=1,3)

contains

    ! loops
    subroutine sger_dc(alpha,a,x,y)
    real, intent(in) :: alpha, x(:), y(:)
    real, intent(inout) :: a(:,:)
    integer :: i, j
    do concurrent(i=1:size(a,1),j=1:size(a,2))
        a(i,j) = a(i,j) + alpha * x(i) * y(j)
    end do
    end subroutine

    ! array syntax
    subroutine sger_arr(alpha,a,x,y)
    real, intent(in) :: alpha, x(:), y(:)
    real, intent(inout) :: a(:,:)
    a = a + alpha * spread(x,2,size(a,2)) &
                  * spread(y,1,size(a,1))
    end subroutine

    ! matmul
    subroutine sger_mm(alpha,a,x,y)
    real, intent(in) :: alpha
    real, intent(in), target :: x(:), y(:)
    real, intent(inout) :: a(:,:)
    real, pointer :: x2(:,:), y2T(:,:)
    x2(1:size(x),1:1) => x
    y2T(1:1,1:size(y)) => y
    a = a + alpha * matmul(x2,y2T)
    end subroutine

    ! reshape
    subroutine sger_reshape(alpha,a,x,y)
    real, intent(in) :: alpha
    real, intent(in) :: x(:), y(:)
    real, intent(inout) :: a(:,:)

    associate(x2 => reshape(x,shape=[size(x),1]), &
              y2 => reshape(y,shape=[size(y),1]))
        a = a + alpha * matmul(x2,transpose(y2))
    end associate

   end subroutine
end program

```

Guess which one is the most efficient in practice? The loop one; for other variants the compilers create temporary arrays

Edit: I was curious how the generated procedures differ in size (or how bloated are they)

```txt
$ gfortran -Wall -O3 -march=skylake -fno-inline -c rank1_update.f90 
$ nm --print-size --size-sort --radix=d rank1_update.o | grep "sger" | awk '{print $2, $4}'
0000000000000193 sger_dc.3.constprop.0.isra.0
0000000000000197 sger_mm.1.constprop.0.isra.0
0000000000000698 sger_arr.2.constprop.0.isra.0
0000000000003307 sger_reshape.0.constprop.0.isra.0

```

(note that gfortran performs constant propagation)

```txt
$ ifort -warn all -O3 -xSKYLAKE -inline-level=0 -c rank1_update.f90 
$ nm --print-size --size-sort --radix=d rank1_update.o | grep "IP_sger" | awk '{print $2, $4}'
0000000000000256 rank1_update_IP_sger_dc_
0000000000000272 rank1_update_IP_sger_arr_
0000000000000368 rank1_update_IP_sger_mm_
0000000000001232 rank1_update_IP_sger_reshape_

```

---

_[View the full topic](https://fortran-lang.discourse.group/t/target-attribute-seemingly-affecting-performances/9137)._
