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

jplephem should cleanup resources better #24

Closed
kghose opened this issue Jun 25, 2018 · 6 comments
Closed

jplephem should cleanup resources better #24

kghose opened this issue Jun 25, 2018 · 6 comments
Assignees

Comments

@kghose
Copy link

kghose commented Jun 25, 2018

Use case

I have a file (ast343de430.bsp) containing about 300 bodies. I want to plot the positions of all the bodies at one given instant of time.

Problem

When I use the following code

import numpy as np
from jplephem.spk import SPK 
kernel = SPK.open('ast343de430.bsp')
pos = np.array([kernel[p].compute(2457061.5) for p in kernel.pairs.keys()])

I get: OSError: [Errno 24] Too many open files

Expected behavior

Each call to kernel[p].compute(2457061.5) should clean up such that we don't run into this issue.

Kudos

BTW, library writers often just get gripes from users, so I'd like to point out here that I really like jplephem and it is my go to Python solution for all things SPK related. Thank you for all the effort you have put into it!

@brandon-rhodes
Copy link
Owner

Thanks for the kudos, that was very nice of you to include! :)

The library is designed to open the file exactly once, when open() is called, so I'm surprised that you're getting a "too many open files" error — but maybe your system also returns than when some other limit is reached, like the number of memory maps? Because the library does map into memory each segment and then keeps it there, so that code like:

a = kernel[p].compute(t0)
a = kernel[p].compute(t1)

—doesn't have to do the expensive memory map operation twice but can keep using the same memory map over again.

How many segments does your kernel have?

What is the full traceback, so that we can see which line of code is erroring? That might tell us which resource is running out on your system.

@kghose
Copy link
Author

kghose commented Jun 26, 2018

In [50]: import numpy as np
    ...: from jplephem.spk import SPK 
    ...: kernel = SPK.open('ast343de430.bsp')
    ...: pos = np.array([kernel[p].compute(2457061.5) for p in kernel.pairs.keys()])
    ...: 
    ...: 
ERROR:root:Internal Python error in the inspect module.
Below is the traceback from this internal error.

Traceback (most recent call last):
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2963, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-50-08a22ec2a65b>", line 4, in <module>
    pos = np.array([kernel[p].compute(2457061.5) for p in kernel.pairs.keys()])
  File "<ipython-input-50-08a22ec2a65b>", line 4, in <listcomp>
    pos = np.array([kernel[p].compute(2457061.5) for p in kernel.pairs.keys()])
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/jplephem/spk.py", line 114, in compute
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/jplephem/spk.py", line 161, in generate
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/jplephem/spk.py", line 136, in _load
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/jplephem/daf.py", line 154, in map_array
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/jplephem/daf.py", line 112, in map_words
OSError: [Errno 24] Too many open files

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 1863, in showtraceback
    stb = value._render_traceback_()
AttributeError: 'OSError' object has no attribute '_render_traceback_'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/IPython/core/ultratb.py", line 1095, in get_records
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/IPython/core/ultratb.py", line 311, in wrapped
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/site-packages/IPython/core/ultratb.py", line 345, in _fixed_getinnerframes
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/inspect.py", line 1480, in getinnerframes
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/inspect.py", line 1438, in getframeinfo
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/inspect.py", line 693, in getsourcefile
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/inspect.py", line 722, in getmodule
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/inspect.py", line 706, in getabsfile
  File "/Users/kghose/miniconda2/envs/kaggle/lib/python3.6/posixpath.py", line 374, in abspath
OSError: [Errno 24] Too many open files

@kghose
Copy link
Author

kghose commented Jun 26, 2018

In [2]: print(kernel)
File type DAF/SPK and format LTL-IEEE with 343 segments:
2287184.50..2688976.50  Sun (10) -> Unknown Target (2000276)
2287184.50..2688976.50  Sun (10) -> Unknown Target (2000145)
2287184.50..2688976.50  Sun (10) -> Unknown Target (2000268)
...

@kghose
Copy link
Author

kghose commented Jun 26, 2018

> launchctl limit maxfiles
	maxfiles    256            unlimited 

Restricting the keys to the first 256 gets rid of this problem, naturally.

Sorry, this number can vary - restricting it to the first 200 seems to always run. Getting closer to 256 starts to get me more and more failures (i.e. there is some variability in when it actually fails)

@brandon-rhodes
Copy link
Owner

Wow ­— 343 segments!

The library's current approach, of only mmap'ing the segments that the user tries to access, is designed to minimize the amount of the process's RAM image that's in use by the library in the case where someone only needs to use a few segements. On 32-bit machines and kernels with only a few huge segments, the trade-off seemed important.

But with modern 64-bit address spaces it probably would make more sense to simply mmap() the entire kernel, instead of making a separate map for each segment — and, of course, that approach would prevent you from running out of file descriptors in the case where the number of segments is large.

I wonder if I should switch the default behavior for everyone, or make the new behavior an option? I'm thinking of kernels like jup310.bsp whose 931M would be a pretty big dent in the address space of a 32-bit program.

@brandon-rhodes brandon-rhodes self-assigned this Jun 27, 2018
@brandon-rhodes
Copy link
Owner

I've just released version 2.8, which should hopefully fix your problem. I'm going to close the issue, but feel free to re-open if you run into this problem again!

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

No branches or pull requests

2 participants