Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test initialization speed of a metalens with 10_000 structures #1201

Merged
merged 1 commit into from
Oct 11, 2023

Conversation

momchil-flex
Copy link
Collaborator

I was playing with a large metalens file and wanted to profile the initialization time (~2.5 minutes). One thing I noticed is that the new _validate_2d_geometry_has_2d_medium validator was taking a noticeable chunk of the time (~50s), essentially because it was working with geometry.bounding_box instead of geometry.bounds. The former constructs a Box object which adds a lot of overhead when dealing with hundreds of thousands of geometries.

This PR does a few things:

  • Adds a test for generating a metalens simulation with 10k structures, and the test will fail if this doesn't work within 3s. Currently, it succeeds in about 2.1s, so if something heavy gets added inadvertently, this should alert us.
  • Introduces a Profile context in tests/utils.py and uses that in the test to display output (if run with pytest -rA) that can help us figure out what can be improved.
  • Fixes the inefficiency of the 2d geometry validator.

Here are the first lines of profiling at the current state. The main thing that we may want to look at at some point is correct_shape. This is specific to the usage of PolySlab. I also looked at just cylinders, and there the pydantic built-ins were dominating, like this validate_singleton. I don't know what this is about.

10026/10005    0.027    0.000    2.119    0.000 /home/momchil/Drive/flexcompute/tidy3d-core/tidy3d_frontend/tidy3d/components/base.py:90(__init__)
10028/10015    0.019    0.000    2.063    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/pydantic/v1/main.py:332(__init__)
10028/10015    0.154    0.000    2.018    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/pydantic/v1/main.py:1032(validate_model)
110223/70072    0.082    0.000    1.801    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/pydantic/v1/fields.py:852(validate)
110202/110116    0.077    0.000    1.582    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/pydantic/v1/fields.py:1152(_apply_validators)
    20028    0.012    0.000    0.982    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/pydantic/v1/class_validators.py:304(<lambda>)
    10000    0.081    0.000    0.889    0.000 /home/momchil/Drive/flexcompute/tidy3d-core/tidy3d_frontend/tidy3d/components/geometry/polyslab.py:76(correct_shape)
    80040    0.167    0.000    0.668    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/shapely/decorators.py:62(wrapped)
110182/90163    0.036    0.000    0.431    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/pydantic/v1/fields.py:1056(_validate_singleton)
    10013    0.018    0.000    0.351    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/shapely/geometry/polygon.py:221(__new__)
   130175    0.043    0.000    0.267    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/pydantic/v1/class_validators.py:337(<lambda>)
20034/20032    0.009    0.000    0.234    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/pydantic/v1/class_validators.py:306(<lambda>)
80054/10028    0.021    0.000    0.200    0.000 /home/momchil/Drive/flexcompute/tidy3d-core/tidy3d_frontend/tidy3d/components/base.py:36(cached_property_getter)
    10013    0.026    0.000    0.169    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/shapely/geometry/polygon.py:62(__new__)
        3    0.000    0.000    0.144    0.048 /home/momchil/Drive/flexcompute/tidy3d-core/tidy3d_frontend/tidy3d/components/validators.py:160(objects_in_sim_bounds)
        8    0.000    0.000    0.144    0.018 /home/momchil/Drive/flexcompute/tidy3d-core/tidy3d_frontend/tidy3d/components/geometry/base.py:180(intersects)
        1    0.001    0.001    0.143    0.143 /home/momchil/Drive/flexcompute/tidy3d-core/tidy3d_frontend/tidy3d/components/geometry/base.py:1994(bounds)
    10001    0.003    0.000    0.134    0.000 /home/momchil/Drive/flexcompute/tidy3d-core/tidy3d_frontend/tidy3d/components/geometry/base.py:2004(<genexpr>)
    10001    0.009    0.000    0.133    0.000 /home/momchil/miniconda3/envs/flex/lib/python3.9/site-packages/shapely/geometry/polygon.py:248(interiors)
    10000    0.040    0.000    0.125    0.000 /home/momchil/Drive/flexcompute/tidy3d-core/tidy3d_frontend/tidy3d/components/geometry/polyslab.py:875(bounds)

@momchil-flex
Copy link
Collaborator Author

momchil-flex commented Oct 11, 2023

Note: I bumped the timeout to 5s since 3s was too little for the github action tests (I guess they are slower than my laptop).

@momchil-flex
Copy link
Collaborator Author

So this fails on windows workers even with 5s timeout. I wonder if that's an issue with e.g. pydantic being slower on windows, or just the github worker.

@tomflexcompute could you pull this branch and try pytest -rA tests/test_components/test_simulation.py -k timeout and show me the output?

@tomflexcompute
Copy link
Contributor

So this fails on windows workers even with 5s timeout. I wonder if that's an issue with e.g. pydantic being slower on windows, or just the github worker.

@tomflexcompute could you pull this branch and try pytest -rA tests/test_components/test_simulation.py -k timeout and show me the output?

I ran it using the command line of Windows subsystem for Linux. Is it what you meant by testing on Windows machine? The output is the following:

test-7.4.0, pluggy-1.2.0
rootdir: /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d
configfile: pytest.ini
plugins: timeout-2.1.0, anyio-3.7.1
collected 78 items / 77 deselected / 1 selected

tests/test_components/test_simulation.py . [100%]

=================================================== warnings summary ===================================================../../../../../../../../usr/lib/python3/dist-packages/scipy/init.py:146
/usr/lib/python3/dist-packages/scipy/init.py:146: UserWarning: A NumPy version >=1.17.3 and <1.25.0 is required for this version of SciPy (detected version 1.25.2
warnings.warn(f"A NumPy version >={np_minversion} and <{np_maxversion}"

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================================================== PASSES ========================================================_____________________________________________ test_many_structures_timeout _____________________________________________------------------------------------------------- Captured stdout call -------------------------------------------------[00:10:04] WARNING: frequency passed to Medium.eps_model()is outside of
Medium.frequency_range = (362698386340789.0, 1329894083249559.8)
[00:10:05] WARNING: frequency passed to Medium.eps_model()is outside of
Medium.frequency_range = (362698386340789.0, 1329894083249559.8)
WARNING: frequency passed to Medium.eps_model()is outside of
Medium.frequency_range = (362698386340789.0, 1329894083249559.8)
WARNING: frequency passed to Medium.eps_model()is outside of
Medium.frequency_range = (362698386340789.0, 1329894083249559.8)
[00:10:06] WARNING: frequency passed to Medium.eps_model()is outside of
Medium.frequency_range = (362698386340789.0, 1329894083249559.8)
WARNING: frequency passed to Medium.eps_model()is outside of
Medium.frequency_range = (362698386340789.0, 1329894083249559.8)
WARNING: frequency passed to Medium.eps_model()is outside of
Medium.frequency_range = (362698386340789.0, 1329894083249559.8)
3515652 function calls (3363979 primitive calls) in 4.361 seconds

Ordered by: cumulative time

ncalls tottime percall cumtime percall filename:lineno(function)
10026/10005 0.025 0.000 4.365 0.000 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/base.py:90(init)
10028/10015 0.019 0.000 2.493 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/main.py:332(init)
10028/10015 0.183 0.000 2.444 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/main.py:1032(validate_model)
110223/70072 0.102 0.000 2.185 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/fields.py:852(validate)
80054/10028 0.030 0.000 2.128 0.000 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/base.py:36(cached_property_getter)
14/8 0.000 0.000 1.936 0.242 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/medium.py:50(_eps_model)
7 0.000 0.000 1.935 0.276 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/log.py:274(warning)
8 0.000 0.000 1.935 0.242 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/log.py:211(_log)
8 0.000 0.000 1.935 0.242 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/log.py:60(handle)
7 0.000 0.000 1.908 0.273 /usr/lib/python3.10/inspect.py:1671(stack)
7 0.001 0.000 1.908 0.273 /usr/lib/python3.10/inspect.py:1643(getouterframes)
337 0.004 0.000 1.906 0.006 /usr/lib/python3.10/inspect.py:1604(getframeinfo)
110202/110116 0.086 0.000 1.897 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/fields.py:1152(_apply_validators)
1 0.000 0.000 1.841 1.841 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/simulation.py:913(_post_init_validators)
1 0.000 0.000 1.841 1.841 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/simulation.py:918(_validate_no_structures_pml)
1 0.000 0.000 1.841 1.841 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/simulation.py:1970(pml_thicknesses)
1 0.000 0.000 1.841 1.841 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/simulation.py:2669(grid)
1 0.000 0.000 1.841 1.841 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/grid/grid_spec.py:516(make_grid)
3 0.000 0.000 1.840 0.613 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/grid/grid_spec.py:26(make_coords)
3 0.000 0.000 1.840 0.613 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/grid/grid_spec.py:319(_make_coords_initial)
3 0.000 0.000 1.837 0.612 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/grid/mesher.py:55(parse_structures)
3 0.000 0.000 1.833 0.611 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/grid/mesher.py:348(structure_steps)
1011 1.809 0.002 1.809 0.002 {built-in method posix.stat}
337 0.006 0.000 1.283 0.004 /usr/lib/python3.10/inspect.py:932(findsource)
674 0.005 0.000 1.227 0.002 /usr/lib/python3.10/inspect.py:813(getsourcefile)
674 0.002 0.000 1.209 0.002 /usr/lib/python3.10/genericpath.py:16(exists)
20028 0.013 0.000 1.056 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/class_validators.py:304()
10000 0.066 0.000 0.932 0.000 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/geometry/polyslab.py:76(correct_shape)
6 0.000 0.000 0.925 0.154 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/medium.py:223(eps_diagonal)
80040 0.186 0.000 0.713 0.000 /home/tom/.local/lib/python3.10/site-packages/shapely/decorators.py:62(wrapped)
337 0.003 0.000 0.593 0.002 /usr/lib/python3.10/linecache.py:52(checkcache)
110182/90163 0.052 0.000 0.477 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/fields.py:1056(_validate_singleton)
20034/20032 0.010 0.000 0.466 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/class_validators.py:306()
10013 0.020 0.000 0.344 0.000 /home/tomg/.local/lib/python3.10/site-packages/shapely/geometry/polygon.py:221(new)
130175 0.053 0.000 0.256 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/class_validators.py:337()
3 0.000 0.000 0.240 0.080 /mnt/c/Users/tomh/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/validators.py:160(objects_in_sim_bounds)
8 0.000 0.000 0.240 0.030 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/geometry/base.py:180(intersects)
1 0.002 0.002 0.240 0.240 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/geometry/base.py:1994(bounds)
10001 0.003 0.000 0.228 0.000 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/geometry/base.py:2004()
10000 0.044 0.000 0.216 0.000 /mnt/c/Users/tom/OneDrive/Desktop/tidy3d codes/tidy3d/tidy3d/components/geometry/polyslab.py:875(bounds)
30050 0.040 0.000 0.190 0.000 /home/tom/.local/lib/python3.10/site-packages/numpy/core/fromnumeric.py:71(_wrapreduction)
10001 0.011 0.000 0.169 0.000 /home/tom/.local/lib/python3.10/site-packages/shapely/geometry/polygon.py:248(interiors)
550695/550689 0.080 0.000 0.161 0.000 {built-in method builtins.isinstance}
10013 0.032 0.000 0.161 0.000 /home/tom/.local/lib/python3.10/site-packages/shapely/geometry/polygon.py:62(new)
10031 0.054 0.000 0.124 0.000 /home/tom/.local/lib/python3.10/site-packages/pydantic/v1/fields.py:973(_validate_tuple)
=============================================== short test summary info ================================================PASSED tests/test_components/test_simulation.py::test_many_structures_timeout
===================================== 1 passed, 77 deselected, 1 warning in 21.51s =====================================tom@tom_flexcompute:/mnt/c/User

Copy link
Contributor

@caseyflex caseyflex left a comment

Choose a reason for hiding this comment

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

Thanks for the catch. It will be good to have this frontend performance test, as this isn't the first time an issue like this has come up.

Copy link
Collaborator

@tylerflex tylerflex left a comment

Choose a reason for hiding this comment

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

Looks good to me, FYI I just tested 4 different implementations of the zero_dims property to see if we could speed it up more:

Tested with N iterations of bound checking:

N = 1_000_000
bounds = ((1,2,3), (4,2,4))
def zero_dims0(bounds):
    """A list of axes along which the :class:`Geometry` is zero-sized based on its bounds."""
    zero_dims = []
    for dim in range(3):
        if bounds[1][dim] - bounds[0][dim] == 0:
            zero_dims.append(dim)
    return zero_dims

332 ms ± 5.88 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

def zero_dims1(bounds):
    """A list of axes along which the :class:`Geometry` is zero-sized based on its bounds."""
    zero_dims = []
    for dim in range(3):
        if bounds[1][dim] == bounds[0][dim]:
            zero_dims.append(dim)
    return zero_dims

294 ms ± 647 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

def zero_dims2(bounds):
    """A list of axes along which the :class:`Geometry` is zero-sized based on its bounds."""
    rmin, rmax = bounds
    zero_dims = []
    for dim, (valmin, valmax) in enumerate(zip(rmin, rmax)):
        if valmax == valmin:
            zero_dims.append(dim)
    return zero_dims

384 ms ± 3.91 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

import numpy as np
def zero_dims3(bounds):
    """A list of axes along which the :class:`Geometry` is zero-sized based on its bounds."""
    bounds = np.array(bounds)
    zero_dims = np.where(bounds[0] == bounds[1])
    return zero_dims

1.29 s ± 6.54 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

So I think if performance is still an issue, replacing

 if self.bounds[1][dim] - self.bounds[0][dim] == 0:

with

 if self.bounds[1][dim] == self.bounds[0][dim]:

seems to shave off about 10%

"""A list of axes along which the :class:`Geometry` is zero-sized based on its bounds."""
zero_dims = []
for dim in range(3):
if self.bounds[1][dim] - self.bounds[0][dim] == 0:
Copy link
Collaborator

Choose a reason for hiding this comment

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

see my note, consider replacing with

if self.bounds[1][dim] == self.bounds[0][dim]:

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Performance for this validator is no longer an issue, but I can do the modification anyway.

@momchil-flex
Copy link
Collaborator Author

Thanks everyone. I think I will only put the commit that fixes the validator speed here, and I'll move the test to the backend. Increasing the timeout beyond 5s (or even beyond 3s...) worries me that significant slow-down may not get caught. Hopefully on the backend the run time is much more uniform (tests are typically run on a more uniform set of machines).

@momchil-flex momchil-flex merged commit 07648ad into develop Oct 11, 2023
14 checks passed
@momchil-flex momchil-flex deleted the momchil/metalens_timeout branch October 11, 2023 20:52
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.

4 participants