简体   繁体   English

为什么烧瓶的jsonify方法很慢?

[英]Why is flask's jsonify method slow?

I'm writing an API in flask that returns json. 我正在烧瓶中写一个返回json的API。 Each flask function is of the form 每个烧瓶功能都是这种形式

from flask import jsonify
@app.route('/getdata')
def get_data():
    data = load_data_as_dict()
    return jsonify(data)

If I return a large amount of data, a call to this function takes around 1.7 seconds. 如果我返回大量数据,则对此函数的调用大约需要1.7秒。 However, if I do this: 但是,如果我这样做:

from flask import Response
@app.route('/getdata')
def get_data():
    data = load_data_as_dict()
    data_as_str = json.dumps(data)
    return Response(response=data_as_str, status=200, mimetype="application/json"

...the function completes in around .05 seconds. ...该功能在.05秒左右完成。

Can anyone tell me why jsonify is so much slower? 谁能告诉我为什么jsonify会慢得多? Is there anything wrong with returning a raw Flask response instead? 返回原始Flask响应有什么问题吗?

My guess is: it has a lot to do with indentation and making a pretty json dump. 我的猜测是:它与缩进和制作pretty json转储有很大关系。 Here's the method definition (I stripped the comments to save space, full code can be found here ) : 这是方法定义(我删除了注释以节省空间,完整代码可以在这里找到):

def jsonify(*args, **kwargs):
    indent = None
    separators = (',', ':')

    if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] and not request.is_xhr:
        indent = 2
        separators = (', ', ': ')

    if args and kwargs:
        raise TypeError('jsonify() behavior undefined when passed both args and kwargs')
    elif len(args) == 1:  # single args are passed directly to dumps()
        data = args[0]
    else:
        data = args or kwargs

    return current_app.response_class(
        (dumps(data, indent=indent, separators=separators), '\n'),
        mimetype=current_app.config['JSONIFY_MIMETYPE']
    )

dumps wraps simplejson.dumps if the module is available, otherwise it uses json.dumps . dumps包装simplejson.dumps如果模块是可用的,否则,使用json.dumps

jsonify() just wraps json.dumps() . jsonify()只包装了json.dumps() However, depending upon the config of your Flask app and the Flask version that you're using, it may pass indent=2 and separators=(', ', ': ') to json.dumps . 但是,根据Flask应用程序的配置和您正在使用的Flask版本,它可能会将indent=2separators=(', ', ': ')传递给json.dumps (See the docs on pretty-printing at https://docs.python.org/3/library/json.html if you're unfamiliar with these arguments). (如果您不熟悉这些参数,请参阅https://docs.python.org/3/library/json.html上关于漂亮打印的文档)。

Passing these arguments slows down json.dumps dramatically. 传递这些参数会大大减慢json.dumps速度。 Using the 181MB citylots.json file from https://github.com/zemirco/sf-city-lots-json as sample data, these pretty-printing arguments increase json.dumps() 's runtime from 7 seconds to 31 seconds on my MacBook Pro: 使用来自https://github.com/zemirco/sf-city-lots-json的181MB citylots.json文件作为样本数据,这些漂亮的打印参数将json.dumps()的运行时间从7秒增加到31秒我的MacBook Pro:

>>> import time 
>>> import json
>>> citylots = json.load(open('citylots.json'))
>>> start = time.time(); x = json.dumps(citylots); print(time.time() - start)
7.165302753448486
>>> x = None
>>> start = time.time(); x = json.dumps(citylots, indent=2, separators=(', ', ': ')); print(time.time() - start)
31.19125771522522

As of Flask 1.0, this costly pretty-printing will happen if either : 由于瓶1.0,这个昂贵的漂亮打印会发生,如果任一

  • You've explicitly set JSONIFY_PRETTYPRINT_REGULAR to True in your app's config (it's False by default), OR 您已在应用程序配置中明确将JSONIFY_PRETTYPRINT_REGULAR设置为True (默认情况下为False ),或者
  • You're running your app in debug mode 您正在调试模式下运行您的应用程序

(You can see these conditions in the Flask 1.0.2 code at https://github.com/pallets/flask/blob/1.0.2/flask/json/__init__.py#L309 .) (您可以在Flask 1.0.2代码中查看这些条件, 网址https://github.com/pallets/flask/blob/1.0.2/flask/json/__init__.py#L309 。)

If you are using Flask >=1.0 and have the (probably unusual) need to disable the pretty-printing even in debug mode, you can always implement your own jsonify by copying and pasting the built-in jsonify 's definition and deleting all the pretty-printing logic: 如果你使用Flask> = 1.0并且即使在调试模式下也有(可能不寻常)需要禁用漂亮的打印,你总是可以通过复制和粘贴内置的jsonify定义并删除所有的jsonify来实现自己的jsonify 。漂亮的印刷逻辑:

from flask import current_app
from json import dumps

def jsonify(*args, **kwargs):
    if args and kwargs:
        raise TypeError('jsonify() behavior undefined when passed both args and kwargs')
    elif len(args) == 1:  # single args are passed directly to dumps()
        data = args[0]
    else:
        data = args or kwargs

    return current_app.response_class(
        dumps(data) + '\n',
        mimetype=current_app.config['JSONIFY_MIMETYPE']
    )

If you're in a version Flask prior to 1.0, then pretty-printing instead happens if both: 如果您使用的是1.0 之前的 Flask版本,那么如果两者都发生漂亮打印:

  • You haven't explicitly set JSONIFY_PRETTYPRINT_REGULAR to False in you app's config (it's True by default), AND 还没有在应用程序的配置中将JSONIFY_PRETTYPRINT_REGULAR显式设置为False (默认情况下为True ),AND
  • The current request is not an XHR request 当前请求不是XHR请求

In those older versions, there is never any need to redefine jsonify to eliminate the pretty-printing, since you can just do: 在那些旧版本中,永远不需要重新定义jsonify以消除漂亮的打印,因为您可以这样做:

app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False

(Alternatively, if you're using a pre-1.0 version of Flask and only want to disable the pretty-printing in production, then there's no need to change your code; instead, just upgrade to the latest version of Flask.) (或者,如果您使用的是1.0之前版本的Flask并且只想禁用生产中的漂亮打印,则无需更改代码;而只需升级到最新版本的Flask。)

It took me a while to figure out, but Flask jsonify sets the sort_keys argument on the encoder and it seems it defaults to True . 我花了一段时间才弄清楚,但Flask jsonify在编码器上设置了sort_keys参数,它似乎默认为True

Adding: 添加:

JSON_SORT_KEYS = False

To the configuration gave me a factor 7 speed up for larger JSON structures. 对于更大的JSON结构,配置为我提供了7倍的加速。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM