News for hackers. Stuff that matters.
Re:Let's Remove the Global Interpreter Lock by eslaught on Monday August 14, 2017 @01:51PM (#15010755)
The comments here are missing a massive use case: shared memory. Shared memory isn't just about programmer convenience. It's about using a machine's memory resources more effectively.

Yes, shared memory is available in multi-processing, but it doesn't necessarily interact well with existing codes.

I've been working on adding Python support to Legion [1], a task-based runtime system for HPC. Legion wants to manage shared memory so that multiple cores don't necessarily need multiple copies of the data, when the executing tasks don't conflict (all are read-only, or access disjoint data). Legion is C++, so this mostly "just works". Some additional work is required to support GPUs, but it's still not so difficult. But with Python, if we go with multiprocessing, we have to switch to a different mechanism. Worse, Python is an optional dependency for Legion, so we can't depend on Python's multiprocessing support either.

If you have a large existing project, and a use case that can take advantage of shared memory, being forced into Python's multiprocessing scheme for parallelism is a pain.

We've been investigating using a dlmopen approach as well, based on this proof of concept [2]. Turns out that dlmopen in every available version of libc has a critical bug that prevents it from being practically useful, if you have any desire to make use of native modules. You can build a custom libc with this patch [3] but rolling a custom libc is also a massive pain.

In all likelihood we'll end up rolling our own multiprocessing to make this work. If the GIL were truly gone though, we could potentially avoid many of these issues.

[1]: http://legion.stanford.edu/

[2]: https://news.ycombinator.com/item?id=11844268

[3]: https://patchwork.ozlabs.org/patch/496559/

Re:Let's Remove the Global Interpreter Lock by detroitcoder on Monday August 14, 2017 @03:38PM (#15012040)
^This. It is a very common usecase for applications I work with to create a very large in memory read-only pd dataframe and then put a flask interface to operations on that dataframe using gunicorn and expose as an API. If I use async workers, the dataframe operations are bound by GIL restraints. If I use sync workers, each process needs a copy of the pd dataframe which the server cannot handle (I have never seen pre-fork shared memory work for this problem). I don't want to introduce another technology to solve this problem.
Re:Let's Remove the Global Interpreter Lock by tinix on Monday August 14, 2017 @11:34PM (#15015200)
FWIW, I routinely throw many GBs of pickled dataframes into Redis all the time, and then cluster the workload between multiple processes that are coordinated as a sort of namespaced job queue, all via Redis pubsub, blpop, l/rpush, and set/get. There are much faster and more efficient serialization formats like msgpack or protocol buffers however, compared to pickle, if you really need to squeeze out performance. You just have to chunk your bulk out into pieces and spread the bulk across multiple workers. You have an orchestrator class that puts things onto the queues, pulls things off, loads any modules you need, handles exceptions, etc...

Then you can namespace your queues (and workers), and have separate queues for results handling to push data to the next stage of the pipeline, etc... With stacks of workers, configured as needed. It's all pretty high level from there. GIL has no effect here, and as a side-effect, now you can utilize a massive number of parallel processes for heavy lifting and crunching, even on different machines over the network, where-as that wouldn't be possible with a traditional threaded architecture.

Not saying this necessarily covers your use-case, but it seems strange to use dataframes as a sort of in-memory database, vs using dataframes as the framing to do the munging and heavy lifting. What are you wanting to put multiple cursors on it or something? You could do this with greenlets, for what it's worth... But as someone who has gone down that route (multiple greenlets working over shared stack) I promise doing it with multiple processes and a queue is better, and ultimately way more flexible. Especially if you use something like msgpack or protocol buffers... Then you can have any workers from multiple programming languages and development paradigms doing different work at different stages, all orchestrated and working together via Redis.

Re:Let's Remove the Global Interpreter Lock by ogrisel on Wednesday August 16, 2017 @05:05AM (#15025995)
The pickling implementation of joblib has support for memory mapping numpy arrays nested in arbitrary data structures such as pandas dataframes.

Save the dataframe in a folder that can be accessed by the gunicorn worker:

    import joblib
    joblib.dump(df, '/folder/shared_data.pkl')
Then in the code run by the flask / gunicorn workers themselves:

    import joblib
    shared_df = joblib.load('/folder/shared_data.pkl', mmap_mode='r')
    # use the shared_df as usual (inplace modifications are not
    # authorized)
Some pandas function can have issues with read-only buffer though: https://github.com/pandas-dev/pandas/issues/17192 (caused by a currently unsolved bug / limitation of Cython) but it can work for your use case.
Re:Let's Remove the Global Interpreter Lock by detroitcoder on Wednesday August 16, 2017 @10:18AM (#15027627)
This looks very interesting. I am reading the docs https://pythonhosted.org/joblib/parallel.html#manual-managem... and it looks like it would help a lot (possibly solve the issue). Do you have any experience using this in production?
Re:Let's Remove the Global Interpreter Lock by detroitcoder on Wednesday August 16, 2017 @12:00PM (#15028534)
DAMN. I just did a basic test and it kinnda just worked?!? I created a test dataframe of 100M rows X 10 cols which took up ~2.3G and then used joblib.dump within the on_starting hook which is run when the gunicorn master starts up. Then loaded that df in with joblib.load within the worker and the total memory consumption was practically flat. Then I bumped up the number of workers to 20 and still flat. That is actually amazing. Coolest thing I have seen in months for how easy it is. Now I have to test out if the analytics actually work and a deep dive into the mechanics of mem-mapping.
Re:Let's Remove the Global Interpreter Lock by ogrisel on Thursday August 24, 2017 @08:31AM (#15089518)
Thanks for your feedback. I am glad I could help you.
Re:Let's Remove the Global Interpreter Lock by zeptomu on Monday August 14, 2017 @07:31PM (#15013983)
> create a very large in memory read-only pd dataframe and then put a flask interface to operations on that dataframe using gunicorn and expose as an API. [...]

May I ask what you consider large memory - MByte, GByte, TByte? The simplest solution is to store it as a blob on a SSD, and read it via simple file IO or put it into a DB. But I assume this was too slow, so it would be interesting to go into more details.

In the end you can do shared memory with multiprocessing in Python, which - I have to admit - requires some setup and bookkeeping work.

Re:Let's Remove the Global Interpreter Lock by detroitcoder on Wednesday August 16, 2017 @10:06AM (#15027536)
Lets say there are a couple dataframes that need a matrix multiply that take up about 10gb on a 32gb host. I want to parameterize these manipulations and expose over http. I can only afford to cache 3 sets of them, which means that I can perform 3 concurrent requests. I would like to provide more concurrency than this without reading from disk or storing the data out of process in a separate service which adds complexity.
Re:Let's Remove the Global Interpreter Lock by marmaduke on Monday August 14, 2017 @09:14PM (#15014550)
Tried a memmap?
Re:Let's Remove the Global Interpreter Lock by sametmax on Tuesday August 15, 2017 @07:27AM (#15017179)
I still wish to find a good tutorial about memmap. The doc about it is very formal. Something with clear use cases, patterns, gotchas and best practice would probably make it more popular.
Re:Let's Remove the Global Interpreter Lock by detroitcoder on Wednesday August 16, 2017 @12:01PM (#15028550)
Check out the joblib.dump example mentioned above. It is pretty impressive so far.
Re:Let's Remove the Global Interpreter Lock by sambe on Monday August 14, 2017 @04:10PM (#15012365)
I rarely hear people complain about genuine use-cases but this would seem to be one. However, aren't most/all of the dataframe operations done in C extensions in these cases?
Re:Let's Remove the Global Interpreter Lock by jzwinck on Monday August 14, 2017 @07:04PM (#15013826)
While a lot of NumPy is C and Fortran, Pandas is mostly pure Python and some Cython. And mostly it does not release the GIL.

You often end up having to implement your own C extensions or use Numba for the core of your processing. Even with BLAS enabled, NumPy has almost zero intrinsic parallelism, np.dot() being the notable exception which releases the GIL and uses multicore by itself.

Re:Let's Remove the Global Interpreter Lock by throwaway613834 on Monday August 14, 2017 @07:14PM (#15013887)
> Even with BLAS enabled, NumPy has almost zero intrinsic parallelism, np.dot() being the notable exception which releases the GIL and uses multicore by itself.

