Skip to content

Guarding against perf regression for acquire_token_for_client() - #580

Merged
Ray Luo (rayluo) merged 2 commits into
devfrom
benchmark
Sep 6, 2023
Merged

Guarding against perf regression for acquire_token_for_client()#580
Ray Luo (rayluo) merged 2 commits into
devfrom
benchmark

Conversation

@rayluo

@rayluo Ray Luo (rayluo) commented Jul 19, 2023

Copy link
Copy Markdown
Contributor

Goals defined in early July 2023

This PR may eventually accomplish the following goals.

  1. Scenarios: A multi-tenant S2S with memory token cache which is (1) initially empty, and all incoming requests will generate cache miss; or (2) prepopulated with 1000 tenants and 10 tokens per tenant, and all incoming requests will generate cache hit.
  2. Metrics: latency in ms.
  3. How data is collected and stored (Aim for 1 DB schema)? TBD
  4. How do we read the historical data into the build (from that central DB described in Q3)? (The intention is that we will have a baseline to compare to, and break the build on regression.)

UPDATE at late July 2023

We followed the spirit of the goals above and pivoted along the way.

  1. This PR populates the token cache for test cases in the following matrix, and during the benchmark, token cache remains unchanged, so that its size would not grow.
New token request result in cache hits New token requests result in cache miss
1 tenant, 1 token in cache
1 tenant, 10 tokens in cache
1 tenant, 1000 tokens in cache
1000 tenants, 10 tokens per tenant in cache
  1. Metrics. Our chosen benchmark tool happens to use "operations per second". Internally we use "time elapsed during a test case". They are mathematically equivalent.

  2. Collecting data into a central database? The purpose was to render historical data into a nice chart. We found a lightweight Github Benchmark Action that can render charts without dependency of a central database. See the diagram generated by this PR.

  3. How to get baseline data to be used as a baseline to detect regression?

    There are two mechanisms available. Neither of them requires a central DB. We may choose either or both of them.

    • The Github Benchmark Action we use will store historical benchmark data into a special git branch.

    • We can also dynamically benchmark a stable computation load as a reference, and then comparing our MSAL performance against that known load. For example, the load can be "the time needed to calculate fibonacci sequence for 20", or just actually benchmark the built-in dictionary implementation of the language we are using. And then we can benchmark MSAL's scenarios (see our goal 1 above), which will be - let's say - 1.234 times slower than the reference. Now, our threshold can be defines as "the performance shall not be longer than 1.234 * (1 + threshold)". This experiment was abandoned, because its reference workload has different characteristic than those real workloads, and the ratio between real workload and reference seems different on different test machines.

    In either approach, the variation is in the range of double-digit percentage. The Github Benchmark Action uses a 200% threshold as its default value.

Implementation Details (useful when you want to review this PR's implementation)

A Perf Test solution contains 3 high level components:

(a) a tailored simulator that can generate load for our test subject;
(b) a perf tester that can run those *simulators" many times, in deterministic amount of time, producing some statistics, preferably with a small standard deviation;
(c) a mechanism to store historical results of perf tester, in order to detect regression and render wow diagrams.

We have to do "a. simulator" ourselves, which we did, in the file tests/simulator.py.

We chose the "Continuous Benchmark" github action which is mainly a tool for "c. detect and render", but it cleverly consumes the output of many other tools that operate in "b. perf tester" space, one per language. In this PR, we use pytest-benchmark as our "perf tester", with test cases defined in test_benchmark.py.

The configuration for Continuous Benchmark is in python-package.yml.

Comment thread .github/workflows/python-package.yml
@rayluo
Ray Luo (rayluo) force-pushed the benchmark branch 3 times, most recently from 033cea3 to a8c4088 Compare August 7, 2023 17:47
@rayluo
Ray Luo (rayluo) force-pushed the benchmark branch 5 times, most recently from 83fc273 to 747b2fd Compare August 16, 2023 02:22
@rayluo
Ray Luo (rayluo) marked this pull request as ready for review August 16, 2023 02:26
@rayluo
Ray Luo (rayluo) force-pushed the benchmark branch 3 times, most recently from 40af0dc to f1854d7 Compare August 16, 2023 04:11
Comment thread .github/workflows/python-package.yml
Comment thread .github/workflows/python-package.yml
Comment thread tests/simulator.py
Turns out we do not really need a full-blown Timeable class

Refactor to use pytest and pytest-benchmark

Calibrate ratios

Adjust detection calculation

Experimenting different reference workload

Add more iterations to quick test cases

Tune reference and each test case to be in tenth of second

Go with fewer loop in hoping for more stable time

Relax threshold to 20%

One more run

Use 40% threshold

Use larger threshold 0.4 * 3

Refactor to potentially use PyPerf

Automatically choose the right number and repeat

Remove local regression detection, for now
Experimenting not using GPO

Use vanilla git command to publish

Do not run benchmark in matrix

Skip chatty test case discovery during benchmark
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants