|
| 1 | +# Performance |
| 2 | + |
| 3 | +Memory use and solve time are two important factors that we try to keep to a minimum in our models. There are multiple |
| 4 | +things one can do to improve performance. |
| 5 | + |
| 6 | +## Solving methods |
| 7 | + |
| 8 | +By far the biggest factor that impacts performance is the method used by Gurobi. The fastest method is barrier solve |
| 9 | +without crossover (use `--recommended-fast`) |
| 10 | +however this method often returns a suboptimal solution. The next fastest is barrier solve followed by crossover and |
| 11 | +simplex (use `--recommended`) which almost always works. In some cases barrier solve encounters numerical issues ( |
| 12 | +see [`Numerical Issues.md`](./Numerical%20Issues.md)) |
| 13 | +in which case the slower Simplex method must be used (`--solver-options-string method=1`). |
| 14 | + |
| 15 | +## Solver interface |
| 16 | + |
| 17 | +Solver interfaces are how Pyomo communicates with Gurobi (or any solver). |
| 18 | + |
| 19 | +There are two solver interfaces that you should know about: `gurobi` and `gurobi_direct`. |
| 20 | + |
| 21 | +- When using `gurobi`, Pyomo will write the entire model to a temporary text file and then start a *separate Gurobi |
| 22 | + process* that will read the file, solve the model and write the results to another temporary text file. Once Gurobi |
| 23 | + finishes writing the results Pyomo will read the results text file and load the results back into the Python program |
| 24 | + before running post_solve (e.g. generate csv files, create graphs, etc). Note that these temporary text files are |
| 25 | + stored in `/tmp` but if you use `--recommended-debug` Pyomo and Gurobi will instead use a `temp` folder in your model. |
| 26 | + |
| 27 | +- `gurobi_direct` uses Gurobi's Python library to create and solve the model directly in Python without the use of |
| 28 | + intermediate text files. |
| 29 | + |
| 30 | +In theory `gurobi_direct` should be faster and more efficient however in practice we find that that's not the case. As |
| 31 | +such we recommend using `gurobi` and all our defaults do so. If someone has the time they could profile `gurobi_direct` |
| 32 | +to improve performance at which point we could make `gurobi_direct` the default (and enable `--save-warm-start` by default, see below). |
| 33 | + |
| 34 | +The `gurobi` interface has the added advantage of separating Gurobi and Pyomo into separate threads. This means that |
| 35 | +while Gurobi is solving and Pyomo is idle, the operating system can automatically move Pyomo's memory usage |
| 36 | +to [virtual memory](https://serverfault.com/questions/48486/what-is-swap-memory) |
| 37 | +which will free up more memory for Gurobi. |
| 38 | + |
| 39 | +## Warm starting |
| 40 | + |
| 41 | +Warm starting is the act of using a solution from a previous similar model to start the solver closer to your expected |
| 42 | +solution. Theoretically this can help performance however in practice there are several limitations. For this section, * |
| 43 | +previous solution* refers to the results from an already solved model that you are using to warm start the solver. * |
| 44 | +Current solution* refers to the solution you are trying to find while using the warm start feature. |
| 45 | + |
| 46 | +- To warm start a model use `switch solve --warm-start <path_to_previous_solution>`. |
| 47 | + |
| 48 | +- Warm starting only works if the previous solution does not break any constraints of the current solution. This usually |
| 49 | + only happens if a) the model has the exact same set of variables b) |
| 50 | + the previous solution was "harder" (e.g. it had more constraints to satisfy). |
| 51 | + |
| 52 | +- Warm starting always uses the slower Simplex method. This means unless you expect the previous solution and current |
| 53 | + solution to be very similar, it may be faster to solve without warm start using the barrier method. |
| 54 | + |
| 55 | +- If your previous solution didn't use crossover (e.g. you used `--recommended-fast`) then warm starting will be even |
| 56 | + slower since the solver will need to first run crossover before warm starting. |
| 57 | + |
| 58 | +- Our implementation of warm starting only works if your previous solution has an `outputs/warm_start.pickle` |
| 59 | + file. This file is only generated when you use `--save-warm-start`. |
| 60 | + |
| 61 | +- `--save-warm-start` and `--warm-start` both use an extension of the `gurobi_direct` solver interface which is |
| 62 | + generally slower than the `gurobi` solver interface (see section above). |
| 63 | + |
| 64 | +## Tools for improving performance |
| 65 | + |
| 66 | +- [Memory profiler](https://pypi.org/project/memory-profiler/) for generating plots of the memory |
| 67 | +use over time. Use `mprof run --interval 60 --multiprocess switch solve ...` and once solving is done |
| 68 | + run `mprof plot -o profile.png` to make the plot. |
| 69 | + |
| 70 | +- [Fil Profiler](https://pypi.org/project/filprofiler/) is an amazing tool for seeing which parts of the code are |
| 71 | +using up memory during peak memory usage. |
| 72 | + |
| 73 | +- Using `switch_model.utilities.StepTimer` to measure how long certain code blocks take to run. See examples |
| 74 | +throughout the code. |
0 commit comments