From b4b1f768b994ed2190eab57fa1a1605a25dd20e2 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas Date: Sat, 9 Aug 2025 20:51:32 -0400 Subject: [PATCH 1/3] Add MKL preference management to autotune system - Analyze if MKL algorithms (MKLLUFactorization) perform best in any category - Write LoadMKL_JLL preference based on benchmark results - Set to false if MKL is never best to avoid loading unnecessary dependencies - Set to true if MKL wins in any category to ensure availability - Add MKL preference display in show_current_preferences - Include MKL preference clearing in clear_algorithm_preferences This optimization reduces startup time and memory usage when MKL is not beneficial for the user's workload. --- .../src/LinearSolveAutotune.jl | 6 ++- lib/LinearSolveAutotune/src/preferences.jl | 28 ++++++++++++ .../test/test_mkl_preference.jl | 44 +++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 lib/LinearSolveAutotune/test/test_mkl_preference.jl diff --git a/lib/LinearSolveAutotune/src/LinearSolveAutotune.jl b/lib/LinearSolveAutotune/src/LinearSolveAutotune.jl index c3146e6ba..f4c95fa0f 100644 --- a/lib/LinearSolveAutotune/src/LinearSolveAutotune.jl +++ b/lib/LinearSolveAutotune/src/LinearSolveAutotune.jl @@ -1,10 +1,13 @@ module LinearSolveAutotune +# Note: MKL preference should be set before loading LinearSolve for optimal performance +# The autotune system will write the appropriate preference based on benchmark results +using Preferences +using MKL_jll using LinearSolve using BenchmarkTools using DataFrames using PrettyTables -using Preferences using Statistics using Random using LinearAlgebra @@ -18,7 +21,6 @@ using CPUSummary using RecursiveFactorization using blis_jll using LAPACK_jll -using MKL_jll using CUDA using Metal diff --git a/lib/LinearSolveAutotune/src/preferences.jl b/lib/LinearSolveAutotune/src/preferences.jl index 6db7fbecc..9cdfc25e2 100644 --- a/lib/LinearSolveAutotune/src/preferences.jl +++ b/lib/LinearSolveAutotune/src/preferences.jl @@ -23,6 +23,8 @@ function set_algorithm_preferences(categories::Dict{String, String}) # Extract benchmarked results by element type and size benchmarked = Dict{String, Dict{String, String}}() + mkl_is_best_somewhere = false # Track if MKL wins any category + for (key, algorithm) in categories if contains(key, "_") eltype, size_range = split(key, "_", limit=2) @@ -30,6 +32,12 @@ function set_algorithm_preferences(categories::Dict{String, String}) benchmarked[eltype] = Dict{String, String}() end benchmarked[eltype][size_range] = algorithm + + # Check if MKL algorithm is best for this category + if contains(algorithm, "MKL") + mkl_is_best_somewhere = true + @info "MKL algorithm ($algorithm) is best for $eltype at size $size_range" + end end end @@ -118,6 +126,16 @@ function set_algorithm_preferences(categories::Dict{String, String}) end end + # Set MKL preference based on whether it was best for any category + # If MKL wasn't best anywhere, disable it to avoid loading unnecessary dependencies + Preferences.set_preferences!(LinearSolve, "LoadMKL_JLL" => mkl_is_best_somewhere; force = true) + + if mkl_is_best_somewhere + @info "MKL was best in at least one category - setting LoadMKL_JLL preference to true" + else + @info "MKL was not best in any category - setting LoadMKL_JLL preference to false to avoid loading unnecessary dependencies" + end + # Set a timestamp for when these preferences were created Preferences.set_preferences!(LinearSolve, "autotune_timestamp" => string(Dates.now()); force = true) @@ -178,6 +196,10 @@ function clear_algorithm_preferences() Preferences.delete_preferences!(LinearSolve, "autotune_timestamp"; force = true) end + # Clear MKL preference + Preferences.delete_preferences!(LinearSolve, "LoadMKL_JLL"; force = true) + @info "Cleared MKL preference" + @info "Preferences cleared from LinearSolve.jl." end @@ -214,6 +236,12 @@ function show_current_preferences() end end + # Show MKL preference + mkl_pref = Preferences.load_preference(LinearSolve, "LoadMKL_JLL", nothing) + if mkl_pref !== nothing + println("\nMKL Usage: $(mkl_pref ? "Enabled" : "Disabled")") + end + timestamp = Preferences.load_preference(LinearSolve, "autotune_timestamp", "unknown") println("\nLast updated: $timestamp") end \ No newline at end of file diff --git a/lib/LinearSolveAutotune/test/test_mkl_preference.jl b/lib/LinearSolveAutotune/test/test_mkl_preference.jl new file mode 100644 index 000000000..2e9febaae --- /dev/null +++ b/lib/LinearSolveAutotune/test/test_mkl_preference.jl @@ -0,0 +1,44 @@ +using LinearSolveAutotune +using LinearSolve +using Test + +@testset "MKL Preference Management" begin + # Test that MKL preference is set before loading LinearSolve + # This has already happened in LinearSolveAutotune.jl module initialization + + # Create some mock categories to test preference setting + categories_with_mkl = Dict{String, String}( + "Float64_tiny (5-20)" => "MKLLUFactorization", + "Float64_small (20-100)" => "RFLUFactorization", + "Float64_medium (100-300)" => "MKLLUFactorization", + "Float32_tiny (5-20)" => "LUFactorization" + ) + + categories_without_mkl = Dict{String, String}( + "Float64_tiny (5-20)" => "RFLUFactorization", + "Float64_small (20-100)" => "RFLUFactorization", + "Float64_medium (100-300)" => "LUFactorization", + "Float32_tiny (5-20)" => "SimpleLUFactorization" + ) + + # Test setting preferences with MKL as best + @info "Testing preference setting with MKL as best algorithm..." + LinearSolveAutotune.set_algorithm_preferences(categories_with_mkl) + + # The MKL preference should be set to true + # Note: We can't directly test the preference value without restarting Julia + # but we can verify the function runs without error + + @info "Testing preference setting without MKL as best algorithm..." + LinearSolveAutotune.set_algorithm_preferences(categories_without_mkl) + + # Clear preferences + @info "Testing preference clearing..." + LinearSolveAutotune.clear_algorithm_preferences() + + # Show current preferences + @info "Testing preference display..." + LinearSolveAutotune.show_current_preferences() + + @test true # If we got here without errors, the test passes +end \ No newline at end of file From 80d7313d7dbfab17a621413282fd7351a578b3b0 Mon Sep 17 00:00:00 2001 From: Christopher Rackauckas Date: Sat, 9 Aug 2025 20:54:09 -0400 Subject: [PATCH 2/3] Delete lib/LinearSolveAutotune/test/test_mkl_preference.jl --- .../test/test_mkl_preference.jl | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 lib/LinearSolveAutotune/test/test_mkl_preference.jl diff --git a/lib/LinearSolveAutotune/test/test_mkl_preference.jl b/lib/LinearSolveAutotune/test/test_mkl_preference.jl deleted file mode 100644 index 2e9febaae..000000000 --- a/lib/LinearSolveAutotune/test/test_mkl_preference.jl +++ /dev/null @@ -1,44 +0,0 @@ -using LinearSolveAutotune -using LinearSolve -using Test - -@testset "MKL Preference Management" begin - # Test that MKL preference is set before loading LinearSolve - # This has already happened in LinearSolveAutotune.jl module initialization - - # Create some mock categories to test preference setting - categories_with_mkl = Dict{String, String}( - "Float64_tiny (5-20)" => "MKLLUFactorization", - "Float64_small (20-100)" => "RFLUFactorization", - "Float64_medium (100-300)" => "MKLLUFactorization", - "Float32_tiny (5-20)" => "LUFactorization" - ) - - categories_without_mkl = Dict{String, String}( - "Float64_tiny (5-20)" => "RFLUFactorization", - "Float64_small (20-100)" => "RFLUFactorization", - "Float64_medium (100-300)" => "LUFactorization", - "Float32_tiny (5-20)" => "SimpleLUFactorization" - ) - - # Test setting preferences with MKL as best - @info "Testing preference setting with MKL as best algorithm..." - LinearSolveAutotune.set_algorithm_preferences(categories_with_mkl) - - # The MKL preference should be set to true - # Note: We can't directly test the preference value without restarting Julia - # but we can verify the function runs without error - - @info "Testing preference setting without MKL as best algorithm..." - LinearSolveAutotune.set_algorithm_preferences(categories_without_mkl) - - # Clear preferences - @info "Testing preference clearing..." - LinearSolveAutotune.clear_algorithm_preferences() - - # Show current preferences - @info "Testing preference display..." - LinearSolveAutotune.show_current_preferences() - - @test true # If we got here without errors, the test passes -end \ No newline at end of file From 5f302596446f5bea16499a75959b233764b853f0 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas Date: Sat, 9 Aug 2025 21:00:47 -0400 Subject: [PATCH 3/3] Force enable MKL during benchmarking to ensure availability - Set LoadMKL_JLL=true before loading LinearSolve in autotune module - This ensures MKL algorithms are available for benchmarking - Track the original preference to inform user of temporary change - Final preference is still set based on benchmark results - Added documentation explaining this behavior --- .../src/LinearSolveAutotune.jl | 23 +++++++++++++++++-- lib/LinearSolveAutotune/src/preferences.jl | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/LinearSolveAutotune/src/LinearSolveAutotune.jl b/lib/LinearSolveAutotune/src/LinearSolveAutotune.jl index f4c95fa0f..6c6bdf0d0 100644 --- a/lib/LinearSolveAutotune/src/LinearSolveAutotune.jl +++ b/lib/LinearSolveAutotune/src/LinearSolveAutotune.jl @@ -1,9 +1,22 @@ module LinearSolveAutotune -# Note: MKL preference should be set before loading LinearSolve for optimal performance -# The autotune system will write the appropriate preference based on benchmark results +# Ensure MKL is available for benchmarking by setting the preference before loading LinearSolve using Preferences using MKL_jll + +# Set MKL preference to true for benchmarking if MKL is available +# We need to use UUID instead of the module since LinearSolve isn't loaded yet +const LINEARSOLVE_UUID = Base.UUID("7ed4a6bd-45f5-4d41-b270-4a48e9bafcae") +if MKL_jll.is_available() + # Force load MKL for benchmarking to ensure we can test MKL algorithms + # The autotune results will determine the final preference setting + current_pref = Preferences.load_preference(LINEARSOLVE_UUID, "LoadMKL_JLL", nothing) + if current_pref !== true + Preferences.set_preferences!(LINEARSOLVE_UUID, "LoadMKL_JLL" => true; force = true) + @info "Temporarily setting LoadMKL_JLL=true for benchmarking (was $(current_pref))" + end +end + using LinearSolve using BenchmarkTools using DataFrames @@ -145,6 +158,12 @@ Run a comprehensive benchmark of all available LU factorization methods and opti - Set Preferences for optimal algorithm selection - Support both CPU and GPU algorithms based on hardware detection - Test algorithm compatibility with different element types + - Automatically manage MKL loading preference based on performance results + +!!! note "MKL Preference Management" + During benchmarking, MKL is temporarily enabled (if available) to test MKL algorithms. + After benchmarking, the LoadMKL_JLL preference is set based on whether MKL algorithms + performed best in any category. This optimizes startup time and memory usage. # Arguments diff --git a/lib/LinearSolveAutotune/src/preferences.jl b/lib/LinearSolveAutotune/src/preferences.jl index 9cdfc25e2..efc171bbb 100644 --- a/lib/LinearSolveAutotune/src/preferences.jl +++ b/lib/LinearSolveAutotune/src/preferences.jl @@ -128,6 +128,8 @@ function set_algorithm_preferences(categories::Dict{String, String}) # Set MKL preference based on whether it was best for any category # If MKL wasn't best anywhere, disable it to avoid loading unnecessary dependencies + # Note: During benchmarking, MKL is temporarily enabled to test MKL algorithms + # This final preference setting determines whether MKL loads in normal usage Preferences.set_preferences!(LinearSolve, "LoadMKL_JLL" => mkl_is_best_somewhere; force = true) if mkl_is_best_somewhere