It gets instantiated for all kinds also, right? So:
generic function my_matmul(A, B) result(C)
type(real, integer, complex), intent(in) :: A(:,:), B(:,:)
type(real, integer, complex) :: C(:,:)
C = matmul(A, B)
end function
How is this going to work:
- will it instantiate all combinations, including “real :: A” and “integer :: C”?
- How will dispatch on result type work? (I am guessing the result cannot be generic, so maybe we would need to pass
C
as an intent(out)
argument.)
- What if you split the
intent(in)
line into two lines type(real, integer, complex), intent(in) :: A(:,:)
and type(real, integer, complex), intent(in) :: B(:,:)
, will they be instantiated as all combinations also?
It seems it should do all combinations per variable, as that would be consistent and independent how you write it. Assuming 3 kinds per type, we get 9^3 combinations, so 729 instantiations that the compiler would have to do for a simple matmul. That seems like a lot. I think it would be much better to do it “on demand”, at “instantiation” time.
Unless the compiler can somehow know that A, B, C should all have the same type and kind, but how would it know it?
Now let’s consider a more realistic example, say the gemv
Lapack operation, here is is how it looks like for a complex
type:
subroutine zgemv ( character TRANS,
integer M,
integer N,
complex ALPHA,
complex, dimension(lda,*) A,
integer LDA,
complex, dimension(*) X,
integer INCX,
complex BETA,
complex, dimension(*) Y,
integer INCY
)
There are 5 variables that should be generic: ALPHA
, A
, X
, BETA
, Y
. The function is doing y := alpha*A*x + beta*y
(plus optional transposes). Let’s do just “real” and “complex”, a modern compiler might have 4 kinds per type, so total of 8^5 = 32768 instantiations.
It would be realistic to have even more generic variables, say 8, then you have 16 million instantiations, which even the fastest compiler will be very slow at doing.
Actually, also the M
, N
, LDA
, INCX
and INCY
would be good to have generic for 32 bit and 64 bit integers. If we assume independent instantiation, that’s another 2^5=32 (at least) options.
So something has to give. The easiest would be to somehow tie the different variables together, that’s what Lapack does (only 4 total instantiations: real single/double and complex single/double), maybe times 2 for the two kinds for the integers dimensions, so 8 total, which is reasonable. I think this would be done with the typeof(x)
like you did for swap
above, correct?
So here is how I would answer my questions:
will it instantiate all combinations, including “real :: A” and “integer :: C”?
Yes. So you want to be careful with this, and use typeof(x)
whenever possible to lower the number of instantiations.
How will dispatch on result type work? (I am guessing the result cannot be generic, so maybe we would need to pass C
as an intent(out)
argument.)
It won’t dispatch on results, so the function result cannot be generic.
What if you split the intent(in)
line into two lines type(real, integer, complex), intent(in) :: A(:,:)
and type(real, integer, complex), intent(in) :: B(:,:)
, will they be instantiated as all combinations also?
Yes, both are equivalent and both will be instantiated with all combinations. So you want to be careful and use typeof(x)
whenever possible.
@everythingfunctional let me know if I got it right.