Skip to content

Conversation

@marcorudolphflex
Copy link
Contributor

@marcorudolphflex marcorudolphflex commented Nov 12, 2025

As our test suite does have a quite long runtime, this PR addresses the identification of the most time-intensive modules/functions/runs and speeds up the most critical ones.

  • added a profile_pytest.py in scripts to analyze the most time-consuming modules/functions/runs (see report below)
  • added a pytest.mark.slow for the most time-consuming tests. May be used later for skipping for faster test modes.
  • made some optimizations in test_autograd.py as the most critical module (10 min raw runtime!) -> cut it down by roughly the half. Made sure these tests run in the beginning such that we avoid non-fully-parallelized works are on them at the end.
    • do we want to skip some structures for async? Shouldn't be the async run functionality independently from the structures?
  • made some "obvious" minor performance optimizations for other test functions
  • reduction of total test time by roughly 30%

I'm open for more inputs how to speedup the tests.

Analysis before PR:

============================== slowest durations ===============================
58.40s call     tests/test_plugins/test_design.py::test_sweep[sweep_method0]
27.68s call     tests/test_components/test_simulation.py::test_num_lumped_elements
21.97s call     tests/test_components/test_scene.py::test_num_mediums
19.13s call     tests/test_components/autograd/test_autograd.py::test_autograd_async[True-<ALL>-diff]
18.59s call     tests/test_components/autograd/test_autograd.py::test_autograd_async[True-<ALL>-field_vol]
18.47s call     tests/test_components/autograd/test_autograd.py::test_autograd_async[True-<ALL>-field_point]
18.32s call     tests/test_components/autograd/test_autograd.py::test_autograd_async[True-<ALL>-mode]
17.95s call     tests/test_components/autograd/test_autograd.py::test_autograd_async[False-<ALL>-diff]
17.77s call     tests/test_components/autograd/test_autograd.py::test_autograd_async[False-<ALL>-field_point]
17.69s call     tests/test_components/autograd/test_autograd.py::test_autograd_async[False-<ALL>-mode]
16.93s call     tests/test_components/test_IO.py::test_validation_speed
16.49s call     tests/test_components/autograd/test_autograd.py::test_autograd_async[False-<ALL>-field_vol]
14.61s call     tests/test_components/test_custom.py::test_custom_lorentz[True]
13.80s call     tests/test_components/test_eme.py::test_eme_sim_data
13.53s call     tests/test_plugins/test_design.py::test_sweep[sweep_method2]
13.50s call     tests/test_plugins/test_invdes.py::test_continue_run_from_file
12.29s call     tests/test_components/autograd/test_autograd.py::test_autograd_async_server[<ALL>-diff]
12.17s call     tests/test_plugins/test_dispersion_fitter.py::test_dispersion_set_wvg_range
11.90s call     tests/test_components/autograd/test_autograd.py::test_autograd_async_server[<ALL>-field_vol]
11.88s call     tests/test_plugins/test_design.py::test_sweep[sweep_method3]
11.65s call     tests/test_components/autograd/test_autograd.py::test_autograd_async_server[<ALL>-mode]
11.62s call     tests/test_components/autograd/test_autograd.py::test_autograd_async_server[<ALL>-field_point]
11.11s call     tests/test_components/test_custom.py::test_custom_sellmeier[True]
10.31s call     tests/test_data/test_sim_data.py::test_plot[1.0]
10.28s call     tests/test_components/test_custom.py::test_custom_pole_residue[True]
10.20s call     tests/test_plugins/test_invdes.py::test_result_store_full_results_is_false
9.68s call     tests/test_components/test_custom.py::test_custom_anisotropic_medium[True]
9.32s call     tests/test_data/test_sim_data.py::test_plot[0]
9.28s call     tests/test_components/autograd/test_autograd.py::test_autograd_async_some_zero_grad[<ALL>-diff]
8.74s call     tests/test_components/autograd/test_autograd.py::test_autograd_async_some_zero_grad[<ALL>-mode]

