LinearAlgebra.mul! with Matrix{Dual} does not dispatch to BLAS
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 52/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- julia
- Área
- performance
Línea de trabajo
Start by running the provided Julia reproducer and inspecting the 3- and 5-argument LinearAlgebra.mul! paths for Matrix{Dual}. The work is done when GEMM cases involving Dual matrices use BLAS-backed operations where applicable and retain the correctness shown by the final comparison.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
I have noticed that GEMM of matrices involving Dual numbers are not dispatched to BLAS, which significantly impacts performance. This happens with 3- and 5-arguments versions of LinearAlgebra.mul!. Generally, any GEMM of matrices resulting in a Matrix{<:Dual} is impacted, e.g. mul!(C, A, B) will be slow if A isa Matrix{<:Dual} and B isa Matrix{Float64}.
This is illustrated in the reproducer below for the 3-argument method. To speed things up, matrices are split into values and partials, and all the cross terms explicitly calculated. These are dispatched to BLAS because values and partials are Matrix{<:BlasFloat}. On my machine, I get ~30x speedup for this example. The exact number varies with matrix size and number of partials, but the workaround is systematically faster.
sing ForwardDiff
using LinearAlgebra
# Build random Dual-valued matrices.
function random_dual_matrix(m, n, N)
[ForwardDiff.Dual(rand(), ntuple(_ -> rand(), N)) for _ in 1:m, _ in 1:n]
end
# BLAS-capable baseline for Dual matrices: split into value and each partial,
# call ordinary BLAS-backed mul! on the plain arrays, and reassemble the result.
function mul_dual_blas!(C, A, B)
N = ForwardDiff.npartials(eltype(A))
A_val = ForwardDiff.value.(A)
B_val = ForwardDiff.value.(B)
A_parts = ntuple(i -> ForwardDiff.partials.(A, i), N)
B_parts = ntuple(i -> ForwardDiff.partials.(B, i), N)
C_val = ForwardDiff.value.(C)
C_parts = ntuple(i -> ForwardDiff.partials.(C, i), N)
mul!(C_val, A_val, B_val) # BLAS for values
for i in 1:N
# ∂C_i = ∂A_i * B + A * ∂B_i
mul!(C_parts[i], A_parts[i], B_val)
mul!(C_parts[i], A_val, B_parts[i], true, true)
end
# Reconstruct C from value and partials.
for j in 1:size(C, 2), i in 1:size(C, 1)
C[i, j] = ForwardDiff.Dual(
C_val[i, j],
ForwardDiff.Partials(ntuple(l -> C_parts[l][i, j], N))
)
end
C
end
N_partials = 3
m, n, k = 500, 400, 300
A = random_dual_matrix(m, k, N_partials)
B = random_dual_matrix(k, n, N_partials)
C_generic = similar(A, m, n)
C_blas = similar(A, m, n)
# Warm up both paths before timing.
mul!(C_generic, A, B)
mul!(C_generic, A, B)
mul_dual_blas!(C_blas, A, B)
mul_dual_blas!(C_blas, A, B)
# Time generic 3-arg mul!.
println("Timing generic 3-arg mul! with Matrix{Dual}...")
t_generic = minimum(@elapsed mul!(C_generic, A, B) for _ in 1:5)
# Time BLAS-based baseline.
println("Timing BLAS-based baseline...")
t_blas = minimum(@elapsed mul_dual_blas!(C_blas, A, B) for _ in 1:5)
println()
println("Generic mul! : $(round(t_generic; digits=4)) s")
println("BLAS baseline: $(round(t_blas; digits=4)) s")
println("Speedup : $(round(t_generic / t_blas; digits=2))x")
# Verify correctness.
mul!(C_generic, A, B)
mul_dual_blas!(C_blas, A, B)
@assert C_generic ≈ C_blas
println()
println("Results match.")
- Lenguaje dominante
- Julia
- Estrellas
- 1k
- Forks
- 162
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de JuliaDiff/ForwardDiff.jl
-
missing floatmin, floatmax, and issubnormal for DualPosiblemente ocupada @devmotion la tomó hace 16 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
JuliaDiff/ForwardDiff.jl#852 · 6 comentarios ·
-
`fma` and `muladd` ignore NaN-safe modePosiblemente ocupada @devmotion la tomó hace 2 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 55/100
JuliaDiff/ForwardDiff.jl#860 ·
-
Enable NaN-safe mode by defaultPosiblemente ocupada @devmotion la tomó hace 4 días. Abierto
Dificultad 5/5 Más de una semana Aptitud para principiantes 25/100
JuliaDiff/ForwardDiff.jl#857 · 1 reacción ·
-
Dificultad 3/5 1-2 días Aptitud para principiantes 75/100
JuliaDiff/ForwardDiff.jl#849 ·
-
`gradient!` leaves part of the result unwritten when `f` does not depend on its argumentPosiblemente ocupada @devmotion la tomó hace 45 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 78/100
JuliaDiff/ForwardDiff.jl#847 ·
Todos los issues de JuliaDiff/ForwardDiff.jl
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
JuliaGPU/KernelAbstractions.jl#897 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
oscar-system/GAP.jl#1455 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
JuliaAI/MLJTransforms.jl#85 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
SciML/OrdinaryDiffEq.jl#4875 ·
Los mantenedores suelen responder en 4 días