Is there any sort of list (comprehensive or otherwise) that denotes which NumPy functions are parallelism-friendly? I mean this whether it's in terms of releasing the GIL, in terms of SIMD support, or in terms of being multi-core.

Re:Let's Remove the Global Interpreter Lock by jzwinck on Monday August 14, 2017 @09:22PM (#15014585)
Why are you asking this using a throwaway?

np.dot() is multicore. np.load () (and family) releases the GIL. SIMD mostly depends on the build system, so if you want it you might need to build NumPy from source.

https://stackoverflow.com/questions/24022723/where-can-i-fin...

Re:Let's Remove the Global Interpreter Lock by goerz on Tuesday August 15, 2017 @10:58AM (#15018787)
Is there a way to disable this? In an HPC environment, I don't want routines going multi-core without my explicit permission, under any circumstances. I will already have manually set up the parallelization to be at the highest logical level. If using Python, that usually means I have planned out the number of processes to be equal to the number of cores. If each process then starts doing its own multicore calculation (badly load-balanced!) it overtaxes the node and slows everything down.

I really wish numpy/pandas/scipy wouldn't do this kind of uncontrollable parallelization.

Re:Let's Remove the Global Interpreter Lock by yaroslavvb on Tuesday August 15, 2017 @07:18PM (#15023619)
Underlying implementations often have a way to disable parallelism, ie, OMP_NUM_THREADS=1 or MKL_NUM_THREADS=1
Re:Let's Remove the Global Interpreter Lock by sirfz on Tuesday August 15, 2017 @09:05AM (#15017794)
pd.HDFStore is a good option for storing large DataFrames and it has some power querying capabilities.
Re:Let's Remove the Global Interpreter Lock by sametmax on Monday August 14, 2017 @03:23PM (#15011867)
But then multi-interpreters would allow that, and the article discard it as a valid solution. I find it harsh. It seems much easier to implement, doesn't have the same serialization problem than multiprocessing has and allow to utilize all the CPUs. Yes it's not as good proper threads because you do have more overhead, but it's an order of magnitude better than what we currently have, while being way easier to do that getting rid of the GIL.

Too bad the current project is on hold.

Re:Let's Remove the Global Interpreter Lock by sitkack on Tuesday August 15, 2017 @12:52PM (#15019919)
The points against sub-interpreters or multi-interpreters is a false dichotomy. There are plenty of scenarios where having multiple interpreters in the same address space would be valuable. Queues could be effectively to communicate between interpreters, no sharing needed. Where sharing is required, those structures can be on their own non-VM heap with the required locks.
Re:Let's Remove the Global Interpreter Lock by sitkack on Tuesday August 15, 2017 @11:16AM (#15018987)
The other glaring issue with CPython is all the globals, meaning there can be only ONE Python VM running in a process space.

The closest work has been done on PyParallel https://news.ycombinator.com/item?id=7861942 but afaik it is only for windows.

Re:Let's Remove the Global Interpreter Lock by lstyls on Monday August 14, 2017 @05:06PM (#15012899)
This is a good point and one of the few convincing arguments I've heard against the GIL. Thanks for providing so much detail!

Did you consider just mounting a ramdisk and storing data as files? At first glance it seems like a decent fit for sharing read-only data in memory.

Re:Let's Remove the Global Interpreter Lock by darpa_escapee on Monday August 14, 2017 @09:04PM (#15014496)
From my experience, tmpfs adds an overhead. If I use an in-menory database for SQLite, it is about twice as fast to interact with than accessing a database file loaded into a tmpfs.
Re:Let's Remove the Global Interpreter Lock by riri-au on Monday August 14, 2017 @06:51PM (#15013735)
Agreed, shared programming is an immensely useful feature for numerical programming, including data science and machine learning. Lots of people will say that those should be written in C++, but I think the rise of machine learning & data science in high level languages argues against their point.
Re:Let's Remove the Global Interpreter Lock by bhntr3 on Monday August 14, 2017 @09:25PM (#15014595)
Yeah. Python is pretty standard now. To implement high throughput scoring on models written in python, I have to run multiple processes with one copy of the python model for each process. For large models like random forests, this can eat up a lot of memory.

Ideally, it would be a single model in memory with access from multiple threads. But that won't work right now cause GIL.