Skip to content

Commit d81f5f2

Browse files
jpn--dhensle
andauthored
Performance docs (#878)
* perf docs part 1 * performance docs * make persistent sharrow cache the default * performance tuning checklist * skim-data-format * recommend explicit chunking * multithread defaults * note on string columns in preprocessors * update dev install docs * address review comments * adding memory profile plotting * favicon for docs * change atol for sharrow tests to 1e-6 * troubleshooting docs * blacken * add link to notebook --------- Co-authored-by: David Hensle <[email protected]>
1 parent 67820ad commit d81f5f2

File tree

18 files changed

+190454
-24
lines changed

18 files changed

+190454
-24
lines changed

activitysim/core/configuration/filesystem.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ def data_model_dirs_must_exist(cls, data_model_dir, values):
105105
"""
106106
Name of the output directory for sharrow cache files.
107107
108-
If not given, a directory named "__sharrowcache__" will be created inside
109-
the general cache directory.
108+
If not given, the sharrow cache is stored in a run-independent persistent
109+
location, according to `platformdirs.user_cache_dir`. See `persist_sharrow_cache`.
110110
"""
111111

112112
settings_file_name: str = "settings.yaml"
@@ -395,7 +395,8 @@ def get_sharrow_cache_dir(self) -> Path:
395395
Path
396396
"""
397397
if self.sharrow_cache_dir is None:
398-
out = self.get_cache_dir("__sharrowcache__")
398+
self.persist_sharrow_cache()
399+
out = self.sharrow_cache_dir
399400
else:
400401
out = self.get_working_subdir(self.sharrow_cache_dir)
401402
if not out.exists():

activitysim/core/interaction_sample.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ def _interaction_sample(
359359
),
360360
interaction_utilities.values,
361361
rtol=1e-2,
362-
atol=0,
362+
atol=1e-6,
363363
err_msg="utility not aligned",
364364
verbose=True,
365365
)
@@ -370,7 +370,7 @@ def _interaction_sample(
370370
interaction_utilities_sh.values,
371371
interaction_utilities.values,
372372
rtol=1e-2,
373-
atol=0,
373+
atol=1e-6,
374374
)
375375
)
376376
_sh_util_miss1 = interaction_utilities_sh.values[

activitysim/core/interaction_simulate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,14 +504,14 @@ def to_series(x):
504504
sh_util.reshape(utilities.values.shape),
505505
utilities.values,
506506
rtol=1e-2,
507-
atol=0,
507+
atol=1e-6,
508508
err_msg="utility not aligned",
509509
verbose=True,
510510
)
511511
except AssertionError as err:
512512
print(err)
513513
misses = np.where(
514-
~np.isclose(sh_util, utilities.values, rtol=1e-2, atol=0)
514+
~np.isclose(sh_util, utilities.values, rtol=1e-2, atol=1e-6)
515515
)
516516
_sh_util_miss1 = sh_util[tuple(m[0] for m in misses)]
517517
_u_miss1 = utilities.values[tuple(m[0] for m in misses)]

activitysim/core/simulate.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,13 +787,15 @@ def eval_utilities(
787787
sh_util,
788788
utilities.values,
789789
rtol=1e-2,
790-
atol=0,
790+
atol=1e-6,
791791
err_msg="utility not aligned",
792792
verbose=True,
793793
)
794794
except AssertionError as err:
795795
print(err)
796-
misses = np.where(~np.isclose(sh_util, utilities.values, rtol=1e-2, atol=0))
796+
misses = np.where(
797+
~np.isclose(sh_util, utilities.values, rtol=1e-2, atol=1e-6)
798+
)
797799
_sh_util_miss1 = sh_util[tuple(m[0] for m in misses)]
798800
_u_miss1 = utilities.values[tuple(m[0] for m in misses)]
799801
_sh_util_miss1 - _u_miss1

docs/_static/favicon.ico

15 KB
Binary file not shown.

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@
175175
# The name of an image file (within the static path) to use as favicon of the
176176
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
177177
# pixels large.
178-
# html_favicon = None
178+
html_favicon = "favicon.ico"
179179

180180
# Add any paths that contain custom static files (such as style sheets) here,
181181
# relative to this directory. They are copied after the builtin static files,

docs/dev-guide/install.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,9 @@ conda activate ./ASIM-ENV
4949
git clone https://github.com/ActivitySim/sharrow.git
5050
python -m pip install -e ./sharrow
5151
git clone https://github.com/ActivitySim/activitysim.git
52-
cd activitysim
53-
git switch develop
54-
cd ..
5552
python -m pip install -e ./activitysim
5653
```
5754

58-
```{note}
59-
If the environment create step above fails due to a 404 missing error,
60-
the main repository may not be up to date with these docs, try this instead:
61-
https://raw.githubusercontent.com/camsys/activitysim/sharrow-black/conda-environments/activitysim-dev-base.yml
62-
```
63-
6455
Note the above commands will create an environment with all the
6556
necessary dependencies, clone both ActivitySim and sharrow from GitHub,
6657
and `pip install` each of these libraries in editable mode, which

docs/dev-guide/using-sharrow.md

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,55 @@ multiprocessing mode after all the compilation for all model components is
1717
complete.
1818
```
1919

20+
### Top-Level Activation Options
21+
22+
Activating sharrow is done at the top level of the model settings file, typically
23+
`settings.yaml`, by setting the `sharrow` configuration setting to `True`:
24+
25+
```yaml
26+
sharrow: True
27+
```
28+
29+
The default operation for sharrow is to attempt to use the sharrow compiler for
30+
all model specifications, and to revert to the legacy pandas-based evaluation
31+
if the sharrow compiler encounters a problem. Alternatively, the `sharrow`
32+
setting can also be set to `require` or `test`. The `require` setting
33+
will cause the model simply fail if sharrow encounters a problem, which is
34+
useful if the user is interested in ensuring maximum performance.
35+
The `test` setting will run the model in a mode where both sharrow and the
36+
legacy pandas-based evaluation are run on each model specification, and the
37+
results are compared to ensure they are substantially identical. This is
38+
useful for debugging and testing, but is not recommended for production runs
39+
as it is much slower than running only one evaluation path or the other.
40+
41+
Testing is strongly recommended during model development, as it is possible
42+
to write expressions that are valid in one evaluation mode but not the other.
43+
This can happen if model data includes `NaN` values
44+
(see [Performance Considerations](#performance-considerations)), or when
45+
using arithmatic on logical values
46+
(see [Arithmetic on Logical Values](#arithmetic-on-logical-values)).
47+
48+
### Caching of Precompiled Functions
49+
50+
The first time you run a model with sharrow enabled, the compiler will run
51+
and create a cache of compiled functions. This can take a long time, especially
52+
for models with many components or complex utility specifications. However,
53+
once the cache is created, subsequent runs of the model will be much faster.
54+
By default, the cached functions are stored in a subdirectory of the
55+
`platformdirs.user_cache_dir` directory, which is located in a platform-specific
56+
location:
57+
58+
- Windows: `%USERPROFILE%\AppData\Local\ActivitySim\ActivitySim\Cache\...`
59+
- MacOS: `~/Library/Caches/ActivitySim/...`
60+
- Linux: `~/.cache/ActivitySim/...` or `~/$XDG_CACHE_HOME/ActivitySim/...`
61+
62+
The cache directory can be changed from this default location by setting the
63+
[`sharrow_cache_dir`](activitysim.core.configuration.FileSystem.sharrow_cache_dir)
64+
setting in the `settings.yaml` file. Note if you change this setting and provide
65+
a relative path, it will be interpreted as relative to the model working directory,
66+
and cached functions may not carry over to other model runs unless copied there
67+
by the user.
68+
2069
## Model Design Requirements
2170

2271
Activating the `sharrow` optimizations also requires using the new
@@ -231,6 +280,35 @@ such string operations won't appear in utility specifications at all, or if they
231280
do appear, they are executed only once and stored in a temporary value for re-use
232281
as needed.
233282

283+
A good approach to reduce string operations in model spec files is to convert
284+
string columns to integer or categorical columns in preprocessors. This can
285+
be done using the `map` method, which can be used to convert strings to integers,
286+
for example:
287+
288+
`df['fuel_type'].map({'Gas': 1, 'Diesel': 2, 'Hybrid': 3}).fillna(-1).astype(int)`
289+
290+
Alternatively, data columns can be converted to categorical columns with well-defined
291+
structures. Recent versions of sharrow have made significant improvements in
292+
handling of unordered categorical values, allowing for the use of possibly
293+
more intuitive categorical columns. For example, the fuel type column above
294+
could instead be redefined as a categorical column with the following code:
295+
296+
`df['fuel_type'].astype(pd.CategoricalDtype(categories=['Gas', 'Diesel', 'Hybrid'], ordered=False))`
297+
298+
It is important that the categories are defined with the same set of values
299+
in the same order, as any deviation will from this will void the compiler cache
300+
and cause the model specification to be recompiled. This means that using
301+
`x.astype('category')` is not recommended, as the categories will be inferred
302+
from the data and may not be consistent across multiple calls to the model
303+
specification evaluator.
304+
305+
```{note}
306+
Beginning with ActivitySim version 1.3, string-valued
307+
columns created in preprocessors are converted to categorical columns automatically,
308+
which means that ignoring encoding for string-valued outputs is equivalent to
309+
using the `astype('category')` method, and is not recommended.
310+
```
311+
234312
For models with utility expressions that include a lot of string comparisons,
235313
(e.g. because they are built for the legacy `pandas.eval` interpreter and have not
236314
been updated) sharrow can be disabled by setting
@@ -410,7 +488,7 @@ taz_skims:
410488
```
411489

412490
If groups of similarly named variables should have the same encoding applied,
413-
they can be identifed by regular expressions ("regex") instead of explicitly
491+
they can be identified by regular expressions ("regex") instead of explicitly
414492
giving each name. For example:
415493

416494
```yaml
@@ -485,3 +563,76 @@ taz_skims:
485563

486564
For more details on all the settings available for digital encoding, see
487565
[DigitalEncoding](activitysim.core.configuration.network.DigitalEncoding).
566+
567+
## Troubleshooting
568+
569+
If you encounter errors when running the model with sharrow enabled, it is
570+
important to address them before using the model for analysis. This is
571+
especially important when errors are found running in "test" mode (activated
572+
by `sharrow: test` in the top level settings.yaml). Errors may
573+
indicate that either sharrow or the legacy evaluator is not correctly processing
574+
the mathematical expressions in the utility specifications.
575+
576+
### "utility not aligned" Error
577+
578+
One common error that can occur when running the model with sharrow in "test"
579+
mode is the "utility not aligned" error. This error occurs when a sharrow
580+
compiled utility calculation does not sufficiently match the legacy utility
581+
calculation. We say "sufficiently" here because the two calculations may have
582+
slight differences due to numerical precision optimizations applied by sharrow.
583+
These optimizations can result in minor differences in the final utility values,
584+
which are typically inconsequential for model results. However, if the differences
585+
are too large, the "utility not aligned" error will be raised. This error does
586+
not indicate whether the incorrect result is from the sharrow or legacy calculation
587+
(or both), and it is up to the user to determine how to align the calculations
588+
so they are reflective of the model developer's intent.
589+
590+
To troubleshoot the "utility not aligned" error, the user can use a Python debugger
591+
to compare the utility values calculated by sharrow and the legacy evaluator.
592+
ActivitySim also includes error handler code that will attempt to find the
593+
problematic utility expression and print it to the console or log file, under the
594+
heading "possible problematic expressions". This can be helpful in quickly narrowing
595+
down which lines of a specification file are causing the error.
596+
597+
Common causes of the "utility not aligned" error include:
598+
599+
- model data includes `NaN` values but the component settings do not
600+
disable `fastmath` (see [Performance Considerations](#performance-considerations))
601+
- incorrect use of arithmatic on logical values (see
602+
[Arithmetic on Logical Values](#arithmetic-on-logical-values))
603+
604+
### Insufficient system resources
605+
606+
For large models run on large servers, it is possible to overwhelm the system
607+
with too many processes and threads, which can result in the following error:
608+
609+
```
610+
OSError: Insufficient system resources exist to complete the requested service
611+
```
612+
613+
This error can be resolved by reducing the number of processes and/or threads per
614+
process. See [Multiprocessing](../users-guide/performance/multiprocessing.md) and
615+
[Multithreading](../users-guide/performance/multithreading.md) in the User's Guide
616+
for more information on how to adjust these settings.
617+
618+
### Permission Error
619+
620+
If running a model using multiprocessing with sharrow enabled, it is necessary
621+
to have pre-compiled all the utility specifications to prevent the multiple
622+
processes from competing to write to the same cache location on disk. Failure
623+
to do this can result in a permission error, as some processes may be unable to
624+
write to the cache location.
625+
626+
```
627+
PermissionError: The process cannot access the file because it is being used by another process
628+
```
629+
630+
To resolve this error, run the model with sharrow enabled in single-process mode
631+
to pre-compile all the utility specifications. If that does not resolve the error,
632+
it is possible that some compiling is being triggered in multiprocess steps that
633+
is not being handled in the single process mode. This is likely due to the presence
634+
of string or categorical columns created in a preprocessor that are not being
635+
stored in a stable data format. To resolve this error, ensure that all expressions
636+
in pre-processors are written in a manner that results in stable data types (e.g.
637+
integers, floats, or categorical columns with a fixed set of categories). See
638+
see [Performance Considerations](#performance-considerations)) for examples.

docs/users-guide/example_models.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2756,7 +2756,7 @@ Skims are named <PATH TYPE>_<MEASURE>__<TIME PERIOD>:
27562756
Configuration
27572757
_____________
27582758

2759-
This section has been moved to :ref:`configuration`.
2759+
This section has been moved to :ref:`user_configuration`.
27602760

27612761
.. _sub-model-spec-files:
27622762

docs/users-guide/index.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ Contents
3333

3434
.. toctree::
3535
:maxdepth: 2
36-
36+
3737
modelsetup
3838
ways_to_run
39+
performance/index
3940
run_primary_example
4041
model_anatomy
4142
../howitworks
@@ -45,5 +46,3 @@ Contents
4546
.. toctree::
4647
:maxdepth: 1
4748
other_examples
48-
49-

0 commit comments

Comments
 (0)