You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[MNT] Switch from np.column_stack() to np.vstack().T for performance #31130
From the discussion in #31001 (comment), it looks like np.column_stack() is generally a slow operation compared to np.vstack().T. This is because the former has to interleave elements in memory whereas the second does contiguous memory copies and returns a view.
It's unclear to me how many of these operations are driving time spent in the hot paths of the code, but we should see a performance improvement by switching things over.
Marking this as an easy first issue since it's largely a find-and-replace.
10,000 elements: 10 runs x 10,000 iterations
With broadcast:
- `np.column_stack(np.broadcast_arrays(x, y))`: 36.47 us
- `np.vstack(np.broadcast_arrays(x, y)).T`: 27.67 us
- `np.empty + assign`: 30.09 us
Without broadcast:
- `np.column_stack([x, y])`: 20.63 us
- `np.vstack((x, y)).T`: 13.18 us
This issue is suited to new contributors because it does not require understanding of the
Matplotlib internals. To get started, please see our contributing
guide.
We do not assign issues. Check the Development section in the sidebar for linked pull
requests (PRs). If there are none, feel free to start working on it. If there is an open PR, please
collaborate on the work by reviewing it rather than duplicating it in a competing PR.
Hid one automatically generated comment from @AiGentsy. This is a low priority, easier task which is better used for human contributors to learn how to contribute.
Reacted by hannah, Ruth Comer, Agriya Khetarpal, Roscoe Skeens, Kristian Thy, sodagunz, Miha Zidar, prabhash-nw, SexyEvilGenius, Do Dinh Thy Son and 4 more
Reacted by matt-civiceye, Vishal Banthia, Sergio Gutiérrez, Chono N and Aankhen
changed the title [-][MNT] Switch from np.column_stack()` to `np.vstack().T` for performance[/-][+][MNT] Switch from `np.column_stack()` to `np.vstack().T` for performance[/+]on Feb 10, 2026
the gain is not so clear. It depends on the array size.
Only larger arrays (N>=3000) benefit. While one cound argue that small arrays are fast anyway, I find the result not compelling enough to run through the codebase and change everything.
Faster column_stack? numpy/numpy#320
Gist: np.column_stack is somewhat slower. Reasons are the ability to accept column blocks (only the first dimension has to match) and the different memory layout, which may or may not make vstack().T faster, also depending on the subsequent access patterns.
I also want to note that numpy/numpy#8082 (comment) measured 10 years ago that column_stack() was a bit faster than vstack().T. So performance characteristics can change over time and I do not think it's worth chasing microsecond performance gains in such a scenario. If we see the need for such mirco-optimization, IMHO we should limit this to hot spots and add performance tests for different implementations so that we note when our "optimized solution" does not perform well anymore.
FWIW I observe a smaller gain for column_stack for small arrays, and a switch at a lower value (100-300 elements), but I agree we should test this properly if we make that change.
I think I have an older machine than most of you, my results are not as smooth :). I personally find a 30-50% boost at large n pretty compelling, but have no issues with closing this and just making changes in the hot paths where we find them.
Putting together a benchmark suite has been on my personal TODO for a while. I'm not sure that it would be useful for catching behavior changes like this however where total impact on runtime from any given call is on the order of a percent or two. That'll get largely washed out in the noise for E2E plotting times, even if more targeted inspections like we are doing here show a difference. Part of why I've been bundling performance improvements - a single change can be hard to justify even if they stack up in the aggregate.
I'm not surprised that you see different numbers. These micro-performance aspects depend on many variables (Python version, numpy version, CPU architecture (e.g. apple silicon vs x86), CPU features like SIMD instructions, CPU cache size). It's really difficult to get a clear recommendation which one is better.
I therefore believe we should focus on other kinds of performance optimizations like preventing unnecessary or duplicate work or extensive python loops. These improvements are clearer and more persistent.
Per the reasons for the closure of this PR, it's unclear whether there are consistent performance gains from this change; therefore we will not be accepting PRs that target this issue.
If someone runs a bench marking suite (such as mpl-bench) and shows clear consistent gains such that this issue is worth reopening, let us know about the performance gains on discourse.matplotlib.org.
Activity
github-actions commentedon Feb 10, 2026
Good first issue - notes for new contributors
This issue is suited to new contributors because it does not require understanding of the
Matplotlib internals. To get started, please see our contributing
guide.
We do not assign issues. Check the Development section in the sidebar for linked pull
requests (PRs). If there are none, feel free to start working on it. If there is an open PR, please
collaborate on the work by reviewing it rather than duplicating it in a competing PR.
If something is unclear, please reach out on any of our communication
channels.
scottshambaugh commentedon Feb 10, 2026
Hid one automatically generated comment from @AiGentsy. This is a low priority, easier task which is better used for human contributors to learn how to contribute.
[-][MNT] Switch from np.column_stack()` to `np.vstack().T` for performance[/-][+][MNT] Switch from `np.column_stack()` to `np.vstack().T` for performance[/+]timhoffm commentedon Feb 11, 2026
In a quick test
Code
the gain is not so clear. It depends on the array size.
Only larger arrays (N>=3000) benefit. While one cound argue that small arrays are fast anyway, I find the result not compelling enough to run through the codebase and change everything.
Context from the numpy issue tracker:
Gist:
np.column_stackis somewhat slower. Reasons are the ability to accept column blocks (only the first dimension has to match) and the different memory layout, which may or may not makevstack().Tfaster, also depending on the subsequent access patterns.I also want to note that numpy/numpy#8082 (comment) measured 10 years ago that
column_stack()was a bit faster thanvstack().T. So performance characteristics can change over time and I do not think it's worth chasing microsecond performance gains in such a scenario. If we see the need for such mirco-optimization, IMHO we should limit this to hot spots and add performance tests for different implementations so that we note when our "optimized solution" does not perform well anymore.anntzer commentedon Feb 11, 2026
FWIW I observe a smaller gain for column_stack for small arrays, and a switch at a lower value (100-300 elements), but I agree we should test this properly if we make that change.

