Skip to content

Commit b247bb2

Browse files
Merge pull request #288 from albertomercurio/master
Fix spurious memory allocations of `AddedOperator` after regression with v1.0 release
2 parents 37194cf + 7f2a61a commit b247bb2

File tree

4 files changed

+82
-74
lines changed

4 files changed

+82
-74
lines changed

src/basic.jl

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -616,33 +616,46 @@ end
616616
w
617617
end
618618
end
619+
619620
# Out-of-place: v is action vector, u is update vector
620621
function (L::AddedOperator)(v::AbstractVecOrMat, u, p, t; kwargs...)
621-
L = update_coefficients(L, u, p, t; kwargs...)
622-
sum(op -> iszero(op) ? zero(v) : op(v, u, p, t; kwargs...), L.ops)
622+
# We don't need to update coefficients of L, as op(v, u, p, t) will do it for each op
623+
sum(op -> op(v, u, p, t; kwargs...), L.ops)
623624
end
624625

625626
# In-place: w is destination, v is action vector, u is update vector
626-
function (L::AddedOperator)(w::AbstractVecOrMat, v::AbstractVecOrMat, u, p, t; kwargs...)
627-
update_coefficients!(L, u, p, t; kwargs...)
628-
for op in L.ops
629-
if !iszero(op)
630-
op(w, v, u, p, t, 1.0, 1.0; kwargs...)
627+
@generated function (L::AddedOperator)(w::AbstractVecOrMat, v::AbstractVecOrMat, u, p, t; kwargs...)
628+
# We don't need to update coefficients of L, as op(w, v, u, p, t) will do it for each op
629+
630+
ops_types = L.parameters[2].parameters
631+
N = length(ops_types)-1
632+
633+
quote
634+
L.ops[1](w, v, u, p, t; kwargs...)
635+
Base.@nexprs $N i->begin
636+
op = L.ops[i+1]
637+
op(w, v, u, p, t, true, true; kwargs...)
631638
end
639+
w
632640
end
633-
w
634641
end
635642

636643
# In-place with scaling: w = α*(L*v) + β*w
637-
function (L::AddedOperator)(w::AbstractVecOrMat, v::AbstractVecOrMat, u, p, t, α, β; kwargs...)
638-
update_coefficients!(L, u, p, t; kwargs...)
639-
lmul!(β, w)
640-
for op in L.ops
641-
if !iszero(op)
642-
op(w, v, u, p, t, α, 1.0; kwargs...)
644+
@generated function (L::AddedOperator)(w::AbstractVecOrMat, v::AbstractVecOrMat, u, p, t, α, β; kwargs...)
645+
# We don't need to update coefficients of L, as op(w, v, u, p, t) will do it for each op
646+
647+
T = L.parameters[1]
648+
ops_types = L.parameters[2].parameters
649+
N = length(ops_types)-1
650+
651+
quote
652+
L.ops[1](w, v, u, p, t, α, β; kwargs...)
653+
Base.@nexprs $N i->begin
654+
op = L.ops[i+1]
655+
op(w, v, u, p, t, α, true; kwargs...)
643656
end
657+
w
644658
end
645-
w
646659
end
647660

