Summary
For those who are new to feq-parse, this package allows users to define functions as character strings and evaluate them on-the-fly. In earlier versions of feq-parse, only scalar inputs were accepted to the evaluate
methods. When working with Fortran arrays, this required a do-loop around the evaluate
call which would re-evaluate the parser objects each time; this is slow! The latest updates on the master branch allow for you to pass 1-D arrays to the evaluate
methods, which provides significantly improved performance for evaluation of functions on arrays of independent variables. In a simple example, evaluating a gaussian on 10 million points, we show ~30x speedup with this new feature.
While implementing this new feature, Iāve also added to the test suite and started tracking code coverage (weāre hitting 92% coverage as of today!), which is now noted in the README. From here, I am working on adding support for higher dimension arrays and will then work on GPU accelerated back-ends for the supported operator and function evaluations for even faster equation evaluation!
Scalar v. Array performance
As an example, consider the following program, where we set up an equation parser object and evaluate it at 10 million points by calling the evaluate
method for each point; this is how we had to do equation evaluations with earlier versions of feq-parse.
program array_with_scalar_eval
use FEQParse
implicit none
integer,parameter :: N = 10000000
type(EquationParser) :: f
character(LEN=1),dimension(1) :: independentVars
character(LEN=30) :: eqChar
real :: x(1)
real :: feval(1:N)
integer :: i
real :: t1,t2
! Specify the independent variables
independentVars = (/'x'/)
! Specify an equation string that we want to evaluate
eqChar = 'f = \exp( -(x^2) )'
! Create the EquationParser object
f = EquationParser(eqChar,independentVars)
! Evaluate the equation
call cpu_time(t1)
do i = 1,N
x(1) = -1.0_real32 + (2.0_real32)/real(N,real32)*real(i - 1,real32)
feval(i) = f % evaluate(x)
end do
call cpu_time(t2)
print *, "runtime :", (t2 - t1)," s"
! Clean up memory
call f % Destruct()
end program array_with_scalar_eval
Compiling and running this example (with gfortran 11.4.0) gives a runtime for the main loop as ~8.1 s
./array_with_scalar_eval
runtime : 8.11188793 s
With the updated version of feq-parse, we can instead pre-load an array for all of the values of x
and call the evaluate
method once. This example is shown below.
program array_with_array_eval
use FEQParse
implicit none
integer,parameter :: N = 10000000
type(EquationParser) :: f
character(LEN=1),dimension(1) :: independentVars
character(LEN=30) :: eqChar
real :: x(1:N,1)
real :: feval(1:N)
integer :: i
real :: t1,t2
! Specify the independent variables
independentVars = (/'x'/)
! Specify an equation string that we want to evaluate
eqChar = 'f = \exp( -(x^2) )'
! Create the EquationParser object
f = EquationParser(eqChar,independentVars)
! Evaluate the equation
call cpu_time(t1)
do i = 1,N
x(i,1) = -1.0_real32 + (2.0_real32)/real(N,real32)*real(i - 1,real32)
end do
feval = f % evaluate(x)
call cpu_time(t2)
print *, "runtime :", (t2 - t1)," s"
! Clean up memory
call f % Destruct()
end program array_with_array_eval
Compiling and running this example (with the same compiler and on the same system) gives a runtime of ~0.27 s ( ~30x speedup )
./array_with_array_eval
runtime : 0.270846009 s