Skip to content

Commit 344d8db

Browse files
committed
add some docs about finalizers
1 parent 2b378c1 commit 344d8db

File tree

2 files changed

+49
-7
lines changed

2 files changed

+49
-7
lines changed

docs/src/faq.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,4 +744,38 @@ autodiff(Forward, f_val, Duplicated(Val(1.0), Val(1.0)))
744744
ERROR: Type of ghost or constant type Duplicated{Val{1.0}} is marked as differentiable.
745745
```
746746

747+
## Finalizers
748+
749+
Julia supports attaching finalizers to objects (see the listing below for an example)
750+
751+
```julia
752+
mutable struct Obj
753+
x::Float64
754+
function Obj(x)
755+
o = new(x)
756+
finalizer(o) do o
757+
# do someting with o
758+
end
759+
return o
760+
end
761+
end
762+
```
763+
764+
When Enzyme encounters a code like:
765+
766+
```julia
767+
function f(x)
768+
o = Obj(x)
769+
# computations over o
770+
return o.x
771+
end
772+
773+
autodiff(Forward, f, Duplicated(1.0, 1.0))
774+
```
775+
776+
Enzyme has to allocate a shadow object for `o` and in the process encounters the finalizer being attached to the primal object.
777+
Now the question is what should Enzyme do with the finalizer for the shadow objects? One option would be to simply ignore it,
778+
but finalizers are often used for resource management (like manually allocating memory) and thus we would leak resources that are attached
779+
to the shadow object. Instead, we define finalizers to be inactive (contain no instructions that are relevant with respect to AD),
780+
yet we must attach them to the shadow object to release resources attached to them.
747781

test/finalizers.jl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ function compute(x::Float64)
2424
return compute(c)
2525
end
2626

27-
compute(1.0)
28-
GC.gc()
29-
@test length(FREE_LIST) == 1
30-
empty!(FREE_LIST)
27+
@testset "primal" begin
28+
x = compute(1.0)
29+
@test x == 1.0
30+
GC.gc()
31+
@test length(FREE_LIST) == 1
32+
empty!(FREE_LIST)
33+
end
3134

3235
@testset "forward" begin
3336
dx, x = autodiff(ForwardWithPrimal, compute, Duplicated(1.0, 2.0))
37+
@test x == 1.0
3438
@test dx == 4.0
3539
GC.gc()
3640
@test length(FREE_LIST) == 2
@@ -45,26 +49,30 @@ end
4549

4650
@testset "batched forward" begin
4751
dx, x = autodiff(ForwardWithPrimal, compute, BatchDuplicated(1.0, (1.0, 2.0)))
48-
@test dx == 4.0
52+
@test x == 1.0
53+
@test dx[1] == 2.0
54+
@test dx[2] == 4.0
4955
GC.gc()
5056
@test length(FREE_LIST) == 3
5157
empty!(FREE_LIST)
5258

5359
dx, = autodiff(Forward, compute, BatchDuplicated(1.0, (1.0, 2.0)))
54-
@test dx == 4.0
60+
@test dx[1] == 2.0
61+
@test dx[2] == 4.0
5562
GC.gc()
5663
@test length(FREE_LIST) == 3
5764
empty!(FREE_LIST)
5865
end
5966

6067
@testset "reverse" begin
6168
((dx,), x) = autodiff(ReverseWithPrimal, compute, Active(1.0))
69+
@test x == 1.0
6270
@test dx == 2.0
6371
GC.gc()
6472
@test length(FREE_LIST) == 2
6573
empty!(FREE_LIST)
6674

67-
((dx,), x) = autodiff(Reverse, compute, Active(1.0))
75+
((dx,),) = autodiff(Reverse, compute, Active(1.0))
6876
@test dx == 2.0
6977
GC.gc()
7078
@test length(FREE_LIST) == 2

0 commit comments

Comments
 (0)