Sto cercando di ridurre il tempo di esecuzione di una query di AppEngine eseguendo più sottoclassi in modo asincrono, utilizzando query.fetch_async(). Tuttavia, sembra che il guadagno sia minimo rispetto all'esecuzione seriale delle query.AppEngine Query.fetch_async non molto asincrono?
seguito alcune codice minimo del campione (in Python) illustra il problema - prima una funzione per l'esecuzione asincrona:
def run_parallel(self, repeats):
start = datetime.utcnow()
futures = []
for i in xrange(0, repeats):
q = User.query()
f = q.fetch_async(300, keys_only=True)
futures.append(f)
while futures:
f = ndb.Future.wait_any(futures)
futures.remove(f)
results = f.get_result()
delta_secs = (datetime.utcnow() - start).total_seconds()
self.response.out.write("Got %d results, delta_sec: %f<br>\n" %(len(results), delta_secs))
Poi una funzione per l'esecuzione seriale corrispondente:
def run_serial(self, repeats):
start = datetime.utcnow()
for i in xrange(0, repeats):
q = User.query()
results = q.fetch(300, keys_only=True)
delta_secs = (datetime.utcnow() - start).total_seconds()
self.response.out.write("Got %d results, delta_sec: %f<br>\n" %(len(results), delta_secs))
L' uscita di eseguire queste due funzioni 10 volte ciascuna (non sul dev-server), cioè delle seguenti chiamate:
run_parallel(10)
run_serial(10)
è la seguente:
Esecuzione di query parallele ...
Got 300 risultati, delta_sec: 0,401,09 mila
Got 300 risultati, delta_sec: 0,501,7 mila
Got 300 risultati, delta_sec: 0.596110
Got 300 risultati, delta_sec : 0.686120
Si 300 risultati, delta_sec: 0,709,22 mila
Si 300 risultati, delta_sec: 0,792,07 mila
Si 300 risultati, delta_sec: 0,816,5 mille
Si 300 risultati, delta_sec: 0,904,36 mila
Got 300 risultati, delta_sec: 0,993,6 mila
Got 300 risultati, delta_sec: 1,017320
l'esecuzione di query di serie ...
Got 300 risultati, delta_sec: 0,114,95 mila
Got 300 risultati, delta_sec: 0,269,01 mila
Got 300 risultati, delta_sec: 0.370590
Si 300 risultati, delta_sec: 0.472090
Si 300 risultati, delta_sec: 0.575130
Si 300 risultati, delta_sec: 0.678900
Got 300 res ULT, delta_sec: 0.782540
Si 300 risultati, delta_sec: 0,883,96 mila
Si 300 risultati, delta_sec: 0,986,37 mila
Si 300 risultati, delta_sec: 1,086500
Quindi le versioni parallele e seriali prendere approssimativamente allo stesso tempo, intorno 1 secondo. L'Appstat sono i seguenti, in cui i primi 10 domande sono quelle parallele e il seguente 10 sono quelli di serie:
Da queste statistiche sembra che i primi 10 query sono effettivamente in esecuzione in parallelo, ma che ognuno di loro sta prendendo una quantità sproporzionata di tempo rispetto alle singole interrogazioni seriali. Sembra che potrebbero bloccarsi in qualche modo, in attesa che si completino l'un l'altro.
Quindi la mia domanda: C'è qualcosa di sbagliato nel mio codice per l'esecuzione di query asincrone? Oppure esiste una limitazione intrinseca nell'efficienza delle query asincrone su AppEngine?
ho chiesto se il comportamento può essere causato da una delle seguenti:
- Esecuzione query asincrone sullo stesso tipo di entità. Tuttavia, un esempio simile che utilizza più tipi di entità differenti mostra risultati simili.
- Esecuzione di query identiche, in qualche modo blocchi delle sezioni dell'indice. Tuttavia, un esempio simile in cui ogni query è diversa (restituendo set di risultati disgiunti) produce risultati simili.
Quindi, sono un po 'una perdita. Ogni suggerimento sarà molto apprezzato.
Update 1
Seguendo il suggerimento di Bruyere Ho provato con db piuttosto che NDB, e ho cercato di scambiare l'ordine del parallelo e le versioni di serie. I risultati sono gli stessi.
Update 2
Ecco un post correlato in questione con lo stesso problema; ancora nessuna risposta sul motivo per cui le query parallele sono così inefficienti:
Best practice to query large number of ndb entities from datastore
Update 3
Il codice corrispondente utilizzando l'SDK di Java è parallelised molto ordinatamente. Ecco il Java appstats:
per essere precisi, questa implementazione Java è esplicitamente, le query multi-threaded in esecuzione in thread separati; questo è necessario perché, contrariamente a quanto afferma la documentazione di AppEngine, l'utilizzo degli iteratori di query non è non, in realtà le query vengono eseguite in parallelo.
Ho provato a utilizzare il multi-threading esplicito con chiamate di query sincrone nella versione Python, ma con gli stessi scarsi risultati della versione Python originale.
Il fatto che la versione Java funzioni come previsto implica che le scarse prestazioni asincrone di Python non siano causate da un collo di bottiglia della CPU di AppEngine.
L'unica spiegazione alternativa che posso pensare è che il Global Interpreter Lock di Python sta causando il thrashing. Ciò è supportato dal fatto che la riduzione dell'intervallo di controllo GIL (utilizzando sys.setcheckinterval) esaspera le scarse prestazioni asincrone.
Questo è sorprendente: il GIL non dovrebbe avere un impatto così grave dato che le query sono legate all'IO. Suppongo che forse i buffer di input RPC siano abbastanza piccoli da richiamare frequentemente le chiamate asincrone durante il recupero dei risultati, il che potrebbe causare il thriller GIL. Ho dato un'occhiata al codice della libreria di AppEngine Python, ma le chiamate RPC di basso livello sono fatte da _apphosting_runtime ___ python__apiproxy.MakeCall() che sembra essere closed-source.
Ahimé, la mia conclusione è che il runtime di AppEngine Python non è adatto per il tipo di query parallela richiesta, lasciandomi senza altra opzione che passare al runtime Java.Mi piacerebbe davvero evitare questo, e quindi spero davvero che mi sbagli e mi sia sfuggito qualcosa di ovvio. Qualsiasi suggerimento o suggerimento sarebbe molto apprezzato.
Grazie!
Sto avendo gli stessi problemi e ho sperimentato sys.setcheckinterval, query parallele in esecuzione, ma nulla ha avuto un impatto significativo sulle prestazioni. Ottenere 9k elementi dall'archivio dati richiede 19 secondi su istanze F2 e 10 secondi su istanze F3. Sembra che l'unico modo per farcela sia buttare l'hardware su di esso? – thomasf1
PS: istanze F1 testate per il confronto ... 27 secondi. Insopportabile. – thomasf1
PS: Sto cercando una soluzione più generale [qui # 26759950] (https://stackoverflow.com/questions/26759950/gae-aggregate-work-results-from-tasks-for-a-gae-query -performance-issue) – thomasf1