2010-02-19 6 views
16

В настоящее время мое приложение кэширует модели в кэше, как это:Каков наилучший способ сделать AppEngine Model Memcaching?

memcache.set("somekey", aModel) 

Но после Никс в http://blog.notdot.net/2009/9/Efficient-model-memcaching предполагает, что первое превращение его в protobuffers является гораздо более эффективным. Но после запуска некоторых тестов я обнаружил, что он действительно меньше по размеру, но на самом деле медленнее (~ 10%).

Есть ли у других опыт, или я что-то не так?

Результаты тестов: http://1.latest.sofatest.appspot.com/?times=1000

import pickle 
import time 
import uuid 

from google.appengine.ext import webapp 
from google.appengine.ext import db 
from google.appengine.ext.webapp import util 
from google.appengine.datastore import entity_pb 
from google.appengine.api import memcache 

class Person(db.Model): 
name = db.StringProperty() 

times = 10000 

class MainHandler(webapp.RequestHandler): 

def get(self): 

    self.response.headers['Content-Type'] = 'text/plain' 

    m = Person(name='Koen Bok') 

    t1 = time.time() 

    for i in xrange(int(self.request.get('times', 1))): 
    key = uuid.uuid4().hex 
    memcache.set(key, m) 
    r = memcache.get(key) 

    self.response.out.write('Pickle took: %.2f' % (time.time() - t1)) 


    t1 = time.time() 

    for i in xrange(int(self.request.get('times', 1))): 
    key = uuid.uuid4().hex 
    memcache.set(key, db.model_to_protobuf(m).Encode()) 
    r = db.model_from_protobuf(entity_pb.EntityProto(memcache.get(key))) 


    self.response.out.write('Proto took: %.2f' % (time.time() - t1)) 


def main(): 
application = webapp.WSGIApplication([('/', MainHandler)], debug=True) 
util.run_wsgi_app(application) 


if __name__ == '__main__': 
main() 
+0

Я просто попробовал его с действительно большими и сложными моделями, но результат был примерно такой же. –

+0

Возможно, на GAE есть http://docs.python.org/library/timeit.html? Это должно показать более точные результаты, но все же - после прочтения записи в блоге, с которой вы связались, я ожидал бы разницу в величине разницы между производительностью protobuffers и pickle - и это должно быть уловлено time.time() в любом случае .. –

+0

i'm используя java appengine, поэтому я слишком ленив, чтобы проверить эту теорию, - это pickle() кэширование результатов за кулисами где-то, а to_protobuf - нет? Основываясь на этой статье, я не уверен, что ожидаю полного увеличения величины скорости, поскольку рассол все еще называется даже с использованием версии protobuf. однако используемое пространство, безусловно, может быть значительно меньше. –

ответ

4

Вызов Memcache еще Pickles объект с использованием или без использования Protobuf. Рассол быстрее с объектом Protobuf, так как он имеет очень простую модель

Plain маринованные объекты крупнее, чем Protobuf + маринованные объектов, следовательно, они экономят время на Memcache, но есть больше времени процессора при этом преобразование Protobuf

Следовательно, в общем, любой из методов работает примерно одинаково ... но

Причина, по которой вы должны использовать protobuf, может обрабатывать изменения между версиями моделей, тогда как Pickle будет ошибочной. Эта проблема укусит вас в один прекрасный день, поэтому лучше справиться с этим раньше

+1

Хотя некоторые хорошие моменты сделаны, но не все сказанное верно. Если вы посмотрите на код, memcache api только рассохнет non-strings. Таким образом, списки с протоподобными моделями будут маринованными, одиночными моделями нет. На самом деле продукция protobufs проще и меньше, мои тесты показывают, что это не менее интенсивный процессор, поэтому исходный вопрос. Точка версий модели действительна, но не слишком важна для меня, поскольку в любом случае вы должны иметь способ иметь дело с недействительными результатами кеша, и это не произойдет очень часто, я полагаю. –

1

Оба пика и протобуфы медленны в App Engine, поскольку они реализованы в чистом Python. Я обнаружил, что написание собственного простого кода сериализации с использованием методов типа str.join имеет тенденцию быть быстрее, поскольку большая часть работы выполняется на C. Но это работает только для простых типов данных.

+0

Вы тоже сделали это для объектов модели? Мне было бы интересно увидеть вашу реализацию. –

+0

Я делал это, но python2.7 дает нам cpickle, и теперь он быстрее. – FoxyLad

1

Один из способов сделать это быстрее - превратить вашу модель в словарь и использовать собственную функцию eval/repr в качестве сериализаторов (de) с осторожностью, как всегда со злым eval, но это должно быть здесь безопасным, поскольку нет внешнего шага.

Ниже приведен пример класса Fake_entity, реализующего именно это. Вы сначала создаете свой словарь через fake = Fake_entity(entity), тогда вы можете просто сохранить свои данные через memcache.set(key, fake.serialize()). Сериализация() является простым вызовом метода родного словаря для публикации, с некоторыми дополнениями, если вам нужно (например, добавить идентификатор в начале строки).

Чтобы получить его обратно, просто используйте fake = Fake_entity(memcache.get(key)). Объект Fake_entity - это простой словарь, ключи которого также доступны как атрибуты. Обычно вы можете получить доступ к своим объектам, за исключением того, что referenceProperties дает ключи вместо того, чтобы извлекать объект (что на самом деле очень полезно). Вы также можете получить() фактический объект с помощью fake.get() или более интересно, изменить его, а затем сохранить с помощью fake.put().

Это не работает со списками (если вы извлекаете несколько объектов из запроса), но их можно легко настроить с помощью функций объединения/разделения с использованием идентификатора типа ### FAKE MODEL ENTITY ### 'в качестве разделителя , Использовать только с db.Model, потребуются небольшие корректировки для Expando.

class Fake_entity(dict): 
    def __init__(self, record): 
     # simple case: a string, we eval it to rebuild our fake entity 
     if isinstance(record, basestring): 
      import datetime # <----- put all relevant eval imports here 
      from google.appengine.api import datastore_types 
      self.update(eval(record)) # careful with external sources, eval is evil 
      return None 

     # serious case: we build the instance from the actual entity 
     for prop_name, prop_ref in record.__class__.properties().items(): 
      self[prop_name] = prop_ref.get_value_for_datastore(record) # to avoid fetching entities 
     self['_cls'] = record.__class__.__module__ + '.' + record.__class__.__name__ 
     try: 
      self['key'] = str(record.key()) 
     except Exception: # the key may not exist if the entity has not been stored 
      pass 

    def __getattr__(self, k): 
     return self[k] 

    def __setattr__(self, k, v): 
     self[k] = v 

    def key(self): 
     from google.appengine.ext import db 
     return db.Key(self['key']) 

    def get(self): 
     from google.appengine.ext import db 
     return db.get(self['key']) 

    def put(self): 
     _cls = self.pop('_cls') # gets and removes the class name form the passed arguments 
     # import xxxxxxx ---> put your model imports here if necessary 
     Cls = eval(_cls) # make sure that your models declarations are in the scope here 
     real_entity = Cls(**self) # creates the entity 
     real_entity.put() # self explanatory 
     self['_cls'] = _cls # puts back the class name afterwards 
     return real_entity 

    def serialize(self): 
     return '### FAKE MODEL ENTITY ###\n' + repr(self) 
     # or simply repr, but I use the initial identifier to test and eval directly when getting from memcache 

Я бы приветствовал тесты скорости на этом, я бы предположил, что это намного быстрее, чем другие подходы. Кроме того, у вас нет никаких рисков, если ваши модели каким-то образом изменились.

Ниже приведен пример того, как выглядит сериализованный поддельный объект.Возьмем конкретный взгляд на DateTime (созданного), а также эталонные свойства (поддомена):

### FAKE МОДЕЛЬ СУЩНОСТЬ ###
{ 'статус': u'admin', 'session_expiry': Нет, ' first_name ': u'Louis', 'last_name': u'Le Sieur ',' modified_by ': None,' password_hash ': u'a9993e364706816aba3e25717000000000000000', 'language': u'fr ',' created ': datetime.datetime (2010, 7, 18, 21, 50, 11, 750000), 'modified': None, 'created_by': None, 'email': u' [email protected] ',' key ':' agdqZXJlZ2xlcgwLEgVMb2dpbhjmAQw ',' session_ref ': None,' _cls ':' models.Login ',' groups ': [],' email___password_hash ': u' [email protected]+a9993e364706816aba3e25717000000000000000', 'subdomain': datastore_types.Key.from_path (u'Subdomain ' , 229L, _app = u'jeregle '),' allowed ': [],' permissions ': []}


Лично я также использую статические переменные (быстрее, чем memcache) для кэширования моих объектов в краткосрочной перспективе, и извлекает хранилище данных, когда сервер изменился, или по какой-то причине его память покраснела (что часто случается довольно часто) ,

Смежные вопросы