UDST / choicemodels

Python library for discrete choice modeling
https://udst.github.io/choicemodels
BSD 3-Clause "New" or "Revised" License
74 stars 33 forks source link

[0.2.2.dev0] Parallel iterative lottery choices #65

Closed mxndrwgrdnr closed 5 years ago

mxndrwgrdnr commented 5 years ago

This PR implements a parallelized version of choicemodels.tools.simulation.iterative_lottery_choices(). Two new methods were added to simulation.py. The first, _parallel_lottery_choices_worker(), is a worker method responsible for executing each individual process and updating the objects shared in memory across processes. The second, parallel_lottery_choices(), is analogous to iterative_lottery_choices() but distributes chooser batches across worker processes instead of running them in sequence.

There are two major caveats to note:

  1. The parallelized lottery choices currently only supports simulation for scenarios in which each alternative has a capacity of 1. I don't think it would be too big a lift to add support for the more general case in the future, but at present this allows me to simply store the alternatives in shared memory as an Array(), because as soon as an alternative is chosen I can drop it from the array. Otherwise, I would need to store a dict of alternatives and their capacities across all worker processes.
  2. Parallel simulation with large choice sets is very much prone to running out of memory. Unlike the implementation of batching in iterative_lottery_choices(), this new functionality is designed to speed things up rather than limit the amount of memory consumed at once. To avoid memory errors, the user must be keenly aware of two things: a) the total amount of memory available on the machine they are using; and b) the number of virtual cores available to them. The way the code is currently implemented, parallel_lottery_choices() will attempt to start a new worker process for every batch. It is therefore critical that the user selects a batch size such that n batches <= n CPU's. Furthermore, it is even more important that the size of each batch in memory is less than total available RAM on the machine divided by the number of batches. For example, if the user is simulating choices for a set of 100 choosers with a batch size of 10, and each batch uses 2 GB of RAM to generate choices, then the user must have a minimum of 10 CPU's and 20 GB of RAM available to them.
coveralls commented 5 years ago

Coverage Status

Coverage increased (+0.9%) to 76.074% when pulling 458bcd4eebf957d4e85dabf015ab36b73fb91056 on parallel_sim into 5cf353e467dd11866c7e60ce56ff58bad87c5812 on master.

smmaurer commented 5 years ago

This is really cool!

Eventually, it seems valuable to be able to generate smart defaults for the number of batches, based on core count and memory constraints. From the unit tests it looks like you can get at least some of this info.. Maybe a good first step would be to print status messages about how the user's batch_size compares to the likely optimum?

smmaurer commented 5 years ago

Non-substantive loose ends:

You can include parallel_lottery_choices() in the Sphinx documentation by listing it in docs/source/simulation-utilities.rst. Content will be taken from the docstrings and it will be rendered automatically when the master branch changes. If you want to preview it locally, instructions are in docs/README.md.

Versioning checklist is here: https://github.com/UDST/choicemodels/blob/master/CONTRIBUTING.md#updating-the-version-number

mxndrwgrdnr commented 5 years ago

Good ideas. I added a batch size warning here: https://github.com/UDST/choicemodels/blob/a094679e12927d9c575cfb43e957f0e532279019/choicemodels/tools/simulation.py#L440-L446

Unfortunately the memory error issue is impossible to catch before a batch actually gets run and its memory footprint is determined, so I'm not sure we can do much about it up front.

Sphynx docs were updated as well.