[Patch, libgfortran] PR21468 Vectorizing matmul, other perf improvements
Janne Blomqvist
jblomqvi@cc.hut.fi
Sun Nov 13 22:05:00 GMT 2005
On Sun, Nov 13, 2005 at 08:48:55PM +0100, Thomas Koenig wrote:
> On Sun, Nov 13, 2005 at 05:52:37PM +0200, Janne Blomqvist wrote:
>
> > I have looked at PR21468, "Vectorizing libgfortran". The attached
> > patch enables vectorizing the main loop for matmul in case of unit
> > stride. It doesn't change the code much, only adds const and restrict
> > appropriately to the type declarations, so that the vectorizer can
> > safely vectorize the main loop.
>
> This is OK. You can commit this part.
Thanks; I'll do that tomorrow evening, unless somebody objects before
then.
> > Another thing that it does is enabling -funroll-loops for matmul_* and
> > matmull_*. This is actually a bigger performance booster than
> > vectorizing, almost doubling performance.
>
> I fear that this may be platform-dependant. Loop unrolling may
> be a big win for architectures with many registers, while on
> architectures with few registers, such as i686, it may cause
> performance degradation.
Yes, but I tested on i686. I don't think we'll find a more register
starved architecture than that in widespread use. ;-) Well, embedded
is of course an entire world on its own, but they can surely compile
with -Os if they ever want libgfortran.
But you're of course right that testing on other platforms, and even
different x86/x86-64 cpu:s, would be nice to have.
> Maybe this needs to become platform-dependent. I think we need
> some more benchmarks here.
I wrote a small benchmark to test matmul performance at different
sizes, attached to this mail. Unfortunately that benchmark failed to
prove my point that unrolling almost doubles performance (which I
somehow managed to see in another even more ad hoc benchmark). Oh, the
joys of benchmarking. Anyway, there is still quite a speedup:
Results on Athlon 1200 MHz, 256 KB cache.
With -funroll-loops:
Matrix side size Speed (Gflops/s) Loop count
=================================================
2 0.063 33333333
4 0.238 3571428
8 0.453 416666
16 0.658 50403
32 0.768 6200
64 0.850 768
128 0.775 95
256 0.509 11
512 0.158 1
1024 0.157 1
Without loop unrolling:
Matrix side size Speed (Gflops/s) Loop count
=================================================
2 0.059 33333333
4 0.226 3571428
8 0.486 416666
16 0.470 50403
32 0.593 6200
64 0.663 768
128 0.682 95
256 0.466 11
512 0.152 1
1024 0.151 1
At small sizes, the execution speed is dimished due to overhead, and
with larger sizes the performance is memory bound. For 128x128 the
matrices still fit onto L2 cache. As libgfortran matmul isn't blocked,
the performance rapidly drops as it spills into main memory. At
intermediate sizes, we see the advantage of -funroll-loops, for 16x16,
32x32 and 64x64 there is a speedup of 40%, 30% and 28%, respectively.
--
Janne Blomqvist
-------------- next part --------------
program matmul_bench
implicit none
integer, parameter :: wp = selected_real_kind(4), &
dp = selected_real_kind(15)
call runbench (1500)
contains
! Run matrix mult benchmark with different sized arrays.
subroutine runbench (nmax)
integer, intent(in) :: nmax
real(wp), allocatable, dimension(:,:) :: a, b, res
integer :: n, loop
real(dp) :: time, flops
print *, 'Matrix side size Speed (Gflops/s) Loop count'
print *, '================================================='
n = 2
do
allocate (a(n,n), b(n,n), res(n,n))
call random_number (a)
call random_number (b)
! matmul for square matrix is (2n-1)*n**2 flops. Assuming on
! average 1 gflop/s cpu, 4e8 flops takes about 0.4 seconds and
! should be enough.
flops = (2 * n - 1) * n**2
loop = max (int (4.0e8_dp / flops), 1)
call matmul_timing (a, b, res, loop, time)
print '(I5,14X,F6.3,15X,I10)', n, flops * real(loop, dp) / time / 1.0e9_dp, loop
deallocate (a, b, res)
n = n * 2
if (n > nmax) exit
end do
end subroutine runbench
! Actual routine, and timing.
subroutine matmul_timing (a, b, res, loop, time)
real(wp), intent(in), dimension(:, :) :: a, b
real(wp), intent(inout) :: res(:,:)
integer, intent(in) :: loop
real(dp), intent(out) :: time
real(dp) :: t1, t2
integer :: i
call cpu_time (t1)
do i = 1, loop
res = matmul (a, b)
end do
call cpu_time (t2)
time = t2 - t1
end subroutine matmul_timing
end program matmul_bench
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 185 bytes
Desc: not available
URL: <http://gcc.gnu.org/pipermail/fortran/attachments/20051113/46a95a4e/attachment.sig>
More information about the Fortran
mailing list