Repository: sphinx-gallery/sphinx-gallery

Issue:
Show memory usage in build output
The `show_memory` option is cumbersome to work with - I want to see which examples have high memory consumption and I do not want to look through each page in the gallery to find out the memory usage. 

Can you all output the memory usage in the build output similar to how execution times are shown and perhaps sort them from high to low useage?

It'd be nice if the output was formatted more clearly into a raw text table. Maybe something like:

```txt
example execution summary:
            Filename                        Time        Memory
    - ../examples/02-plot/orbit.py         39 sec       282 MB
    - ../examples/02-plot/opacity.py       22 sec       250 MB
    - ../examples/02-plot/lighting.py      21 sec       300 MB
    - ../examples/02-plot/texture.py       20 sec       133 MB
    - ../examples/01-filter/slicing.py     19 sec        85 MB
```

-----

Also, every so often I run into an issue with zombie threads - anyone else experience ths when using `'show_memory': True`:

```zsh
(dev) ➜  docs git:(patch/subplot) ✗ rm -r examples
(dev) ➜  docs git:(master) ✗ make html
Running Sphinx v2.1.2
Adding copy buttons to code blocks...
loading pickled environment... done
generating gallery...

Exception occurred:
  File "/Users/bane/.local/lib/python3.7/site-packages/psutil/_psosx.py", line 370, in catch_zombie
    raise ZombieProcess(proc.pid, proc._name, proc._ppid)
psutil.ZombieProcess: psutil.ZombieProcess process still exists but it's a zombie (pid=71575)
The full traceback has been saved in /var/folders/2x/4df66vz13pv4hj_ysv23tyyc0000gn/T/sphinx-err-8aw1_qos.log, if you want to report the issue to the developers.
Please also report this if it was a user error, so that a better error message can be provided next time.
A bug report can be filed in the tracker at <https://github.com/sphinx-doc/sphinx/issues>. Thanks!
make: *** [html] Error 2
```

Discussion:
larsoner: Example output currently, for `tinybuild`:
```
computation time summary:
    - examples/plot_svg.py:                     0.18 sec   11.2 MB
    - examples/plot_numpy_matplotlib.py:        0.18 sec    9.6 MB
    - examples/plot_future_imports.py:          0.11 sec    9.4 MB
    - examples/plot_second_future_imports.py:   0.08 sec    9.4 MB
    - examples/plot_future_imports_broken.py:   0.06 sec    9.4 MB
    - examples/local_module.py:                 0.00 sec    0.0 MB
```
and for SG:
```
computation time summary:
    - ../tutorials/seaborn/plot_seaborn_notebook.py:       48.32 sec    11.7 MB
    - ../examples/plot_seaborn.py:                         22.96 sec    59.0 MB
    - ../tutorials/plot_notebook.py:                        2.39 sec    14.3 MB
    - ../mayavi_examples/plot_3d.py:                        1.88 sec   114.1 MB
    - ../examples/plot_quantum.py:                          1.03 sec     9.4 MB
    - ../examples/plot_function_identifier.py:              0.92 sec     9.5 MB
    - ../examples/plot_colors.py:                           0.83 sec    25.4 MB
    - ../examples/plot_unicode_everywhere.py:               0.74 sec     9.5 MB
    - ../tutorials/plot_parse.py:                           0.64 sec     9.4 MB
    - ../examples/sin_func/plot_sin_black_background.py:    0.46 sec     9.4 MB
    - ../examples/plot_choose_thumbnail.py:                 0.43 sec     9.4 MB
    - ../examples/no_output/plot_raise.py:                  0.41 sec     9.4 MB
    - ../examples/plot_exp.py:                              0.38 sec     9.4 MB
    - ../examples/plot_gallery_version.py:                  0.33 sec     9.5 MB
    - ../examples/sin_func/plot_sin.py:                     0.31 sec     9.4 MB
    - ../examples/no_output/plot_strings.py:                0.26 sec     9.5 MB
    - ../examples/no_output/plot_syntaxerror.py:            0.16 sec     9.4 MB
    - ../examples/plot_sys_argv.py:                         0.14 sec     9.4 MB
    - ../examples/local_module.py:                          0.00 sec     0.0 MB
    - ../examples/no_output/just_code.py:                   0.00 sec     0.0 MB

```
And the HTML file, courtesy CircleCI:

https://969-25860190-gh.circle-artifacts.com/0/rtd_html/auto_examples/sg_execution_times.html

