MongoMallard README + benchmarks
This commit is contained in:
parent
478062cb0f
commit
d7b4ad08cb
5 changed files with 176 additions and 3 deletions
85
README.md
Normal file
85
README.md
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
MongoMallard
|
||||||
|
============
|
||||||
|
|
||||||
|
MongoMallard is a fast ORM-like layer on top of PyMongo, based on MongoEngine.
|
||||||
|
|
||||||
|
* Repository: https://github.com/elasticsales/mongomallard
|
||||||
|
* See [README_MONGOENGINE](https://github.com/elasticsales/mongomallard/blob/master/README_MONGOENGINE.rst) for MongoEngine's README.
|
||||||
|
* See [DIFFERENCES](https://github.com/elasticsales/mongomallard/blob/master/DIFFERENCES.md) for differences between MongoEngine and MongoMallard.
|
||||||
|
|
||||||
|
|
||||||
|
Benchmarks
|
||||||
|
----------
|
||||||
|
|
||||||
|
Sample run on a 2.7 GHz Intel Core i5 running OS X 10.8.3
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>MongoEngine 0.8.2 (ede9fcf)</th>
|
||||||
|
<th>MongoMallard (478062c)</th>
|
||||||
|
<th>Speedup</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Doc initialization</td>
|
||||||
|
<td>52.494us</td>
|
||||||
|
<td>25.195us</td>
|
||||||
|
<td>2.08x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Doc getattr</td>
|
||||||
|
<td>1.339us</td>
|
||||||
|
<td>0.584us</td>
|
||||||
|
<td>2.29x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Doc setattr</td>
|
||||||
|
<td>3.064us</td>
|
||||||
|
<td>2.550us</td>
|
||||||
|
<td>1.20x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Doc to mongo</td>
|
||||||
|
<td>49.415us</td>
|
||||||
|
<td>26.497us</td>
|
||||||
|
<td>1.86x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Load from SON</td>
|
||||||
|
<td>61.475us</td>
|
||||||
|
<td>4.510us</td>
|
||||||
|
<td>13.63x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Save to database</td>
|
||||||
|
<td>434.389us</td>
|
||||||
|
<td>289.972us</td>
|
||||||
|
<td>2.29x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Load from database</td>
|
||||||
|
<td>558.178us</td>
|
||||||
|
<td>480.690us</td>
|
||||||
|
<td>1.16x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Save/delete big object to database</td>
|
||||||
|
<td>98.838ms</td>
|
||||||
|
<td>65.789ms</td>
|
||||||
|
<td>1.50x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Serialize big object from database</td>
|
||||||
|
<td>31.390ms</td>
|
||||||
|
<td>20.265ms</td>
|
||||||
|
<td>1.55x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Load big object from database</td>
|
||||||
|
<td>41.159ms</td>
|
||||||
|
<td>1.400ms</td>
|
||||||
|
<td>29.40x</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
See [tests/benchmark.py](https://github.com/elasticsales/mongomallard/blob/master/tests/benchmark.py) for source code.
|
||||||
|
|
@ -16,6 +16,7 @@ __all__ = (list(document.__all__) + fields.__all__ + connection.__all__ +
|
||||||
list(queryset.__all__) + signals.__all__ + list(errors.__all__))
|
list(queryset.__all__) + signals.__all__ + list(errors.__all__))
|
||||||
|
|
||||||
VERSION = (0, 8, 2)
|
VERSION = (0, 8, 2)
|
||||||
|
MALLARD = True
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
|
|
|
||||||
|
|
@ -180,9 +180,6 @@ class DocumentProxy(LocalProxy):
|
||||||
|
|
||||||
def _get_current_object(self):
|
def _get_current_object(self):
|
||||||
if self.__document == None:
|
if self.__document == None:
|
||||||
#print 'fetching', self.__document_type, self.__pk
|
|
||||||
#import traceback
|
|
||||||
#traceback.print_stack()
|
|
||||||
collection = self.__document_type._get_collection()
|
collection = self.__document_type._get_collection()
|
||||||
son = collection.find_one({'_id': self.__pk})
|
son = collection.find_one({'_id': self.__pk})
|
||||||
document = self.__document_type._from_son(son)
|
document = self.__document_type._from_son(son)
|
||||||
|
|
|
||||||
90
tests/benchmark.py
Normal file
90
tests/benchmark.py
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
from mongoengine import *
|
||||||
|
from timeit import repeat
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
conn_settings = {
|
||||||
|
'db': 'mongomallard-test',
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(**conn_settings)
|
||||||
|
|
||||||
|
def timeit(f, n=10000):
|
||||||
|
return min(repeat(f, repeat=3, number=n))/float(n)
|
||||||
|
|
||||||
|
class BenchmarkTestCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
class Book(Document):
|
||||||
|
name = StringField()
|
||||||
|
pages = IntField()
|
||||||
|
tags = ListField(StringField())
|
||||||
|
is_published = BooleanField()
|
||||||
|
|
||||||
|
Book.drop_collection()
|
||||||
|
|
||||||
|
create_book = lambda: Book(name='Always be closing', pages=100, tags=['self-help', 'sales'], is_published=True)
|
||||||
|
print 'Doc initialization: %.3fus' % (timeit(create_book, 1000) * 10**6)
|
||||||
|
|
||||||
|
b = create_book()
|
||||||
|
|
||||||
|
print 'Doc getattr: %.3fus' % (timeit(lambda: b.name, 10000) * 10**6)
|
||||||
|
|
||||||
|
print 'Doc setattr: %.3fus' % (timeit(lambda: setattr(b, 'name', 'New name'), 10000) * 10**6)
|
||||||
|
|
||||||
|
print 'Doc to mongo: %.3fus' % (timeit(b.to_mongo, 1000) * 10**6)
|
||||||
|
|
||||||
|
def save_book():
|
||||||
|
b._mark_as_changed('name')
|
||||||
|
b._mark_as_changed('tags')
|
||||||
|
b.save()
|
||||||
|
|
||||||
|
save_book()
|
||||||
|
son = b.to_mongo()
|
||||||
|
|
||||||
|
print 'Load from SON: %.3fus' % (timeit(lambda: Book._from_son(son), 1000) * 10**6)
|
||||||
|
|
||||||
|
print 'Save to database: %.3fus' % (timeit(save_book, 100) * 10**6)
|
||||||
|
|
||||||
|
print 'Load from database: %.3fus' % (timeit(lambda: Book.objects[0], 100) * 10**6)
|
||||||
|
|
||||||
|
def test_embedded(self):
|
||||||
|
class Contact(EmbeddedDocument):
|
||||||
|
name = StringField()
|
||||||
|
title = StringField()
|
||||||
|
address = StringField()
|
||||||
|
|
||||||
|
class Company(Document):
|
||||||
|
name = StringField()
|
||||||
|
contacts = ListField(EmbeddedDocumentField(Contact))
|
||||||
|
|
||||||
|
Company.drop_collection()
|
||||||
|
|
||||||
|
def get_company():
|
||||||
|
return Company(
|
||||||
|
name='Elastic',
|
||||||
|
contacts=[
|
||||||
|
Contact(
|
||||||
|
name='Contact %d' % x,
|
||||||
|
title='CEO',
|
||||||
|
address='Address %d' % x,
|
||||||
|
)
|
||||||
|
for x in range(1000)]
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_company():
|
||||||
|
c = get_company()
|
||||||
|
c.save()
|
||||||
|
c.delete()
|
||||||
|
|
||||||
|
print 'Save/delete big object to database: %.3fms' % (timeit(create_company, 10) * 10**3)
|
||||||
|
|
||||||
|
c = get_company().save()
|
||||||
|
|
||||||
|
print 'Serialize big object from database: %.3fms' % (timeit(c.to_mongo, 100) * 10**3)
|
||||||
|
print 'Load big object from database: %.3fms' % (timeit(lambda: Company.objects[0], 100) * 10**3)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue