| # Performance Benchmarking |
| |
| ICU4X Benchmark Guideline |
| ========================= |
| |
| ICU4X is aiming to be a modular, fast and slim set of internationalization components indented |
| for use from many environments and ecosystems. |
| In order to achieve those goals, ICU4X has established a strong culture of measuring various |
| aspects of its performance over time in order to make informed decisions and notice regressions early. |
| |
| At the moment we aim to measure four primary classes of benchmarks: |
| |
| 1) Performance |
| 2) Memory utilization |
| 3) Binary and data size |
| 4) Code coverage |
| |
| ## Performance |
| |
| Performance is primarily measured via a network of microbenchmarks located in each component |
| in its `benches/` directory. |
| Those benchmarks, running via the `criterion` statistical framework, are intended to give a reasonable |
| approximation of how the component should behave in the wild. |
| |
| ### Overview Benchmarks |
| |
| Each component provides one or more tests with a `/overview` postfix, which can be filtered for using |
| `cargo bench overview`. Those benchmarks are intended to be run often in the change/test development cycle and give a good |
| overview of how the changes affect performance of the module in the most common scenarios. |
| |
| This overview should be composed of no more than 5 tests (for a total of 1min per execution) and single iteration |
| should aim to be around or slightly above 1 microseconds and below 1ms on an average laptop to give |
| high p95 and a low standard deviation. |
| |
| An example of a high-level overview from `PluralRules` component would be: |
| - `plurals/operands/overview` - overview of the `PluralOperands` creation cost |
| - `plurals/parser/overview` - overview of the parser cost |
| - `plurals/pluralrules/overview` - overview of the construction and common scenario usage of the main API |
| |
| The naming convention is to start with the name of the crate (this helps when benchmarks are run from the top level across |
| multiple crates), then name of the module and then the name of the test, in this case `/overview`. |
| |
| ### Detailed Benchmarks |
| |
| If more detailed benchmarks drilling into various facets of the component are needed, they should be put |
| in the same benchmark naming scheme, without the `overview` keyword. |
| |
| For example: |
| - `plurals/parser/lexing` |
| - `plurals/operands/isize` |
| - `plurals/operands/usize` |
| - `plurals/operands/string` |
| - `plurals/operands/fixed_Decimal` |
| - `plurals/pluralrules/construct` |
| - `plurals/pluralrules/select` |
| |
| This allows a user to incorporate `cargo bench` into their change->test->change cycle, while allowing them |
| to drill into a more detailed view of the performance impact if needed by calling `cargo bench operands`. |
| |
| ### Wall time vs. Count of instructions |
| |
| At the moment our microbenchmarks are using `wall-time` measurements which are prone to high stdev and will vary |
| from machine to machine and may be impacted by background load, CPU profiler and other external factors. |
| |
| In the future we'd like to switch to measuring count of instructions as a more reliable measurement of performance |
| impact. |
| |
| ### Quasi "real-world" benchmarks |
| |
| On top of micro-benchmarks, we will use small stand-alone binaries performing quasi real-world tasks |
| to compare our implementation against reference ICU4C equivalents, and between different ICU4C outputs (such |
| as WebAssembly, Dart, JS etc.). |
| |
| ## Memory |
| |
| Memory benchmarks are not ready yet. |
| |
| For memory benchmarks, we will measure two main concepts - Peak Memory Utilization and Data/Struct Allocation |
| |
| ### Peak Memory Utilization |
| |
| We will attempt to instrument example binaries to collect peak memory usage and observe the utilization over time. |
| |
| ### Allocation |
| |
| In many scenarios we can expect a certain instances of our structures to remain allocated in memory. |
| |
| Examples of such are lists of locales, or total memory cost of data allocated on a `DateTimeFormat` instance. |
| |
| We will measure that memory usage over time. |
| |
| ### Binary and Data Size |
| |
| For binaries, we'll take apps in the `example/` directory of each component and instrument an optimized |
| builds and collect the total size of a static binary over time. |
| |
| For data size, we will generate production ICU4X data and measure total size of the payload. |
| |
| ## Coverage |
| |
| In order to ensure that our data driven testing provides sufficient coverage of our codepaths, |
| we'll want to run our instrumentation against only data-driven tests (as opposed to documentation tests, |
| in-module unit tests and imperative integration tests), and verify that the code coverage remains high. |
| |
| We don't have an exact bar set yet for how high we want it to be, but we're aiming for total test coverage |
| to be above 90% and most of it should be covered by data driven tests. |