I am working with Julia 1.5.3 on the development of a new package DescriptorSystems, which basically manipulates 5-tuples of matrices (which describe so called descriptor systems). Recently, I added a new object, which is a rational function (i.e., a pair of numerator and denominator polynomials) + a real entity called sampling time. The definition of a rational transfer function is
struct RationalTransferFunction{T,X} <: AbstractRationalTransferFunction
num::Polynomial{T,X} # numerator polynomial
den::Polynomial{T,X} # denominator polynomial
Ts::Union{Real,Nothing} # sampling time
end
where the Polynomial object is defined in the last version (v2.0.0) of the Polynomials package.
One of the basic constructors of descriptor systems (a function called dss
) takes a rational matrix formed with elements rational transfer functions and produces a 5-tuple of matrices (called a realization). The generation is quite involved and calls functions for manipulation of matrix pencils (or pairs of matrices) provided by the MatrixPencils package.
A typical sequence used for test purposes is:
julia> @time z = rtf('z'); # define the variable z as a rational function
0.133723 seconds (212.48 k allocations: 11.060 MiB, 30.90% gc time)
julia> @time Gd = [z^2 z/(z-2); 0 1/z]; # define the 2-by-2 improper Gd(z)
11.165572 seconds (44.79 M allocations: 2.435 GiB, 9.00% gc time)
julia> @time sysd = dss(Gd,minimal = true,Ts = 1);
647.785513 seconds (1.86 G allocations: 110.919 GiB, 4.23% gc time)
where I included the times for the first execution (thus compilation times also included). The time for executing dss
is by no means acceptable for an user, even if the second exection is very fast:
julia> @time z = rtf('z'); # define the variable z as rational function
0.000020 seconds (16 allocations: 1.125 KiB)
julia> @time Gd = [z^2 z/(z-2); 0 1/z]; # define the 2-by-2 improper Gd(z)
0.000089 seconds (134 allocations: 8.453 KiB)
julia> @time sysd = dss(Gd,minimal = true,Ts = 1);
0.000230 seconds (349 allocations: 499.531 KiB)
The alternative construction using two polynomial matrices (for numerators and denominators) behaves much better at the first execution:
julia> @time z = Polynomial([0, 1],'z') # define z as a monomial
0.017358 seconds (55.52 k allocations: 2.897 MiB)
Polynomial(z)
julia> @time Nd = [z^2 z; 0 1]; Dd = [1 z-2; 1 z]; # define numerators and denominators
0.628269 seconds (2.70 M allocations: 140.437 MiB, 2.96% gc time)
julia> @time sysd = dss(Nd,Dd,minimal = true,Ts = 1);
25.080765 seconds (83.56 M allocations: 3.849 GiB, 5.50% gc time)
I would appreciate any help, which would contribute to improving substantially the compilation times.
I have the feeling that I did something basically wrong in the defintion of my rational function object, but I simply don’t know how to trace this issue back to the roots of performance loss.
I would like to add that before switching to v2.0 of Polynomials, the compilation times were more or less acceptable (the whole test suite required about 20-30 minutes). Presently, with Julia 1.6, the execution time of the whole test suite takes about 22 minutes, 44 minutes with Julia 1.5, and over 1.5 hours for Julia 1.2, 1.3 and 1.4.
Many thanks in advance for your time and interest.