TL;DR:mongoengine bruger aldre på at konvertere alle de returnerede arrays til dicts
For at teste dette opbyggede jeg en samling med et dokument med et DictField
med en stor indlejret dict
. Dokumentet er cirka i dit 5-10 MB rækkevidde.
Vi kan derefter bruge timeit.timeit
for at bekræfte forskellen i læsninger ved hjælp af pymongo og mongoengine.
Vi kan derefter bruge pycallgraph og GraphViz at se, hvad der tager mongoengine så lang tid.
Her er koden i sin helhed:
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()
Og outputtet beviser, at mongoengine er meget langsom sammenlignet med pymongo:
pymongo took 0.87s
mongoengine took 25.81118331072267
Den resulterende opkaldsgraf illustrerer ret tydeligt, hvor flaskehalsen er:
I bund og grund vil mongoengine kalde to_python-metoden på hvert DictField
at den kommer tilbage fra db. to_python
er ret langsom, og i vores eksempel bliver det kaldt et sindssygt antal gange.
Mongoengine bruges til elegant at kortlægge din dokumentstruktur til python-objekter. Hvis du har meget store ustrukturerede dokumenter (hvilket mongodb er fantastisk til), så er mongoengine ikke rigtigt det rigtige værktøj, og du bør bare bruge pymongo.
Men hvis du kender strukturen, kan du bruge EmbeddedDocument
felter for at få lidt bedre ydeevne fra mongoengine. Jeg har kørt en lignende, men ikke tilsvarende test kode i denne oversigt
og outputtet er:
pymongo with dict took 0.12s
pymongo with embed took 0.12s
mongoengine with dict took 4.3059175412661075
mongoengine with embed took 1.1639373211854682
Så du kan lave mongoengine hurtigere, men pymongo er meget hurtigere endnu.
OPDATERING
En god genvej til pymongo-grænsefladen her er at bruge aggregeringsrammen:
def mongoengine_agg_doc():
return list(MyModel.objects.aggregate({"$limit":1}))[0]