Closed vieyahn2017 closed 4 years ago
python2.7在内存管理上相比python3还是有些坑的,其释放后的内存仍然保留在python的内存池中,不被系统所用。python循环引用的变量不会被回收,这会导致程序越运行,占用的内存越大。我在跑py-faster-rcnn的demo时,基本上跑2000张图像,16g内存就要爆了。于是尝试用python的内存监控工具来调试程序,找到不能膨胀的变量,然后del之,再手动回收内存gc.collec()
下面是我用的两个内存监视工具,一个是按每行代码查看内存占用的工具memory_profiler,一个是查看占用内存前十位变量的工具guppy。
首先是安装:
pip install -U memory_profiler
然后用profile修饰想要查看的函数名:如:
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
输出结果:
Line # Mem usage Increment Line Contents
==============================================
3 @profile
4 5.97 MB 0.00 MB def my_func():
5 13.61 MB 7.64 MB a = [1] * (10 ** 6)
6 166.20 MB 152.59 MB b = [2] * (2 * 10 ** 7)
7 13.61 MB -152.59 MB del b
8 13.61 MB 0.00 MB return a
memory_profiler功能强大,更多功能可以看官网这里
首先安装:
pip install guppy
然后import下
from guppy import hpy
hxx = hpy()
heap = hxx.heap()
byrcs = hxx.heap().byrcs;
在主程序下增加:
print(heap) 输出示例:
Index Count % Size % Cumulative % Kind (class / dict of class)
0 10124 22 81944416 95 81944416 95 list
1 16056 34 1325464 2 83269880 96 str
2 9147 20 745616 1 84015496 97 tuple
3 102 0 366480 0 84381976 98 dict of module
4 287 1 313448 0 84695424 98 dict of type
5 2426 5 310528 0 85005952 98 types.CodeType
6 2364 5 283680 0 85289632 99 function
7 287 1 256960 0 85546592 99 type
8 169 0 192088 0 85738680 99 dict (no owner)
9 123 0 142728 0 85881408 99 dict of class
可以看到第一个list占了95%的内存,若print(heap)在主程序的循环中,可以查看每次循环后的变量内存占用情况。
输入以下命令,查看这个占内存最大的list中的数据类型:
byrcs[0].byid
最后测试后发现,test.py下get_im_blob等函数占用内存不断增大,每检测一副图像,该函数增加6-10MB内存开销。但奇怪的是用guppy查看前十个变量,并没有发现哪个变量有明显的内存增大迹象。于是猜测可能是每张图像推理后,推理的结果bbox,label,img等数据保存在了内存中,这样方便所有图像推理结束后,plt.show().于是修改程序,每张图像推理后,plt.show()一下。用memory_profiler发现内存不再继续增大,interesting!其实把plt.show()改成plt.close()也可以防止内存不断增大。具体原因肯定是python 的内存回收机制规则导致的。
原创ybdesire 最后发布于2017-06-13 22:41:20 阅读数 8709 收藏 https://blog.csdn.net/ybdesire/java/article/details/73128353
问题描述
调试python程序时,用下面这段代码,可以获得进程占用系统内存值。程序跑一段时间后,就能画出进程对内存的占用情况。
def memory_usage_psutil():
# return the memory usage in MB
import psutil,os
process = psutil.Process(os.getpid())
mem = process.memory_info()[0] / float(2 ** 20)
return mem
发现进程的内存占用一直再上涨,而这从逻辑上来说是不正常的,所以想到程序可能发生了Memory Leak。
python程序不可能像C/C++一样出现malloc了的内存没有free这样的Memory Leak。但也会遇到“逻辑上没free”的情况,如下代码所示。
def foo(a=[]):
a.append(time.time())
return a
参数a这样可迭代的对象,稍不注意,它就能增长的很快。说白了,python的Memory Leak,就是“进程占用的内存莫名其妙一直再升高”。进程占用内存一直升高,与逻辑预期不一致,就可能发生了Memory Leak。
以下面程序为例说明Memory Leak调试的过程:
def memory_usage_psutil():
# return the memory usage in MB
import psutil,os
process = psutil.Process(os.getpid())
mem = process.memory_info()[0] / float(2 ** 20)
return mem
def get_current_obj(a=[]):
a.append([0]*1000)
return a
def main():
obj = []
for i in range(10000):
obj = get_current_obj(obj)
if(i%100==0):
print(memory_usage_psutil())
if __name__=='__main__':
main()
用pmap -x [pid]查看进程占用的堆内存大小 首先想到,会不会是上面用的memory_usage_psutil函数统计错误呢。
先运行程序,再用pmap查看,发现进程内存占用确实很高。多次执行该命令,也可以发现内存一直升高。
强制执行GC(gc.collect()) 在需要执行GC的地方加上gc.collect()
def main():
obj = []
for i in range(10000):
obj = get_current_obj(obj)
import gc;gc.collect()
if(i%100==0):
print(memory_usage_psutil())
可以看到,强制GC后,程序执行变慢,但内存依然不断升高。
安装memory_profiler pip install -U memory_profiler
用@profile修饰需要查看内存的函数
@profile
def main():
obj = []
for i in range(10000):
obj = get_current_obj(obj)
if(i%100==0):
print(memory_usage_psutil())
用如下命令运行程序 python -m memory_profiler main.py
可以看到程序执行完成后,输出结果如下
Line # Mem usage Increment Line Contents
================================================
12 28.570 MiB 0.000 MiB @profile
13 def main():
14 28.570 MiB 0.000 MiB obj = []
15 106.203 MiB 77.633 MiB for i in range(10000):
16 106.203 MiB 0.000 MiB obj = get_current_obj(obj)
17 106.203 MiB 0.000 MiB if(i%100==0):
18 105.445 MiB -0.758 MiB print(memory_usage_psutil())
这样就能看到导致内存上涨最快的那几行代码。
将main修改如下,即可查看python对堆内存的占用量。
def main():
obj = []
for i in range(10000):
obj = get_current_obj(obj)
if(i%100==0):
print(memory_usage_psutil())
from guppy import hpy;hxx = hpy();heap = hxx.heap()
print(heap)
下面就是输出结果,python程序中各个对象对内存的占用从大到小排列。
Index Count % Size % Cumulative % Kind (class / dict of class)
0 10124 22 81944416 95 81944416 95 list
1 16056 34 1325464 2 83269880 96 str
2 9147 20 745616 1 84015496 97 tuple
3 102 0 366480 0 84381976 98 dict of module
4 287 1 313448 0 84695424 98 dict of type
5 2426 5 310528 0 85005952 98 types.CodeType
6 2364 5 283680 0 85289632 99 function
7 287 1 256960 0 85546592 99 type
8 169 0 192088 0 85738680 99 dict (no owner)
9 123 0 142728 0 85881408 99 dict of class
可以从结果中看到,95%的进程内存,都被一个list占用。
还可以通过下面这种方式,查看这个占内存最大的list中的数据类型。
from guppy import hpy;hxx = hpy();byrcs = hxx.heap().byrcs; byrcs[0].byid
原创99zhenzhen 最后发布于2017-06-10 21:46:55 阅读数 15603 收藏 https://blog.csdn.net/kelindame/article/details/73008487
最近在我的项目中,出现了内存持续增长的情况。这也是我第一次碰到这种情况,以前在写c++都没试过,难得啊,所以记录,积累一下经验。
生产者和消费者,操作kafka,使用的是pykafka库。消费者从kafka获取消息后,发送给业务处理服务,使用的是requests库。
在此次过程中,使用过的工具或者库。 1)、memory_profiler库。 可以使用这个库监控内存的消耗,以及基于行的内存分析 官方文档:https://pypi.python.org/pypi/memory_profiler
2)、objgraph库。 可以显示占据python程序内存的前n个对象 可以统计一段时间内,对象增加减少的数量 可以以图片形式,展现对象的引用关系 官网文档:http://mg.pov.lt/objgraph/index.html
3)graphviz工具 将objgraph库生成的dot文件生成图片
使用python开发程序,内存泄露的几个基本原因: 1、存在循环引用,gc不能释放; 2、存在全局对象,该对象不断的变大,占据内存; 3、使用了c或者c++扩展,扩展内存溢出了; 初次分析的时候,先进行代码review,并没有发现存在循环引用的变量和全局对象,而且我也没有调用c或者c++扩展。所以以上三点排除。 使用memory_profiler库,将输出记录到文本中进行分析。
发现并没有内存增加的情况。 后面加上gc.collect()也没有效果。
第一分析无果,然后当时没想到什么方法。只能暴力,注释一部分代码进行排查。发现注释掉将消息发送给业务服务的代码,内存不会持续增长。 怀疑是不是requests库的问题。然后进行google:requests memory leak,发现还真有这个问题。地址:https://github.com/requests/requests/issues/1685 他们的讨论非常精彩。不过上面的讨论是在pypy下,requests出现内存泄露。而我用的Cpython。而且后面的pypy版本也已经修复了这个bug。不过当时不信邪, 还是认为requests库有问题,所以改换成python3的urllib去发送信息,发现结果还是一样。然后网上一查,特么requests底层是封装url库的。已哭晕在厕所。
第二次分析还是没有结果。只能看是不是有循环引用,导致内存泄露。使用objgraph查看引用和对象生成关系。 1、使用objgraph.show_most_common_types(limit=10),查看占据内存前10的对象变化关系; 一开始:
运行一段时间后:
发现没太大变化的对象。 2、使用objgraph.show_growth(),观察对象增长情况。
grep "^Message" 1.txt
发现Message对象不断的增加。 3、查看Message到底是何方妖怪。 1)使用pdb断点进行查看:
发现Message是pykafka的Message实列。 2)进行引用查看。
由于没有安装xdot,使用graphviz工具查看。
发现没有出现循环引用。 3、继续单点调试。
发现这个Message不断的变大。这时候怀疑是不是pykafka这个库导致的。然后一查。厉害了,我的哥 ,pykafka的balancedconsumer类有个参数queued_max_messages。 这个参数的意思balancedconsumer会为每个分区缓存消息,默认一个分区是2000条。如果一条消息是1M,只有一个分区的话,就缓存了2000M,对于不够内存的机器那就gg了。 至此,问题找到了。解决方法是将这个参数改小点就好了。
原创Q博士 最后发布于2019-07-10 22:57:02 阅读数 1558 收藏
https://blog.csdn.net/qhshiniba/java/article/details/95392266
记一次 Python 内存泄漏的排查
背景 上周使用我的python web框架开发的第二个项目上线了,但是没运行几天机器内存就报警了,8G内存使用了7G,怀疑有内存泄漏,这个项目提供的功能就是一堆机器学习模型,对历史数据进行训练,挑选出最优的5个模型,用作未来数据的预测,所以整个项目有着数据量大,运行时间长的特点,就是把策略的离线工作搬到了线上。
上pympler检查是否有内存泄漏,程序入口处初始化该工具
from pympler import tracker,summary,muppy
memory_tracker = tracker.SummaryTracker()
接口返回处打印内存差异,观察内存是否有泄漏
memory_tracker.print_diff() # 本次内存和上次内存块的差异
我们用的sanic,所以直接在main.py文件添加如下代码:
from pympler import tracker,summary,muppy
memory_tracker = tracker.SummaryTracker()
@app.middleware('request')
async def set_request_id(request):
log_id = request.headers.get('log-id')
threading.currentThread().logid = log_id
gc.collect()
memory_tracker.print_diff()
然后我们访问接口,多触发几次,不用看前两次,等输出稳定后,如果有内存泄漏是如下输出:
上图显示每次都有4类泄漏对象,一共泄漏约60K的内存
如果没有内存泄漏,没有数据输出
我们确定程序有内存泄漏后,就想办法定位到代码块,就是我们自己写的代码,通过一步一步debug,注释,return,continue等方式定位到造成泄漏的代码块,下面的代码块就是遍历所有模型,然后挨个执行训练方法,因为有20多个模型,我不能挨个注释每次对象来定位,卡在这里了。
第三步:确定泄漏点 上tracemalloc定位泄漏点,python3.7.3自带,在main.py中添加如下代码:
tracemalloc.start(25)
snapshot = tracemalloc.take_snapshot()
@app.middleware('response')
async def print_on_response(request, response):
global snapshot
gc.collect()
snapshot1 = tracemalloc.take_snapshot()
top_stats = snapshot1.compare_to(snapshot, 'lineno')
print("[ Top 10 differences ]")
for stat in top_stats[:10]:
if stat.size_diff < 0:
continue
print(stat)
snapshot = tracemalloc.take_snapshot()
继续访问接口,多访问几次,输出如下,直接定位到具体泄漏的代码位置
图中所有的泄漏点都定位到pandas库,但是我用这些文件搜索内存泄漏,都没有搜到相关内存泄漏的问题,所以得寻找谁调用这些地方,以/home/doctorq/.local/share/virtualenvs/scscore-K9x97I77/lib/python3.7/site-packages/pandas/core/window.py:859为例,我们要找到我们写的代码哪里调用触发泄漏点
看了tracemalloc文档也没找到打印调用链的方法,后来灵机一动直接在这个文件加了下面代码:
raise Exception("doctorq")
然后在接口里catch异常,添加logging.exception(e),然后触发接口,打印堆栈信息:
ERROR:root:doctorq
Traceback (most recent call last):
File "/home/doctorq/python-dev/scscore/src/forecasting/forecast.py", line 83, in update_method
n_fraction=n_fraction)
File "/home/doctorq/python-dev/scscore/src/forecasting/trainer.py", line 113, in training
n_fraction=n_fraction)
File "/home/doctorq/python-dev/scscore/src/forecasting/trainer.py", line 205, in train_machine_learning_model
is_train=True).dropna()
File "/home/doctorq/python-dev/scscore/src/feature_engineering/features.py", line 34, in get_feature
history_same_periods=history_same_periods, zero_replace=zero_replace)
File "/home/doctorq/python-dev/scscore/src/feature_engineering/sale_relate_feature.py", line 65, in get_feature
store_and_sku=store_and_sku)
File "/home/doctorq/python-dev/scscore/src/feature_engineering/sale_relate_feature.py", line 85, in get_rolling_feature
rolling_result = self.get_rolling_result(window, rolling_obj, rolling_types)
File "/home/doctorq/python-dev/scscore/src/feature_engineering/sale_relate_feature.py", line 169, in get_rolling_result
rolling_result = self.rolling__(rolling_obj, rolling_type)
File "/home/doctorq/python-dev/scscore/src/feature_engineering/sale_relate_feature.py", line 190, in rolling__
return rolling_obj.min()
File "/home/doctorq/.local/share/virtualenvs/scscore-K9x97I77/lib/python3.7/site-packages/pandas/core/window.py", line 1723, in min
return super(Rolling, self).min(*args, **kwargs)
File "/home/doctorq/.local/share/virtualenvs/scscore-K9x97I77/lib/python3.7/site-packages/pandas/core/window.py", line 1069, in min
return self._apply('roll_min', 'min', **kwargs)
File "/home/doctorq/.local/share/virtualenvs/scscore-K9x97I77/lib/python3.7/site-packages/pandas/core/window.py", line 879, in _apply
result = np.apply_along_axis(calc, self.axis, values)
File "/home/doctorq/.local/share/virtualenvs/scscore-K9x97I77/lib/python3.7/site-packages/numpy/lib/shape_base.py", line 380, in apply_along_axis
res = asanyarray(func1d(inarr_view[ind0], *args, **kwargs))
File "/home/doctorq/.local/share/virtualenvs/scscore-K9x97I77/lib/python3.7/site-packages/pandas/core/window.py", line 875, in calc
closed=self.closed)
File "/home/doctorq/.local/share/virtualenvs/scscore-K9x97I77/lib/python3.7/site-packages/pandas/core/window.py", line 858, in func
raise Exception("doctorq")
Exception: doctorq
定位到我们代码触发点如下:
调用的就是pandas的Rolling的一系列方法,然后搜索该方法是否有泄漏问题
第一个链接链接就是说这些方法(rolling.min/max)有泄漏,pandas rolling max leak memory,具体因为啥泄漏的,也没时间细究,反正issue里说回退到0.23.4是没问题的,那么就回退试试:
pipenv install pandas==0.23.4
然后我们再用pympler定位有没有内存泄漏,pandas内存泄漏的问题是修复,剩下来就省memoryview的小泄漏了,明天继续
定位的过程略耗时,不过经过这么一折腾,也算是有经验了,各种工具一阵堆,泄漏问题确定-定位代码块-定位泄漏点-搜索已知泄漏点-解决掉。
另外,python本身自带了内存分析模块
https://blog.csdn.net/kongxx/java/article/details/52216850
在使用Python进行开发的过程中,有时候我们需要对Python程序的执行性能进行分析,此时我们就需要用到Python的性能分析工具,这里我就简单说说Python中的profile和pstats库。
下面是一个测试程序
import os
def a():
sum = 0
for i in range(1, 10001):
sum += i
return sum
def b():
sum = 0
for i in range(1, 100):
sum += a()
return sum
print b()
此时我们可以使用下面的命令来查看分析结果
python -m cProfile test.py
运行结果如下:
$ python -m cProfile test.py
4950495000
202 function calls in 0.040 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.040 0.040 test.py:1(<module>)
99 0.035 0.000 0.040 0.000 test.py:3(a)
1 0.000 0.000 0.040 0.040 test.py:9(b)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
100 0.005 0.000 0.005 0.000 {range}
其中:
也可以使用下面的命令把结果保存在一个文件里
python -m cProfile -o result test.py
然后使用pstats来格式化显示结果
python -c "import pstats; pstats.Stats('result').sort_stats(-1).print_stats()"
Thu Aug 11 13:20:20 2016 result
202 function calls in 0.037 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.037 0.037 test.py:1(<module>)
99 0.032 0.000 0.036 0.000 test.py:3(a)
1 0.000 0.000 0.037 0.037 test.py:9(b)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
100 0.004 0.000 0.004 0.000 {range}
另外,我们也可以在代码里直接嵌入代码来使用cProfile和pstats模块,这样在程序退出时就会自动生成分析结果并打印,如下:
import os
def a():
sum = 0
for i in range(1, 10001):
sum += i
return sum
def b():
sum = 0
for i in range(1, 100):
sum += a()
return sum
print b()
import cProfile
#cProfile.run("b()")
cProfile.run("b()", "result")
import pstats
pstats.Stats('result').sort_stats(-1).print_stats()
此时运行上面的程序就只要使用下面的命令
python test.py
的可视化工具 https://blog.csdn.net/u012409883/article/details/53039568
首先安装easy_install 点击打开链接 安装snakeviz命令:easy_install snakeviz
然后就开始实战吧
# -*- coding:utf-8 -*-
def fun1():
for i in range(0, 1000):
pass
def fun2():
for i in range(0, 10000):
pass
fun1()
import cProfile
cProfile.run('fun2()', "result")
在命令行运行:snakeviz result 会生成一个html的分析报告
分析报告(html) 网页源码如下
<!doctype html>
<html>
<head>
<meta charset="utf-8" .>
<title>C:\Users\ywx536122\Downloads\result</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/static/snakeviz.css" rel="stylesheet">
<!-- DataTables CSS -->
<link href="/static/vendor/jquery.dataTables.min.css" rel="stylesheet">
</head>
<body>
<h1 id="snakeviz-text">
<a href="https://jiffyclub.github.io/snakeviz/">SnakeViz</a>
</h1>
<!-- reset button -->
<span id="resetbuttons">
<div class="button-div">
<button id="resetbutton-root" disabled="True">Reset Root</button>
</div>
<div class="button-div">
<button id="resetbutton-zoom" disabled="True">Reset Zoom</button>
</div>
</span>
<!-- style select -->
<label id='sv-style-label'>Style:
<select name="sv-style" id="sv-style-select">
<option value="icicle" selected>Icicle</option>
<option value="sunburst">Sunburst</option>
</select>
</label>
<!-- depth select -->
<label id='sv-depth-label'>Depth:
<select name="sv-depth" id="sv-depth-select">
<option value="3" >3</option>
<option value="5" >5</option>
<option value="10" selected>10</option>
<option value="15" >15</option>
<option value="20" >20</option>
</select>
</label>
<!-- cutoff select -->
<label id='sv-cutoff-label'>Cutoff:
<select name="sv-cutoff" id="sv-cutoff-select">
<option value="0.001" selected>1 ⁄ 1000</option>
<option value="0.01">1 ⁄ 100</option>
<option value="0">None</option>
</select>
</label>
<!-- information div -->
<div id="sv-info-div"></div>
<!-- call stack -->
<div id="sv-call-stack">
<div id="working-spinner" class="spinner">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
<div style="display: inline-block;">
<button id="sv-call-stack-btn">Call Stack</button>
</div>
<div id="sv-call-stack-list"></div>
</div>
<!-- Error message -->
<div id="sv-error-div" class="sv-error-msg">
<p>
An error occurred processing your profile.
You can try a lower depth, a larger cutoff,
or try profiling a smaller portion of your code.
If you continue to have problems you can
<a href="https://github.com/jiffyclub/snakeviz/issues">
contact us on GitHub</a>.
</p>
<div id="sv-error-close-div" class="sv-error-close">Close</div>
</div>
<!-- vis -->
<div style="text-align: center;">
<div id="chart"></div>
</div>
<br>
<!-- stats table -->
<div id="table_div">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="pstats-table">
</table>
</div>
<!-- footer -->
<footer class="sv-footer">
<a class="footer-link" href="https://jiffyclub.github.io/snakeviz/">SnakeViz Docs</a>
</footer>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<!-- Vendor JS -->
<script src="/static/vendor/jquery-3.2.1.min.js"></script>
<script src="/static/vendor/d3.v3.min.js"></script>
<script src="/static/vendor/jquery.dataTables.min.js"></script>
<script src="/static/vendor/lodash.min.js"></script>
<script src="/static/vendor/immutable.min.js"></script>
<!-- SnakeViz JS -->
<script>
// Make the stats table
var table_data = [[['1', 1], '1.117e-06', '1.117e-06', '1.117e-06', '1.117e-06', '~:0(<method 'disable' of '_lsprof.Profiler' objects>)', "~:0(<method 'disable' of '_lsprof.Profiler' objects>)"], [['1', 1], '0.0001905', '0.0001905', '0.0003439', '0.0003439', '2.py:6(fun2)', '2.py:6(fun2)'], [['1', 1], '2.179e-05', '2.179e-05', '2.822e-05', '2.822e-05', '2.py:2(fun1)', '2.py:2(fun1)'], [['2', 2], '0.0001316', '6.579e-05', '0.0001316', '6.579e-05', '~:0(<range>)', '~:0(<range>)'], [['1', 1], '3.911e-06', '3.911e-06', '0.0003478', '0.0003478', '<string>:1(<module>)', '<string>:1(<module>)']];
$(document).ready(function() {
var table = $('#pstats-table').dataTable({
'data': table_data,
'columns': [
{'title': 'ncalls', 'type': 'num', 'searchable': 'false',
'data': {
'_': function (row) {return row[0][0];},
'sort': function (row) {return row[0][1]}
}},
{'title': 'tottime', 'type': 'num', 'searchable': 'false'},
{'title': 'percall', 'type': 'num', 'searchable': 'false'},
{'title': 'cumtime', 'type': 'num', 'searchable': 'false'},
{'title': 'percall', 'type': 'num', 'searchable': 'false'},
{'title': 'filename:lineno(function)'}
],
'order': [1, 'desc'],
'paging': false
}).api();
$('#pstats-table tbody').on('click', 'tr', function() {
var name = table.row(this).data()[6];
sv_root_func_name = name;
sv_draw_vis(name);
sv_call_stack = [name];
sv_update_call_stack_list();
$('#resetbutton-zoom').prop('disabled', true);
$('#resetbutton-root').prop('disabled', false);
}).on('mouseenter', 'tr', function () {
$(this).children('td').addClass('data-table-hover');
}).on('mouseleave', 'tr', function () {
$(this).children('td').removeClass('data-table-hover');
});
});
</script>
<!-- Web worker code for generating D3 JSON in a separate thread -->
<script id="hierarchy-worker" type="javascript/worker">
// This will all go into a web worker that will be used to generate
// the visualization JSON while leaving the rest of the app responsive.
//
// We put this here instead of in a separate JS file so that the worker
// can be stopped and restarted without loading the code from the server,
// and so that the stats data can be embedded in the worker.
var stats = {'2.py:6(fun2)': {'display_name': '2.py:6(fun2)', 'callers': {'<string>:1(<module>)': [1, 1, 0.00019052700832088994, 0.00034389845636805796]}, 'stats': [1, 1, 0.00019052700832088994, 0.00034389845636805796], 'children': {'~:0(<range>)': [1, 1, 0.00012515557144832654, 0.00012515557144832654], '2.py:2(fun1)': [1, 1, 2.179047895752114e-05, 2.8215876598841475e-05]}}, '~:0(<range>)': {'display_name': '~:0(<range>)', 'callers': {'2.py:6(fun2)': [1, 1, 0.00012515557144832654, 0.00012515557144832654], '2.py:2(fun1)': [1, 1, 6.425397641320336e-06, 6.425397641320336e-06]}, 'stats': [2, 2, 0.00013158096908964686, 0.00013158096908964686], 'children': {}}, '<string>:1(<module>)': {'display_name': '<string>:1(<module>)', 'callers': {}, 'stats': [1, 1, 3.9111116077602044e-06, 0.00034780956797581816], 'children': {'2.py:6(fun2)': [1, 1, 0.00019052700832088994, 0.00034389845636805796]}}, '2.py:2(fun1)': {'display_name': '2.py:2(fun1)', 'callers': {'2.py:6(fun2)': [1, 1, 2.179047895752114e-05, 2.8215876598841475e-05]}, 'stats': [1, 1, 2.179047895752114e-05, 2.8215876598841475e-05], 'children': {'~:0(<range>)': [1, 1, 6.425397641320336e-06, 6.425397641320336e-06]}}};
function sv_build_hierarchy(
node_name, depth, max_depth, cutoff, node_time, parent_name, call_stack) {
// track visited functions so we can avoid infinitely displaying
// instances of recursion
if (_.isUndefined(call_stack)) {
var call_stack = Immutable.Set([node_name])
} else {
var call_stack = call_stack.add(node_name);
};
var data = {
name: node_name,
display_name: stats[node_name]['display_name'],
time: node_time,
cumulative: stats[node_name]['stats'][3],
parent_name: parent_name
};
if (depth < max_depth && !_.isEmpty(stats[node_name]['children'])) {
child_names = {};
for (var child_name in stats[node_name]['children']) {
// Cut off children that have already been visited (recursion)
if (!call_stack.contains(child_name)) {
child_names[child_name] = stats[node_name]['children'][child_name];
}
}
// Normalize the child times.
// Unfortunately, stats[child_name]['callers'][node_name][3] doesn't
// give us the time that child spend under node in this instance, but
// in all instances across the call tree. Yikes!
// This may lead to unexpected behavior, e.g., the child times add up
// to more than the node time. A normalization is necessary.
var child_times = {};
var total_children_time = 0.0;
for (var child_name in child_names) {
child_times[child_name] = stats[child_name]['callers'][node_name][3];
total_children_time += child_times[child_name];
}
if (total_children_time > node_time) {
for (var child_name in child_names) {
child_times[child_name] *= (node_time / total_children_time);
}
}
data['children'] = [];
// recurse
for (var child_name in child_names) {
if (child_times[child_name]/node_time > cutoff) {
data['children'].push(
sv_build_hierarchy(
child_name, depth+1, max_depth, cutoff,
child_times[child_name], node_name, call_stack
));
}
}
// D3, the plotting framework, only accounts for times in the leaf
// nodes. Hence, if the node_time exceeds the total time in its
// children, we need to add another child node to account for the the
// time spent in node itself.
if (total_children_time < node_time) {
data['children'].push({
name: node_name,
display_name: data['display_name'],
parent_name: data['parent_name'],
cumulative: stats[node_name]['stats'][3],
time: node_time - total_children_time
});
}
}
return data;
}
self.onmessage = function (event) {
// Try loading JS from CDN in case snakeviz server is off
try {
importScripts(
"https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js");
}
// If the user is offline try loading from the SnakeViz server
catch (e) {
try {
importScripts(
event.data['url'] + "/static/vendor/immutable.min.js",
event.data['url'] + "/static/vendor/lodash.min.js");
}
catch (e) {
throw 'Could not load JS libraries in worker.';
}
}
var depth = 0;
var max_depth = event.data['depth'];
var cutoff = event.data['cutoff'];
var node_name = event.data['name'];
var parent_name = event.data['parent_name'];
var node_time = stats[node_name]['stats'][3];
self.postMessage(JSON.stringify(
sv_build_hierarchy(
node_name, depth, max_depth, cutoff, node_time, parent_name
)));
};
</script>
<!-- Load SnakeViz JS Files -->
<script src='/static/snakeviz.js'></script>
<script src='/static/drawsvg.js'></script>
<!-- Do initial setup stuff -->
<script>
// Initialize data
$(document).ready(_.defer(function () {
var profile_data = {'2.py:6(fun2)': {'display_name': '2.py:6(fun2)', 'callers': {'<string>:1(<module>)': [1, 1, 0.00019052700832088994, 0.00034389845636805796]}, 'stats': [1, 1, 0.00019052700832088994, 0.00034389845636805796], 'children': {'~:0(<range>)': [1, 1, 0.00012515557144832654, 0.00012515557144832654], '2.py:2(fun1)': [1, 1, 2.179047895752114e-05, 2.8215876598841475e-05]}}, '~:0(<range>)': {'display_name': '~:0(<range>)', 'callers': {'2.py:6(fun2)': [1, 1, 0.00012515557144832654, 0.00012515557144832654], '2.py:2(fun1)': [1, 1, 6.425397641320336e-06, 6.425397641320336e-06]}, 'stats': [2, 2, 0.00013158096908964686, 0.00013158096908964686], 'children': {}}, '<string>:1(<module>)': {'display_name': '<string>:1(<module>)', 'callers': {}, 'stats': [1, 1, 3.9111116077602044e-06, 0.00034780956797581816], 'children': {'2.py:6(fun2)': [1, 1, 0.00019052700832088994, 0.00034389845636805796]}}, '2.py:2(fun1)': {'display_name': '2.py:2(fun1)', 'callers': {'2.py:6(fun2)': [1, 1, 2.179047895752114e-05, 2.8215876598841475e-05]}, 'stats': [1, 1, 2.179047895752114e-05, 2.8215876598841475e-05], 'children': {'~:0(<range>)': [1, 1, 6.425397641320336e-06, 6.425397641320336e-06]}}};
sv_json_cache = {};
sv_worker = sv_make_worker();
sv_root_func_name = sv_find_root(profile_data);
sv_root_func_name__cached = sv_root_func_name;
sv_call_stack = [sv_root_func_name];
sv_total_time = profile_data[sv_root_func_name]['stats'][3];
}));
// Initialize the call stack button
$(document).ready(_.defer(function () {
sv_update_call_stack_list();
sv_call_stack_btn_for_show();
}));
// Draw the visualization
$(document).ready(_.defer(function () {
sv_draw_vis(sv_root_func_name);
}));
</script>
</body>
</html>
python内存监控工具memory_profiler和guppy的用法 https://blog.csdn.net/meccaendless/article/details/79701726