Skip to content

Speedup jsonnet generation by running in parallel #1908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 3, 2023

Conversation

sthaha
Copy link
Contributor

@sthaha sthaha commented Mar 2, 2023

  • I added CHANGELOG entry for this change.
  • No user facing changes, so no entry in CHANGELOG was needed.

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 2, 2023
@jan--f
Copy link
Contributor

jan--f commented Mar 7, 2023

Do you have some performance numbers on the speedup you're seeing? I did a make clean; time make generate (run multiple times in a row) on master and on your PR with counter intuitive results:
master:

make generate  32.63s user 4.52s system 65% cpu 56.911 total
make generate  33.86s user 4.96s system 63% cpu 1:01.18 total

This PR:

make generate  43.42s user 6.48s system 114% cpu 43.555 total
make generate  43.82s user 5.42s system 125% cpu 39.260 total

@sthaha
Copy link
Contributor Author

sthaha commented Mar 8, 2023

Do you have some performance numbers on the speedup you're seeing? I did a make clean; time make generate (run multiple times in a row) on master and on your PR with counter intuitive results: master:

I don't think make generate is the correct target to run since it does more than just generate yaml from jsonnet. Please try
time -p make build-jsonnet which produces the following on my machine. Are you running this in a container with only one cpu allocated by any chance?

time -p make build-jsonnet

# master 
  attempt 1: real 28.69. user 27.60  sys 4.08
  attempt 2: real 28.40 user 27.30 sys 4.11

# this pr 
  attempt 1: real 10.62 user 34.76 sys 5.13
  attempt 2: real 10.68 user 34.92 sys 5.15

If you checkout the pr and run this locally, the difference is quite perceivable (at least one my 8 core linux box).

May be try to look at only the total time

export TIMEFMT='%E'; time make build-jsonnet

On my machine, I see that this PR makes it at least 3 times faster 28.88s vs 10.81s.

@jan--f
Copy link
Contributor

jan--f commented Mar 8, 2023

I still get very similar results with make clean; export TIMEFMT='%E'; time make build-jsonnet:
main:

47.94s 47.37s

PR:

45.35s 39.62s

@sthaha
Copy link
Contributor Author

sthaha commented Mar 8, 2023

make clean; export TIMEFMT='%E'; time make build-jsonnet

I think this could be the issue, that make clean deletes everything, so the time is actually measuring all the download of tools , jsonnet bundles etc .. E.g.

❯ make clean; export TIMEFMT='%E'; time make build-jsonnet
rm -rf jsonnet/vendor operator .hack-operator-image tmp/
mkdir -p /home/sthaha/dev/rh/monitoring/downstream/cmo/wt/improve-jsonnet-gen/tmp/bin
Installing tools from hack/tools.go
go build '-mod=mod' -o cmo/wt/improve-jsonnet-gen/tmp/bin github.com/brancz/gojsontoyaml
go build '-mod=mod' -o cmo/wt/improve-jsonnet-gen/tmp/bin github.com/campoy/embedmd
go build '-mod=mod' -o cmo/wt/improve-jsonnet-gen/tmp/bin github.com/google/go-jsonnet/cmd/jsonnet
go build '-mod=mod' -o cmo/wt/improve-jsonnet-gen/tmp/bin github.com/google/go-jsonnet/cmd/jsonnetfmt
go build '-mod=mod' -o cmo/wt/improve-jsonnet-gen/tmp/bin github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb
go build '-mod=mod' -o cmo/wt/improve-jsonnet-gen/tmp/bin github.com/prometheus/prometheus/cmd/promtool
cd jsonnet && cmo/wt/improve-jsonnet-gen/tmp/bin/jb install
GET https://github.com/prometheus-operator/kube-prometheus/archive/74cb76d338f021bdca9af635c2eef6b196804b57.tar.gz 200
GET https://github.com/prometheus-operator/prometheus-operator/archive/f337e4ecf88d2c228c2325249bf338c6aa999488.tar.gz 200
GET https://github.com/openshift/openshift-state-metrics/archive/7beb8807e2c0092b5e580a29903ff060140d8ff0.tar.gz 200
GET https://github.com/openshift/telemeter/archive/ee1ba4699b82ecb2033f886af12b9e2e451d2296.tar.gz 200

I think a better way is to time the script itself. Could you please try this?

diff --git a/Makefile b/Makefile
index b77423bc..2cf60da1 100644
--- a/Makefile
+++ b/Makefile
@@ -130,7 +130,7 @@ $(ASSETS): build-jsonnet
 
 .PHONY: build-jsonnet
 build-jsonnet: $(JSONNET_BIN) $(GOJSONTOYAML_BIN) $(JSONNET_SRC) $(JSONNET_VENDOR) json-manifests json-crds
-	./hack/build-jsonnet.sh
+	time ./hack/build-jsonnet.sh
 
 $(JSON_MANIFESTS): $(MANIFESTS)
 	cat $(MANIFESTS_DIR)/$(patsubst %.json,%.yaml,$(@F)) | $(GOJSONTOYAML_BIN) -yamltojson > $@

@jan--f
Copy link
Contributor

jan--f commented Mar 8, 2023

Yeah that looks more promising:

master:

real	0m19.456s
user	0m19.835s
sys	0m1.445s
52.00s

real	0m20.376s
user	0m20.634s
sys	0m1.533s
49.92s

PR:

real	0m8.408s
user	0m31.937s
sys	0m2.293s
38.19s

real	0m8.448s
user	0m31.868s
sys	0m2.275s
39.01s

