Slower update performance with ImageExporter than GUI window

51 views
Skip to first unread message

jimbo1qaz

unread,
Nov 22, 2018, 12:50:52 AM11/22/18
to pyqtgraph
I'm developing an audio oscilloscope-type program, which outputs frames as raw bytes to ffmpeg and ffplay (which plays back video and audio together, synchronized). I can also tell ffmpeg to output to file instead.

Originally I used matplotlib. I tried switching to pyqtgraph because I thought it would be faster.

Unfortunately it ended up too slow for my use case.
  • Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz, turbo 2.70GHz
  • Windows 10 x64 (I can try Kubuntu 18.04 later)
  • Miniconda Python 3.6.6
  • pyqtgraph 0.11.0.dev0+g2e69b9c
I adapted pyqtgraph's speed test to resemble my data more closely (hide all axises, no title, don't write fps to canvas):
In my own app rendering tests, I usually get 50-120fps, depending on data and number of plots. In any case, this is far worse than matplotlib with axis drawing disabled (118-158fps).

In either case, stopping in PyCharm (which sends Ctrl+C) results in "Process finished with exit code -1" instead of a stacktrace, which prevents me from taking a cProfile snapshot of my above speed demo.

I attached 2 cProfile logs. I closed the GUI after an approximate amount of time, and terminated the QImage code using "for i in range(600)".
pyqtgraph cprofile.7z

jimbo1qaz

unread,
Nov 22, 2018, 2:08:39 AM11/22/18
to pyqtgraph
I've attached new profiles, where both GUI and QImage run 600 times.

pyqtgraph cprofile 600x.7z

jimbo1qaz

unread,
Nov 23, 2018, 5:16:50 AM11/23/18
to pyqtgraph
It turns out ImageExporter is terribly optimized for speed. I assume the authors designed it to be acceptable for one-shot screenshots, and never tested it for exporting animations.
  • Initializing image to background color:
    • buf[:,:,0] = color.red()...etc. is slow.
    • Solution? QImage.fill(color) is near-instant.
  • GUI plots render to QPixmap (GPU/etc texture), and no copy is performed?. Image plots are inherently slower since they require copying to QImage (memory buffer).
    • This is very slow with Format_ARGB32, but much faster with Format_RGB32.
    • Solution: I create QImage(w, h, Format_RGB32) without passing in `bg` ndarray.
  • Speed of various QImage formats:
    • Format_RGB32 = 280fps or less
    • Format_ARGB32 = 150fps or less
    • Format_RGB888 = 110fps
    • I have no need for an alpha channel when piping video frames to ffmpeg, and I doubt many plots have transparent backgrounds.
  • Initializes `self.bg` buffer and `self.png` QImage, and overwrites with new objects during next screenshot (discards local QPainter too, but it isn't a major slowdown)
    • Not 100% sure if this causes slowdown. Changing QPainter(QPixmap()) and calling qpixmap.toImage() performs wasteful allocations? but isn't noticeably slower than my final solution.
    • Solution? Save and reuse QPainter and QImage (I renamed self.png to qimage, and self.painter).
      • Delete variable self.bg, since I let QImage allocate memory.
  • self.setExportMode() is slow, but is called twice per screenshot (once to enable, once to disable).
    • Solution? Call it once during the first screenshot, and never turn it back off.
      • This is not an issue for me, since I never display the widgets in a window.
      • You could move enabling/disabling setExportMode to a `with` statement, which Exporter callers are required to use.
    • Called every frame: 180fps
    • Called once: 250fps
    • Not called: 200fps?
Source code for my modified ImageExporter: https://gist.github.com/jimbo1qaz/24b0f74dfac8d48dc998bcf6a9451f96

On Wednesday, November 21, 2018 at 9:50:52 PM UTC-8, jimbo1qaz wrote:
Reply all
Reply to author
Forward
0 new messages