(28084 durations < 0.005s hidden.  Use -vv to show these durations.)
========== 10559 passed, 67 skipped, 6 warnings in 195.88s (0:03:15) ===========
Aggregated durations (by file):
  635.72s  tests/test_components/autograd/test_autograd.py
  132.66s  tests/test_components/test_simulation.py
  109.70s  tests/test_plugins/test_design.py
   77.06s  tests/test_components/test_custom.py
   58.46s  tests/test_plugins/test_mode_solver.py
   50.78s  tests/test_components/test_IO.py
   49.24s  tests/test_plugins/test_dispersion_fitter.py
   45.02s  tests/test_plugins/test_invdes.py
   40.38s  tests/test_plugins/smatrix/test_terminal_component_modeler.py
   35.89s  tests/test_data/test_sim_data.py
   32.64s  tests/test_plugins/autograd/invdes/test_filters.py
   32.34s  tests/test_data/test_monitor_data.py
   29.58s  tests/test_components/test_scene.py
   18.20s  tests/test_components/test_eme.py
   18.17s  tests/test_web/test_webapi.py
   16.96s  tests/test_package/test_parametric_variants.py
   16.66s  tests/test_components/test_sidewall.py
   11.37s  tests/test_components/test_microwave.py
   10.19s  tests/test_plugins/autograd/invdes/test_penalties.py
    9.67s  tests/test_plugins/smatrix/test_component_modeler.py
    9.65s  tests/test_data/test_datasets.py
    8.41s  tests/test_plugins/autograd/invdes/test_parametrizations.py
    8.16s  tests/test_components/test_medium.py
    7.56s  tests/test_components/autograd/test_autograd_polyslab.py
    6.30s  tests/test_components/test_heat_charge.py
    6.22s  tests/test_plugins/smatrix/test_component_modeler_autograd.py
    5.00s  tests/test_components/test_mode.py
    4.59s  tests/test_components/test_field_projection.py
    4.34s  tests/test_components/test_parameter_perturbation.py
    4.26s  tests/test_components/test_heat.py

Aggregated durations (by test (parametrizations combined)):
  220.42s  tests/test_components/autograd/test_autograd.py::test_autograd_async
   98.23s  tests/test_plugins/test_design.py::test_sweep
   72.02s  tests/test_components/autograd/test_autograd.py::test_autograd_async_server
   56.05s  tests/test_components/autograd/test_autograd.py::test_autograd_async_some_zero_grad
   46.61s  tests/test_components/autograd/test_autograd.py::test_multi_frequency_equivalence
   39.50s  tests/test_components/autograd/test_autograd.py::test_autograd_objective
   38.17s  tests/test_components/autograd/test_autograd.py::test_autograd_server
   37.65s  tests/test_components/autograd/test_autograd.py::TestFieldProjection::test_field_projection_grad_prop
   31.49s  tests/test_components/test_simulation.py::test_sim_subsection
   27.68s  tests/test_components/test_simulation.py::test_num_lumped_elements
   25.01s  tests/test_components/autograd/test_autograd.py::test_interp_objectives
   21.97s  tests/test_components/test_scene.py::test_num_mediums
   20.08s  tests/test_components/test_simulation.py::TestAnisotropicPlotting::test_plot_fully_anisotropic_medium_diff
   20.00s  tests/test_components/autograd/test_autograd.py::TestFieldProjection::test_error_if_server_side_projection
   19.63s  tests/test_data/test_sim_data.py::test_plot
   18.48s  tests/test_components/autograd/test_autograd.py::test_vjp_nan
   17.49s  tests/test_components/test_custom.py::test_custom_lorentz
   16.93s  tests/test_components/test_IO.py::test_validation_speed
   16.19s  tests/test_plugins/autograd/invdes/test_filters.py::TestMakeFilter::test_make_filter
   16.12s  tests/test_package/test_parametric_variants.py::test_graphene
   15.68s  tests/test_data/test_monitor_data.py::TestZBF::test_fielddata_tozbf_readzbf
   13.80s  tests/test_components/test_eme.py::test_eme_sim_data
   13.50s  tests/test_plugins/test_invdes.py::test_continue_run_from_file
   12.86s  tests/test_components/test_custom.py::test_custom_sellmeier
   12.73s  tests/test_web/test_webapi.py::test_batch_run_accepts_pathlike_dir
   12.47s  tests/test_components/test_custom.py::test_custom_pole_residue
   12.17s  tests/test_plugins/test_dispersion_fitter.py::test_dispersion_set_wvg_range
   10.92s  tests/test_plugins/test_mode_solver.py::test_mode_solver_unstructured_custom_medium
   10.48s  tests/test_components/test_custom.py::test_custom_debye
   10.46s  tests/test_components/autograd/test_autograd.py::test_multi_freq_edge_cases

Analysis after PR