Comment on lines 26 to 35
for file in "${files[@]}"; do
(
dir=$(dirname "${file}")
path="${prefix}/${dir}"
mkdir -p "${path}"

# convert file name from camelCase to snake-case
fullfile=$(echo "${file}" | sed 's/\(.\)\([A-Z]\)/\1-\2/g' | tr '[:upper:]' '[:lower:]')
jq -r ".[\"${file}\"]" "${TMP}/main.json" | gojsontoyaml > "${prefix}/${fullfile}.yaml"
)&
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
for file in "${files[@]}"; do
(
dir=$(dirname "${file}")
path="${prefix}/${dir}"
mkdir -p "${path}"
# convert file name from camelCase to snake-case
fullfile=$(echo "${file}" | sed 's/\(.\)\([A-Z]\)/\1-\2/g' | tr '[:upper:]' '[:lower:]')
jq -r ".[\"${file}\"]" "${TMP}/main.json" | gojsontoyaml > "${prefix}/${fullfile}.yaml"
)&
for file in "${files[@]}"; do
dir=$(dirname "${file}")
path="${prefix}/${dir}"
mkdir -p "${path}"
# convert file name from camelCase to snake-case
fullfile=$(echo "${file}" | sed 's/\(.\)\([A-Z]\)/\1-\2/g' | tr '[:upper:]' '[:lower:]')
jq -r ".[\"${file}\"]" "${TMP}/main.json" | gojsontoyaml > "${prefix}/${fullfile}.yaml" &

I get slightly better results with this change (shave a few seconds off the results fairly consistently). Can you give this a try as well?

Copy link
Contributor Author

@sthaha sthaha Mar 9, 2023

Choose a reason for hiding this comment

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

This was my first version :) but I felt starting a subshell at the start of for loop makes it easier to reason with. Since it boils down to

for f in files {
    ( process this file) &  // in background
    wait if there are more than N processes
}

I think, we can achieve the same without starting a subshell by using a block and run that in background.

@sthaha sthaha force-pushed the improve-jsonnet-gen branch from 3305ebf to 03f4e79 Compare March 9, 2023 00:54
Previously, jsonnet to yaml conversion was done sequentially and this
took quite some time. The patch speeds that up by running them in
parallel . The number of jobs to run in parallel is determined by the
number of CPU cores (thus requires nproc to be in path).

Signed-off-by: Sunil Thaha <[email protected]>
@sthaha sthaha force-pushed the improve-jsonnet-gen branch from 03f4e79 to 7eb4068 Compare March 9, 2023 00:55
@sthaha
Copy link
Contributor Author

sthaha commented Mar 9, 2023

/retest required

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 9, 2023

@sthaha: The /retest command does not accept any targets.
The following commands are available to trigger required jobs:

  • /test e2e-agnostic-operator
  • /test e2e-aws-ovn
  • /test e2e-aws-ovn-upgrade
  • /test generate
  • /test go-fmt
  • /test images
  • /test jsonnet-fmt
  • /test rules
  • /test shellcheck
  • /test unit
  • /test vendor
  • /test verify

The following commands are available to trigger optional jobs:

  • /test e2e-aws-ovn-single-node
  • /test versions

Use /test all to run all jobs.

In response to this:

/retest required

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@sthaha
Copy link
Contributor Author

sthaha commented Mar 9, 2023

/retest-required

# convert file name from camelCase to snake-case
fullfile=$(echo "${file}" | sed 's/\(.\)\([A-Z]\)/\1-\2/g' | tr '[:upper:]' '[:lower:]')
jq -r ".[\"${file}\"]" "${TMP}/main.json" | gojsontoyaml > "${prefix}/${fullfile}.yaml"
}&
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jan--f this should give us the same perf as jq -r ".[\"${file}\"]" "${TMP}/main.json" | gojsontoyaml > "${prefix}/${fullfile}.yaml" & with the additional benefit of easier to reason with. I.E. consider the entire block as a function.

@sthaha
Copy link
Contributor Author

sthaha commented Mar 9, 2023

/retest-required

6 similar comments
@sthaha
Copy link
Contributor Author

sthaha commented Mar 10, 2023

/retest-required

@sthaha
Copy link
Contributor Author

sthaha commented Mar 13, 2023

/retest-required

@raptorsun
Copy link
Contributor

/retest-required

@raptorsun
Copy link
Contributor

/retest-required

@sthaha
Copy link
Contributor Author

sthaha commented Mar 27, 2023

/retest-required

@sthaha
Copy link
Contributor Author

sthaha commented Apr 3, 2023

/retest-required

@sthaha
Copy link
Contributor Author

sthaha commented Apr 3, 2023

@jan--f now that the CI gods have blessed this, can merge this one please?

@jan--f
Copy link
Contributor

jan--f commented Apr 3, 2023

yes, sorry for the lag!
just did a run again and get consistently numbers like real 0m19.300s prior to this change, real 0m7.420s with this change.
/lgtm
/label docs-approved
/label px-approved
/label qe-approved
Adding labels for an internal build optimization.

@openshift-ci openshift-ci bot added docs-approved Signifies that Docs has signed off on this PR px-approved Signifies that Product Support has signed off on this PR qe-approved Signifies that QE has signed off on this PR labels Apr 3, 2023
@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Apr 3, 2023
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Apr 3, 2023

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jan--f, sthaha

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Apr 3, 2023

@sthaha: all tests passed!

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@openshift-merge-robot openshift-merge-robot merged commit 0142eb7 into openshift:master Apr 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. docs-approved Signifies that Docs has signed off on this PR lgtm Indicates that a PR is ready to be merged. px-approved Signifies that Product Support has signed off on this PR qe-approved Signifies that QE has signed off on this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants