2010-02-19 14 views
16

Derzeit meine Cashes Modelle in memcache wie folgt aus:Was ist der beste Weg, AppEngine Model Memcaching zu machen?

memcache.set("somekey", aModel) 

Aber Nicks Beitrag bei http://blog.notdot.net/2009/9/Efficient-model-memcaching legt nahe, dass es zuerst zu protobuffers Umwandlung viel effizienter ist. Aber nach einigen Tests habe ich herausgefunden, dass es in der Tat kleiner ist, aber eigentlich langsamer (~ 10%).

Haben andere die gleiche Erfahrung oder mache ich etwas falsch?

Testergebnisse: 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

Ich habe es gerade mit sehr großen und komplexen Modellen versucht, aber das Ergebnis war ungefähr das Gleiche. –

+0

Vielleicht gibt es http://docs.python.org/library/timeit.html auf GAE? Dies sollte genauere Ergebnisse zeigen, aber immer noch - nach dem Lesen des Blogeintrags, mit dem ich verlinkt habe, würde ich einen Größenunterschied zwischen der Leistung von Protobuffern und Gurke erwarten - und dies sollte sowieso von time.time() abgefangen werden .. –

+0

ich bin Mit Java Appengine, also bin ich zu faul, um diese Theorie zu testen - ist Pickle() die Ergebnisse hinter den Kulissen irgendwo zwischenspeichern, während to_protobuf nicht ist? Basierend auf dem Artikel, ich bin mir nicht sicher, dass ich eine volle Größenordnung der Erhöhung der Geschwindigkeit erwarten würde, wie Gurke immer noch aufgerufen wird, auch mit der Protobuf-Version. Der genutzte Raum könnte allerdings deutlich kleiner sein. –

Antwort

4

Der Memcache Aufruf Pickles noch das Objekt mit oder ohne protobuf zu verwenden. Pickle ist schneller mit einem protobuf Objekt, da es ein sehr einfaches Modell

Plain Gurke Objekte sind größer als protobuf + Gurke Objekte hat, damit sie Zeit auf Memcache speichern, aber es gibt mehr Prozessorzeit die protobuf Umwandlung

dabei

Daher im Allgemeinen beide Verfahren etwa gleich ... aber

Der Grund, warum Sie Protobuf verwenden sollten, ist es kann Änderungen zwischen den Versionen der Modelle behandeln, während Pickle wird Fehler. Dieses Problem wird Sie eines Tages beißen, also am besten früher damit umgehen

+1

Obwohl einige gute Punkte gemacht werden, aber nicht alles angegeben ist wahr. Wenn Sie sich den Code ansehen, pickt der memcache api nur Nicht-Strings. So werden Listen mit protobuffed Modellen gebeizt, einzelne Modelle nicht. Tatsächlich ist die Ausgabe von protobufs einfacher und kleiner, meine Tests legen nahe, dass es nicht weniger CPU-intensiv ist - daher die ursprüngliche Frage. Der Modellversions-Punkt ist gültig, aber nicht zu wichtig für mich, da Sie sowieso eine Möglichkeit haben sollten, mit ungültigen Cache-Ergebnissen umzugehen, und es wird nicht sehr oft vorkommen, nehme ich an. –

1

Beide Gurken und Protobufs sind in App Engine langsam, da sie in reinem Python implementiert sind. Ich habe festgestellt, dass das Schreiben meines eigenen, einfachen Serialisierungscodes mit Methoden wie str.join schneller ist, da die meiste Arbeit in C erledigt wird. Aber das funktioniert nur für einfache Datentypen.

+0

Haben Sie das auch für Modellobjekte gemacht? Ich wäre gespannt auf Ihre Umsetzung. –

+0

Ich habe das gemacht, aber python2.7 gibt uns cpickle und es ist jetzt schneller. – FoxyLad

1

Eine Möglichkeit, es schneller zu machen, ist, Ihr Modell in ein Wörterbuch zu verwandeln und die native Eval/Rep-Funktion als Ihre (De-) Serialisierer zu benutzen - mit Vorsicht natürlich wie immer mit dem eval eval, aber es sollte sei hier sicher, da es keinen externen Schritt gibt.

Unten ein Beispiel für eine Klasse Fake_entity, die genau das implementiert. Sie erstellen zuerst Ihr Wörterbuch über fake = Fake_entity(entity), dann können Sie Ihre Daten einfach über memcache.set(key, fake.serialize()) speichern. Die serialize() ist ein einfacher Aufruf der nativen Wörterbuchmethode von repr, mit einigen Hinzufügungen, falls erforderlich (z. B. Hinzufügen einer Kennung am Anfang der Zeichenfolge).

Um es zurück zu holen, einfach fake = Fake_entity(memcache.get(key)) verwenden. Das Fake_entity-Objekt ist ein einfaches Wörterbuch, dessen Schlüssel auch als Attribute zugänglich sind. Sie können normal auf Ihre Entity-Eigenschaften zugreifen, außer dass referenceProperties Schlüssel gibt, anstatt das Objekt zu holen (was sehr nützlich ist). Sie können() auch die tatsächliche Entität mit fake.get(), oder interessanterweise, ändern und dann mit fake.put() speichern.

Es funktioniert nicht mit Listen (wenn Sie mehrere Entitäten aus einer Abfrage abrufen), aber könnte leicht mit Join/Split-Funktionen mit einem Bezeichner wie '### FAKE MODEL ENTITY ###' als Trennzeichen angepasst werden . Verwenden Sie nur mit db.Model, würde kleine Anpassungen für Expando benötigen.

Ich würde gerne Geschwindigkeitstests auf diesem, würde ich davon ausgehen, dass dies schneller als die anderen Ansätze ist. Außerdem haben Sie kein Risiko, wenn sich Ihre Modelle in der Zwischenzeit irgendwie verändert haben.

Unten ein Beispiel, wie die serialisierte falsche Entität aussieht.Werfen Sie einen besonderen Blick auf Datetime (erstellt) sowie Referenzeigenschaften (Sub-Domain):

### FAKE MODEL ENTITY ###
{ 'Status': u'admin', 'session_expiry': Keine " first_name ': u'Louis', 'last_name': u'Le Sieur ',' modified_by ': Keine,' password_hash ': u'a9993e364706816aba3e25717000000000000000', 'Sprache': u'fr ',' created ': datetime.datetime (2010, 7, 18, 21, 50, 11, 750000), 'modifiziert': Keine, 'created_by': Keine, 'email': u' [email protected] ',' key ':' agdqZXJlZ2xlcgwLEgVMb2dpbhjmAQw ',' session_ref ': Keine,' _cls ':' models.Login ',' Gruppen ': [],' email___password_hash ': u' [email protected]+a9993e364706816aba3e25717000000000000000', 'Subdomäne': datastore_types.Key.from_path (u'Subdomain ' , 229L, _app = u'jeregle '),' erlaubt ': [],' permissions ': []}


Persönlich verwende ich auch statische Variablen (schneller als Memcache), um meine Entitäten kurzfristig zwischenzuspeichern und den Datastore zu holen, wenn der Server sich geändert hat oder sein Speicher aus irgendeinem Grund geleert wurde (was ziemlich oft passiert) .

Verwandte Themen