Asyncpg no es tan rapido como pinta, pero no esta mal

Hace un par de aƱos, tirando a tres, con la incorporacion de async / await en python, aparecio el siguiente articulo

1M rows/s from Postgres to Python

En el que un nuevo driver, asyncpg, batia al tradicional psycopg por goleada. Los autores lo atribuian al hecho de usar el protocolo binario siempre que tenian oportunidad.

Pero en realidad el mayor ahorro esta en ignorar las conversiones automaticas de tipos, en especial la de json. Se consigue practicamente la misma ganancia tirando de json::text y retrasando el parseo hasta que sea necesario.

Ojo, aun asi sigue siendo mas rapido asyncpg. Un “SELECT id, json FROM” junto con su parseo, para calcular un estadistico trivial de 337209 lineas me tarda 2.23 segundos via asyncpg contra 3.62 con psycopg. En una maquina con disco duro antiguo, e incluyendo toda la operacion, el parsing del json y la conexion a la bbdd. Si hacemos un SELECT mas trivial, sin json ni texto por medio, alcanzamos el millon de filas por segundo prometido: comparando, nos ponemos en 0.293 segundos asyncpg versus 0.520 segundos el psycopg, y lo cierto es que ninguno de los dos esta usando el 100% de cpu.

La misma proporcion se mantiene si el select es de una variable text: casi un factor dos de ventaja.

En realidad el factor de ventaja no es tal, sino que depende de la cantidad de lineas a recuperar. Si intentamos un “SELECT id::text FROM x LIMIT 1” la diferencia es menor. De hecho, si para afinar un poco reutilizamos la conexion (sin el LIMIT, conectar era solo un 10% del tiempo, pero es mas relevante con pocas lineas), la diferencia desaparece totalmente y solo empieza a ser apreciable para queries con mas de 5000 lineas (o elementos). Si tu query devuelve solo dos mil o tres mil elementos, no hay ganancia… lo que tambien significa que puedes plantearte hacer la parte async con la libreria aiopg, que simplemente se pone por encima de psycopg2.

EXTRATIP: si no le tienes miedo a los SERVER SIDE CURSORs, puedes conseguir la misma ganancia que en asyncpg utilizando uno, lo que en las ultimas versiones de psycopg2 es relativamente facil: consiste en dar un name=”xxx”.. y hacer el bucle con cursor.fetchmany(size=1000) para que no se nos cree demasiada basura en memoria.

Otra brutalidad, pero no la he probado, seria coger todo el resultado con un .copy_to sin parsear

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.