TL;DR:mongoengine menghabiskan waktu lama untuk mengubah semua array yang dikembalikan menjadi dicts
Untuk mengujinya, saya membuat koleksi dengan dokumen dengan DictField
dengan dict
bersarang besar . Dokumen kira-kira dalam kisaran 5-10MB Anda.
Kami kemudian dapat menggunakan timeit.timeit
untuk mengonfirmasi perbedaan pembacaan menggunakan pymongo dan mongoengine.
Kami kemudian dapat menggunakan pycallgraph dan GraphViz untuk melihat apa yang membuat mongoengine begitu lama.
Berikut kode lengkapnya:
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()
Dan hasilnya membuktikan bahwa mongoengine sangat lambat dibandingkan dengan pymongo:
pymongo took 0.87s
mongoengine took 25.81118331072267
Grafik panggilan yang dihasilkan menggambarkan dengan cukup jelas di mana letak leher botol:
Pada dasarnya mongoengine akan memanggil metode to_python pada setiap DictField
bahwa itu akan kembali dari db. to_python
cukup lambat dan dalam contoh kami itu disebut gila berkali-kali.
Mongoengine digunakan untuk memetakan struktur dokumen Anda secara elegan ke objek python. Jika Anda memiliki dokumen tidak terstruktur yang sangat besar (yang sangat bagus untuk mongodb) maka mongoengine bukanlah alat yang tepat dan Anda sebaiknya menggunakan pymongo.
Namun, jika Anda mengetahui strukturnya, Anda dapat menggunakan EmbeddedDocument
bidang untuk mendapatkan kinerja yang sedikit lebih baik dari mongoengine. Saya telah menjalankan kode dalam inti ini
yang serupa tetapi tidak setara dan hasilnya adalah:
pymongo with dict took 0.12s
pymongo with embed took 0.12s
mongoengine with dict took 4.3059175412661075
mongoengine with embed took 1.1639373211854682
Jadi Anda dapat membuat mongoengine lebih cepat tetapi pymongo masih jauh lebih cepat.
PERBARUI
Jalan pintas yang baik untuk antarmuka pymongo di sini adalah dengan menggunakan kerangka kerja agregasi:
def mongoengine_agg_doc():
return list(MyModel.objects.aggregate({"$limit":1}))[0]