Looks like AppVeyor is broken on `master` and failing on this PR, will probably try to fix that mess separately.
codecov-io: # [Codecov](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523?src=pr&el=h1) Report
> Merging [#523](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523?src=pr&el=desc) into [master](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/commit/056adb872a407778cb351303931b0f7da55af848?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `91.37%`.

[![Impacted file tree graph](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523/graphs/tree.svg?width=650&token=K4eytvhwU5&height=150&src=pr)](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #523      +/-   ##
==========================================
+ Coverage   96.24%   96.24%   +<.01%     
==========================================
  Files          29       29              
  Lines        2687     2714      +27     
==========================================
+ Hits         2586     2612      +26     
- Misses        101      102       +1
```


| [Impacted Files](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [sphinx\_gallery/gen\_rst.py](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523/diff?src=pr&el=tree#diff-c3BoaW54X2dhbGxlcnkvZ2VuX3JzdC5weQ==) | `97.58% <100%> (ø)` | :arrow_up: |
| [sphinx\_gallery/gen\_gallery.py](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523/diff?src=pr&el=tree#diff-c3BoaW54X2dhbGxlcnkvZ2VuX2dhbGxlcnkucHk=) | `92.69% <90.38%> (+0.33%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523?src=pr&el=footer). Last update [056adb8...68638bb](https://codecov.io/gh/sphinx-gallery/sphinx-gallery/pull/523?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

larsoner: @choldgraf this one should be good to go. We can improve aesthetics in subsequent PRs if people are motivated, but they are already a nice upgrade.
choldgraf: @banesullivan have you given this one a chance?
larsoner: Here is the output on [CircleCI for MNE](https://circleci.com/gh/mne-tools/mne-python/14606#artifacts/containers/0):
<details>

    - ../examples/time_frequency/plot_source_power_spectrum_opm.py:                                    158.79 sec   2103.6 MB
    - ../tutorials/source-modeling/plot_mne_solutions.py:                                              129.81 sec    434.4 MB
    - ../examples/inverse/plot_covariance_whitening_dspm.py:                                            94.78 sec   1040.6 MB
    - ../tutorials/source-modeling/plot_forward.py:                                                     91.85 sec   1135.5 MB
    - ../examples/visualization/plot_montage.py:                                                        74.03 sec    309.8 MB
    - ../tutorials/sample-datasets/plot_brainstorm_auditory.py:                                         65.71 sec    529.9 MB
    - ../tutorials/source-modeling/plot_mne_dspm_source_localization.py:                                63.07 sec    114.6 MB
    - ../examples/connectivity/plot_mne_inverse_envelope_correlation.py:                                60.42 sec    749.7 MB
    - ../examples/inverse/plot_mixed_source_space_inverse.py:                                           49.15 sec   1347.9 MB
    - ../examples/connectivity/plot_mixed_source_space_connectivity.py:                                 47.82 sec   1002.0 MB
    - ../tutorials/stats-source-space/plot_stats_cluster_spatio_temporal_2samp.py:                      46.45 sec      8.6 MB
    - ../examples/forward/plot_source_space_morphing.py:                                                46.43 sec    581.5 MB
    - ../examples/visualization/plot_make_report.py:                                                    45.16 sec    663.9 MB
    - ../examples/time_frequency/plot_time_frequency_global_field_power.py:                             45.12 sec   1519.0 MB
    - ../tutorials/source-modeling/plot_eeg_no_mri.py:                                                  44.42 sec    441.5 MB
    - ../examples/inverse/plot_dics_source_power.py:                                                    43.77 sec    453.0 MB
    - ../tutorials/source-modeling/plot_dipole_orientations.py:                                         41.44 sec    454.2 MB
    - ../tutorials/stats-source-space/plot_stats_cluster_spatio_temporal_repeated_measures_anova.py:    41.19 sec      8.3 MB
    - ../tutorials/simulation/plot_dics.py:                                                             40.32 sec     79.8 MB
    - ../tutorials/intro/plot_10_overview.py:                                                           39.86 sec    563.0 MB
    - ../examples/preprocessing/plot_run_ica.py:                                                        39.72 sec    457.9 MB
    - ../tutorials/preprocessing/plot_40_artifact_correction_ica.py:                                    38.74 sec    774.8 MB
    - ../tutorials/simulation/plot_point_spread.py:                                                     38.16 sec    441.9 MB
    - ../examples/connectivity/plot_mne_inverse_envelope_correlation_volume.py:                         36.39 sec    605.5 MB
    - ../examples/simulation/plot_simulated_raw_data_using_subject_anatomy.py:                          34.82 sec    836.6 MB
    - ../examples/preprocessing/plot_ica_comparison.py:                                                 34.54 sec    519.2 MB
    - ../examples/inverse/plot_morph_volume_stc.py:                                                     33.79 sec    604.0 MB
    - ../tutorials/sample-datasets/plot_sleep.py:                                                       31.99 sec    548.1 MB
    - ../examples/decoding/plot_decoding_csp_timefreq.py:                                               31.44 sec      8.2 MB
    - ../tutorials/source-modeling/plot_visualize_stc.py:                                               30.90 sec    657.1 MB
    - ../tutorials/evoked/plot_whitened.py:                                                             29.18 sec    272.0 MB
    - ../examples/decoding/plot_decoding_spoc_CMC.py:                                                   29.00 sec    962.6 MB
    - ../examples/visualization/plot_xhemi.py:                                                          28.83 sec     88.8 MB
    - ../tutorials/preprocessing/plot_45_ica_from_raw.py:                                               28.58 sec    455.3 MB
    - ../tutorials/machine-learning/plot_sensors_decoding.py:                                           28.10 sec    487.1 MB
    - ../tutorials/sample-datasets/plot_brainstorm_phantom_elekta.py:                                   26.81 sec    160.0 MB
    - ../examples/inverse/plot_lcmv_beamformer_volume.py:                                               26.69 sec    845.0 MB
    - ../tutorials/time-freq/plot_sensors_time_frequency.py:                                            23.86 sec    311.2 MB
    - ../tutorials/source-modeling/plot_dipole_fit.py:                                                  23.61 sec      8.3 MB
    - ../examples/inverse/plot_lcmv_beamformer.py:                                                      23.58 sec    798.9 MB
    - ../tutorials/source-modeling/plot_compute_covariance.py:                                          22.60 sec      8.2 MB
    - ../examples/inverse/plot_tf_dics.py:                                                              21.83 sec    486.6 MB
    - ../tutorials/evoked/plot_visualize_evoked.py:                                                     21.69 sec     31.8 MB
    - ../examples/inverse/plot_tf_lcmv.py:                                                              21.33 sec    473.8 MB
    - ../examples/visualization/plot_eeg_on_scalp.py:                                                   20.98 sec     51.6 MB
    - ../examples/inverse/plot_gamma_map_inverse.py:                                                    20.72 sec     69.8 MB
    - ../examples/datasets/plot_opm_data.py:                                                            20.67 sec    944.9 MB
    - ../examples/time_frequency/plot_compute_csd.py:                                                   20.39 sec    230.2 MB
    - ../tutorials/discussions/plot_background_statistics.py:                                           20.36 sec      8.8 MB
    - ../examples/inverse/plot_morph_surface_stc.py:                                                    19.77 sec      8.6 MB
    - ../examples/visualization/plot_evoked_arrowmap.py:                                                19.44 sec     15.0 MB
    - ../tutorials/sample-datasets/plot_brainstorm_phantom_ctf.py:                                      18.53 sec    391.5 MB
    - ../examples/inverse/plot_time_frequency_mixed_norm_inverse.py:                                    18.10 sec      8.5 MB
    - ../tutorials/discussions/plot_background_filtering.py:                                            18.10 sec      8.5 MB
    - ../examples/decoding/plot_decoding_spatio_temporal_source.py:                                     17.51 sec     91.1 MB
    - ../tutorials/source-modeling/plot_source_alignment.py:                                            17.31 sec      9.2 MB
    - ../tutorials/epochs/plot_epoching_and_averaging.py:                                               17.26 sec    100.9 MB
    - ../examples/connectivity/plot_mne_inverse_psi_visual.py:                                          17.19 sec      8.5 MB
    - ../tutorials/machine-learning/plot_receptive_field.py:                                            17.02 sec      8.4 MB
    - ../examples/connectivity/plot_mne_inverse_coherence_epochs.py:                                    16.94 sec      8.6 MB
    - ../tutorials/stats-sensor-space/plot_stats_spatio_temporal_cluster_sensors.py:                    16.92 sec      8.6 MB
    - ../examples/preprocessing/plot_otp.py:                                                            16.44 sec     44.8 MB
    - ../tutorials/stats-source-space/plot_stats_cluster_spatio_temporal.py:                            16.31 sec      8.3 MB
    - ../tutorials/epochs/plot_visualize_epochs.py:                                                     15.98 sec    219.4 MB
    - ../tutorials/evoked/plot_eeg_erp.py:                                                              15.46 sec    127.7 MB
    - ../tutorials/source-modeling/plot_object_source_estimate.py:                                      15.12 sec      8.6 MB
    - ../tutorials/raw/plot_40_visualize_raw.py:                                                        14.71 sec   1868.5 MB
    - ../tutorials/preprocessing/plot_15_handling_bad_channels.py:                                      14.32 sec     15.4 MB
    - ../tutorials/stats-sensor-space/plot_stats_cluster_erp.py:                                        14.19 sec      8.5 MB
    - ../examples/datasets/plot_brainstorm_data.py:                                                     13.84 sec   1450.3 MB
    - ../examples/time_frequency/plot_source_label_time_frequency.py:                                   13.65 sec    171.8 MB
    - ../examples/visualization/plot_topo_customized.py:                                                13.40 sec    216.9 MB
    - ../tutorials/raw/plot_10_raw_overview.py:                                                         13.40 sec    214.9 MB
    - ../tutorials/preprocessing/plot_10_preprocessing_overview.py:                                     12.76 sec    318.7 MB
    - ../examples/inverse/plot_mixed_norm_inverse.py:                                                   12.72 sec     25.3 MB
    - ../tutorials/epochs/plot_object_epochs.py:                                                        12.66 sec    829.3 MB
    - ../examples/preprocessing/plot_interpolate_bad_channels.py:                                       12.63 sec      8.1 MB
    - ../examples/decoding/plot_receptive_field_mtrf.py:                                                12.41 sec      8.5 MB
    - ../examples/io/plot_elekta_epochs.py:                                                             11.84 sec     97.5 MB
    - ../tutorials/misc/plot_ecog.py:                                                                   11.59 sec      9.0 MB
    - ../examples/forward/plot_left_cerebellum_volume_source.py:                                        11.43 sec     47.4 MB
    - ../tutorials/stats-source-space/plot_stats_cluster_time_frequency_repeated_measures_anova.py:     11.24 sec    160.7 MB
    - ../tutorials/preprocessing/plot_50_artifact_correction_ssp.py:                                    11.14 sec    247.4 MB
    - ../examples/datasets/plot_limo_data.py:                                                           10.41 sec    577.9 MB
    - ../tutorials/intro/plot_40_projectors_background.py:                                               9.65 sec    211.7 MB
    - ../examples/time_frequency/plot_time_frequency_erds.py:                                            9.56 sec      8.3 MB
    - ../examples/preprocessing/plot_resample.py:                                                        9.51 sec    588.7 MB
    - ../tutorials/sample-datasets/plot_phantom_4DBTi.py:                                                9.48 sec      8.3 MB
    - ../examples/inverse/plot_mne_crosstalk_function.py:                                                9.48 sec      9.3 MB
    - ../examples/inverse/plot_mne_point_spread_function.py:                                             9.41 sec      8.6 MB
    - ../examples/preprocessing/plot_metadata_query.py:                                                  9.38 sec    581.1 MB
    - ../examples/forward/plot_forward_sensitivity_maps.py:                                              9.35 sec      8.3 MB
    - ../examples/preprocessing/plot_virtual_evoked.py:                                                  9.26 sec      8.2 MB
    - ../examples/time_frequency/plot_time_frequency_simulated.py:                                       9.13 sec     11.0 MB
    - ../tutorials/preprocessing/plot_20_rejecting_bad_data.py:                                          8.72 sec    176.4 MB
    - ../tutorials/epochs/plot_metadata_epochs.py:                                                       8.59 sec      8.3 MB
    - ../tutorials/raw/plot_30_annotate_raw.py:                                                          8.46 sec    216.7 MB
    - ../examples/visualization/plot_evoked_whitening.py:                                                8.40 sec    127.9 MB
    - ../examples/inverse/plot_label_from_stc.py:                                                        8.40 sec      8.8 MB
    - ../examples/visualization/plot_parcellation.py:                                                    8.21 sec      8.8 MB
    - ../tutorials/misc/plot_sensor_locations.py:                                                        7.49 sec    486.9 MB
    - ../tutorials/intro/plot_50_configure_mne.py:                                                       7.47 sec      8.1 MB
    - ../examples/simulation/plot_simulate_raw_data.py:                                                  7.43 sec    275.0 MB
    - ../examples/connectivity/plot_mne_inverse_label_connectivity.py:                                   7.27 sec      9.4 MB
    - ../tutorials/preprocessing/plot_60_maxwell_filtering_sss.py:                                       7.24 sec    486.8 MB
    - ../examples/inverse/plot_vector_mne_solution.py:                                                   7.04 sec     12.4 MB
    - ../examples/inverse/plot_rap_music.py:                                                             6.88 sec      8.3 MB
    - ../tutorials/raw/plot_20_event_arrays.py:                                                          6.81 sec    111.5 MB
    - ../examples/time_frequency/plot_compute_source_psd_epochs.py:                                      6.81 sec     86.0 MB
    - ../tutorials/intro/plot_20_events_from_raw.py:                                                     6.64 sec    214.9 MB
    - ../examples/visualization/plot_3d_to_2d.py:                                                        6.57 sec      8.4 MB
    - ../examples/time_frequency/plot_source_space_time_frequency.py:                                    6.52 sec    586.1 MB
    - ../tutorials/evoked/plot_object_evoked.py:                                                         6.19 sec      8.2 MB
    - ../tutorials/intro/plot_30_info.py:                                                                6.16 sec      8.1 MB
    - ../examples/simulation/plot_simulate_evoked_data.py:                                               5.93 sec    383.0 MB
    - ../examples/decoding/plot_linear_model_patterns.py:                                                5.81 sec   1033.0 MB
    - ../examples/visualization/plot_ssp_projs_sensitivity_map.py:                                       5.76 sec    148.1 MB
    - ../examples/inverse/plot_compute_mne_inverse_volume.py:                                            5.75 sec    581.1 MB
    - ../tutorials/misc/plot_modifying_data_inplace.py:                                                  5.69 sec    464.8 MB
    - ../examples/decoding/plot_decoding_unsupervised_spatial_filter.py:                                 5.62 sec      8.4 MB
    - ../examples/decoding/plot_decoding_time_generalization_conditions.py:                              5.60 sec      8.5 MB
    - ../examples/connectivity/plot_sensor_connectivity.py:                                              5.59 sec      9.8 MB
    - ../examples/preprocessing/plot_xdawn_denoising.py:                                                 5.56 sec    127.9 MB
    - ../tutorials/simulation/plot_creating_data_structures.py:                                          5.50 sec      8.1 MB
    - ../examples/preprocessing/plot_movement_compensation.py:                                           5.47 sec     15.4 MB
    - ../examples/time_frequency/plot_source_power_spectrum.py:                                          5.35 sec    108.0 MB
    - ../examples/connectivity/plot_cwt_sensor_connectivity.py:                                          5.04 sec      8.1 MB
    - ../examples/visualization/plot_meg_sensors.py:                                                     5.02 sec      9.3 MB
    - ../examples/decoding/plot_decoding_xdawn_eeg.py:                                                   4.71 sec      8.2 MB
    - ../examples/time_frequency/plot_compute_raw_data_spectrum.py:                                      4.59 sec    318.3 MB
    - ../examples/visualization/plot_evoked_topomap.py:                                                  4.56 sec      8.3 MB
    - ../tutorials/preprocessing/plot_30_filtering_resampling.py:                                        4.29 sec      8.1 MB
    - ../examples/inverse/plot_custom_inverse_solver.py:                                                 4.25 sec      8.4 MB
    - ../examples/simulation/plot_source_simulator.py:                                                   4.19 sec    113.8 MB
    - ../examples/visualization/plot_roi_erpimage_by_rt.py:                                              4.19 sec      8.1 MB
    - ../examples/inverse/plot_read_inverse.py:                                                          4.13 sec      8.4 MB
    - ../examples/stats/plot_linear_regression_raw.py:                                                   4.08 sec      9.3 MB
    - ../examples/decoding/plot_decoding_csp_eeg.py:                                                     3.86 sec      8.5 MB
    - ../tutorials/epochs/plot_epochs_to_data_frame.py:                                                  3.79 sec      9.3 MB
    - ../examples/inverse/plot_read_source_space.py:                                                     3.74 sec      8.3 MB
    - ../examples/stats/plot_sensor_permutation_test.py:                                                 3.65 sec    251.5 MB
    - ../examples/decoding/plot_ems_filtering.py:                                                        3.63 sec      8.1 MB
    - ../examples/io/plot_read_proj.py:                                                                  3.56 sec     14.0 MB
    - ../examples/io/plot_objects_from_arrays.py:                                                        3.53 sec     75.6 MB
    - ../tutorials/stats-sensor-space/plot_stats_cluster_time_frequency.py:                              3.43 sec      8.1 MB
    - ../examples/stats/plot_sensor_regression.py:                                                       3.41 sec     11.0 MB
    - ../examples/visualization/plot_topo_compare_conditions.py:                                         3.29 sec      8.2 MB
    - ../examples/stats/plot_fdr_stats_evoked.py:                                                        3.26 sec      8.6 MB
    - ../examples/inverse/plot_compute_mne_inverse_epochs_in_label.py:                                   3.25 sec      8.2 MB
    - ../examples/connectivity/plot_mne_inverse_connectivity_spectrum.py:                                3.21 sec      8.7 MB
    - ../examples/stats/plot_cluster_stats_evoked.py:                                                    3.15 sec      8.7 MB
    - ../tutorials/stats-sensor-space/plot_stats_cluster_1samp_test_time_frequency.py:                   3.11 sec      8.5 MB
    - ../examples/datasets/plot_hf_sef_data.py:                                                          2.86 sec      8.2 MB
    - ../examples/preprocessing/plot_find_ecg_artifacts.py:                                              2.83 sec      8.1 MB
    - ../examples/preprocessing/plot_rereference_eeg.py:                                                 2.28 sec    127.8 MB
    - ../examples/preprocessing/plot_define_target_events.py:                                            2.19 sec      8.1 MB
    - ../examples/io/plot_read_and_write_raw_data.py:                                                    2.18 sec     32.4 MB
    - ../examples/io/plot_read_evoked.py:                                                                2.06 sec      8.4 MB
    - ../examples/time_frequency/plot_temporal_whitening.py:                                             2.06 sec      8.2 MB
    - ../examples/inverse/plot_compute_mne_inverse_raw_in_label.py:                                      1.91 sec      8.2 MB
    - ../examples/visualization/plot_channel_epochs_image.py:                                            1.84 sec     11.6 MB
    - ../examples/inverse/plot_snr_estimate.py:                                                          1.80 sec      8.6 MB
    - ../examples/visualization/plot_sensor_noise_level.py:                                              1.72 sec    166.9 MB
    - ../examples/preprocessing/plot_head_positions.py:                                                  1.71 sec      8.1 MB
    - ../examples/preprocessing/plot_eog_artifact_histogram.py:                                          1.67 sec    127.8 MB
    - ../examples/inverse/plot_label_source_activations.py:                                              1.62 sec      8.2 MB
    - ../examples/io/plot_read_epochs.py:                                                                1.53 sec     22.2 MB
    - ../examples/preprocessing/plot_find_eog_artifacts.py:                                              1.31 sec      8.1 MB
    - ../examples/preprocessing/plot_shift_evoked.py:                                                    1.26 sec      8.2 MB
    - ../examples/inverse/plot_read_stc.py:                                                              1.23 sec      8.1 MB
    - ../examples/io/plot_read_events.py:                                                                1.19 sec      8.1 MB
    - ../examples/inverse/plot_label_activation_from_stc.py:                                             1.13 sec      8.1 MB
    - ../examples/io/plot_read_noise_covariance_matrix.py:                                               1.09 sec      8.2 MB
```
</details>
banesullivan: @choldgraf - I haven't given it a try yet, apologies! I'll try experimenting with it today!
choldgraf: @banesullivan sounds good - once I get a 👍 from you then I think we should merge, please feel free to give a code review as well!
larsoner: ... and for `matplotlib`:

```
computation time summary:
    - ../examples/animation/animation_demo.py:                                     8.51 sec   0.0 MB
    - ../tutorials/intermediate/constrainedlayout_guide.py:                        5.82 sec   0.0 MB
    - ../examples/style_sheets/style_sheets_reference.py:                          5.44 sec   0.0 MB
    - ../tutorials/colors/colormaps.py:                                            3.92 sec   0.0 MB
    - ../tutorials/intermediate/gridspec.py:                                       3.74 sec   0.0 MB
    - ../examples/lines_bars_and_markers/markevery_demo.py:                        3.71 sec   0.0 MB
    - ../examples/showcase/mandelbrot.py:                                          3.01 sec   0.0 MB
    - ../examples/userdemo/demo_gridspec06.py:                                     1.41 sec   0.0 MB
    - ../tutorials/intermediate/tight_layout_guide.py:                             1.40 sec   0.0 MB
    - ../examples/subplots_axes_and_figures/secondary_axis.py:                     1.39 sec   0.0 MB
    - ../examples/text_labels_and_annotations/demo_text_rotation_mode.py:          1.09 sec   0.0 MB
    - ../examples/subplots_axes_and_figures/demo_tight_layout.py:                  1.08 sec   0.0 MB
    - ../examples/images_contours_and_fields/plot_streamplot.py:                   1.04 sec   0.0 MB
    - ../examples/subplots_axes_and_figures/subplots_demo.py:                      1.03 sec   0.0 MB

```

Given it works on each of these repos I'll go ahead and merge @banesullivan. We can always iterate in subsequent PRs if we want to tweak things!

Patch:
diff --git a/sphinx_gallery/gen_gallery.py b/sphinx_gallery/gen_gallery.py
index 300a60d..f66c1f2 100644
--- a/sphinx_gallery/gen_gallery.py
+++ b/sphinx_gallery/gen_gallery.py
@@ -245,7 +245,7 @@ def generate_gallery_rst(app):
 
     seen_backrefs = set()
 
-    computation_times = []
+    costs = []
     workdirs = _prepare_sphx_glr_dirs(gallery_conf,
                                       app.builder.srcdir)
 
@@ -261,12 +261,11 @@ def generate_gallery_rst(app):
 
         # Here we don't use an os.walk, but we recurse only twice: flat is
         # better than nested.
-        this_fhindex, this_computation_times = generate_dir_rst(
+        this_fhindex, this_costs = generate_dir_rst(
             examples_dir, gallery_dir, gallery_conf, seen_backrefs)
 
-        computation_times += this_computation_times
-        write_computation_times(gallery_conf, gallery_dir,
-                                this_computation_times)
+        costs += this_costs
+        write_computation_times(gallery_conf, gallery_dir, this_costs)
 
         # we create an index.rst with all examples
         index_rst_new = os.path.join(gallery_dir, 'index.rst.new')
@@ -278,13 +277,12 @@ def generate_gallery_rst(app):
                     app.builder.srcdir, examples_dir, gallery_conf):
                 src_dir = os.path.join(examples_dir, subsection)
                 target_dir = os.path.join(gallery_dir, subsection)
-                this_fhindex, this_computation_times = \
+                this_fhindex, this_costs = \
                     generate_dir_rst(src_dir, target_dir, gallery_conf,
                                      seen_backrefs)
                 fhindex.write(this_fhindex)
-                computation_times += this_computation_times
-                write_computation_times(gallery_conf, target_dir,
-                                        this_computation_times)
+                costs += this_costs
+                write_computation_times(gallery_conf, target_dir, this_costs)
 
             if gallery_conf['download_all_examples']:
                 download_fhindex = generate_zipfiles(gallery_dir)
@@ -296,16 +294,20 @@ def generate_gallery_rst(app):
 
     if gallery_conf['plot_gallery']:
         logger.info("computation time summary:", color='white')
-        for time_elapsed, fname in sorted(computation_times, reverse=True):
-            fname = os.path.relpath(fname,
-                                    os.path.normpath(gallery_conf['src_dir']))
-            if time_elapsed is not None:
-                if time_elapsed >= gallery_conf['min_reported_time']:
-                    logger.info("    - %s: %.2g sec", fname, time_elapsed)
+        lines, lens = _format_for_writing(
+            costs, os.path.normpath(gallery_conf['src_dir']), kind='console')
+        for name, t, m in lines:
+            text = ('    - %s:   ' % (name,)).ljust(lens[0] + 10)
+            if t is None:
+                text += '(not run)'
+                logger.info(text)
             else:
-                logger.info("    - %s: not run", fname)
+                t_float = float(t.split()[0])
+                if t_float >= gallery_conf['min_reported_time']:
+                    text += t.rjust(lens[1]) + '   ' + m.rjust(lens[2])
+                    logger.info(text)
         # Also create a junit.xml file, useful e.g. on CircleCI
-        write_junit_xml(gallery_conf, app.builder.outdir, computation_times)
+        write_junit_xml(gallery_conf, app.builder.outdir, costs)
 
 
 SPHX_GLR_COMP_TIMES = """
@@ -331,8 +333,32 @@ def _sec_to_readable(t):
     return t
 
 
-def write_computation_times(gallery_conf, target_dir, computation_times):
-    if all(time[0] == 0 for time in computation_times):
+def cost_name_key(cost_name):
+    cost, name = cost_name
+    # sort by descending computation time, descending memory, alphabetical name
+    return (-cost[0], -cost[1], name)
+
+
+def _format_for_writing(costs, path, kind='rst'):
+    lines = list()
+    for cost in sorted(costs, key=cost_name_key):
+        if kind == 'rst':  # like in sg_execution_times
+            name = ':ref:`sphx_glr_{0}_{1}` (``{1}``)'.format(
+                path, os.path.basename(cost[1]))
+            t = _sec_to_readable(cost[0][0])
+        else:  # like in generate_gallery
+            assert kind == 'console'
+            name = os.path.relpath(cost[1], path)
+            t = '%0.2f sec' % (cost[0][0],)
+        m = '{0:.1f} MB'.format(cost[0][1])
+        lines.append([name, t, m])
+    lens = [max(x) for x in zip(*[[len(l) for l in ll] for ll in lines])]
+    return lines, lens
+
+
+def write_computation_times(gallery_conf, target_dir, costs):
+    total_time = sum(cost[0][0] for cost in costs)
+    if total_time == 0:
         return
     target_dir_clean = os.path.relpath(
         target_dir, gallery_conf['src_dir']).replace(os.path.sep, '_')
@@ -340,18 +366,23 @@ def write_computation_times(gallery_conf, target_dir, computation_times):
     with codecs.open(os.path.join(target_dir, 'sg_execution_times.rst'), 'w',
                      encoding='utf-8') as fid:
         fid.write(SPHX_GLR_COMP_TIMES.format(new_ref))
-        total_time = sum(ct[0] for ct in computation_times)
         fid.write('**{0}** total execution time for **{1}** files:\n\n'
                   .format(_sec_to_readable(total_time), target_dir_clean))
-        # sort by time (descending) then alphabetical
-        for ct in sorted(computation_times, key=lambda x: (-x[0], x[1])):
-            name = os.path.basename(ct[1])
-            example_link = 'sphx_glr_%s_%s' % (target_dir_clean, name)
-            fid.write(u'- **{0}**: :ref:`{2}` (``{1}``)\n'.format(
-                _sec_to_readable(ct[0]), name, example_link))
-
-
-def write_junit_xml(gallery_conf, target_dir, computation_times):
+        lines, lens = _format_for_writing(costs, target_dir_clean)
+        del costs
+        hline = ''.join(('+' + '-' * (l + 2)) for l in lens) + '+\n'
+        fid.write(hline)
+        format_str = ''.join('| {%s} ' % (ii,)
+                             for ii in range(len(lines[0]))) + '|\n'
+        for line in lines:
+            line = [ll.ljust(len_) for ll, len_ in zip(line, lens)]
+            text = format_str.format(*line)
+            assert len(text) == len(hline)
+            fid.write(text)
+            fid.write(hline)
+
+
+def write_junit_xml(gallery_conf, target_dir, costs):
     if not gallery_conf['junit'] or not gallery_conf['plot_gallery']:
         return
     failing_as_expected, failing_unexpectedly, passing_unexpectedly = \
@@ -362,8 +393,8 @@ def write_junit_xml(gallery_conf, target_dir, computation_times):
     elapsed = 0.
     src_dir = gallery_conf['src_dir']
     output = ''
-    for ct in computation_times:
-        t, fname = ct
+    for cost in costs:
+        (t, _), fname = cost
         if not any(fname in x for x in (gallery_conf['passing_examples'],
                                         failing_unexpectedly,
                                         failing_as_expected,
@@ -391,7 +422,7 @@ def write_junit_xml(gallery_conf, target_dir, computation_times):
                                escape(traceback)))
         output += u'</testcase>'
         n_tests += 1
-        elapsed += ct[0]
+        elapsed += t
     output += u'</testsuite>'
     output = (u'<?xml version="1.0" encoding="utf-8"?>'
               u'<testsuite errors="0" failures="{0}" name="sphinx-gallery" '
diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py
index efcc438..8980bb4 100644
--- a/sphinx_gallery/gen_rst.py
+++ b/sphinx_gallery/gen_rst.py
@@ -305,17 +305,17 @@ def generate_dir_rst(src_dir, target_dir, gallery_conf, seen_backrefs):
     sorted_listdir = sorted(
         listdir, key=gallery_conf['within_subsection_order'](src_dir))
     entries_text = []
-    computation_times = []
+    costs = []
     build_target_dir = os.path.relpath(target_dir, gallery_conf['src_dir'])
     iterator = sphinx_compatibility.status_iterator(
         sorted_listdir,
         'generating gallery for %s... ' % build_target_dir,
         length=len(sorted_listdir))
     for fname in iterator:
-        intro, time_elapsed = generate_file_rst(
+        intro, cost = generate_file_rst(
             fname, target_dir, src_dir, gallery_conf)
         src_file = os.path.normpath(os.path.join(src_dir, fname))
-        computation_times.append((time_elapsed, src_file))
+        costs.append((cost, src_file))
         this_entry = _thumbnail_div(target_dir, gallery_conf['src_dir'],
                                     fname, intro) + """
 
@@ -336,7 +336,7 @@ def generate_dir_rst(src_dir, target_dir, gallery_conf, seen_backrefs):
     fhindex += """.. raw:: html\n
     <div style='clear:both'></div>\n\n"""
 
-    return fhindex, computation_times
+    return fhindex, costs
 
 
 def handle_exception(exc_info, src_file, script_vars, gallery_conf):
@@ -633,8 +633,8 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf):
     -------
     intro: str
         The introduction of the example
-    time_elapsed : float
-        seconds required to run the script
+    cost : tuple
+        A tuple containing the ``(time, memory)`` required to run the script.
     """
     src_file = os.path.normpath(os.path.join(src_dir, fname))
     target_file = os.path.join(target_dir, fname)
@@ -648,7 +648,7 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf):
     if md5sum_is_current(target_file):
         if executable:
             gallery_conf['stale_examples'].append(target_file)
-        return intro, 0
+        return intro, (0, 0)
 
     image_dir = os.path.join(target_dir, 'images')
     if not os.path.exists(image_dir):
@@ -695,7 +695,7 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf):
     save_notebook(example_nb, ipy_fname)
     _replace_md5(ipy_fname)
 
-    return intro, time_elapsed
+    return intro, (time_elapsed, memory_used)
 
 
 def rst_blocks(script_blocks, output_blocks, file_conf, gallery_conf):


Test patch:
diff --git a/sphinx_gallery/tests/test_full.py b/sphinx_gallery/tests/test_full.py
index a6d31f5..f56604d 100644
--- a/sphinx_gallery/tests/test_full.py
+++ b/sphinx_gallery/tests/test_full.py
@@ -26,7 +26,8 @@ import pytest
 N_TOT = 5
 N_FAILING = 1
 N_GOOD = N_TOT - N_FAILING
-N_RST = 12 + N_TOT
+N_RST = 14 + N_TOT
+N_RST = '(%s|%s)' % (N_RST, N_RST - 1)  # AppVeyor weirdness
 
 
 @pytest.fixture(scope='module')
@@ -101,7 +102,7 @@ def test_junit(sphinx_app, tmpdir):
     new_toctree_dir = op.join(new_src_dir, '_build', 'toctrees')
     passing_fname = op.join(new_src_dir, 'examples',
                             'plot_numpy_matplotlib.py')
-    failing_fname = op.join(new_src_dir, 'examples',
+    failing_fname = op.join(new_src_dir, 'examples', 'future',
                             'plot_future_imports_broken.py')
     shutil.move(passing_fname, passing_fname + '.temp')
     shutil.move(failing_fname, passing_fname)
@@ -242,13 +243,16 @@ def test_rebuild(tmpdir_factory, sphinx_app):
     # First run completes in the fixture.
     #
     status = sphinx_app._status.getvalue()
-    want = '.*%s added, 0 changed, 0 removed$.*' % (N_RST,)
-    assert re.match(want, status, re.MULTILINE | re.DOTALL) is not None
-    assert re.match('.*targets for 1 source files that are out of date$.*',
-                    status, re.MULTILINE | re.DOTALL) is not None
+    lines = [line for line in status.split('\n') if 'removed' in line]
+    want = '.*%s added, 0 changed, 0 removed.*' % (N_RST,)
+    assert re.match(want, status, re.MULTILINE | re.DOTALL) is not None, lines
+    want = '.*targets for 2 source files that are out of date$.*'
+    lines = [line for line in status.split('\n') if 'out of date' in line]
+    assert re.match(want, status, re.MULTILINE | re.DOTALL) is not None, lines
+    lines = [line for line in status.split('\n') if 'on MD5' in line]
     want = ('.*executed %d out of %d.*after excluding 0 files.*based on MD5.*'
             % (N_GOOD, N_TOT))
-    assert re.match(want, status, re.MULTILINE | re.DOTALL) is not None
+    assert re.match(want, status, re.MULTILINE | re.DOTALL) is not None, lines
     old_src_dir = (tmpdir_factory.getbasetemp() / 'root_old').strpath
     shutil.copytree(sphinx_app.srcdir, old_src_dir)
     generated_modules_0 = sorted(
@@ -379,7 +383,7 @@ def test_rebuild(tmpdir_factory, sphinx_app):
                          buildername='html', status=StringIO())
         new_app.build(False, [])
     status = new_app._status.getvalue()
-    n = '[2|3]'
+    n = '[2|3|4]'
     lines = [line for line in status.split('\n') if 'source files tha' in line]
     want = '.*targets for %s source files that are out of date$.*' % n
     assert re.match(want, status, re.MULTILINE | re.DOTALL) is not None, lines
diff --git a/sphinx_gallery/tests/test_gen_gallery.py b/sphinx_gallery/tests/test_gen_gallery.py
index fb72f8e..9af4dc1 100644
--- a/sphinx_gallery/tests/test_gen_gallery.py
+++ b/sphinx_gallery/tests/test_gen_gallery.py
@@ -23,7 +23,8 @@ from sphinx.util.docutils import docutils_namespace
 
 from sphinx_gallery import sphinx_compatibility
 from sphinx_gallery.gen_gallery import (check_duplicate_filenames,
-                                        collect_gallery_files)
+                                        collect_gallery_files,
+                                        write_computation_times)
 
 
 @pytest.fixture
@@ -139,6 +140,24 @@ def test_no_warning_simple_config(sphinx_app_wrapper):
     assert build_warn == ''
 
 
+@pytest.mark.conf_file(content="""
+sphinx_gallery_conf = {
+    'reset_modules': ('foo',),
+}""")
+def test_bad_reset_str(sphinx_app_wrapper):
+    with pytest.raises(ValueError, match='Unknown module resetter'):
+        sphinx_app_wrapper.create_sphinx_app()
+
+
+@pytest.mark.conf_file(content="""
+sphinx_gallery_conf = {
+    'reset_modules': (1.,),
+}""")
+def test_bad_reset_callable(sphinx_app_wrapper):
+    with pytest.raises(ValueError, match='Module resetter .* was not callab'):
+        sphinx_app_wrapper.create_sphinx_app()
+
+
 @pytest.mark.conf_file(content="""
 sphinx_gallery_conf = {
     'mod_example_dir' : os.path.join('modules', 'gen'),
@@ -372,3 +391,7 @@ def test_first_notebook_cell_config(sphinx_app_wrapper):
     # First cell must be str
     with pytest.raises(ValueError):
         parse_config(sphinx_app_wrapper.create_sphinx_app())
+
+
+def test_write_computation_times_noop():
+    write_computation_times(None, None, [[[0]]])
diff --git a/sphinx_gallery/tests/tinybuild/conf.py b/sphinx_gallery/tests/tinybuild/conf.py
index 7c88e4c..05c37c4 100644
--- a/sphinx_gallery/tests/tinybuild/conf.py
+++ b/sphinx_gallery/tests/tinybuild/conf.py
@@ -44,7 +44,7 @@ sphinx_gallery_conf = {
     'backreferences_dir': 'gen_modules/backreferences',
     'within_section_order': FileNameSortKey,
     'image_scrapers': (matplotlib_format_scraper(),),
-    'expected_failing_examples': ['examples/plot_future_imports_broken.py'],
+    'expected_failing_examples': ['examples/future/plot_future_imports_broken.py'],  # noqa
     'show_memory': True,
     'junit': op.join('sphinx-gallery', 'junit-results.xml'),
 }
diff --git a/sphinx_gallery/tests/tinybuild/examples/future/README.rst b/sphinx_gallery/tests/tinybuild/examples/future/README.rst
new file mode 100644
index 0000000..5cace96
--- /dev/null
+++ b/sphinx_gallery/tests/tinybuild/examples/future/README.rst
@@ -0,0 +1,4 @@
+Future examples
+===============
+
+Examples that use ``__future__``.