Skip to content

Commit 7cab0e8

Browse files
authored
More thread-friendly compilation API method (#55)
allow compilation API to be used in thread-friendly way, and remove thread-unfriendly convenience methods
1 parent 40473fe commit 7cab0e8

File tree

12 files changed

+149
-230
lines changed

12 files changed

+149
-230
lines changed

Diff for: benchmark/benchmark.jl

+20-17
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,26 @@ function grad_benchmark_driver!(out, f, x)
3030
gc()
3131

3232
if length(tp.tape) <= 10000
33-
ctp = ReverseDiff.compile(tp)
34-
35-
# warmup
36-
ReverseDiff.gradient!(out, ctp, x)
37-
ReverseDiff.forward_pass!(ctp)
38-
ReverseDiff.reverse_pass!(ctp)
39-
40-
# actual
41-
print(" gradient! (compiled): ")
42-
@time ReverseDiff.gradient!(out, ctp, x)
43-
gc()
44-
print(" forward pass (compiled): ")
45-
@time ReverseDiff.forward_pass!(ctp)
46-
gc()
47-
print(" reverse pass (compiled): ")
48-
@time ReverseDiff.reverse_pass!(ctp)
49-
gc()
33+
@eval begin
34+
out, x = $out, $x
35+
ctp = ReverseDiff.compile($tp)
36+
37+
# warmup
38+
ReverseDiff.gradient!(out, ctp, x)
39+
ReverseDiff.forward_pass!(ctp)
40+
ReverseDiff.reverse_pass!(ctp)
41+
42+
# actual
43+
print(" gradient! (compiled): ")
44+
@time ReverseDiff.gradient!(out, ctp, x)
45+
gc()
46+
print(" forward pass (compiled): ")
47+
@time ReverseDiff.forward_pass!(ctp)
48+
gc()
49+
print(" reverse pass (compiled): ")
50+
@time ReverseDiff.reverse_pass!(ctp)
51+
gc()
52+
end
5053
else
5154
println("skipping compiled GradientTape benchmark because the tape is too long ($(length(tp.tape)) elements)")
5255
end

Diff for: docs/src/api.md

-3
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,20 @@ CurrentModule = ReverseDiff
99
```@docs
1010
ReverseDiff.gradient
1111
ReverseDiff.gradient!
12-
ReverseDiff.compile_gradient
1312
```
1413

1514
## Jacobians of `f(x::AbstractArray{<:Real}...)::AbstractArray{<:Real}`
1615

1716
```@docs
1817
ReverseDiff.jacobian
1918
ReverseDiff.jacobian!
20-
ReverseDiff.compile_jacobian
2119
```
2220

2321
## Hessians of `f(x::AbstractArray{<:Real})::Real`
2422

2523
```@docs
2624
ReverseDiff.hessian
2725
ReverseDiff.hessian!
28-
ReverseDiff.compile_hessian
2926
```
3027

3128
## The `AbstractTape` API

Diff for: examples/gradient.jl

+13-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using ReverseDiff: GradientTape, GradientConfig, gradient, gradient!, compile_gradient
1+
using ReverseDiff: GradientTape, GradientConfig, gradient, gradient!, compile
22

33
#########
44
# setup #
@@ -7,8 +7,11 @@ using ReverseDiff: GradientTape, GradientConfig, gradient, gradient!, compile_gr
77
# some objective function to work with
88
f(a, b) = sum(a' * b + a * b')
99

10-
# generate a gradient function for `f` using inputs of shape 100x100 with Float64 elements
11-
const ∇f! = compile_gradient(f, (rand(100, 100), rand(100, 100)))
10+
# pre-record a GradientTape for `f` using inputs of shape 100x100 with Float64 elements
11+
const f_tape = GradientTape(f, (rand(100, 100), rand(100, 100)))
12+
13+
# compile `f_tape` into a more optimized representation
14+
const compiled_f_tape = compile(f_tape)
1215

1316
# some inputs and work buffers to play around with
1417
a, b = rand(100, 100), rand(100, 100)
@@ -20,16 +23,19 @@ cfg = GradientConfig(inputs)
2023
# taking gradients #
2124
####################
2225

23-
# with a pre-recorded/comiled tape (generated in the setup above) #
26+
# with pre-recorded/compiled tapes (generated in the setup above) #
2427
#-----------------------------------------------------------------#
28+
2529
# this should be the fastest method, and non-allocating
30+
gradient!(results, compiled_f_tape, inputs)
2631

27-
∇f!(results, inputs)
32+
# this should be the second fastest method, and also non-allocating
33+
gradient!(results, f_tape, inputs)
2834

2935
# with a pre-allocated GradientConfig #
3036
#-------------------------------------#
31-
# this is more flexible than a pre-recorded tape, but can be wasteful since the tape
32-
# will be re-recorded for every call.
37+
# these methods are more flexible than a pre-recorded tape, but can be
38+
# wasteful since the tape will be re-recorded for every call.
3339

3440
gradient!(results, f, inputs, cfg)
3541

Diff for: examples/jacobian.jl

+14-7
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ using ReverseDiff: JacobianTape, JacobianConfig, jacobian, jacobian!, compile_ja
88
f(a, b) = (a + b) * (a * b)'
99
g!(out, a, b) = A_mul_Bc!(out, a + b, a * b)
1010

11-
# generate a jacobian function for `f` using inputs of shape 10x10 with Float64 elements
12-
const Jf! = compile_jacobian(f, (rand(10, 10), rand(10, 10)))
13-
const Jg! = compile_jacobian(g!, rand(10, 10), (rand(10, 10), rand(10, 10)))
11+
# pre-record JacobianTapes for `f` and `g` using inputs of shape 10x10 with Float64 elements
12+
const f_tape = JacobianTape(f, (rand(10, 10), rand(10, 10)))
13+
const g_tape = JacobianTape(g!, rand(10, 10), (rand(10, 10), rand(10, 10)))
14+
15+
# compile `f_tape` and `g_tape` into more optimized representations
16+
const compiled_f_tape = compile(f_tape)
17+
const compiled_g_tape = compile(g_tape)
1418

1519
# some inputs and work buffers to play around with
1620
a, b = rand(10, 10), rand(10, 10)
@@ -24,13 +28,16 @@ gcfg = JacobianConfig(output, inputs)
2428
# taking Jacobians #
2529
####################
2630

27-
# with a pre-recorded/comiled tape (generated in the setup above) #
31+
# with pre-recorded/compiled tapes (generated in the setup above) #
2832
#-----------------------------------------------------------------#
29-
# this should be the fastest method, and non-allocating
3033

31-
Jf!(results, inputs)
34+
# these should be the fastest methods, and non-allocating
35+
jacobian!(results, compiled_f_tape, inputs)
36+
jacobian!(results, compiled_g_tape, inputs)
3237

33-
Jg!(results, inputs)
38+
# these should be the second fastest methods, and also non-allocating
39+
jacobian!(results, f_tape, inputs)
40+
jacobian!(results, g_tape, inputs)
3441

3542
# with a pre-allocated JacobianConfig #
3643
#-------------------------------------#

Diff for: src/api/gradients.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ call.
2020
"""
2121
function gradient(f, input, cfg::GradientConfig = GradientConfig(input))
2222
tape = GradientTape(f, input, cfg)
23-
result = construct_result(tape.input)
23+
result = construct_result(input_hook(tape))
2424
seeded_reverse_pass!(result, tape)
2525
empty!(cfg.tape)
2626
return result
@@ -59,7 +59,7 @@ If `input` is a tuple of `AbstractArray`s, assume `tape` represents a function o
5959
of `f` w.r.t. `input[i].`
6060
"""
6161
function gradient!(tape::Union{GradientTape,CompiledGradient}, input)
62-
result = construct_result(tape.input)
62+
result = construct_result(input_hook(tape))
6363
gradient!(result, tape, input)
6464
return result
6565
end

Diff for: src/api/hessians.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Assuming `tape` represents a function of the form `f(::AbstractArray{<:Real})::R
6767
return the Hessian `H(f)(input)`.
6868
"""
6969
function hessian!(tape::Union{HessianTape,CompiledHessian}, input::AbstractArray)
70-
result = construct_result(tape.output, tape.input)
70+
result = construct_result(output_hook(tape), input_hook(tape))
7171
hessian!(result, tape, input)
7272
return result
7373
end
@@ -92,7 +92,7 @@ end
9292
function hessian!(result::DiffResult, tape::Union{HessianTape,CompiledHessian}, input::AbstractArray)
9393
seeded_forward_pass!(tape, input)
9494
seeded_reverse_pass!(DiffResult(DiffBase.gradient(result), DiffBase.hessian(result)), tape)
95-
DiffBase.value!(result, tape.func(input))
95+
DiffBase.value!(result, func_hook(tape)(input))
9696
return result
9797
end
9898

Diff for: src/api/jacobians.jl

+11-11
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function jacobian(f!, output, input, cfg::JacobianConfig = JacobianConfig(output
6060
tape = JacobianTape(f!, output, input, cfg)
6161
isa(input, TrackedArray) && empty!(input.tape)
6262
result = jacobian!(tape, input)
63-
extract_result_value!(output, tape.output)
63+
extract_result_value!(output, output_hook(tape))
6464
empty!(tape.tape)
6565
return result
6666
end
@@ -75,7 +75,7 @@ function jacobian!(result, f!, output, input, cfg::JacobianConfig = JacobianConf
7575
tape = JacobianTape(f!, output, input, cfg)
7676
isa(input, TrackedArray) && empty!(input.tape)
7777
jacobian!(result, tape, input)
78-
extract_result_value!(output, tape.output)
78+
extract_result_value!(output, output_hook(tape))
7979
empty!(tape.tape)
8080
return result
8181
end
@@ -102,7 +102,7 @@ execute `tape` with new `input` values. There is no way to re-run `tape`'s tape
102102
new `output` values into the tape.
103103
"""
104104
function jacobian!(tape::Union{JacobianTape,CompiledJacobian}, input)
105-
result = construct_result(tape.output, tape.input)
105+
result = construct_result(output_hook(tape), input_hook(tape))
106106
jacobian!(result, tape, input)
107107
return result
108108
end
@@ -138,32 +138,32 @@ differentation.
138138

139139
# function jacobian(f, input, cfg::JacobianConfig = JacobianConfig(input))
140140
# tape = JacobianTape(f, input, cfg)
141-
# result = construct_result(tape.output, tape.input)
142-
# seeded_reverse_pass!(result, tape.output, tape.input, tape.tape)
141+
# result = construct_result(output_hook(tape), input_hook(tape))
142+
# seeded_reverse_pass!(result, output_hook(tape), input_hook(tape), tape.tape)
143143
# empty!(tape.tape)
144144
# return result
145145
# end
146146
#
147147
# function jacobian!(result, f, input, cfg::JacobianConfig = JacobianConfig(input))
148148
# tape = JacobianTape(f, input, cfg)
149-
# seeded_reverse_pass!(result, tape.output, tape.input, tape.tape)
149+
# seeded_reverse_pass!(result, output_hook(tape), input_hook(tape), tape.tape)
150150
# empty!(tape.tape)
151151
# return result
152152
# end
153153
#
154154
# function jacobian(f!, output, input, cfg::JacobianConfig = JacobianConfig(output, input))
155155
# tape = JacobianTape(f!, output, input, cfg)
156-
# result = construct_result(tape.output, tape.input)
157-
# seeded_reverse_pass!(result, tape.output, tape.input, tape.tape)
158-
# extract_result_value!(output, tape.output)
156+
# result = construct_result(output_hook(tape), input_hook(tape))
157+
# seeded_reverse_pass!(result, output_hook(tape), input_hook(tape), tape.tape)
158+
# extract_result_value!(output, output_hook(tape))
159159
# empty!(tape.tape)
160160
# return result
161161
# end
162162
#
163163
# function jacobian!(result, f!, output, input, cfg::JacobianConfig = JacobianConfig(output, input))
164164
# tape = JacobianTape(f!, output, input, cfg)
165-
# seeded_reverse_pass!(result, tape.output, tape.input, tape.tape)
166-
# extract_result_value!(output, tape.output)
165+
# seeded_reverse_pass!(result, output_hook(tape), input_hook(tape), tape.tape)
166+
# extract_result_value!(output, output_hook(tape))
167167
# empty!(tape.tape)
168168
# return result
169169
# end

0 commit comments

Comments
 (0)