TL; DR: mongoengine sta spendendo età la conversione di tutti gli array restituiti al dicts
Per testare questo fuori ho costruito una collezione con un documento con una DictField
con un grande nidificato dict
. Il documento è all'incirca nella tua gamma 5-10 MB.
Possiamo quindi utilizzare timeit.timeit
per confermare la differenza di letture utilizzando pymongo e mongoengine.
Possiamo quindi utilizzare pycallgraph e GraphViz per vedere cosa sta prendendo mongoengine così dannatamente lungo.
Ecco il codice per intero:
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()
E l'uscita dimostra che mongoengine è essere molto lento rispetto a pymongo:
pymongo took 0.87s
mongoengine took 25.81118331072267
Il grafico delle chiamate risultante illustra abbastanza chiaramente dove il collo della bottiglia è:
In sostanza mongoengine chiamerà il metodo to_python su ogni DictField
che torna dal db. to_python
è piuttosto lento e nel nostro esempio viene chiamato un numero folle di volte.
Mongoengine viene utilizzato per mappare elegantemente la struttura del documento agli oggetti Python. Se disponi di documenti non strutturati molto grandi (a cui mongodb è utile), la mongoengine non è davvero lo strumento giusto e dovresti semplicemente usare pymongo.
Tuttavia, se si conosce la struttura, è possibile utilizzare i campi EmbeddedDocument
per ottenere prestazioni leggermente migliori rispetto a mongoengine.Ho eseguito un test simile ma non equivalente code in this gist e l'output è:
pymongo with dict took 0.12s
pymongo with embed took 0.12s
mongoengine with dict took 4.3059175412661075
mongoengine with embed took 1.1639373211854682
modo da poter rendere più veloce mongoengine ma pymongo è molto più veloce ancora.
UPDATE
Un buon collegamento all'interfaccia pymongo qui è quello di utilizzare il quadro di aggregazione:
def mongoengine_agg_doc():
return list(MyModel.objects.aggregate({"$limit":1}))[0]
Sembra che ci sono 2 cose diverse. Il metodo nativo carica 1 documento e si ferma, mentre mongoengine carica tutti i documenti e restituisce il primo. Prova a cambiare da 'find_one()' a 'lista (db.collection.find()) [0]' a metodi uguali. – Valijon
Ho anche provato ad aggiungere il limite (1) nella query di mongoengine, non ha aiutato. Sembra che la maggior parte del tempo viene speso per costruire l'oggetto mongoengine documento, con tutti gli oggetti nidificati .. –
'' skip' e lavoro limit' una volta i documenti vengono caricati :(Prova a filtrare per query specifica ... [http://docs.mongoengine.org/guide/querying.html#query-operators](http://docs.mongoengine.org/guide/querying.html#query-operators) – Valijon