Skip to content

Speed up python tests#11975

Merged
RAMitchell merged 17 commits intodmlc:masterfrom
RAMitchell:slow_python_tests
Feb 5, 2026
Merged

Speed up python tests#11975
RAMitchell merged 17 commits intodmlc:masterfrom
RAMitchell:slow_python_tests

Conversation

@RAMitchell
Copy link
Member

No description provided.

@RAMitchell RAMitchell marked this pull request as ready for review January 30, 2026 09:58
@RAMitchell
Copy link
Member Author

I spent a bit of time on the CPU dask tests. Fixtures werent being reused, so now the tests are 3 times faster on my machine.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR speeds up Python tests by introducing session-scoped Dask client fixtures and reducing test data sizes. The changes eliminate redundant cluster creation/teardown operations across tests, while maintaining test coverage and validity.

Changes:

  • Introduced shared session-scoped Dask client fixtures in a new conftest.py file to avoid repeated cluster setup
  • Reduced test data sizes and iteration counts (e.g., 10000→3000 samples, 16→8 boost rounds, hypothesis max_examples reduced)
  • Updated test functions to use shared fixtures instead of creating their own clusters
  • Enhanced npstr_to_arrow_strarr in _data_utils.py to handle more array types robustly

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_distributed/test_with_dask/conftest.py New file with session-scoped client fixtures to share Dask clusters across tests
tests/test_distributed/test_with_dask/test_with_dask.py Removed local fixtures, updated tests to use shared clients, reduced data sizes, improved compatibility with sklearn versions
tests/test_distributed/test_with_dask/test_ranking.py Removed duplicate fixtures, adjusted training parameters for faster convergence
tests/python/test_updaters.py Reduced hypothesis test example counts for faster execution
tests/python/test_ranking.py Reduced hypothesis test example counts for faster execution
python-package/xgboost/testing/updater.py Reduced boost rounds for faster test execution
python-package/xgboost/testing/dask.py Reduced feature counts and partition sizes for faster test execution
python-package/xgboost/_data_utils.py Enhanced string array handling to support more input types (pandas, byte strings)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import socket
import tempfile
from concurrent.futures import ThreadPoolExecutor
from contextlib import ExitStack
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The ExitStack import is unused and should be removed.

Suggested change
from contextlib import ExitStack

Copilot uses AI. Check for mistakes.
RAMitchell and others added 3 commits February 2, 2026 10:21
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@RAMitchell
Copy link
Member Author

I don't think I've made much of a dent in overall CI time but its something.

@trivialfis
Copy link
Member

Please let me know if the PR is ready.

Thus far, the time for CPU tests is mostly spent on the pyspark tests: https://github.com/dmlc/xgboost/actions/runs/21631091583/job/62344616444

For GPU tests, I need to reduce the time on cuDF tests with the ordinal re-coder. cuDF is quite slow when the number of columns is large.

@RAMitchell
Copy link
Member Author

Yes its ready.

Maybe we can also improve the fixturing for spark.

21.06s call     test_distributed/test_with_spark/test_spark_local.py::TestPySparkLocal::test_classifier_with_cross_validator
19.48s call     test_distributed/test_with_spark/test_spark_local_cluster.py::XgboostLocalClusterTestCase::test_classifier_distributed_weight_eval
18.90s call     test_distributed/test_with_spark/test_spark_local.py::TestPySparkLocal::test_classifier_with_weight_eval
18.55s call     test_distributed/test_with_spark/test_spark_local_cluster.py::XgboostLocalClusterTestCase::test_regressor_distributed_weight_eval
18.47s call     test_distributed/test_with_spark/test_spark_local.py::XgboostLocalTest::test_classifier_with_base_margin
18.39s call     test_distributed/test_with_spark/test_spark_local.py::TestPySparkLocal::test_regressor_with_weight_eval
17.65s call     test_distributed/test_with_spark/test_spark_local.py::XgboostLocalTest::test_train_with_initial_model
16.47s setup    test_distributed/test_with_spark/test_spark_local_cluster.py::XgboostLocalClusterTestCase::test_classifier_distributed_basic
13.64s call     test_distributed/test_with_spark/test_spark_local_cluster.py::TestPySparkLocalCluster::test_regressor_basic_with_params
12.88s call     test_distributed/test_with_spark/test_spark_local.py::XgboostLocalTest::test_empty_validation_data
12.82s call     test_distributed/test_with_spark/test_spark_local_cluster.py::TestPySparkLocalCluster::test_callbacks
12.41s call     test_distributed/test_with_spark/test_spark_local.py::XgboostLocalTest::test_classifier_with_sparse_optim
11.97s call     test_distributed/test_with_spark/test_spark_local.py::XgboostLocalTest::test_regressor_with_sparse_optim
11.36s call     test_distributed/test_with_spark/test_spark_local.py::XgboostLocalTest::test_empty_partition
10.22s call     test_distributed/test_with_spark/test_spark_local_cluster.py::XgboostLocalClusterTestCase::test_classifier_distributed_basic
9.66s call     test_distributed/test_with_spark/test_spark_local.py::TestPySparkLocal::test_regressor_basic
8.54s call     test_distributed/test_with_spark/test_spark_local.py::TestPySparkLocal::test_gpu_transform
7.71s call     test_distributed/test_with_spark/test_spark_local.py::TestPySparkLocal::test_classifier_model_pipeline_save_load
7.52s call     test_distributed/test_with_spark/test_spark_local.py::TestPySparkLocal::test_classifier_model_save_load
7.42s call     test_distributed/test_with_spark/test_spark_local.py::TestPySparkLocal::test_regressor_model_pipeline_save_load


def npstr_to_arrow_strarr(strarr: np.ndarray) -> Tuple[np.ndarray, str]:
"""Convert a numpy string array to an arrow string array."""
def npstr_to_arrow_strarr(strarr: Any) -> Tuple[np.ndarray, str]:
Copy link
Member

Choose a reason for hiding this comment

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

What's being changed here?

Copy link
Member Author

Choose a reason for hiding this comment

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

With my dependencies strarr was not an np.ndarray but rather an arrow data structure.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, you are using pandas 3.0 and have pyarrow in your environment.

import socket
import tempfile
from concurrent.futures import ThreadPoolExecutor
from contextlib import ExitStack
Copy link
Member

Choose a reason for hiding this comment

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

ExitStack and Generator imports are not used.


def npstr_to_arrow_strarr(strarr: np.ndarray) -> Tuple[np.ndarray, str]:
"""Convert a numpy string array to an arrow string array."""
def npstr_to_arrow_strarr(strarr: Any) -> Tuple[np.ndarray, str]:
Copy link
Member

Choose a reason for hiding this comment

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

Okay, you are using pandas 3.0 and have pyarrow in your environment.

@RAMitchell RAMitchell merged commit f816eae into dmlc:master Feb 5, 2026
78 checks passed
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