Skip to content

Stabilize MulAddMul strategically#52439

Merged
dkarrasch merged 11 commits into
masterfrom
dk/muladdmul
May 8, 2024
Merged

Stabilize MulAddMul strategically#52439
dkarrasch merged 11 commits into
masterfrom
dk/muladdmul

Conversation

@dkarrasch

Copy link
Copy Markdown
Member

Manual rebase of #47206. Closes #47206.

Co-authored-by: Ashley Milsted ashmilsted@gmail.com

@brenhinkeller brenhinkeller added performance Must go faster linear algebra Linear algebra labels Dec 7, 2023
@KristofferC

Copy link
Copy Markdown
Member

Bump, what is the status here?

@dkarrasch

Copy link
Copy Markdown
Member Author

This is internal, but breaking. It affects a function that we encouraged data storage packages such as SparseArrays.jl, GPUArrays.jl etc. to overload. If we want to do that, then it requires organized action. I thought that would be best done right after the v1.11 cut, since I didn't have time to follow up all the packages before the branching.

@dkarrasch
dkarrasch force-pushed the dk/muladdmul branch 3 times, most recently from 9ebeeb3 to 21e3f79 Compare February 27, 2024 11:01
Co-authored-by: Ashley Milsted <ashmilsted@gmail.com>
@dkarrasch

Copy link
Copy Markdown
Member Author

@nanosoldier runbenchmarks("linalg", vs = ":master")

@nanosoldier

Copy link
Copy Markdown
Collaborator

Your benchmark job has completed - no performance regressions were detected. A full report can be found here.

@dkarrasch

Copy link
Copy Markdown
Member Author

Awesome, this seems to speed up multiplication of small matrices significantly.

@dkarrasch

Copy link
Copy Markdown
Member Author

@nanosoldier runtests()

@dkarrasch

Copy link
Copy Markdown
Member Author

Let's see what the pkgeval run says, but I think we should/could slowly start to collectively think about whether we want to do this, or what should be taken care of to finish this.

The clear upside is that for packages that have their own 5-arg mul! implementations like the packages that have already received PRs (except for GPUArrays.jl), there is no MulAddMul in the pathway anymore, so no issues with dynamic calls to generic_mat[vec/mat]mul! due to type instability of the _add::MulAddMul argument. Those pathways should be somewhat lighter on the compiler, because alpha and beta are just pushed forward.

The downside is that those pathways that run by MulAddMul constructors (including the *diagonal ones and the most generic _generic_matmatmul!) need to compile four different multiplication kernel methods. So, initially, the load on the compiler is increased, for the benefit of avoiding dynamic calls/allocation of MulAddMul objects.

I'd like to call for some support here, especially with running some benchmarks of your favorite workload and reporting issues.

@maleadt On the above PRs, the nightly runs seem to fail (independently from my changes?). Is there a way to test this PR here together with the PRs from GPUArrays.jl and CUDA/oneAPI/Metal respectively?

@chriselrod Would you be able to run some of your compilation benchmarks? I'm afraid this will undo some of the speed-ups we achieved in v1.10, but let's see the numbers.

If we agree to include this, I believe it would be good to have it early in the cycle, so that chances are high to catch packages that rely on these internal functions.

@nanosoldier

Copy link
Copy Markdown
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

@maleadt

maleadt commented Mar 1, 2024

Copy link
Copy Markdown
Member

On the above PRs, the nightly runs seem to fail (independently from my changes?). Is there a way to test this PR here together with the PRs from GPUArrays.jl and CUDA/oneAPI/Metal respectively?

No, there isn't.

@dkarrasch

Copy link
Copy Markdown
Member Author

@nanosoldier runtests(["FloatTracker", "EulerAngles", "NumericalAlgorithms", "ConvexHulls2d", "SubSIt", "CompressedSparseBlocks", "MLKernels", "NMF", "VibrationGEPHelpers", "JSOSolvers", "ExtendableSparse", "EcologicalNetworksPlots", "CrystalNets", "OptimalPortfolios", "GLPK", "PlantRayTracer", "ProximalOperators", "Gtk4", "SkyDomes", "BasisMatrices", "TaylorIntegration", "TransitionsInTimeseries", "MixedModels", "ClimaCoreSpectra", "ClimaCorePlots", "LowLevelParticleFilters", "MultiStateSystems", "ManifoldDiffEq", "ConScape", "BLASBenchmarksCPU", "LowRankIntegrators", "SpiDy", "AiidaDFTK", "Petri", "StirredReactor", "BatchReactor", "ONSAS", "ChargeTransport", "SMLMFrameConnection", "MathepiaModels", "NamedTrajectories", "BloqadeGates", "Population"])

