Skip to content

Add basic benchmark test for Python#17793

Merged
lidizheng merged 4 commits intogrpc:masterfrom
lidizheng:py-bm
Jan 28, 2019
Merged

Add basic benchmark test for Python#17793
lidizheng merged 4 commits intogrpc:masterfrom
lidizheng:py-bm

Conversation

@lidizheng
Copy link
Copy Markdown
Contributor

@lidizheng lidizheng commented Jan 23, 2019

Running a single performance benchmark scenario for Python takes 15 minutes. The scenario here named "python_protobuf_sync_unary_ping_pong".

$ time tools/run_tests/run_performance_tests.py --language python -r='^python_protobuf_sync_unary_ping_pong$'
# Skip outputs
real	4m24.825s
user	15m16.941s
sys	1m48.508s

To boost our productivity, we have to speed up the benchmark test. The whole benchmark mechanism is designed for CI, and it is mixing the control flow with data flow. The related code amount is huge (thousands of lines across languages & folders). IMO it is better to utilize existing code as much as I can without changing its behavior.

The first change is building Python C extension incrementally. To achieve that, this PR added a Bazel BUILD file to instruct Bazel how to build Python benchmark related code. Then make it depends on our core package //src/python/grpcio/grpc:grpcio.

The second change is adding a basic_benchmark_test. It runs two scenario that could cover major use cases of gRPC Python. What is does is that it spins up two Python qps_worker to serve as client and server, and then spins up a qps_json_driver to control the two worker to run benchmarks. The result will be print to stdout. And this PR includes a simple doc to make it easier to use.

The time usage reduces dramatically (and it is running two scenarios):

$ time bazel test --test_output=streamed src/python/grpcio_tests/tests/qps:basic_benchmark_test
# Skip outputs
real	0m26.429s
user	0m0.039s
sys	0m0.050s

PS: If I use a py_test, the sanity check will complain that I didn't add it to the tests.json. Then, I need to disable it at two or three places...

Copy link
Copy Markdown
Contributor

@gnossen gnossen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some nits. Looking really nice.

function join { local IFS="$1"; shift; echo "$*"; }

if [[ -e "${SCENARIOS_FILE}" ]]; then
echo "Running against scenarios.json:"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be echo "Running against ${SCENARIOS_FILE}:"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's better!


## Why keep the scenario file if it can be generated?

Well... The `tools/run_tests/performance/scenario_config.py` is 1274 lines long. The intention of building these benchmark tools is reducing the complexity of existing infrastructure code. Depending on something that is
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfinished sentence?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out. The updated version:

Well... The tools/run_tests/performance/scenario_config.py is 1274 lines long. The intention of building these benchmark tools is reducing the complexity of existing infrastructure code. So, instead of calling layers of abstraction to generate the scenario file, keeping a valid static copy is more preferable.

@lidizheng lidizheng changed the title [WIP] Add basic benchmark test for Python Add basic benchmark test for Python Jan 23, 2019
@jtattermusch jtattermusch self-requested a review January 25, 2019 07:50
@jtattermusch
Copy link
Copy Markdown
Contributor

A few insights:

  • AFAIK the only reason why tools/run_tests/run_performance_tests.py is slow is that python build is also slow. The script doesn't do much beside building and then running the scenario (each scenario takes ~35 secs to run). Perhaps we should focus on making python build faster (and make sure the right args are used in
    python tools/run_tests/run_tests.py -l "$language" -c "$CONFIG" --compiler python2.7 --build_only -j 8
    )
  • there is a kokoro job that allows you to run the performance benchmark in a single-machine setup:
    https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod%3Agrpc%2Fcore%2Fmaster%2Flinux%2Fgrpc_e2e_performance_singlevm
  • before submitting please doublecheck that the performance benchmark scenarios continue to work as they are a bit fragile (there is a way to run an adhoc run with multimachine setup, I can help with that once I have a bit of free time).
  • please do not submit any changes to performance benchmarks without me reviewing first.

@jtattermusch
Copy link
Copy Markdown
Contributor

jtattermusch commented Jan 25, 2019

I scanned through the code quickly and looks like this is only adding a bazel test that runs a few basic python benchmarks locally. If that's the case and there are no changes to the run_performance_tests.py suite, I probably don't need to review.

(but I still don't quite understand why running the run_performance_tests.py script is so slow for python - we have accelerated the python build recently, haven't we?).

@lidizheng
Copy link
Copy Markdown
Contributor Author

There are two major performance issue with setuptools which is the build tooling gRPC Python is using. The first is that it builds C files one-by-one which solved by your PR. And the second is that it doesn't do incremental builds as I wrote in the description... In comparison, Bazel is doing really well to produce a development build, it is fast and stable.

The purpose of this PR is adding a handy tool to tune performance locally with frequent code changes. However, we still need the build_python.sh to depend on setuptools since only setuptools is able to generate distribution packages which is the version actually used by our users.

Copy link
Copy Markdown
Contributor

@ericgribkoff ericgribkoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Regarding build speed of python: the recent changes did make it faster, but it's still on the order of minutes when building core as well. It seems that setuptools has little (afaict, no) support for incremental compilation: we found https://stackoverflow.com/questions/47539538/setup-py-build-that-doesnt-reinvoke-compiler-on-every-c-source-file-increment, which suggests an approach like plugging in bazel to do the c/cython step within setuptools may be a viable solution, but that's a longer term approach.

bazel test --test_output=streamed src/python/grpcio_tests/tests/qps:basic_benchmark_test
```

## How is the output look like?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/How is/What does/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.


## Why keep the scenario file if it can be generated?

Well... The `tools/run_tests/performance/scenario_config.py` is 1274 lines long. The intention of building these benchmark tools is reducing the complexity of existing infrastructure code. So, instead of calling layers of abstraction to generate the scenario file, keeping a valid static copy is more preferable.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/more preferable/preferable/

Optional: Add a "future-proofing" sentence like - "If the use case for this tool grows beyond these two scenarios, we can incorporate automatic generation and selection of scenarios into the tool."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added:

Also, if the use case for this tool grows beyond simple static scenarios, we can incorporate automatic generation and selection of scenarios into the tool.

* python_protobuf_sync_streaming_qps_unconstrained
* python_protobuf_sync_unary_ping_pong_1MB

Here I picked the top 2 most representative scenarios of them, and reduce their benchmark duration from 30 seconds to 10 seconds:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "most representative" backed by an objective measurement? If not its fine, perhaps just reword to "I [we? I think we usually use we in the readme files] picked a small but representative subset"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@lidizheng lidizheng merged commit 16b8279 into grpc:master Jan 28, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Apr 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants