-
Notifications
You must be signed in to change notification settings - Fork 41
Add ASV benchmark suite for dpctl #2378
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
Open
vchamarthi
wants to merge
3
commits into
IntelPython:master
Choose a base branch
from
vchamarthi:asv-benchmarks
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # dpctl ASV Benchmarks | ||
|
|
||
| Runtime-overhead benchmarks for [dpctl](https://github.com/IntelPython/dpctl) | ||
| using [ASV](https://asv.readthedocs.io/en/stable/): object construction, | ||
| queue caching, USM allocation, device enumeration, kernel bundle | ||
| compilation, data movement, kernel submission. No compute throughput. | ||
|
|
||
| ## Coverage | ||
|
|
||
| | File | API | | ||
| |------|-----| | ||
| | `bench_construct.py` | `SyclDevice`/`SyclContext`/`SyclQueue`/`SyclPlatform` construction, device/queue attribute reads | | ||
| | `bench_queue_cache.py` | `get_device_cached_queue` for each key kind, vs. an uncached baseline | | ||
| | `bench_usm.py` | `MemoryUSM{Device,Host,Shared}` alloc/free, aligned alloc, first touch, USM pointer queries | | ||
| | `bench_enumerate.py` | `get_devices`, `get_num_devices`, `get_platforms`, `select_*_device`, `has_*_devices`, `select_device_with_aspects` | | ||
| | `bench_compile.py` | Kernel bundles from SPIR-V / OpenCL C source / SYCL source, kernel lookup, availability probes | | ||
| | `bench_copy.py` | `SyclQueue.memcpy`/`memcpy_async`/`fill`/`memset`, `_Memory.copy_to_host`/`copy_from_host`/`copy_from_device` | | ||
| | `bench_submit.py` | `submit`, `submit_async`, batched submission, `submit_barrier`, idle `wait` | | ||
|
|
||
| ## Device axis | ||
|
|
||
| Benchmarks parameterize over the `cpu`/`gpu` filter selectors and skip when a | ||
| selector has no matching device, so the same benchmark names run on any | ||
| node. Sizes over 25% of a device's `global_mem_size` skip the same way. | ||
|
|
||
| ## Compilation caching | ||
|
|
||
| `benchmarks/__init__.py` disables the persistent JIT cache | ||
| (`SYCL_CACHE_PERSISTENT=0`). `time_bundle_from_source_cold` mints a unique | ||
| kernel name per call to defeat the in-memory cache too; `_warm` reuses one | ||
| name to measure the cache-hit path. | ||
|
|
||
| `create_kernel_bundle_from_source` runs on the OpenCL backend only. | ||
| `create_kernel_bundle_from_sycl_source` needs a device where | ||
| `can_compile("sycl")` is true. | ||
|
|
||
| ## Running | ||
|
|
||
| ```bash | ||
| pip install ".[benchmark]" | ||
| cd benchmarks && asv machine --yes && asv run --python=same --quick HEAD^! | ||
| ``` | ||
|
|
||
| One module: `asv run --python=same --quick --bench bench_compile HEAD^!` | ||
|
|
||
| Compare commits: `asv continuous --python=same HEAD~1 HEAD` | ||
|
|
||
| View results: `asv publish && asv preview` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| { | ||
| "version": 1, | ||
| "project": "dpctl", | ||
| "project_url": "https://github.com/IntelPython/dpctl", | ||
| "show_commit_url": "https://github.com/IntelPython/dpctl/commit/", | ||
| "repo": "..", | ||
| "branches": [ | ||
| "master", | ||
| "dev-milestone" | ||
| ], | ||
| "environment_type": "conda", | ||
| "conda_channels": [ | ||
| "https://software.repos.intel.com/python/conda/", | ||
| "conda-forge" | ||
| ], | ||
| "benchmark_dir": "benchmarks", | ||
| "env_dir": ".asv/env", | ||
| "results_dir": ".asv/results", | ||
| "html_dir": ".asv/html", | ||
| "build_cache_size": 2, | ||
| "default_benchmark_timeout": 900, | ||
| "regressions_thresholds": { | ||
| ".*": 0.2 | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Data Parallel Control (dpctl) | ||
| # | ||
| # Copyright 2026 Intel Corporation | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """ASV benchmarks for dpctl.""" | ||
|
|
||
| import os | ||
|
|
||
| # Disable the persistent JIT cache before dpctl is imported, so cold-compile | ||
| # benchmarks in bench_compile.py measure a real compile, not a cache hit. | ||
| os.environ.setdefault("SYCL_CACHE_PERSISTENT", "0") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # Data Parallel Control (dpctl) | ||
| # | ||
| # Copyright 2026 Intel Corporation | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """Shared utilities for dpctl benchmarks. | ||
|
|
||
| Every benchmark that needs a device goes through :func:`queue_for` or | ||
| :func:`device_for` so that benchmark names stay identical on every node in | ||
| the pool: a node without a given device reports the parameter as skipped | ||
| rather than failing the whole suite. | ||
| """ | ||
|
|
||
| import os | ||
|
|
||
| from asv_runner.benchmarks.mark import SkipNotImplemented | ||
|
|
||
| import dpctl | ||
|
|
||
| # Device axis. Fully-qualified filter-selector strings, so a node exposing | ||
| # a device through more than one backend still resolves each benchmark to a | ||
| # fixed, unambiguous device. | ||
| _SELECTORS = ["opencl:cpu", "level_zero:gpu"] | ||
|
|
||
| # Allocation and transfer size sweep, in bytes: 4 KiB, 1 MiB, 16 MiB, 256 MiB. | ||
| _SIZES = [4 * 1024, 1024**2, 16 * 1024**2, 256 * 1024**2] | ||
|
|
||
| # USM kinds, mapped to their constructors. | ||
| _USM_TYPES = ["device", "host", "shared"] | ||
|
|
||
| # Fraction of device global memory a single benchmark allocation may claim. | ||
| _MEM_BUDGET = 0.25 | ||
|
|
||
| _queues = {} | ||
| _devices = {} | ||
| _spirv = None | ||
|
|
||
|
|
||
| def queue_for(selector): | ||
| """Return a memoized queue for *selector*, or skip when unavailable.""" | ||
| if selector not in _queues: | ||
| try: | ||
| _queues[selector] = dpctl.SyclQueue(selector) | ||
| except dpctl.SyclQueueCreationError: | ||
| _queues[selector] = None | ||
| q = _queues[selector] | ||
| if q is None: | ||
| raise SkipNotImplemented(f"no {selector} device available") | ||
| return q | ||
|
|
||
|
|
||
| def device_for(selector): | ||
| """Return a memoized device for *selector*, or skip when unavailable.""" | ||
| if selector not in _devices: | ||
| try: | ||
| _devices[selector] = dpctl.SyclDevice(selector) | ||
| except dpctl.SyclDeviceCreationError: | ||
| _devices[selector] = None | ||
| d = _devices[selector] | ||
| if d is None: | ||
| raise SkipNotImplemented(f"no {selector} device available") | ||
| return d | ||
|
|
||
|
|
||
| def usm_class(usm_type): | ||
| """Return the dpctl.memory class allocating *usm_type* memory.""" | ||
| import dpctl.memory as dpm | ||
|
|
||
| return { | ||
| "device": dpm.MemoryUSMDevice, | ||
| "host": dpm.MemoryUSMHost, | ||
| "shared": dpm.MemoryUSMShared, | ||
| }[usm_type] | ||
|
|
||
|
|
||
| def skip_unless_fits(queue, nbytes): | ||
| """Skip when *nbytes* exceeds this device's allocation budget.""" | ||
| budget = _MEM_BUDGET * queue.sycl_device.global_mem_size | ||
| if nbytes > budget: | ||
| raise SkipNotImplemented( | ||
| f"{nbytes} bytes exceeds the device memory budget" | ||
| ) | ||
|
|
||
|
|
||
| def opencl_queue_or_skip(): | ||
| """Return a memoized OpenCL queue, or skip. | ||
|
|
||
| ``create_kernel_bundle_from_source`` only supports the OpenCL backend. | ||
| """ | ||
| return queue_for("opencl") | ||
|
|
||
|
|
||
| def sycl_source_queue_or_skip(selector): | ||
| """Return a queue whose device can compile SYCL source, or skip.""" | ||
| try: | ||
| import dpctl.compiler as dpc | ||
| except ImportError: | ||
| raise SkipNotImplemented("dpctl.compiler is not available") | ||
| q = queue_for(selector) | ||
| if not dpc.is_sycl_source_compilation_available(): | ||
| raise SkipNotImplemented("SYCL source compilation extension absent") | ||
| if not q.sycl_device.can_compile("sycl"): | ||
| raise SkipNotImplemented("device cannot compile SYCL source") | ||
| return q | ||
|
|
||
|
|
||
| def spirv_bytes(): | ||
| """Return the SPIR-V module shipped with the installed dpctl, or skip. | ||
|
|
||
| Defines ``add(int*, int*, int*)`` and ``axpy(int*, int*, int*, int)``. | ||
| """ | ||
| global _spirv | ||
| if _spirv is None: | ||
| path = os.path.join( | ||
| os.path.dirname(os.path.abspath(dpctl.__file__)), | ||
| "tests", | ||
| "input_files", | ||
| "multi_kernel.spv", | ||
| ) | ||
| if not os.path.exists(path): | ||
| raise SkipNotImplemented(f"SPIR-V module not found at {path}") | ||
| with open(path, "rb") as fh: | ||
| _spirv = fh.read() | ||
| return _spirv | ||
|
|
||
|
|
||
| def ocl_axpy_source(kernel_name="axpy"): | ||
| """Return OpenCL C source for an axpy kernel called *kernel_name*.""" | ||
| return ( | ||
| f"kernel void {kernel_name}(" | ||
| " global int *a, global int *b, global int *c, int d) {" | ||
| " size_t index = get_global_id(0);" | ||
| " c[index] = d * a[index] + b[index];" | ||
| "}" | ||
| ) | ||
|
|
||
|
|
||
| def sycl_axpy_source(kernel_name="axpy"): | ||
| """Return SYCL source for an axpy kernel called *kernel_name*.""" | ||
| return f""" | ||
| #include <sycl/sycl.hpp> | ||
|
|
||
| namespace syclext = sycl::ext::oneapi::experimental; | ||
|
|
||
| extern "C" SYCL_EXTERNAL | ||
| SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclext::nd_range_kernel<1>)) | ||
| void {kernel_name}(int* a, int* b, int* c, int d) {{ | ||
| sycl::nd_item<1> item = | ||
| sycl::ext::oneapi::this_work_item::get_nd_item<1>(); | ||
| size_t i = item.get_global_linear_id(); | ||
| c[i] = d * a[i] + b[i]; | ||
| }} | ||
| """ | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all of these comments can be cleaned up to be less verbose/unnecessarily specific (i.e., we don't need the phrase 'USM kinds, mapped to their constructors', we can just say 'USM kinds')