@dkarrasch

Copy link
Copy Markdown
Member Author

@nanosoldier runtests(["FloatTracker", "EulerAngles", "NumericalAlgorithms", "ConvexHulls2d", "SubSIt", "CompressedSparseBlocks", "MLKernels", "NMF", "VibrationGEPHelpers", "JSOSolvers", "ExtendableSparse", "EcologicalNetworksPlots", "CrystalNets", "OptimalPortfolios", "GLPK", "PlantRayTracer", "ProximalOperators", "Gtk4", "SkyDomes", "BasisMatrices", "TaylorIntegration", "TransitionsInTimeseries", "MixedModels", "ClimaCoreSpectra", "ClimaCorePlots", "LowLevelParticleFilters", "MultiStateSystems", "ManifoldDiffEq", "ConScape", "BLASBenchmarksCPU", "LowRankIntegrators", "SpiDy", "AiidaDFTK", "Petri", "StirredReactor", "BatchReactor", "ONSAS", "ChargeTransport", "SMLMFrameConnection", "MathepiaModels", "NamedTrajectories", "BloqadeGates", "Population"])

@nanosoldier

Copy link
Copy Markdown
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

@nanosoldier

Copy link
Copy Markdown
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

@nanosoldier

Copy link
Copy Markdown
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

@dkarrasch

Copy link
Copy Markdown
Member Author

@nanosoldier runtests(["SubSIt", "ReduceWindows", "CompressedSparseBlocks", "EasyCurl", "IterativeSolvers", "LinearMaps", "VibrationGEPHelpers", "PreallocationTools", "ParameterizedQuantumControl", "CrystalNets", "MRICoilSensitivities", "CDDLib", "TaylorIntegration", "BasisMatrices", "ReservoirComputing", "MixedModels", "StructuralEquationModels", "LongwaveModePropagator", "HierarchicalGaussianFiltering", "LowRankIntegrators", "MimiRFFSPs", "Plots", "Knockoffs", "ConceptualClimateModels", "AstrodynamicalModels", "MinimallyDisruptiveCurves", "Biofilm", "SMLMSim"])

Most failing tests were due to time-out. From what I've seen there are no failures due to method errors or things like that. One possible source of extensive runtimes is that SparseArrays.jl is run without adaptation, so multiplication runs by the most generic kernel, and not the sparse kernels.

@nanosoldier

Copy link
Copy Markdown
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

@dkarrasch

Copy link
Copy Markdown
Member Author

A few packages throw some signal in the most generic multiplication kernel, in the simd-loop applying muladd. It could be that this is related to sparse arrays, which don't get caught by the status quo of SparseArrays.jl. Failures, however, are limited to a handful of packages, so if we decide to go with this, we could first merge the SparseArrays.jl PR, then bump the stdlib, then update this branch, and rerun pkgeval.

@dkarrasch

dkarrasch commented Apr 30, 2024

Copy link
Copy Markdown
Member Author

The following compile time benchmark from @chriselrod

using ForwardDiff, LinearAlgebra

d(x, n) = ForwardDiff.Dual(x, ntuple(_ -> randn(), n))

function dualify(A, n, j)
  if n > 0
    A = d.(A, n)
    if (j > 0)
      A = d.(A, j)
    end
  end
  A
end

