1 # Copyright 2018 The TensorFlow Authors. All Rights Reserved. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # ============================================================================== 15 """Tests for memory leaks in eager execution. 16 17 It is possible that this test suite will eventually become flaky due to taking 18 too long to run (since the tests iterate many times), but for now they are 19 helpful for finding memory leaks since not all PyObject leaks are found by 20 introspection (test_util decorators). Please be careful adding new tests here. 21 """ 22 23 from __future__ import absolute_import 24 from __future__ import division 25 from __future__ import print_function 26 27 import time 28 import six 29 30 from tensorflow.python import keras 31 from tensorflow.python.eager import backprop 32 from tensorflow.python.eager import context 33 from tensorflow.python.eager import test 34 from tensorflow.python.framework import dtypes 35 from tensorflow.python.ops import array_ops 36 from tensorflow.python.ops.variables import Variable 37 38 # memory_profiler might not be available in the OSS version of TensorFlow. 39 try: 40 import memory_profiler # pylint:disable=g-import-not-at-top 41 except ImportError: 42 memory_profiler = None 43 44 45 class SingleLayerNet(keras.Model): 46 """Simple keras model used to ensure that there are no leaks.""" 47 48 def __init__(self): 49 super(SingleLayerNet, self).__init__() 50 self.fc1 = keras.layers.Dense(5) 51 52 def call(self, x): 53 return self.fc1(x) 54 55 56 class MemoryTest(test.TestCase): 57 58 def assertNotIncreasingMemory(self, 59 f, 60 num_iters=100000, 61 increase_threshold_absolute_mb=10): 62 """Assert memory usage doesn't increase beyond given threshold for f.""" 63 64 with context.eager_mode(): 65 # Warm up. 66 f() 67 68 # Wait for background threads to start up and take over memory. 69 # FIXME: The nature of this test leaves few other options. Maybe there 70 # is a better way to do this. 71 time.sleep(4) 72 73 initial = memory_profiler.memory_usage(-1)[0] 74 75 for _ in six.moves.range(num_iters): 76 f() 77 78 increase = memory_profiler.memory_usage(-1)[0] - initial 79 80 assert increase < increase_threshold_absolute_mb, ( 81 "Increase is too high. Initial memory usage: %f MB. Increase: %f MB. " 82 "Maximum allowed increase: %f") % (initial, increase, 83 increase_threshold_absolute_mb) 84 85 def testMemoryLeakAnonymousVariable(self): 86 if memory_profiler is None: 87 self.skipTest("memory_profiler required to run this test") 88 89 def f(): 90 inputs = Variable(array_ops.zeros([32, 100], dtypes.float32)) 91 del inputs 92 93 self.assertNotIncreasingMemory(f, num_iters=10000) 94 95 def testMemoryLeakInSimpleModelForwardOnly(self): 96 if memory_profiler is None: 97 self.skipTest("memory_profiler required to run this test") 98 99 inputs = array_ops.zeros([32, 100], dtypes.float32) 100 net = SingleLayerNet() 101 102 def f(): 103 with backprop.GradientTape(): 104 net(inputs) 105 106 self.assertNotIncreasingMemory(f) 107 108 def testMemoryLeakInSimpleModelForwardAndBackward(self): 109 if memory_profiler is None: 110 self.skipTest("memory_profiler required to run this test") 111 112 inputs = array_ops.zeros([32, 100], dtypes.float32) 113 net = SingleLayerNet() 114 115 def f(): 116 with backprop.GradientTape() as tape: 117 result = net(inputs) 118 119 tape.gradient(result, net.variables) 120 121 del tape 122 123 self.assertNotIncreasingMemory(f) 124 125 126 if __name__ == "__main__": 127 test.main() 128