648661
"""

src/matrix.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ function (L::MatrixOperator)(w::AbstractVecOrMat, v::AbstractVecOrMat, u, p, t;
201201
end
202202

203203
# In-place with scaling: w = α*(L*v) + β*w
204-
function (L::MatrixOperator)(w::AbstractVecOrMat, v::AbstractVecOrMat, u, p, t, α, β; kwargs...)
204+
Base.@constprop :aggressive function (L::MatrixOperator)(w::AbstractVecOrMat, v::AbstractVecOrMat, u, p, t, α, β; kwargs...)
205205
update_coefficients!(L, u, p, t; kwargs...)
206206
mul!(w, L.A, v, α, β)
207207
end

test/downstream/Project.toml

Lines changed: 0 additions & 5 deletions
This file was deleted.

test/downstream/alloccheck.jl

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,62 @@
1-
using SciMLOperators, AllocCheck, Random, SparseArrays, Test
1+
using SciMLOperators, Random, SparseArrays, Test
22
using SciMLOperators: IdentityOperator,
33
NullOperator,
44
ScaledOperator,
55
AddedOperator
6-
Random.seed!(0)
7-
N = 8
8-
K = 12
9-
A = rand(N, N) |> MatrixOperator
10-
B = rand(N, N) |> MatrixOperator
11-
C = rand(N, N) |> MatrixOperator
12-
α = rand()
13-
β = rand()
14-
u = rand(N, K) # Update vector
15-
v = rand(N, K) # Action vector
16-
w = zeros(N, K) # Output vector
17-
p = ()
18-
t = 0
19-
op = AddedOperator(A, B)
20-
21-
# Define a function to test allocations with the new interface
22-
@check_allocs ignore_throw = true function apply_op!(H, w, v, u, p, t)
6+
7+
function apply_op!(H, w, v, u, p, t)
238
H(w, v, u, p, t)
249
return nothing
2510
end
2611

27-
if VERSION >= v"1.12-beta"
28-
apply_op!(op, w, v, u, p, t)
29-
else
30-
@test_throws AllocCheckFailure apply_op!(op, w, v, u, p, t)
31-
end
32-
33-
for T in (Float32, Float64, ComplexF32, ComplexF64)
34-
N = 100
35-
A1_sparse = MatrixOperator(sprand(T, N, N, 5 / N))
36-
A2_sparse = MatrixOperator(sprand(T, N, N, 5 / N))
37-
A3_sparse = MatrixOperator(sprand(T, N, N, 5 / N))
38-
39-
A1_dense = MatrixOperator(rand(T, N, N))
40-
A2_dense = MatrixOperator(rand(T, N, N))
41-
A3_dense = MatrixOperator(rand(T, N, N))
42-
43-
coeff1(a, u, p, t) = sin(p.ω * t)
44-
coeff2(a, u, p, t) = cos(p.ω * t)
45-
coeff3(a, u, p, t) = sin(p.ω * t) * cos(p.ω * t)
46-
47-
c1 = ScalarOperator(rand(T), coeff1)
48-
c2 = ScalarOperator(rand(T), coeff2)
49-
c3 = ScalarOperator(rand(T), coeff3)
50-
51-
H_sparse = c1 * A1_sparse + c2 * A2_sparse + c3 * A3_sparse
52-
H_dense = c1 * A1_dense + c2 * A2_dense + c3 * A3_dense
53-
54-
u = rand(T, N)
55-
v = rand(T, N)
56-
w = similar(u)
57-
p == 0.1,)
58-
t = 0.1
59-
60-
@test_throws AllocCheckFailure apply_op!(H_sparse, w, v, u, p, t)
61-
@test_throws AllocCheckFailure apply_op!(H_dense, w, v, u, p, t)
12+
test_apply_noalloc(H, w, v, u, p, t) = @test (@allocations apply_op!(H, w, v, u, p, t)) == 0
13+
14+
@testset "Allocations Check" begin
15+
Random.seed!(0)
16+
N = 8
17+
K = 12
18+
A = rand(N, N) |> MatrixOperator
19+
B = rand(N, N) |> MatrixOperator
20+
u = rand(N, K) # Update vector
21+
v = rand(N, K) # Action vector
22+
w = zeros(N, K) # Output vector
23+
p = ()
24+
t = 0
25+
op = AddedOperator(A, B)
26+
27+
apply_op!(op, w, v, u, p, t) # Warm up
28+
test_apply_noalloc(op, w, v, u, p, t)
29+
30+
for T in (Float32, Float64, ComplexF32, ComplexF64)
31+
N = 100
32+
A1_sparse = MatrixOperator(sprand(T, N, N, 5 / N))
33+
A2_sparse = MatrixOperator(sprand(T, N, N, 5 / N))
34+
A3_sparse = MatrixOperator(sprand(T, N, N, 5 / N))
35+
36+
A1_dense = MatrixOperator(rand(T, N, N))
37+
A2_dense = MatrixOperator(rand(T, N, N))
38+
A3_dense = MatrixOperator(rand(T, N, N))
39+
40+
coeff1(a, u, p, t) = sin(p.ω * t)
41+
coeff2(a, u, p, t) = cos(p.ω * t)
42+
coeff3(a, u, p, t) = sin(p.ω * t) * cos(p.ω * t)
43+
44+
c1 = ScalarOperator(rand(T), coeff1)
45+
c2 = ScalarOperator(rand(T), coeff2)
46+
c3 = ScalarOperator(rand(T), coeff3)
47+
48+
H_sparse = c1 * A1_sparse + c2 * A2_sparse + c3 * A3_sparse
49+
H_dense = c1 * A1_dense + c2 * A2_dense + c3 * A3_dense
50+
51+
u = rand(T, N)
52+
v = rand(T, N)
53+
w = similar(u)
54+
p == 0.1,)
55+
t = 0.1
56+
57+
apply_op!(H_sparse, w, v, u, p, t) # Warm up
58+
apply_op!(H_dense, w, v, u, p, t) # Warm up
59+
test_apply_noalloc(H_sparse, w, v, u, p, t)
60+
test_apply_noalloc(H_dense, w, v, u, p, t)
61+
end
6262
end

0 commit comments

Comments
 (0)