1
1
// RUN: %target-run-simple-swift-control-flow-differentiation
2
2
// REQUIRES: executable_test
3
3
4
- // A test that we can properly differentiate types that require refcounting .
4
+ // Test differentiation-related memory leaks .
5
5
6
6
import StdlibUnittest
7
7
import DifferentiationUnittest
8
8
9
9
var LeakCheckingTests = TestSuite ( " LeakChecking " )
10
10
11
11
/// Execute body, check expected leak count, and reset global leak count.
12
- func testWithLeakChecking( expectedLeakCount: Int = 0 , _ body: ( ) -> Void ) {
12
+ func testWithLeakChecking(
13
+ expectedLeakCount: Int = 0 , file: String = #file, line: UInt = #line,
14
+ _ body: ( ) -> Void
15
+ ) {
13
16
body ( )
14
- expectEqual ( expectedLeakCount, _GlobalLeakCount. count, " Leak detected. " )
17
+ expectEqual (
18
+ expectedLeakCount, _GlobalLeakCount. count, " Leak detected. " ,
19
+ file: file, line: line)
15
20
_GlobalLeakCount. count = 0
16
21
}
17
22
@@ -23,27 +28,122 @@ struct ExampleLeakModel : Differentiable {
23
28
}
24
29
}
25
30
31
+ struct FloatPair : Differentiable & AdditiveArithmetic {
32
+ var first , second : Tracked < Float >
33
+ init ( _ first: Tracked < Float > , _ second: Tracked < Float > ) {
34
+ self . first = first
35
+ self . second = second
36
+ }
37
+ }
38
+
39
+ struct Pair < T : Differentiable , U : Differentiable > : Differentiable
40
+ where T == T . AllDifferentiableVariables , T == T . TangentVector ,
41
+ U == U . AllDifferentiableVariables , U == U . TangentVector
42
+ {
43
+ var first : Tracked < T >
44
+ var second : Tracked < U >
45
+ init ( _ first: Tracked < T > , _ second: Tracked < U > ) {
46
+ self . first = first
47
+ self . second = second
48
+ }
49
+ }
50
+
26
51
LeakCheckingTests . test ( " BasicVarLeakChecking " ) {
27
- do {
52
+ testWithLeakChecking {
53
+ var model = ExampleLeakModel ( )
54
+ let x : Tracked < Float > = 1.0
55
+ _ = model. gradient ( at: x) { m, x in m. applied ( to: x) }
56
+ }
57
+
58
+ testWithLeakChecking {
59
+ var model = ExampleLeakModel ( )
60
+ let x : Tracked < Float > = 1.0
61
+
62
+ _ = model. gradient { m in m. applied ( to: x) }
63
+ for _ in 0 ..< 10 {
64
+ _ = model. gradient { m in m. applied ( to: x) }
65
+ }
66
+ }
67
+
68
+ testWithLeakChecking {
69
+ var model = ExampleLeakModel ( )
70
+ var x : Tracked < Float > = 1.0
71
+ _ = model. gradient { m in
72
+ x = x + x
73
+ var y = x + Tracked < Float > ( x. value)
74
+ return m. applied ( to: y)
75
+ }
76
+ }
77
+
78
+ // TODO: Fix memory leak.
79
+ testWithLeakChecking ( expectedLeakCount: 1 ) {
28
80
var model = ExampleLeakModel ( )
29
81
let x : Tracked < Float > = 1.0
30
- let _ = model. gradient ( at: x) { m, x in m. applied ( to: x) }
82
+ _ = model. gradient { m in
83
+ var model = m
84
+ // Next line causes leak.
85
+ model. bias = x
86
+ return model. applied ( to: x)
87
+ }
31
88
}
32
- expectEqual ( 0 , _GlobalLeakCount. count, " Leak detected. " )
33
89
}
34
90
35
91
LeakCheckingTests . test ( " ControlFlow " ) {
36
- // TODO: Add more `var` + control flow tests.
37
- // Porting tests from test/AutoDiff/control_flow.swift requires more support
38
- // for `Tracked<Float>`.
92
+ // FIXME: Fix control flow AD memory leaks.
93
+ // See related FIXME comments in adjoint value/buffer propagation in
94
+ // lib/SILOptimizer/Mandatory/Differentiation.cpp.
95
+ testWithLeakChecking ( expectedLeakCount: 105 ) {
96
+ func cond_nestedtuple_var( _ x: Tracked < Float > ) -> Tracked < Float > {
97
+ // Convoluted function returning `x + x`.
98
+ var y = ( x + x, x - x)
99
+ var z = ( y, x)
100
+ if x > 0 {
101
+ var w = ( x, x)
102
+ y. 0 = w. 1
103
+ y. 1 = w. 0
104
+ z. 0 . 0 = z. 0 . 0 - y. 0
105
+ z. 0 . 1 = z. 0 . 1 + y. 0
106
+ } else {
107
+ z = ( ( y. 0 - x, y. 1 + x) , x)
108
+ }
109
+ return y. 0 + y. 1 - z. 0 . 0 + z. 0 . 1
110
+ }
111
+ expectEqual ( ( 8 , 2 ) , Tracked < Float > ( 4 ) . valueWithGradient ( in: cond_nestedtuple_var) )
112
+ expectEqual ( ( - 20 , 2 ) , Tracked < Float > ( - 10 ) . valueWithGradient ( in: cond_nestedtuple_var) )
113
+ expectEqual ( ( - 2674 , 2 ) , Tracked < Float > ( - 1337 ) . valueWithGradient ( in: cond_nestedtuple_var) )
114
+ }
115
+
116
+ // FIXME: Fix control flow AD memory leaks.
117
+ // See related FIXME comments in adjoint value/buffer propagation in
118
+ // lib/SILOptimizer/Mandatory/Differentiation.cpp.
119
+ testWithLeakChecking ( expectedLeakCount: 379 ) {
120
+ func cond_nestedstruct_var( _ x: Tracked < Float > ) -> Tracked < Float > {
121
+ // Convoluted function returning `x + x`.
122
+ var y = FloatPair ( x + x, x - x)
123
+ var z = Pair ( Tracked ( y) , x)
124
+ if x > 0 {
125
+ var w = FloatPair ( x, x)
126
+ y. first = w. second
127
+ y. second = w. first
128
+ z. first = Tracked ( FloatPair ( z. first. value. first - y. first,
129
+ z. first. value. second + y. first) )
130
+ } else {
131
+ z = Pair ( Tracked ( FloatPair ( y. first - x, y. second + x) ) , x)
132
+ }
133
+ return y. first + y. second - z. first. value. first + z. first. value. second
134
+ }
135
+ expectEqual ( ( 8 , 2 ) , Tracked < Float > ( 4 ) . valueWithGradient ( in: cond_nestedstruct_var) )
136
+ expectEqual ( ( - 20 , 2 ) , Tracked < Float > ( - 10 ) . valueWithGradient ( in: cond_nestedstruct_var) )
137
+ expectEqual ( ( - 2674 , 2 ) , Tracked < Float > ( - 1337 ) . valueWithGradient ( in: cond_nestedstruct_var) )
138
+ }
39
139
40
140
// FIXME: Fix control flow AD memory leaks.
41
141
// See related FIXME comments in adjoint value/buffer propagation in
42
142
// lib/SILOptimizer/Mandatory/Differentiation.cpp.
43
143
testWithLeakChecking ( expectedLeakCount: 9 ) {
44
144
var model = ExampleLeakModel ( )
45
145
let x : Tracked < Float > = 1.0
46
- let _ = model. gradient ( at: x) { m, x in
146
+ _ = model. gradient ( at: x) { m, x in
47
147
let result : Tracked < Float >
48
148
if x > 0 {
49
149
result = m. applied ( to: x)
@@ -60,7 +160,7 @@ LeakCheckingTests.test("ControlFlow") {
60
160
testWithLeakChecking ( expectedLeakCount: 14 ) {
61
161
var model = ExampleLeakModel ( )
62
162
let x : Tracked < Float > = 1.0
63
- let _ = model. gradient ( at: x) { m, x in
163
+ _ = model. gradient ( at: x) { m, x in
64
164
var result : Tracked < Float > = x
65
165
if x > 0 {
66
166
result = result + m. applied ( to: x)
0 commit comments