Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Faster path drawing for the cairo backend (cairocffi only)#8787

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
timhoffm merged 9 commits intomatplotlib:masterfromanntzer:cairo-fast-path
May 6, 2018

Conversation

@anntzer
Copy link
Contributor

@anntzeranntzer commentedJun 22, 2017
edited
Loading

When using cairocffi only:

Accelerate Path drawing by directly constructing a cairo_path_t using numpy operations rather than repeatedly calling the cairo API to add one point at a time.
Accelerate PathCollection drawing by concatenating Paths that have the same drawing parameters (color, etc., i.e. GraphicsContext attributes).
Minor additional performance improvements (remove an unneeded ndarray copy and an unneeded context save/restore pair).

Overall, this brings the performance of examples/mplot3d/wire3d_animation.py (used as a microbenchmark) from ~8.3fps to ~11.6fps on the same computer (just optimizing Paths instead of PathCollections gives ~10.5fps), using the gtk3cairo backend. As a comparison point, the gtk3agg backend renders ~16.2fps for that example.

From profiling, I believe that further improvements (on that same microbenchmark) could be achieved by improving the text drawing (currently, we use the "toy" API, which is unfortunately the only one exposed by cairocffi so far; additionally, I am not certain whether ft2font exposes pointers to the underlying freetype structures).

Or (for other performance tests) draw_markers could probably also benefit from a similar approach as this PR.

@anntzeranntzerforce-pushed thecairo-fast-path branch 3 times, most recently fromfe66d3b to37775fbCompareJune 22, 2017 22:11
@tacaswelltacaswell added this to the2.2 (next next feature release) milestoneJun 24, 2017
cur=ctx.get_current_point()
ctx.curve_to(
*np.concatenate([cur/3+points[:2]*2/3,
points[:2]*2/3+points[-2:]/3]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Not sure why this is different than before?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

previous version was actually incorrect (see basicallyhttps://en.wikipedia.org/wiki/B%C3%A9zier_curve#Degree_elevation)..



def_convert_paths(ctx,paths,transforms,clip=None):
return (_convert_paths_fastifHAS_CAIRO_CFFIelse_convert_paths_slow)(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Maybeif at top-level would be quicker/simpler?

anntzer reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

By top-level, I meantif HAS_CAIRO_CFFI: _convert_paths = _convert_paths_fast ...

anntzer reacted with thumbs up emoji

def_convert_paths_fast(ctx,paths,transforms,clip=None):
# We directly convert to the internal representation used by cairo, for
# which ABI compatibility is guaranteed. The layout is for each item is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

s/is for/for/

anntzer reacted with thumbs up emoji
transform= (transform
+Affine2D().scale(1.0,-1.0).translate(0,self.height))

+Affine2D().scale(1,-1).translate(0,self.height))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Could be+= to make one line?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

transforms are mutable but don't implement iadd (so += actually doesn't modify the transform in place), but I was too lazy to check and want to make clear that we're indeed not mutating the transform.


transform= (transform
+Affine2D().scale(1.0,-1.0).translate(0,self.height))
+Affine2D().scale(1,-1).translate(0,self.height))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Also+=?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

as above

self.convert_path(
ctx,marker_path,marker_trans+Affine2D().scale(1.0,-1.0))
_convert_path(
ctx,marker_path,marker_trans+Affine2D().scale(1,-1))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This fits on one line, no?

anntzer reacted with thumbs up emoji
vars(gc).update(gc_vars)
fork,vingc_vars.items():
try:
getattr(gc,"set"+k)(v)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

No underscore is needed?

Copy link
ContributorAuthor

@anntzeranntzerFeb 3, 2018
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Actually no, I'm just copying the__dict__ of the original gc, and (kind of an implementation detail...) GraphicsContextBase stores "property"foo (accessed by get/set_foo) in._foo). Not very elegant I admit.

antialiaseds,urls,offset_position):
path,transform=path_id
transform= (Affine2D(transform.get_matrix()).translate(xo,yo)
+Affine2D().scale(1,-1).translate(0,self.height))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Why do you need the secondAffine2D?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't.

ifnew_key==reuse_key:
grouped_draw.append((path,transform))
else:
_draw_paths()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Do we need to worry about draw order and such here?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The draw order is maintained, it's just that I batch the draws that use the same gc together.

as_int[::4][cairo_type_positions]=codes
as_int[1::4][cairo_type_positions]=cairo_type_sizes
mask[::2][cairo_type_positions]=mask[1::2][cairo_type_positions]=False
as_float[mask]=vertices.ravel()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Could be re-arranged slightly to keep theint andfloat bits together.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Not sure what you meant?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Well, you start with a buffer, make anint version, then afloat version, then a mask (which is forfloat). But then go back to filling theint version, modifying the mask (which isfloat stuff again), then fill thefloat version. So I mean more like:

buf=np.empty(cairo_num_data*16,np.uint8)as_int=np.frombuffer(buf.data,np.int32)as_int[::4][cairo_type_positions]=codesas_int[1::4][cairo_type_positions]=cairo_type_sizesas_float=np.frombuffer(buf.data,np.float64)mask=np.ones_like(as_float,bool)mask[::2][cairo_type_positions]=mask[1::2][cairo_type_positions]=Falseas_float[mask]=vertices.ravel()

@anntzer
Copy link
ContributorAuthor

anntzer commentedFeb 3, 2018
edited
Loading

Comments addressed.

@anntzeranntzerforce-pushed thecairo-fast-path branch 2 times, most recently froma103612 to245f5deCompareFebruary 6, 2018 08:24
@anntzer
Copy link
ContributorAuthor

comments addressed

@QuLogic
Copy link
Member

Generally, I don't see a problem with this change, but using your microbenchmark, I can't seem to reproduce the speedup you see:

$ python3 examples/mplot3d/wire3d_animation.pyMatplotlib version: 2.1.1.post1307.dev0+g84008ea05Backend: TkAggAverage FPS: 19.965329$ python3 examples/mplot3d/wire3d_animation.py/usr/lib/python3.6/site-packages/cairocffi/surfaces.py:651: UserWarning: implicit cast from 'char *' to a different pointer type: will be forbidden in the future (check that the types are as you expect; use an explicit ffi.cast() if they are correct)  ffi.cast('char*', address), format, width, height, stride)Matplotlib version: 2.1.1.post1307.dev0+g84008ea05Backend: GTK3AggAverage FPS: 20.217351$ python3 examples/mplot3d/wire3d_animation.pyMatplotlib version: 2.1.1.post1307.dev0+g84008ea05Backend: aggAverage FPS: 22.100502$ python3 examples/mplot3d/wire3d_animation.pyMatplotlib version: 2.1.1.post1307.dev0+g84008ea05Backend: GTK3CairoAverage FPS: 12.131168$ python3 examples/mplot3d/wire3d_animation.pyMatplotlib version: 2.1.1.post1307.dev0+g84008ea05Backend: cairoAverage FPS: 70.399400

vs.

$ python3 examples/mplot3d/wire3d_animation.pyMatplotlib version: 2.1.1.post18.dev0+gebcfd908cBackend: TkAggAverage FPS: 18.767531$ python3 examples/mplot3d/wire3d_animation.py/usr/lib/python3.6/site-packages/cairocffi/surfaces.py:651: UserWarning: implicit cast from 'char *' to a different pointer type: will be forbidden in the future (check that the types are as you expect; use an explicit ffi.cast() if they are correct)  ffi.cast('char*', address), format, width, height, stride)Matplotlib version: 2.1.1.post18.dev0+gebcfd908cBackend: GTK3AggAverage FPS: 19.497821$ python3 examples/mplot3d/wire3d_animation.pyMatplotlib version: 2.1.1.post18.dev0+gebcfd908cBackend: aggAverage FPS: 21.796568$ python3 examples/mplot3d/wire3d_animation.pyMatplotlib version: 2.1.1.post18.dev0+gebcfd908cBackend: GTK3CairoAverage FPS: 12.018317$ python3 examples/mplot3d/wire3d_animation.pyMatplotlib version: 2.1.1.post18.dev0+gebcfd908cBackend: cairoAverage FPS: 68.990914

@anntzer
Copy link
ContributorAuthor

Are you certain your gtk3cairo is using cairocffi rather than pycairo (which is not accelerated by this PR)?
I just tried and can still get the difference, ~8.7fps vs 12.2fps.

@QuLogic
Copy link
Member

OK, I'm not really sure what happened, but I just tried again and I get 12.705427 vs. 17.470702 now.

@anntzeranntzer modified the milestones:needs sorting,v3.0Feb 26, 2018
Copy link
Member

@timhoffmtimhoffm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Not a cario expert, but look good to me.

Not really clear with the naming ofconvert_paths. To me convert feels like it should return the converted object. Maybeapply_paths? If that's not good, please add a short docstring to the convert functions.

flatten() always makes a copy, whereas ravel() does not.
The removed pair of ctx.save and ctx.restore was clearly unnecessary(the outer one is still there, and the font is reset at each loopiteration).
Improves the performance of mplot3d/wire3d_animation on the gtk3cairobackend from ~8.3fps to ~10.5fps (as a comparison, gtk3agg is at~16.2fps).
Further increase the performance of mplot3d/wire3d_animation on thegtk3cairo backend from ~10.5fps to ~11.6fps (as a comparison, gtk3agg isat ~16.2fps).
For the slow code path, implement the degree elevation formula.For the fast code path, the path cleaner was already handling this forus, converting everything to lines.
@anntzer
Copy link
ContributorAuthor

Renamed to _append_path, which matches the name of the function in the cairo API:https://cairographics.org/manual/cairo-Paths.html#cairo-append-path

@timhoffmtimhoffm merged commit0d71eba intomatplotlib:masterMay 6, 2018
@anntzeranntzer deleted the cairo-fast-path branchMay 6, 2018 23:20
@lazka
Copy link
Contributor

Would it help you if pycairo had something likecairo.Path.create_for_data(buffer, num_data) ?

@anntzer
Copy link
ContributorAuthor

I moved my efforts to just doing everything in C(++) (https://github.com/anntzer/mplcairo) but I guess such a method can't hurt in general...
Perhaps also worth (for you) to discuss this with cairocffi to present the same API in both projects.

@lazka
Copy link
Contributor

I moved my efforts to just doing everything in C(++) (https://github.com/anntzer/mplcairo) but I guess such a method can't hurt in general...

ok

Perhaps also worth (for you) to discuss this with cairocffi to present the same API in both projects.

yeah, but it's unmaintained atm

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@QuLogicQuLogicQuLogic approved these changes

@timhoffmtimhoffmtimhoffm approved these changes

Assignees

No one assigned

Projects

None yet

Milestone

v3.0.0

Development

Successfully merging this pull request may close these issues.

5 participants

@anntzer@QuLogic@lazka@timhoffm@tacaswell

[8]ページ先頭

©2009-2025 Movatter.jp