============================== slowest durations ===============================
40.14s call     tests/test_components/autograd/test_autograd.py::test_autograd_async_all
39.39s call     tests/test_components/autograd/test_autograd.py::test_autograd_async_server
21.09s call     tests/test_plugins/test_mode_solver.py::test_sort_spec_track_freq
16.44s call     tests/test_components/test_eme.py::test_eme_sim_data
14.87s call     tests/test_components/test_custom.py::test_custom_lorentz[True]
14.56s call     tests/test_plugins/test_invdes.py::test_continue_run_from_file
14.02s call     tests/test_plugins/test_design.py::test_sweep[sweep_method0]
12.81s call     tests/test_plugins/test_design.py::test_sweep[sweep_method3]
12.45s call     tests/test_plugins/test_dispersion_fitter.py::test_dispersion_set_wvg_range
12.12s call     tests/test_components/test_custom.py::test_custom_sellmeier[True]
10.32s call     tests/test_plugins/test_invdes.py::test_result_store_full_results_is_false
10.29s call     tests/test_plugins/test_design.py::test_sweep[sweep_method2]
10.28s call     tests/test_components/test_custom.py::test_custom_pole_residue[True]
9.83s call     tests/test_components/test_custom.py::test_custom_anisotropic_medium[True]
9.75s call     tests/test_plugins/test_dispersion_fitter.py::test_dispersion_loss_samples
9.73s call     tests/test_data/test_sim_data.py::test_plot[1.0]
9.46s call     tests/test_components/test_sidewall.py::test_intersection_with_inside_poly
9.30s call     tests/test_components/test_simulation.py::test_sim_subsection[True-13]
9.23s call     tests/test_data/test_sim_data.py::test_plot[0]
8.70s call     tests/test_plugins/test_dispersion_fitter.py::test_lossy_dispersion
8.57s call     tests/test_components/test_custom.py::test_custom_debye[True]
8.54s call     tests/test_components/test_mode.py::test_mode_sim
8.37s call     tests/test_components/test_microwave.py::test_mode_solver_with_microwave_mode_spec
8.17s call     tests/test_plugins/test_invdes.py::test_continue_run_fns
8.15s call     tests/test_plugins/test_dispersion_fitter.py::test_lossless_dispersion
8.04s call     tests/test_plugins/test_design.py::test_sweep[sweep_method4]
7.58s call     tests/test_components/autograd/test_autograd.py::test_autograd_server[<ALL>-field_point]
7.58s call     tests/test_components/autograd/test_autograd.py::test_web_run_duplicate_simulations
7.42s call     tests/test_components/test_simulation.py::test_sim_subsection[True-1]
7.40s call     tests/test_plugins/test_mode_solver.py::test_mode_solver_straight_vs_angled

(27789 durations < 0.005s hidden.  Use -vv to show these durations.)
========= 10469 passed, 66 skipped, 193 warnings in 137.23s (0:02:17) ==========
Aggregated durations (by file):
  330.78s  tests/test_components/autograd/test_autograd.py
  113.18s  tests/test_plugins/test_mode_solver.py
  101.27s  tests/test_components/test_simulation.py
   79.02s  tests/test_components/test_custom.py
   62.36s  tests/test_plugins/test_design.py
   52.02s  tests/test_plugins/test_dispersion_fitter.py
   47.17s  tests/test_plugins/test_invdes.py
   39.07s  tests/test_plugins/smatrix/test_terminal_component_modeler.py
   35.71s  tests/test_data/test_sim_data.py
   34.52s  tests/test_components/test_IO.py
   33.87s  tests/test_plugins/autograd/invdes/test_filters.py
   31.98s  tests/test_data/test_monitor_data.py
   21.22s  tests/test_components/test_eme.py
   19.61s  tests/test_components/test_microwave.py
   17.33s  tests/test_components/test_sidewall.py
   11.67s  tests/test_web/test_webapi.py
   10.97s  tests/test_plugins/autograd/invdes/test_penalties.py
   10.11s  tests/test_data/test_datasets.py
    9.85s  tests/test_plugins/smatrix/test_component_modeler.py
    9.49s  tests/test_components/test_mode.py
    9.28s  tests/test_package/test_parametric_variants.py
    8.79s  tests/test_plugins/autograd/invdes/test_parametrizations.py
    7.86s  tests/test_components/test_medium.py
    7.71s  tests/test_components/test_scene.py
    7.64s  tests/test_components/autograd/test_autograd_polyslab.py
    7.12s  tests/test_components/test_heat_charge.py
    6.17s  tests/test_plugins/smatrix/test_component_modeler_autograd.py
    5.24s  tests/test_components/test_parameter_perturbation.py
    5.13s  tests/test_plugins/autograd/test_functions.py
    4.86s  tests/test_plugins/test_array_factor.py

Aggregated durations (by test (parametrizations combined)):
   51.15s  tests/test_plugins/test_design.py::test_sweep
   40.99s  tests/test_components/autograd/test_autograd.py::test_autograd_server
   40.67s  tests/test_components/autograd/test_autograd.py::test_autograd_objective
   40.14s  tests/test_components/autograd/test_autograd.py::test_autograd_async_all
   39.44s  tests/test_components/autograd/test_autograd.py::TestFieldProjection::test_field_projection_grad_prop
   39.39s  tests/test_components/autograd/test_autograd.py::test_autograd_async_server
   30.82s  tests/test_components/test_simulation.py::test_sim_subsection
   26.26s  tests/test_components/autograd/test_autograd.py::test_interp_objectives
   24.16s  tests/test_plugins/test_mode_solver.py::test_mode_solver_unstructured_custom_medium
   22.55s  tests/test_components/autograd/test_autograd.py::TestFieldProjection::test_error_if_server_side_projection
   21.09s  tests/test_plugins/test_mode_solver.py::test_sort_spec_track_freq
   19.51s  tests/test_components/test_simulation.py::TestAnisotropicPlotting::test_plot_fully_anisotropic_medium_diff
   18.96s  tests/test_data/test_sim_data.py::test_plot
   17.98s  tests/test_components/test_custom.py::test_custom_lorentz
   16.51s  tests/test_plugins/autograd/invdes/test_filters.py::TestMakeFilter::test_make_filter
   16.44s  tests/test_components/test_eme.py::test_eme_sim_data
   15.57s  tests/test_data/test_monitor_data.py::TestZBF::test_fielddata_tozbf_readzbf
   14.56s  tests/test_plugins/test_invdes.py::test_continue_run_from_file
   13.92s  tests/test_components/test_custom.py::test_custom_sellmeier
   12.89s  tests/test_components/autograd/test_autograd.py::test_multi_freq_edge_cases
   12.70s  tests/test_components/test_custom.py::test_custom_pole_residue
   12.45s  tests/test_plugins/test_dispersion_fitter.py::test_dispersion_set_wvg_range
   10.97s  tests/test_plugins/autograd/invdes/test_penalties.py::test_make_erosion_dilation_penalty
   10.57s  tests/test_components/test_custom.py::test_custom_debye
   10.32s  tests/test_plugins/test_invdes.py::test_result_store_full_results_is_false
    9.92s  tests/test_plugins/test_mode_solver.py::test_mode_solver_simple
    9.83s  tests/test_components/test_custom.py::test_custom_anisotropic_medium
    9.75s  tests/test_plugins/test_dispersion_fitter.py::test_dispersion_loss_samples
    9.46s  tests/test_components/test_sidewall.py::test_intersection_with_inside_poly
    8.79s  tests/test_plugins/autograd/invdes/test_parametrizations.py::test_make_filter_and_project

Greptile Overview

Greptile Summary

This PR successfully optimizes the test suite runtime by ~30% (from 195s to 137s total, with test_autograd.py reduced from 635s to 330s) through strategic refactoring:

Major Changes:

  • Added profile_pytest.py script to identify and track slow tests
  • Consolidated parameterized async autograd tests into batched operations with equivalence validation
  • Introduced @pytest.mark.slow and @pytest.mark.perf markers for better test categorization
  • Added pytest-order dependency to run heavy autograd tests first (avoiding non-parallelized tail execution)
  • Reduced test parameter spaces where appropriate (e.g., design sweep grid points 5→3, validation test iterations 10→2)
  • Used monkeypatch to reduce MAX_NUM_MEDIUMS constant from production values to 3 in tests

Trade-offs:
The optimizations reduce some test coverage (e.g., test_multi_frequency_equivalence now only tests one structure type instead of 12, async tests run 2 structure/monitor combinations instead of 16×2=32), but this is reasonable since the core functionality is validated through equivalence checks and the reduced coverage focuses on structural diversity rather than functional correctness.

Confidence Score: 4/5

  • This PR is safe to merge with minor review of test coverage trade-offs
  • The changes successfully achieve the performance goal with well-structured optimizations. Score of 4 (not 5) reflects intentional test coverage reductions that, while reasonable for speed improvements, should be acknowledged. The refactored async test logic properly validates equivalence between sync and async modes, and the new profiling infrastructure is valuable. One minor style issue with an outdated comment.
  • No files require special attention - the test coverage reductions are intentional and documented in the PR description

Important Files Changed

File Analysis

Filename Score Overview
scripts/profile_pytest.py 5/5 New profiling script added to help analyze test performance - well-structured helper utility with proper argument parsing and documentation
pyproject.toml 5/5 Added pytest-order dependency and new test markers (perf, slow) - proper pytest configuration updates
tests/test_components/autograd/test_autograd.py 4/5 Major refactoring of async tests to reduce parameterization and run them first via pytest.mark.order(0). Consolidated multiple parameterized test runs into batched operations. Some test coverage reduced but performance significantly improved
tests/test_components/test_IO.py 5/5 Reduced validation speed test iterations from 10 to 2 and max structures from 100 to 2, marked with @pytest.mark.perf
tests/test_plugins/test_design.py 5/5 Reduced test parameter space (grid points 5→3, sphere range 0-3→0-2, tag values 3→2), marked with @pytest.mark.slow

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant Script as profile_pytest.py
    participant PyTest as pytest
    participant Tests as Test Suite
    
    Dev->>Script: Run profiling script
    Script->>PyTest: Execute with --durations flag
    PyTest->>Tests: Run test collection
    
    Note over Tests: Tests run with optimizations:<br/>- Reduced parameterizations<br/>- Batched async operations<br/>- Marked slow tests<br/>- Reduced iteration counts
    
    Tests-->>PyTest: Test results + timing data
    PyTest-->>Script: Duration statistics
    Script->>Script: Parse & aggregate durations
    Script->>Script: Generate report by file & test
    Script-->>Dev: Performance report (pytest_profile_stats.txt)
    
    Note over Dev: 30% faster test suite<br/>635s → 330s for autograd tests
Loading

@marcorudolphflex
Copy link
Contributor Author

@greptile

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

14 files reviewed, 7 comments

Edit Code Review Agent Settings | Greptile

@marcorudolphflex marcorudolphflex force-pushed the FXC-3721-speed-up-test-suite branch from dd55b29 to 0d5292d Compare November 12, 2025 12:11
@marcorudolphflex marcorudolphflex marked this pull request as ready for review November 12, 2025 12:11
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

14 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@marcorudolphflex marcorudolphflex force-pushed the FXC-3721-speed-up-test-suite branch 2 times, most recently from 700ecb3 to 693676d Compare November 12, 2025 12:19
@github-actions
Copy link
Contributor

Diff Coverage

Diff: origin/develop...HEAD, staged and unstaged changes

No lines with coverage information in this diff.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering a bit what benefit this provides over just running pytest --durations > times.txt?

Comment on lines +185 to +190
N_tests = 2 # may be increased temporarily, makes it slow for routine tests
max_structures = np.log10(2) # may be increased temporarily, makes it slow for routine tests

# adjust as needed, keeping small to speed tests up
num_structures = np.logspace(0, 2, N_tests).astype(int)
num_structures = np.logspace(0, max_structures, N_tests).astype(int)
Copy link
Collaborator

Choose a reason for hiding this comment

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

i think this turns the test in this file into a smoke-test essentially. i believe the purpose there was to catch regressions related to validation time where the serialization scales poorly as structure count grows. now we basically just assert that both cases work. but to be fair the old test didnt actually assert any regressions so i'm not really sure what makes sense here, maybe @momchil-flex has an opinion?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

made for now a "perf" marker and deselected for regular tests

@marcorudolphflex marcorudolphflex force-pushed the FXC-3721-speed-up-test-suite branch from 5bbca11 to 3f326b4 Compare November 13, 2025 16:45
@marcorudolphflex marcorudolphflex force-pushed the FXC-3721-speed-up-test-suite branch from 3f326b4 to 9c5ff18 Compare November 27, 2025 15:17
@marcorudolphflex marcorudolphflex force-pushed the FXC-3721-speed-up-test-suite branch from 9c5ff18 to 8e7ff17 Compare November 27, 2025 15:34
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

14 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@marcorudolphflex marcorudolphflex force-pushed the FXC-3721-speed-up-test-suite branch from 9bef6f0 to f310e32 Compare December 2, 2025 08:38
@marcorudolphflex marcorudolphflex force-pushed the FXC-3721-speed-up-test-suite branch from f310e32 to 8c9050b Compare December 2, 2025 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants