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
On the fly vtk import #1300
On the fly vtk import #1300
Conversation
Yeah so we definitely need this. I tested a server task with latest pre/2.5 vs. using this modification, and there was a 5-6s difference (faster with this change), because we probably load vtk more than once in subprocesses. The task doesn't need it at all. I am just not sure if this is the implementation we want to go with. @tylerflex @daquinteroflex what do you think? |
Writing a few notes as was having a look through it on a first pass:
How did you reproduce the timing test? I assume on the cloud on something like a reproducible hardware instance. So this may sound stupid because I believe you're getting a dependency import speedup, (and I know it might not be necessarily helpful) but I'm not able to reproduce it locally reliably, but I can share how I tested the timing. I just thought having a benchmark test that could be reproduced in multiple machines might be good in order to compare with previous and future versions + any changes on this import front. I had a look at this, this as references. Assuming we're testing the root So I run to get the breakdown of the imports and verifies that
where This should print a breakdown of the import timing in hierarchy. Note the cumulative timing is in relation to the printed output in hierarchy. On
and with this PR, the same test does not import
However, I noticed that locally the test is not quite reproducible as it depends on the tasks the computer is running and the amount of compute dedicated to the taskand looked more into this, so I suspect maybe your cloud process is more reproducible in this case. And since we know For example if I run the same above multiple times just in case you're interested: import subprocess,time
n=100
python_load_time = 0
tidy3d_load_time = 0
for i in range(n):
s = time.time()
subprocess.call(["python","-c","import tidy3d"])
tidy3d_load_time += time.time()-s
s = time.time()
subprocess.call(["python","-c","pass"])
python_load_time += time.time()-s
print("average tidy3d load time = {}".format((tidy3d_load_time-python_load_time)/n)) This took a bit of time to run and outputted: (I'd be interested in seeing how this is quantified in the cloud subprocess personally.)
With this PR:
I'm relatively interesting if this is reproduced in the cloud, or if I'm not testing this properly. I wonder if more specifically we want to test a class import that does not use a method with |
This is the same sequence as the wrapper I short circuited for a moment when writing this
I think the timing principle of not importing unless the method is required will still work, whilst the import cache should mean that it doesn't get reloaded, whilst you're still able to do module imports and use the internal library functionality as usual within the methods accordingly without the need of the |
@daquinteroflex thanks for looking into timings so carefully. When I run these scripts on my side locally I get the following results: For
For
So, it seems these results are consistent with each other. That is, importing
which is similar to what you get. Anyway, 0.25-0.3s still seems quite far from from what @momchil observed |
Yeah, I don't really have a preference between going the wrapper way or the helper function way. I do agree that dictionary thing is kinda of awkward. On the other hand, it seems using a helper function would require explicitly calling
In case we decide to stick with the wrapper approach, we could maybe slightly improve that by replacing vtk dictionary with a vtk config similar to tidy3d config https://github.com/flexcompute/tidy3d/blob/pre/2.5/tidy3d/config.py. |
Hi Daniil, Thanks for this, you were right, I found there was something wrong on my setup. I was being stupid. I didn't do These were the commands I ran on both this PR and pre-PR which have worked on my poetry setup and expected it to be the same on the older version.
and
However, to my surprise, because I checked the Once I properly did the installation running
|
Edit: I've moved the corrections to my previous comment to that comment directly. On further thought: I see what you're proposing. Yeah that could work as well. I am wondering if we have any particular timing costs in between these and am testing that atm. |
Apologies for the earlier edits, I think I needed a bit of a break after being connected to the computer for a bit without thinking too clearly. So I've done a bit of testing, mainly I was curious about what was the time cost of a function call with the import. The memory cost is minimal if we assume the test setup to be valid. In a directory, create
This gets imported as a function call in the
and then the timing test setup over 100 runs:
when I run it:
So it's a minimal cost, but it maybe reimporting again with the current function wrapper is fine within the actual functions rather than variables? Apologies for the mess of messages earlier! I'm going to call it a day as I think I need to unplug from the terminal. |
All things considered maybe yeah the current approach with this improvement is good enough? I'm even fine with merging as is for now - I just want to get this fix in, even if it's not the prettiest under the hood. |
Not exactly that, but I just did a quick test of executing a method that requires vtk 10000 times in a loop with with PR and without, and I didn't see any difference performance wise. Probably we don't need to worry about performance either way, especially given that typically methods that require vtk are expected to be on the heavy side, so their execution time will strongly dominate overheads from reading a dict, creating additional variables, etc.
Yeah, we can probably just merge as it is and then refine the implementation in a separate PR. Perhaps, we should take a look at other heavy imports, like |
All sounds good! |
For some reason couldn't make it work with plain global variables, so ended up using a dict that contains relevant info. One issue with it (and probably in general using this on the fly approach) is that we can't use imported modules members in definition of function signatures. For this particular case, it is not critical, but could be for others.