Skip to content

Conversation

@gmierz
Copy link
Collaborator

@gmierz gmierz commented Dec 2, 2025

This patch adds multiprocessing capabilities for the MWU test version processing. It should provide some significant performance gains for API requests from PerfCompare. Local runs for a worst case scenario suggest improvements from 40s to 18s for a request like this one when silverman/KDE was enabled: https://treeherder.mozilla.org/api/perfcompare/results/?base_repository=try&base_revision=bddcdb7187bbbae40da023ff24315d5e499dc0a3&new_repository=try&new_revision=7a30b2c44b19a3576f8c87c56a001792d549e5e0&framework=13&no_subtests=true&replicates=true&test_version=mann-whitney-u

When silverman/KDE is disabled, we still see a pretty big improvement going from 4s without multiprocessing to ~2.5s with multiprocessing.

@gmierz gmierz marked this pull request as draft December 2, 2025 15:06
@gmierz gmierz changed the title Run new stats with multiprocessing. Bug 2004406 - Use multiprocessing for the MWU test version, and clean up the code. Dec 5, 2025
@gmierz gmierz changed the title Bug 2004406 - Use multiprocessing for the MWU test version, and clean up the code. Bug 2004406 - Use multiprocessing for the MWU test version, and clean up the API code. Dec 5, 2025
@gmierz gmierz marked this pull request as ready for review December 5, 2025 16:25
Comment on lines 1165 to 1169
sig_identifier = perfcompare_utils.get_sig_identifier(header, platform)
base_sig = base_signatures_map.get(sig_identifier, {})
base_sig_id = base_sig.get("id", None)
new_sig = new_signatures_map.get(sig_identifier, {})
new_sig_id = new_sig.get("id", None)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this could be part of the _build_common_result logic as it is repeated in _process_mann_whitney_u_version and _process_student_t_version

Comment on lines +1294 to +1301
base_avg_value = perfcompare_utils.get_avg(statistics_base_perf_data, header)
base_stddev = perfcompare_utils.get_stddev(statistics_base_perf_data, header)
base_median_value = perfcompare_utils.get_median(statistics_base_perf_data)
new_avg_value = perfcompare_utils.get_avg(statistics_new_perf_data, header)
new_stddev = perfcompare_utils.get_stddev(statistics_new_perf_data, header)
new_median_value = perfcompare_utils.get_median(statistics_new_perf_data)
base_stddev_pct = perfcompare_utils.get_stddev_pct(base_avg_value, base_stddev)
new_stddev_pct = perfcompare_utils.get_stddev_pct(new_avg_value, new_stddev)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not for now, but these fields seem to me like they could be part of the _build_common_result method, in the mann-whitney logic we retrieve them for the base_standard_stats and new_standard_stats fields.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point! Actually, it could also be useful to get those out of the MWU process_stats so we could then at least provide some measures even if the comparison fails. Let me file a bug for this.

)

row_result = {
**common_result,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Simplify the task method by only passing the base/new stat arrays, keep the large common_result in _process_mann_whitney_u_version and merge the results there (each result could have a task index to easily merge the results).

Copy link
Collaborator Author

@gmierz gmierz Dec 11, 2025

Choose a reason for hiding this comment

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

Can you elaborate on what kind of benefits you think that may provide? It's definitely something we could do, but I think it may make the code more complex when we're handling the results with limited potential benefits.

)
# Process tasks in parallel using multiprocessing
workers = multiprocessing.cpu_count()
with multiprocessing.Pool(processes=workers) as pool:
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the event that multiprocessing fails, should the exception be handled by defaulting to sequential processing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm, good question. I'm not sure since we would probably hit the same failure with sequential processing? Also, there's a chance that if it fails and we then try a sequential run, that we would hit a network timeout on the perfcompare side. I'm going to run a small test to see what happens when we trigger an artificial failure though.

Copy link
Collaborator

@beatrice-acasandrei beatrice-acasandrei left a comment

Choose a reason for hiding this comment

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

I don't have other suggestions besides the nit comments.

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