Skip to content

prevent unnecessary repeated squaring calculation #58720

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

nsajko
Copy link
Contributor

@nsajko nsajko commented Jun 12, 2025

Fixes #58719

Copy link

Hello! I am a bot.

Thank you for your pull request!

I have assigned @vtjnash to this pull request.

@vtjnash can either choose to review this pull request themselves, or they can choose to find someone else to review this pull request.

Note: If you are a Julia committer, please make sure that your organization membership is public.

@nsajko nsajko force-pushed the power_by_squaring__inline__to_power_type__to__prevent__unnecessary__squaring branch from 69f72f5 to e82b63c Compare June 12, 2025 16:43
@nsajko
Copy link
Contributor Author

nsajko commented Jun 12, 2025

Benchmark:

julia> using BenchmarkTools

julia> @btime m^Int(2) setup=m=rand(1:10,100,100);
  457.701 μs (6 allocations: 156.42 KiB)

julia> @eval Base @assume_effects :terminates_locally function power_by_squaring(x_, p::Integer; mul=*)
           [...]
       end
power_by_squaring (generic function with 3 methods)

julia> @btime m^Int(2) setup=m=rand(1:10,100,100);
  230.714 μs (3 allocations: 78.21 KiB)

@nsajko nsajko added backport 1.12 Change should be backported to release-1.12 performance Must go faster maths Mathematical functions labels Jun 12, 2025
@nsajko
Copy link
Contributor Author

nsajko commented Jun 12, 2025

This PR currently breaks LazyArrays.jl, because LazyArrays.jl adds a method to the function to_power_type, even though to_power_type is not public. This PR currently keeps the function but leaves it unused, leading to an exception being thrown during exponentiation:

julia> using LazyArrays

julia> ApplyArray(exp, [1 2; 3 4]) ^ Int(2)
ERROR: MethodError: Cannot `convert` an object of type 
  ApplyArray{Float64,2,typeof(exp),Tuple{Matrix{Int64}}} to an object of type 
  ApplyArray{Float64,2,typeof(*),Tuple{ApplyMatrix{Float64, typeof(exp), Tuple{Matrix{Int64}}},ApplyMatrix{Float64, typeof(exp), Tuple{Matrix{Int64}}}}}
The function `convert` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  (::Type{ApplyArray{T, N, F, Args}} where {T, N, F, Args<:Tuple})(::Any, ::Any)
   @ LazyArrays ~/.julia/packages/LazyArrays/ltmzk/src/lazyapplying.jl:196
  convert(::Type{T}, ::T) where T
   @ Base Base_compiler.jl:136
  convert(::Type{T}, ::LinearAlgebra.Factorization) where T<:AbstractArray
   @ LinearAlgebra ~/tmp/jl/jl/julia-e82b63cced/share/julia/stdlib/v1.13/LinearAlgebra/src/factorization.jl:104
  ...

Stacktrace:
 [1] power_by_squaring(x_::ApplyMatrix{Float64, typeof(exp), Tuple{Matrix{Int64}}}, p::Int64; mul::typeof(*))
   @ Base ./intfuncs.jl:361
 [2] power_by_squaring(x_::ApplyMatrix{Float64, typeof(exp), Tuple{Matrix{Int64}}}, p::Int64)
   @ Base ./intfuncs.jl:353
 [3] _power_by_squaring
   @ ~/.julia/packages/ArrayLayouts/B2wRU/src/mul.jl:486 [inlined]
 [4] power_by_squaring
   @ ~/.julia/packages/ArrayLayouts/B2wRU/src/mul.jl:494 [inlined]
 [5] ^(A::ApplyMatrix{Float64, typeof(exp), Tuple{Matrix{Int64}}}, p::Int64)
   @ LinearAlgebra ~/tmp/jl/jl/julia-e82b63cced/share/julia/stdlib/v1.13/LinearAlgebra/src/dense.jl:558
 [6] top-level scope
   @ REPL[2]:1

IMO the proper fix might be for LazyArrays.jl to implement their own exponentiation, however I could also try to make this PR less disruptive by using to_power_type conditionally, if applicable says that there's an applicable method. EDIT: the latter is now implemented.

@nsajko nsajko force-pushed the power_by_squaring__inline__to_power_type__to__prevent__unnecessary__squaring branch 2 times, most recently from 2e3578f to a196d0b Compare June 12, 2025 21:12
@jishnub
Copy link
Member

jishnub commented Jun 13, 2025

I don't think we should be checking for isapplicable, and such breakages are inevitable when a package uses internals. Patching LazyArrays is the better approach here.

@nsajko nsajko force-pushed the power_by_squaring__inline__to_power_type__to__prevent__unnecessary__squaring branch from 336dfa3 to 6e29b5c Compare June 13, 2025 10:00
@nsajko
Copy link
Contributor Author

nsajko commented Jun 13, 2025

The current version of the PR doesn't use isapplicable and deletes the to_power_type function.

nsajko added a commit to nsajko/julia that referenced this pull request Jun 13, 2025
The registered package in question will be broken for an unrelated
reason now anyway by PR JuliaLang#58720, so there's no sense in trying to
protect it any more.

In any case, it doesn't make sense to hold back the language just
to protect a single broken package.
@nsajko
Copy link
Contributor Author

nsajko commented Jun 19, 2025

bump

@oscardssmith
Copy link
Member

This is somewhat ugly, but it seems reasonable enough.

@nsajko nsajko added the merge me PR is reviewed. Merge when all tests are passing label Jun 21, 2025
@nsajko
Copy link
Contributor Author

nsajko commented Jun 21, 2025

I suppose labelling as "merge me" is OK.

@nsajko
Copy link
Contributor Author

nsajko commented Jun 21, 2025

TODO: notify affected packages of breakage.

@IanButterworth IanButterworth merged commit f61c640 into JuliaLang:master Jun 22, 2025
8 checks passed
@oscardssmith oscardssmith removed the merge me PR is reviewed. Merge when all tests are passing label Jun 22, 2025
@nsajko nsajko deleted the power_by_squaring__inline__to_power_type__to__prevent__unnecessary__squaring branch June 22, 2025 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport 1.12 Change should be backported to release-1.12 maths Mathematical functions performance Must go faster
Projects
None yet
Development

Successfully merging this pull request may close these issues.

performance regression in power_by_squaring in julia 1.12
6 participants