Skip to content

Commit f903e7f

Browse files
authored
Update sklearnex gpu to use array_api_dispatch and dpnp (#218)
* Update sklearnex gpu to use array_api_dispatch and dpnp * ensure GPU ops on same queue * address comments * black
1 parent 22e3c83 commit f903e7f

4 files changed

Lines changed: 34 additions & 10 deletions

File tree

configs/BENCH-CONFIG-SPEC.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Configs have the three highest parameter keys:
9898
| `data`:`distributed_split` | None | None, `rank_based` | Split type used to distribute data between machines in distributed algorithm. `None` type means usage of all data without split on all machines. `rank_based` type splits the data equally between machines with split sequence based on rank id from MPI. |
9999
|<h3>Algorithm parameters</h3>||||
100100
| `algorithm`:`library` | None | | Python module containing measured entity (class or function). |
101-
| `algorithm`:`device` | `default` | `default`, `cpu`, `gpu` | Device selected for computation. |
101+
| `algorithm`:`device` | `default` | `default`, `cpu`, `gpu` | Device selected for computation. `sklearnex`+`gpu` cases enable sklearn's `array_api_dispatch` and use `dpnp` data by default (see `sklearn_context` below). |
102102

103103
## Benchmark-Specific Parameters
104104

@@ -109,7 +109,7 @@ Configs have the three highest parameter keys:
109109
| `algorithm`:`estimator` | None | | Name of measured estimator. |
110110
| `algorithm`:`estimator_params` | Empty `dict` | | Parameters for estimator constructor. |
111111
| `algorithm`:`batch_size`:`{stage}` | None | Any positive integer | Enables online mode for `{stage}` methods of estimator (sequential calls for each batch). |
112-
| `algorithm`:`sklearn_context` | None | | Parameters for sklearn `config_context` used over estimator. |
112+
| `algorithm`:`sklearn_context` | None | | Parameters for sklearn `config_context` used over estimator. `array_api_dispatch` requires `SCIPY_ARRAY_API=1`, which scikit-learn_bench sets by default if it is unset in the environment. |
113113
| `algorithm`:`sklearnex_context` | None | | Parameters for sklearnex `config_context` used over estimator. Updated by `sklearn_context` if set. |
114114
| `bench`:`ensure_sklearnex_patching` | True | | If True, warns about sklearnex patching failures. |
115115

configs/common/sklearn.json

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@
66
{ "library": "sklearnex", "device": "cpu" }
77
]
88
},
9-
"sklearn-ex[cpu,gpu] implementations": {
10-
"algorithm": [
11-
{ "library": "sklearn", "device": "cpu" },
12-
{ "library": "sklearnex", "device": ["cpu", "gpu"] }
13-
]
14-
},
9+
"sklearn-ex[cpu,gpu] implementations": [
10+
{ "algorithm": { "library": "sklearn", "device": "cpu" } },
11+
{ "algorithm": { "library": "sklearnex", "device": "cpu" } },
12+
{
13+
"algorithm": {
14+
"library": "sklearnex",
15+
"device": "gpu",
16+
"sklearn_context": { "array_api_dispatch": true }
17+
},
18+
"data": { "format": "dpnp", "order": "C" }
19+
}
20+
],
1521
"sklearnex spmd implementation": {
1622
"algorithm": {
1723
"library": "sklearnex.spmd",

sklbench/benchmarks/sklearn_estimator.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
from importlib.metadata import PackageNotFoundError, version
2323
from typing import Dict, List, Union
2424

25+
# sklbench uses array API by default; must precede scipy import to take effect
26+
os.environ.setdefault("SCIPY_ARRAY_API", "1")
27+
2528
import numpy as np
2629
import pandas as pd
2730
from sklearn.base import BaseEstimator

sklbench/datasets/transformer.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
from ..utils.logger import logger
2727

2828

29-
def convert_data(data, dformat: str, order: str, dtype: str, device: str = None):
29+
def convert_data(
30+
data, dformat: str, order: str, dtype: str, device: str = None, sycl_queue=None
31+
):
3032
if isinstance(data, csr_matrix) and dformat != "csr_matrix":
3133
data = data.toarray()
3234
if dtype == "preserve":
@@ -46,6 +48,11 @@ def convert_data(data, dformat: str, order: str, dtype: str, device: str = None)
4648
elif dformat == "dpnp":
4749
import dpnp
4850

51+
# Pin every subset to one shared queue: sklearnex builds its internal
52+
# arrays (e.g. the take() indices in KNN predict) on the device's default
53+
# queue, and array_api_dispatch requires all arrays share one queue object.
54+
if sycl_queue is not None:
55+
return dpnp.asarray(data, dtype=dtype, order=order, sycl_queue=sycl_queue)
4956
return dpnp.array(data, dtype=dtype, order=order, device=device)
5057
elif dformat == "dpctl":
5158
warnings.warn(
@@ -143,6 +150,14 @@ def split_and_transform_data(bench_case, data, data_description):
143150

144151
device = get_bench_case_value(bench_case, "algorithm:device", None)
145152
common_data_format = get_bench_case_value(bench_case, "data:format", "pandas")
153+
154+
# Resolve one queue for the device up front so all dpnp subsets share it;
155+
# dpnp.array(device=...) per subset can otherwise land on distinct queues.
156+
sycl_queue = None
157+
if common_data_format == "dpnp" and device is not None:
158+
import dpnp
159+
160+
sycl_queue = dpnp.array([], device=device).sycl_queue
146161
common_data_order = get_bench_case_value(bench_case, "data:order", "F")
147162
common_data_dtype = get_bench_case_value(bench_case, "data:dtype", "float32")
148163

@@ -177,7 +192,7 @@ def split_and_transform_data(bench_case, data, data_description):
177192
data_dtype = required_label_dtype
178193

179194
converted_data = convert_data(
180-
subset_content, data_format, data_order, data_dtype, device
195+
subset_content, data_format, data_order, data_dtype, device, sycl_queue
181196
)
182197
data_dict[subset_name] = converted_data
183198
if not is_label:

0 commit comments

Comments
 (0)