@time for n = 0:8, j = (n!=0):4
  A = dualify.(randn(5,5), n, j);
  B = dualify.(randn(5,5), n, j);
  C = similar(A);
  mul!(C, A, B);
  mul!(C, A', B);
  mul!(C, A, B');
  mul!(C, A', B');
  mul!(C, transpose(A), B);
  mul!(C, A, transpose(B));
  mul!(C, transpose(A), transpose(B));
end

doesn't show a dramatic increase, something like 10%. Testing with this:

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.12.0-DEV.394 (2024-04-22)
 _/ |\__'_|_|_|\__'_|  |  dk/muladdmul/0562602986* (fork: 6 commits, 7 days)
|__/                   |

julia> using LinearAlgebra

julia> A = randn(BigFloat, 5, 5);

julia> B = randn(BigFloat, 5, 5);

julia> @time C = A * B;
  0.358988 seconds (988.50 k allocations: 49.872 MiB, 31.52% gc time, 99.89% compilation time) # PR
  0.188448 seconds (671.25 k allocations: 32.915 MiB, 99.79% compilation time) # v1.11-beta1

julia> @time mul!(C, A, B, true, false);
  0.169249 seconds (6.58 k allocations: 306.602 KiB, 99.96% compilation time: 100% of which was recompilation) # PR
  0.036884 seconds (1.60 k allocations: 76.930 KiB, 99.78% compilation time) # v1.11-beta1

julia> @time mul!(C, A, B, true, true);
  0.000080 seconds (600 allocations: 30.469 KiB) # PR
  0.115821 seconds (252.68 k allocations: 12.372 MiB, 13.96% gc time, 99.80% compilation time) # v1.11-beta1

julia> @time mul!(C, A, B, 3.7, false);
  0.313045 seconds (804.67 k allocations: 40.479 MiB, 5.31% gc time, 99.97% compilation time) # PR
  0.139149 seconds (496.09 k allocations: 24.297 MiB, 99.82% compilation time) # v1.11-beta1

julia> @time mul!(C, A, B, 3.7, 2.8);
  0.384808 seconds (804.58 k allocations: 40.463 MiB, 24.57% gc time, 99.97% compilation time) # PR
  0.142509 seconds (504.06 k allocations: 24.781 MiB, 9.86% gc time, 99.81% compilation time) # v1.11-beta1

however, surprises me a bit. Why does it need to recompile mul!(C, A, B, true, false)? Other than that, the increase in compile times is less than factor 4, which one would naively expect by how the macro is set up. OTOH, this may be outbalanced by the fact that external packages don't need to compile through MulAddMul anymore, only w.r.t. the types of the alpha and beta arguments. Plus: small matrix multiplication is dramatically sped up!

I'd like to suggest to leave this for a few more days and wait for comments, and absent objections merge this.

@dkarrasch

Copy link
Copy Markdown
Member Author

@nanosoldier runbenchmarks("linalg", vs = ":master")

@nanosoldier

Copy link
Copy Markdown
Collaborator

Your benchmark job has completed - no performance regressions were detected. A full report can be found here.

@dkarrasch

Copy link
Copy Markdown
Member Author

@nanosoldier runbenchmarks("linalg", vs = ":release-v1.11")

@amilsted

Copy link
Copy Markdown
Contributor

@dkarrasch thanks for picking this up! Did you consider keeping an inference barrier at the call to generic_matmatmul to avoid compile-time overhead in BLAS or small-matrix cases, at the expense of slightly slowing down the generic_matmatmul case due to always-on runtime dispatch? The last version of my PR made that choice - I assumed generic_matmatmul is typically a slow path anyway. Not sure if that's right in all cases though!

@dkarrasch

Copy link
Copy Markdown
Member Author

@nanosoldier runbenchmarks("linalg", vs = ":release-v1.10")

@dkarrasch

Copy link
Copy Markdown
Member Author

The last version of my PR

Could you please point me to that commit?

@nanosoldier runbenchmarks("linalg", vs = ":release-1.11")

@nanosoldier

Copy link
Copy Markdown
Collaborator

Your benchmark job has completed - no performance regressions were detected. A full report can be found here.

@amilsted

amilsted commented Apr 30, 2024

Copy link
Copy Markdown
Contributor

Could you please point me to that commit?

It's in this commit. It just means not using the macro for the generic_matmatmul call (see comment).

I decided not to do it for the vector case (see here).

@dkarrasch

Copy link
Copy Markdown
Member Author

I'll try that out. Thanks!

@dkarrasch

dkarrasch commented May 1, 2024

Copy link
Copy Markdown
Member Author

Ok, I included that suggestion, but kept the macro call at the most generic version (for now). I believe this should not affect the usual small-matrix- or blas-paths. I'll double-check, and then re-evaluate.

ADDENDUM: I made the inference barrier complete. That generic method may get called even by BLAS-type matrices and hence gets compiled. So, for generic or mixed eltypes this PR doesn't change anything about the runtime dispatch.

@dkarrasch

Copy link
Copy Markdown
Member Author

All green, let's go!

@dkarrasch
dkarrasch merged commit 29ced9e into master May 8, 2024
@dkarrasch
dkarrasch deleted the dk/muladdmul branch May 8, 2024 07:57
xlxs4 pushed a commit to xlxs4/julia that referenced this pull request May 9, 2024
Co-authored-by: Ashley Milsted <ashmilsted@gmail.com>
lazarusA pushed a commit to lazarusA/julia that referenced this pull request Jul 12, 2024
Co-authored-by: Ashley Milsted <ashmilsted@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

linear algebra Linear algebra performance Must go faster

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants