Merged
Conversation
039ce34 to
83a9026
Compare
Contributor
Author
|
pyqtgraph-free script to demonstrate any performance differences with using sip array. from PyQt6 import QtCore, QtGui
import PyQt6.sip as sip
import numpy as np
import itertools
from time import perf_counter
class LineSegments:
def __init__(self, use_sip_array):
self.use_sip_array = hasattr(sip, 'array') and use_sip_array
self.alloc(0)
def alloc(self, size):
if self.use_sip_array:
self.objs = sip.array(QtCore.QLineF, size)
vp = sip.voidptr(self.objs, len(self.objs)*4*8)
self.arr = np.frombuffer(vp, dtype=np.float64).reshape((-1, 4))
else:
self.arr = np.empty((size, 4), dtype=np.float64)
self.objs = list(map(sip.wrapinstance,
itertools.count(self.arr.ctypes.data, self.arr.strides[0]),
itertools.repeat(QtCore.QLineF, self.arr.shape[0])))
def get(self, size):
if size != self.arr.shape[0]:
self.alloc(size)
return self.objs, self.arr
def run(size, use_sip_array):
qimg = QtGui.QImage(640, 480, QtGui.QImage.Format.Format_RGB32)
qimg.fill(QtCore.Qt.GlobalColor.transparent)
segments = LineSegments(use_sip_array)
objs, arr = segments.get(size)
arr[:, 0] = 0
arr[:, 1] = 0
arr[:, 2] = 10
arr[:, 3] = 10
painter = QtGui.QPainter(qimg)
painter.setPen(QtCore.Qt.GlobalColor.cyan)
t0 = perf_counter()
painter.drawLines(objs)
t1 = perf_counter()
painter.end()
return t1 - t0
size = 1_000_000
for use_sip_array in [False, True]:
dt = run(size, use_sip_array)
print(f'{use_sip_array=} {dt:.3f}') |
Contributor
Author
|
MWE take 2. Make it more explicit that the same lines object is being re-used for multiple frames. from PyQt6 import QtCore, QtGui
import PyQt6.sip as sip
import numpy as np
import itertools
from time import perf_counter
class LineSegments:
def __init__(self, use_sip_array):
self.use_sip_array = hasattr(sip, 'array') and use_sip_array
self.alloc(0)
def alloc(self, size):
if self.use_sip_array:
self.objs = sip.array(QtCore.QLineF, size)
vp = sip.voidptr(self.objs, len(self.objs)*4*8)
self.arr = np.frombuffer(vp, dtype=np.float64).reshape((-1, 4))
else:
self.arr = np.empty((size, 4), dtype=np.float64)
self.objs = list(map(sip.wrapinstance,
itertools.count(self.arr.ctypes.data, self.arr.strides[0]),
itertools.repeat(QtCore.QLineF, self.arr.shape[0])))
def get(self, size):
if size != self.arr.shape[0]:
self.alloc(size)
return self.objs, self.arr
def generate_pattern(nsegs, size):
rng = np.random.default_rng()
x = rng.random(nsegs) * size
y = rng.random(nsegs) * size
arr = np.empty((nsegs, 4), dtype=np.float64)
arr[:, 0] = x
arr[:, 1] = y
arr[:, 2] = x + 2
arr[:, 3] = y + 2
return arr
nsegs = 10_000
size = 500
nframes = 100
pattern = generate_pattern(nsegs, size)
def run(use_sip_array):
# generate lines once
segments = LineSegments(use_sip_array)
lines, memory = segments.get(nsegs)
memory[:] = pattern
# draw multiple frames using the same lines array
t0 = perf_counter()
for _ in range(nframes):
qimg = QtGui.QImage(size, size, QtGui.QImage.Format.Format_RGB32)
qimg.fill(QtCore.Qt.GlobalColor.transparent)
painter = QtGui.QPainter(qimg)
painter.setPen(QtCore.Qt.GlobalColor.cyan)
painter.drawLines(lines)
painter.end()
t1 = perf_counter()
return t1 - t0
for use_sip_array in [False, True]:
duration = run(use_sip_array)
fps = int(nframes / duration)
print(f'{use_sip_array=} {duration=:.3f} {fps=}') |
a5c6e41 to
afc1fd8
Compare
'finite' - all finite -> handled by 'all' - not all finite -> handled by 'array' the previous implementation assumed that segs was a list, which is no longer the case with sip.array
Contributor
Author
|
Note that https://pypi.org/project/PyQt6/6.3.1/ has been released and supports this feature. |
Member
Nice! Probably should get on someone to merge this ASAP 👀 |
Member
|
Tested this and got a performance improvement on both PlotSpeedTest and ScatterPlotSpeedTest on macOS; also considering this is now a zero-copy operation, this should be a vast improvement. Thanks for your efforts in implementing this @pijyoi ! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Quick test of PyQt sip.array.
It can be exercised by running PlotSpeedTest.py and triggering the segmented line mode (by increasing the line width for example).