scottshambaugh commentedon Feb 11, 2026
I think I have an older machine than most of you, my results are not as smooth :). I personally find a 30-50% boost at large
npretty compelling, but have no issues with closing this and just making changes in the hot paths where we find them.Putting together a benchmark suite has been on my personal TODO for a while. I'm not sure that it would be useful for catching behavior changes like this however where total impact on runtime from any given call is on the order of a percent or two. That'll get largely washed out in the noise for E2E plotting times, even if more targeted inspections like we are doing here show a difference. Part of why I've been bundling performance improvements - a single change can be hard to justify even if they stack up in the aggregate.
timhoffm commentedon Feb 11, 2026
I'm not surprised that you see different numbers. These micro-performance aspects depend on many variables (Python version, numpy version, CPU architecture (e.g. apple silicon vs x86), CPU features like SIMD instructions, CPU cache size). It's really difficult to get a clear recommendation which one is better.
I therefore believe we should focus on other kinds of performance optimizations like preventing unnecessary or duplicate work or extensive python loops. These improvements are clearer and more persistent.
scottshambaugh commentedon Feb 11, 2026
Works for me!
jklymak commentedon Feb 11, 2026
Note that we have a benchmark suite. https://matplotlib.org/mpl-bench/
But it is probably not routinely run, whereas I think it should be run more often to make sure we are not regressing on speed
Borda commentedon Feb 12, 2026
story645 commentedon Feb 12, 2026
Per the reasons for the closure of this PR, it's unclear whether there are consistent performance gains from this change; therefore we will not be accepting PRs that target this issue.
If someone runs a bench marking suite (such as mpl-bench) and shows clear consistent gains such that this issue is worth reopening, let us know about the performance gains on discourse.matplotlib.org.
Comments on our AI policy should be directed to https://discourse.matplotlib.org/t/ai-policy-discussion/26116