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
Support for gc.freeze() for apps that use preloading #1640
Comments
See also Instagram's post on |
I don't understand how to use it. Does fix already in 3.7 release? |
@Somewater this is not implemented yet in Gunicorn. |
Oh, my fault. I thought you were asking about Gunicorn. Please keep general questions about |
What does |
@PetrochukM for the benefits to be large, the application probably needs to be using For maximum benefit, we might want to pause garbage collection in the master process, but I suspect Gunicorn will need some changes to make that safe or else the master process will continue to grow in memory as workers restart. Please post any results if you do experiment! |
@tilgovi Hi I think users can call I think it's cool to have an option do enable it, for users can found it and know this feature. Furthermore, maybe we can enable it automacally if |
@aisk yes, that's why I'm encouraging experimentation with this by anyone who does feel comfortable doing this by hand. It would definitely be a better experience for users if it's automatic, but for that to be possible we have to be convinced that there are no bad effects. For that we need people to test and for that we don't need any code changes, just people who are willing to add some calls to their hooks. |
@tilgovi Thank you! I was wondering if there is anything special that was needed, and it sounds like nothing special is required apart from running I am testing a change with |
@PetrochukM I do not, but I'm very interested to hear what you find! Thanks for trying it out. |
FYI @jab I have some projects in company, which need a big readonly shared dict on start up, after |
It can be done with the following
When testing this you'll see the effect with time. The memory usage of worker processes will initially be the same as without this configuration but it should grow slower when you hit the workers with traffic because GC won't touch shared memory areas. Note that Instagram did this because they have low-memory servers (the original post shows 32 CPUs and 32GB of memory), today you won't find this type of offering from any cloud provider. You save some memory and some CPU cycles because GC doesn't touch these memory areas but don't expect much from it. Leaving GC disabled is probably not an option, in my case worker RSS skyrockets eating up all the benefits from it, just like Instagram had to give up on it. |
For details see benoitc/gunicorn#1640 and https://instagram-engineering.com/copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf I think this is the most subtle to test change. I believe this is working. I started a gunicorn instance with 4 workers: ``` GALAXY_CONFIG_FILE="config/galaxy.yml" gunicorn 'galaxy.webapps.galaxy.fast_factory:factory()' -k galaxy.webapps.galaxy.workers.Worker --pythonpath lib --bind=localhost:8080 --config lib/galaxy/web_stack/gunicorn_config.py --preload -w 4 ``` Then i use the following script against that instance ``` import threading import requests def req(): for i in range(10000): requests.get('http://localhost:8080/history/current_history_json') for i in range(10): threading.Thread(target=req).start() ``` I see that the memory consumption increases much more *during* requests without this commit. It eventually decreases again, but I think not to the same baseline level (hard to tell without more elaborate testing). I attribute the higher memory load during requests to the fact that the garbage collection requiring to inspect more objects, taking more time to run and therefor not running as fast? I'm really not sure, I think we should just roll this out and see, it should be fairly obvious from the grafana dashboards.
For details see benoitc/gunicorn#1640 and https://instagram-engineering.com/copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf I think this is the most subtle to test change. I believe this is working. I started a gunicorn instance with 4 workers: ``` GALAXY_CONFIG_FILE="config/galaxy.yml" gunicorn 'galaxy.webapps.galaxy.fast_factory:factory()' -k galaxy.webapps.galaxy.workers.Worker --pythonpath lib --bind=localhost:8080 --config lib/galaxy/web_stack/gunicorn_config.py --preload -w 4 ``` Then i use the following script against that instance ``` import threading import requests def req(): for i in range(10000): requests.get('http://localhost:8080/history/current_history_json') for i in range(10): threading.Thread(target=req).start() ``` I see that the memory consumption increases much more *during* requests without this commit. It eventually decreases again, but I think not to the same baseline level (hard to tell without more elaborate testing). I attribute the higher memory load during requests to the fact that the garbage collection requiring to inspect more objects, taking more time to run and therefor not running as fast? I'm really not sure, I think we should just roll this out and see, it should be fairly obvious from the grafana dashboards.
For details see benoitc/gunicorn#1640 and https://instagram-engineering.com/copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf I think this is the most subtle to test change. I believe this is working. I started a gunicorn instance with 4 workers: ``` GALAXY_CONFIG_FILE="config/galaxy.yml" gunicorn 'galaxy.webapps.galaxy.fast_factory:factory()' -k galaxy.webapps.galaxy.workers.Worker --pythonpath lib --bind=localhost:8080 --config lib/galaxy/web_stack/gunicorn_config.py --preload -w 4 ``` Then i use the following script against that instance ``` import threading import requests def req(): for i in range(10000): requests.get('http://localhost:8080/history/current_history_json') for i in range(10): threading.Thread(target=req).start() ``` I see that the memory consumption increases much more *during* requests without this commit. It eventually decreases again, but I think not to the same baseline level (hard to tell without more elaborate testing). I attribute the higher memory load during requests to the fact that the garbage collection requiring to inspect more objects, taking more time to run and therefor not running as fast? I'm really not sure, I think we should just roll this out and see, it should be fairly obvious from the grafana dashboards.
For details see benoitc/gunicorn#1640 and https://instagram-engineering.com/copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf I think this is the most subtle to test change. I believe this is working. I started a gunicorn instance with 4 workers: ``` GALAXY_CONFIG_FILE="config/galaxy.yml" gunicorn 'galaxy.webapps.galaxy.fast_factory:factory()' -k galaxy.webapps.galaxy.workers.Worker --pythonpath lib --bind=localhost:8080 --config lib/galaxy/web_stack/gunicorn_config.py --preload -w 4 ``` Then i use the following script against that instance ``` import threading import requests def req(): for i in range(10000): requests.get('http://localhost:8080/history/current_history_json') for i in range(10): threading.Thread(target=req).start() ``` I see that the memory consumption increases much more *during* requests without this commit. It eventually decreases again, but I think not to the same baseline level (hard to tell without more elaborate testing). I attribute the higher memory load during requests to the fact that the garbage collection requiring to inspect more objects, taking more time to run and therefor not running as fast? I'm really not sure, I think we should just roll this out and see, it should be fairly obvious from the grafana dashboards.
In python 3.7 (still alpha), a new API was added: gc.freeze. The rationale and advantages (and caveats) are explained in the ticket.
Basically, the garbage collector touches a lot of objects and removes a lot of the benefits of preload, this API allows you to stop that and really share memory between processes.
The text was updated successfully, but these errors were encountered: