TL; DR:mongoengine đang dành nhiều thời gian để chuyển đổi tất cả các mảng được trả về thành các mảng
Để kiểm tra điều này, tôi đã tạo một bộ sưu tập với tài liệu có DictField
với một dict
lồng nhau lớn . Tài liệu nằm trong phạm vi 5-10MB của bạn.
Sau đó, chúng tôi có thể sử dụng timeit.timeit
để xác nhận sự khác biệt về số lần đọc bằng cách sử dụng pymongo và mongoengine.
Sau đó, chúng tôi có thể sử dụng pycallgraph và GraphViz để xem điều gì đang khiến mongoengine chết tiệt lâu như vậy.
Đây là mã đầy đủ:
import datetime
import itertools
import random
import sys
import timeit
from collections import defaultdict
import mongoengine as db
from pycallgraph.output.graphviz import GraphvizOutput
from pycallgraph.pycallgraph import PyCallGraph
db.connect("test-dicts")
class MyModel(db.Document):
date = db.DateTimeField(required=True, default=datetime.date.today)
data_dict_1 = db.DictField(required=False)
MyModel.drop_collection()
data_1 = ['foo', 'bar']
data_2 = ['spam', 'eggs', 'ham']
data_3 = ["subf{}".format(f) for f in range(5)]
m = MyModel()
tree = lambda: defaultdict(tree) # http://stackoverflow.com/a/19189366/3271558
data = tree()
for _d1, _d2, _d3 in itertools.product(data_1, data_2, data_3):
data[_d1][_d2][_d3] = list(random.sample(range(50000), 20000))
m.data_dict_1 = data
m.save()
def pymongo_doc():
return db.connection.get_connection()["test-dicts"]['my_model'].find_one()
def mongoengine_doc():
return MyModel.objects.first()
if __name__ == '__main__':
print("pymongo took {:2.2f}s".format(timeit.timeit(pymongo_doc, number=10)))
print("mongoengine took", timeit.timeit(mongoengine_doc, number=10))
with PyCallGraph(output=GraphvizOutput()):
mongoengine_doc()
Và kết quả đầu ra chứng tỏ mongoengine đang rất chậm so với pymongo:
pymongo took 0.87s
mongoengine took 25.81118331072267
Biểu đồ cuộc gọi kết quả minh họa khá rõ ràng cổ chai nằm ở đâu:
Về cơ bản mongoengine sẽ gọi phương thức to_python trên mọi DictField
mà nó nhận được trở lại từ db. to_python
khá chậm và trong ví dụ của chúng tôi, nó được gọi là một số lần điên rồ.
Mongoengine được sử dụng để ánh xạ cấu trúc tài liệu của bạn sang các đối tượng python một cách trang nhã. Nếu bạn có các tài liệu không có cấu trúc rất lớn (mongodb rất tuyệt vời) thì mongoengine không thực sự là công cụ phù hợp và bạn chỉ nên sử dụng pymongo.
Tuy nhiên, nếu bạn biết cấu trúc, bạn có thể sử dụng EmbeddedDocument
để có được hiệu suất tốt hơn một chút từ mongoengine. Tôi đã chạy một bài kiểm tra tương tự nhưng không tương đương với mã trong ý chính này
và đầu ra là:
pymongo with dict took 0.12s
pymongo with embed took 0.12s
mongoengine with dict took 4.3059175412661075
mongoengine with embed took 1.1639373211854682
Vì vậy, bạn có thể làm cho mongoengine nhanh hơn nhưng pymongo vẫn nhanh hơn nhiều.
CẬP NHẬT
Một lối tắt tốt cho giao diện pymongo ở đây là sử dụng khung tổng hợp:
def mongoengine_agg_doc():
return list(MyModel.objects.aggregate({"$limit":1}))[0]