mplot3d plot_surface performance: specialized fast path for regular surface meshes?

Hi, I investigated a performance issue in mplot3d for Axes3D.plot_surface(...), focusing on regular surface meshes such as spheres.

I ran a small set of layered experiments:

  • baseline redraw profiling
  • no_agg comparison
  • no_projection comparison
  • Poly3DCollection.do_3d_projection() breakdown
  • set_verts() timing before/after a prototype fast path

These experiments consistently suggest that the main bottleneck is not primarily:

  • Agg rasterization
  • projection math itself
  • the sorted(...) call itself

Instead, the dominant cost appears to be the rebuild pipeline inside the surface redraw path, especially:

  • per-face rebuild work in Poly3DCollection.do_3d_projection()
  • downstream polygon/path rebuild in PolyCollection.set_verts()

For example, in one mesh=96 profiling run, Poly3DCollection.do_3d_projection() took about 380.22 ms, while proj_transform itself was only about 1.04 ms (0.27%). The dominant costs were the rebuild stages, especially sort_and_stack and set_verts.

I then built a small prototype specialized for regular quad surfaces:

  • keep the existing projection step
  • replace the per-face rebuild/sort packaging path with a batch-oriented C++ helper
  • emit regular ndarray[num_faces, 4, 2] vertex data for downstream use

This is not a full patch, only a focused prototype to validate the direction.

On my test cases, it reduced average redraw time substantially:

  • mesh=12: 5.58 ms -> 2.32 ms (-58.4%)
  • mesh=24: 16.84 ms -> 4.32 ms (-74.3%)
  • mesh=48: 67.53 ms -> 12.00 ms (-82.2%)
  • mesh=96: 537.18 ms -> 187.87 ms (-65.0%)

It also reduced set_verts() time significantly:

  • mesh=12: 1.32 ms -> 0.24 ms (-81.8%)
  • mesh=24: 4.81 ms -> 0.90 ms (-81.3%)
  • mesh=48: 21.92 ms -> 3.63 ms (-83.4%)
  • mesh=96: 176.40 ms -> 87.64 ms (-50.3%)

My current interpretation is:

  1. the main performance problem is the rebuild pipeline for many faces
  2. regular surface meshes may benefit from a specialized fast path
  3. making the intermediate geometry data more regular also helps downstream set_verts()

I am not proposing that this prototype should be merged as-is.

What I want to ask is:

  1. Would a plot_surface-specific fast path for regular surface meshes be considered in scope for Matplotlib?
  2. If yes, would the project be open to an implementation that reduces Python-side per-face rebuild work, potentially with a compiled helper?
  3. If not, is there a preferred direction for improving this part of mplot3d?

If helpful, I can also provide a more structured write-up of the profiling steps and prototype details.

Some results are here , I feel faster when I drag them!

origin:


origin:


I expect it would be even faster if C++ patches were added for other parameters as well!

With what version did you benchmark? 3.11 will have several improvements to some parts of 3D plotting.

Thanks, that makes sense. I benchmarked with Matplotlib 3.10.8.

I tested the 3.11 release candidate and the improvement is indeed significant for my surface redraw case. I will wait for the stable 3.11 release and then rerun the full benchmark.

After that, if there is still a remaining bottleneck, I will focus the investigation on the post-3.11 path, especially the downstream set_verts / Path rebuild cost.