在接口测试时,绝大部分的响应都json格式,json的取值就一定绕不过jsonpath.
某天,就在技术群讨论了jsonpath相关的问题.
讨论归讨论,结果最终还是的看实际代码怎么样.
接下来,将会实测python中几个常用jsonpath库(jsonpath,jsonpath-ng,jmespath,gjson
)性能到底如何
shell/Users/rikasai/.virtualenvs/jsonpath_compare/bin/python /Users/rikasai/code/python/jsonpath_compare/main.py test_jsonpath 总耗时: 0.002014 秒 test_jsonpath 使用了 0.038136 MB 内存,峰值为 0.039424 MB test_gjson 总耗时: 0.001517 秒 test_gjson 使用了 0.023019 MB 内存,峰值为 0.028319 MB test_jsonpath_ng 总耗时: 0.027746 秒 test_jsonpath_ng 使用了 0.2931 MB 内存,峰值为 0.323259 MB test_jmespath 总耗时: 0.000571 秒 test_jmespath 使用了 0.022307 MB 内存,峰值为 0.025619 MB
运行一次的结果大概已经能看到jsonpath_ng,不管内存占用还是耗时都是跟另外三个相差甚远
shell/Users/rikasai/.virtualenvs/jsonpath_compare/bin/python /Users/rikasai/code/python/jsonpath_compare/main.py test_jsonpath 总耗时: 0.126801 秒 test_jsonpath 使用了 0.218614 MB 内存,峰值为 2.219974 MB test_gjson 总耗时: 0.264562 秒 test_gjson 使用了 0.108182 MB 内存,峰值为 2.135345 MB test_jsonpath_ng 总耗时: 17.054877 秒 test_jsonpath_ng 使用了 3.061619 MB 内存,峰值为 6.086255 MB test_jmespath 总耗时: 0.137950 秒 test_jmespath 使用了 0.137203 MB 内存,峰值为 1.973523 MB
1次可能说明不了问题,运行1000次, 跟运行1次时结果是类似.
可以看到jsonpath_ng相比另外三个要,耗时和内存占用都是非常离谱,完全不应该放在一起比较那种
另外三个jmespath,jsonpath,gjson不相伯仲
下面分别对比这四个库,json执行一次jsonpath取值,涉及函数调用次数和原始调用耗时
完整的调用统计非常多,只放出调用次数较多的部分,按照调用次数到序排序
在cProfile的输出中,ncalls列表示函数被调用的次数。看到219/196这意味着函数被递归地调用了。
第一个数字219表示函数被调用的总次数。
第二个数字196表示函数被非递归地调用的次数。
shell/Users/rikasai/.virtualenvs/jsonpath_compare/bin/python /Users/rikasai/code/python/jsonpath_compare/main.py 1417 function calls (1350 primitive calls) in 0.001 seconds Ordered by: call count ncalls tottime percall cumtime percall filename:lineno(function) 251 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 219/196 0.000 0.000 0.000 0.000 {built-in method builtins.len} 160 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance} 119 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:164(__getitem__) 61 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:233(__next) 56 0.000 0.000 0.000 0.000 {built-in method builtins.min} 50 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:160(__len__) 46 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:254(get) 33 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:172(append) 33 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:249(match) 25 0.000 0.000 0.000 0.000 {built-in method builtins.ord} 24/8 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:174(getwidth)
shell1167 function calls (1088 primitive calls) in 0.001 seconds Ordered by: call count ncalls tottime percall cumtime percall filename:lineno(function) 176 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 164/146 0.000 0.000 0.000 0.000 {built-in method builtins.len} 127 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance} 59 0.000 0.000 0.000 0.000 {method 'startswith' of 'str' objects} 52 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:164(__getitem__) 42 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:233(__next) 34 0.000 0.000 0.000 0.000 {built-in method builtins.min} 27 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:254(get) 26 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:160(__len__) 26 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:249(match)
shell24933 function calls (24438 primitive calls) in 0.013 seconds Ordered by: call count ncalls tottime percall cumtime percall filename:lineno(function) 3668 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects} 3634 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 2528 0.000 0.000 0.000 0.000 {built-in method builtins.id} 2241 0.000 0.000 0.000 0.000 {built-in method builtins.getattr} 1623/1420 0.000 0.000 0.000 0.000 {built-in method builtins.len} 984 0.001 0.000 0.002 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/ply/yacc.py:2165(lr0_goto) 897 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:233(__next) 850 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance} 795 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/ply/yacc.py:127(__getattribute__) 795 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/ply/yacc.py:130(__call__) 622 0.000 0.000 0.000 0.000 {method 'match' of 're.Pattern' objects} 386 0.000 0.000 0.000 0.000 /Users/rikasai/.pyenv/versions/3.9.11/lib/python3.9/sre_parse.py:164(__getitem__) 299 0.000 0.000 0.000 0.000 {built-in method builtins.min}
shell163 function calls (154 primitive calls) in 0.000 seconds Ordered by: call count ncalls tottime percall cumtime percall filename:lineno(function) 28 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/jmespath/lexer.py:129(_next) 14 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/jmespath/parser.py:463(_current_token) 12 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/jmespath/lexer.py:26(tokenize) 11 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects} 11 0.000 0.000 0.000 0.000 {built-in method builtins.getattr} 10 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/jmespath/parser.py:460(_advance) 8/1 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/jmespath/visitor.py:87(visit) 6 0.000 0.000 0.000 0.000 {built-in method builtins.len} 6 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/jmespath/parser.py:469(_lookahead_token) 4 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/jmespath/parser.py:466(_lookahead) 3/1 0.000 0.000 0.000 0.000 /Users/rikasai/.virtualenvs/jsonpath_compare/lib/python3.9/site-packages/jmespath/parser.py:118(_expression)
cProfile结果和上面的耗时和结果大体是吻合的
很明显的看到jsonpath_ng取值一次居然涉及到了24933次函数调用
而jsonpath和gjson都是一千多次,jmespath最好,仅仅需要163次,遥遥领先!
还是有一个疑惑,上面1000次耗时和内存测试,发现jmespath,jsonpath,gjson是不相伯仲的
但jmespath函数调用确实比另外两个相差了至少1000次,结果似乎对不上
把运行次数拉大到10000次,结果就很明显了,确实是jmespath更强
shelltest_jsonpath 总耗时: 1.746682 秒 test_jsonpath 使用了 0.210114 MB 内存,峰值为 19.831589 MB test_gjson 总耗时: 2.730377 秒 test_gjson 使用了 0.184749 MB 内存,峰值为 18.914425 MB test_jmespath 总耗时: 1.194248 秒 test_jmespath 使用了 0.127859 MB 内存,峰值为 18.852175 MB
为什么这里jsonpath_ng没有10000次的测试结果,因为太消耗时间了,被我手动终止...
四个库同时运行1000次,因为jsonpath_ng占用时间太大了
可以看到,大部分的耗时都是在这个函数parse_token_stream(isonpath_ng/parser.py:47)
如果需要优化,可以从这个地方着手
通过以上的测试和分析,我们可以得出以下结论:
在耗时和内存占用方面,jsonpath-ng
的表现是最差的,与其它三个库相比,无论是运行1次还是1000次,jsonpath-ng
的耗时和内存占用都是最高的。因此,在性能要求较高的场景下,jsonpath-ng
不是一个好的选择。
jmespath
在运行1次和1000次的耗时和内存占用都表现较好,且cProfile的结果显示jmespath
的函数调用次数也是最少的,因此,jmespath
是性能最好的一个选项。
jsonpath
和gjson
在耗时和内存占用方面表现相近,且都优于jsonpath-ng
。在函数调用次数方面,jsonpath
和gjson
也相近,但略多于jmespath
。
综上所述,如果你在寻找一个性能优越的jsonpath库,jmespath
是最佳选择。而jsonpath
和gjson
则是性能较为中等的选项,jsonpath-ng
则是最不推荐的选项。
本次测试的所有代码和用到的数据都会可以直接在我的GitHub仓库看到 https://github.com/lihuacai168/jsonpath_compare
本文作者:花菜
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!