I have a Flask app that is displaying some data as a png. Below is a simple dummy example:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from io import BytesIO
import logging
logger = logging.getLogger(__name__)
def specimage():
plt.clf()
plt.plot(np.arange(3000), np.random.random(3000), drawstyle="steps-post")
plt.loglog()
out = BytesIO()
logger.debug("Rendering...")
plt.savefig(out, format='png')
logger.debug("Done rendering")
out.seek(0)
return out.getvalue()
if I run this interactively, the function takes maybe 1 second to run. But if I call it from a flask app:
import render
@app.route('/getfig')
def getfig():
return Response(render.specimage(), content_type="application/png")
Now the call to savefig
takes at least 15 seconds, with the CPU pegged at 100% the whole time!
If I take this view and insert it into a very basic Flask app, it runs at a reasonable speed. But my app is quite complicated. Where can I look to find weird interactions that could be slowing matplotlib down so much?
EDIT A bit more information that might be relevant. While the image is being rendered, the application has launched a separate thread doing some lengthy calculations. It looks like the image generating hangs until this other thread finishes.
So that raises the question, why is matplotlib stuck? I can access any other view in my flask app and it returns instantly, so in general the threading is working correctly. Am I running into some weird GIL issue here? The other thread is doing some zlib compression, and I think PNG uses zlib as well, so, maybe that's the issue?
EDIT2 Removing zlib calls from the other thread didn't help, so it's not zlib's fault. The other thread is not calling any matplot functions, but it is in the same module as the plotting thread, meaning matplotlib.pyplot is imported there...