Skip to content

Commit a309aa2

Browse files
Added an example to exercise pybind11 bindings for dpctl.program.SyclKernel
1 parent 375fa77 commit a309aa2

File tree

10 files changed

+365
-0
lines changed

10 files changed

+365
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
cmake_minimum_required(VERSION 3.21)
2+
3+
project(use_queue_device LANGUAGES CXX)
4+
5+
set(DPCTL_CMAKE_MODULES_PATH "${CMAKE_SOURCE_DIR}/../../../cmake")
6+
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${DPCTL_CMAKE_MODULES_PATH})
7+
find_package(IntelDPCPP REQUIRED PATHS ${DPCTL_CMAKE_MODULES_PATH} NO_DEFAULT_PATH)
8+
9+
set(CMAKE_CXX_STANDARD 17)
10+
set(CMAKE_CXX_STANDARD_REQUIRED True)
11+
set(CMAKE_BUILD_TYPE Debug)
12+
13+
# Fetch pybind11
14+
include(FetchContent)
15+
FetchContent_Declare(
16+
pybind11
17+
URL https://github.com/pybind/pybind11/archive/refs/tags/v2.10.0.tar.gz
18+
URL_HASH SHA256=eacf582fa8f696227988d08cfc46121770823839fe9e301a20fbce67e7cd70ec
19+
)
20+
FetchContent_MakeAvailable(pybind11)
21+
22+
find_package(PythonExtensions REQUIRED)
23+
find_package(Dpctl REQUIRED)
24+
find_package(NumPy REQUIRED)
25+
26+
set(py_module_name _use_kernel)
27+
pybind11_add_module(${py_module_name}
28+
MODULE
29+
use_kernel/_example.cpp
30+
)
31+
target_include_directories(${py_module_name} PUBLIC ${Dpctl_INCLUDE_DIRS})
32+
install(TARGETS ${py_module_name}
33+
DESTINATION use_kernel
34+
)
35+
36+
set(ignoreMe "${SKBUILD}")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Usage of dpctl Entities in Pybind11
2+
3+
## Description
4+
5+
This extension demonstrates how you can use dpctl Python types,
6+
such as ``dpctl.SyclQueue``, in Pybind11
7+
extensions.
8+
9+
10+
## Building
11+
12+
To build the extension, run:
13+
```
14+
source /opt/intel/oneapi/compiler/latest/env/vars.sh
15+
CXX=icpx python setup.py build_ext --inplace
16+
python -m pytest tests
17+
python example.py
18+
```
19+
20+
# Sample output
21+
22+
```
23+
(idp) [17:25:27 ansatnuc04 use_dpctl_syclqueue]$ python example.py
24+
EU count returned by Pybind11 extension 24
25+
EU count computed by dpctl 24
26+
27+
Computing modular reduction using SYCL on a NumPy array
28+
Offloaded result agrees with reference one computed by NumPy
29+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Data Parallel Control (dpctl)
2+
#
3+
# Copyright 2020-2022 Intel Corporation
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# coding: utf-8
18+
19+
import use_kernel as eg
20+
21+
import dpctl
22+
import dpctl.program as dppr
23+
import dpctl.tensor as dpt
24+
25+
q = dpctl.SyclQueue()
26+
27+
with open("resource/double_it.spv", "br") as fh:
28+
il = fh.read()
29+
30+
pr = dppr.create_program_from_spirv(q, il, "")
31+
assert pr.has_sycl_kernel("double_it")
32+
33+
krn = pr.get_sycl_kernel("double_it")
34+
assert krn.num_args == 2
35+
36+
x = dpt.arange(0, stop=13, step=1, dtype="i4", sycl_queue=q)
37+
y = dpt.empty_like(x)
38+
39+
eg.submit_custom_kernel(q, krn, x, y)
40+
41+
print(dpt.asnumpy(y))
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Rebuilding SPIR-V file from source
2+
3+
```bash
4+
export TOOLS_DIR=$(dirname $(dirname $(which icx)))/bin-llvm
5+
$TOOLS_DIR/clang -cc1 -triple spir double_it.cl -finclude-default-header -flto -emit -llvm-bc -o double_it.bc
6+
$TOOLS_DIR/llvm-spirv double_it.bc -o double_it.spv
7+
rm double_it.bc
8+
```
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Data Parallel Control (dpctl)
3+
4+
Copyright 2020-2022 Intel Corporation
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
===----------------------------------------------------------------------===
19+
20+
\file
21+
This file implements a sample OpenCL kernel to use in this example.
22+
23+
===----------------------------------------------------------------------===
24+
*/
25+
26+
__kernel void double_it(__global int *x, __global int *y) {
27+
uint idx = get_global_id(0);
28+
29+
y[idx] = 2 * x[idx];
30+
}
Binary file not shown.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Data Parallel Control (dpctl)
2+
#
3+
# Copyright 2020-2022 Intel Corporation
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from skbuild import setup
18+
19+
setup(
20+
name="use_kernel",
21+
version="0.0.1",
22+
description="an example of SYCL-powered Python package (with pybind11)",
23+
author="Intel Scripting",
24+
license="Apache 2.0",
25+
packages=["use_kernel"],
26+
)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Data Parallel Control (dpctl)
2+
#
3+
# Copyright 2020-2022 Intel Corporation
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# coding: utf-8
18+
19+
import os.path
20+
21+
import numpy as np
22+
import pytest
23+
import use_kernel as uk
24+
25+
import dpctl
26+
import dpctl.program as dpm
27+
import dpctl.tensor as dpt
28+
29+
30+
def _get_spv_path():
31+
uk_dir = os.path.dirname(os.path.abspath(uk.__file__))
32+
proj_dir = os.path.dirname(uk_dir)
33+
return os.path.join(proj_dir, "resource", "double_it.spv")
34+
35+
36+
def test_spv_file_exists():
37+
assert os.path.exists(_get_spv_path())
38+
39+
40+
def test_kernel_can_be_found():
41+
fn = _get_spv_path()
42+
with open(fn, "br") as f:
43+
il = f.read()
44+
try:
45+
q = dpctl.SyclQueue()
46+
except dpctl.SyclQueueCreationError:
47+
pytest.skip("Could not create default queue")
48+
pr = dpm.create_program_from_spirv(q, il, "")
49+
assert pr.has_sycl_kernel("double_it")
50+
51+
52+
def test_kernel_submit_through_extension():
53+
fn = _get_spv_path()
54+
with open(fn, "br") as f:
55+
il = f.read()
56+
try:
57+
q = dpctl.SyclQueue()
58+
except dpctl.SyclQueueCreationError:
59+
pytest.skip("Could not create default queue")
60+
pr = dpm.create_program_from_spirv(q, il, "")
61+
krn = pr.get_sycl_kernel("double_it")
62+
assert krn.num_args == 2
63+
64+
x = dpt.arange(0, stop=13, step=1, dtype="i4", sycl_queue=q)
65+
y = dpt.zeros_like(x)
66+
67+
uk.submit_custom_kernel(q, krn, x, y, [])
68+
69+
assert np.array_equal(dpt.asnumpy(y), np.arange(0, 26, step=2, dtype="i4"))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Data Parallel Control (dpctl)
2+
#
3+
# Copyright 2020-2022 Intel Corporation
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# coding: utf-8
18+
19+
from ._use_kernel import submit_custom_kernel
20+
21+
__all__ = [
22+
"submit_custom_kernel",
23+
]
24+
25+
__doc__ = """
26+
Example pybind11 extension demonstrating binding of dpctl entities to
27+
SYCL entities.
28+
29+
dpctl provides type casters that bind ``sycl::kernel`` to
30+
`dpctl.program.SyclKernel`, ``sycl::device`` to `dpctl.SyclDevice`, etc.
31+
32+
Use of these type casters simplifies writing of Python extensions and compile
33+
then using SYCL C++ compilers, such as Intel(R) oneAPI DPC++ compiler.
34+
"""
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//==- _example.cpp - Example of Pybind11 extension working with =---------===//
2+
// dpctl Python objects.
3+
//
4+
// Data Parallel Control (dpctl)
5+
//
6+
// Copyright 2020-2022 Intel Corporation
7+
//
8+
// Licensed under the Apache License, Version 2.0 (the "License");
9+
// you may not use this file except in compliance with the License.
10+
// You may obtain a copy of the License at
11+
//
12+
// http://www.apache.org/licenses/LICENSE-2.0
13+
//
14+
// Unless required by applicable law or agreed to in writing, software
15+
// distributed under the License is distributed on an "AS IS" BASIS,
16+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
// See the License for the specific language governing permissions and
18+
// limitations under the License.
19+
//
20+
//===----------------------------------------------------------------------===//
21+
///
22+
/// \file
23+
/// This file implements Pybind11-generated extension exposing functions that
24+
/// take dpctl Python objects, such as dpctl.SyclQueue, dpctl.SyclDevice as
25+
/// arguments.
26+
///
27+
//===----------------------------------------------------------------------===//
28+
29+
#include "dpctl4pybind11.hpp"
30+
#include <CL/sycl.hpp>
31+
#include <cstdint>
32+
#include <pybind11/pybind11.h>
33+
#include <pybind11/stl.h>
34+
#include <vector>
35+
36+
namespace py = pybind11;
37+
38+
void submit_custom_kernel(sycl::queue q,
39+
sycl::kernel krn,
40+
dpctl::tensor::usm_ndarray x,
41+
dpctl::tensor::usm_ndarray y,
42+
const std::vector<sycl::event> &depends = {})
43+
{
44+
if (x.get_ndim() != 1 || !x.is_c_contiguous() || y.get_ndim() != 1 ||
45+
!y.is_c_contiguous())
46+
{
47+
throw py::value_error(
48+
"src and dst arguments must be 1D and contiguous.");
49+
}
50+
51+
auto const &api = dpctl::detail::dpctl_capi::get();
52+
if (x.get_typenum() != api.UAR_INT32_ || y.get_typenum() != api.UAR_INT32_)
53+
{
54+
throw py::value_error(
55+
"src and dst arguments must have int32 element data types.");
56+
}
57+
58+
size_t n_x = x.get_size();
59+
size_t n_y = y.get_size();
60+
61+
if (n_x != n_y) {
62+
throw py::value_error("src and dst arguments must have equal size.");
63+
}
64+
65+
if (!dpctl::utils::queues_are_compatible(q, {x.get_queue(), y.get_queue()}))
66+
{
67+
throw std::runtime_error(
68+
"Execution queue is not compatible with allocation queues");
69+
}
70+
71+
void *x_data = x.get_data<void>();
72+
void *y_data = y.get_data<void>();
73+
74+
sycl::event e = q.submit([&](sycl::handler &cgh) {
75+
cgh.depends_on(depends);
76+
cgh.set_arg(0, x_data);
77+
cgh.set_arg(1, y_data);
78+
cgh.parallel_for(sycl::range<1>(n_x), krn);
79+
});
80+
81+
e.wait();
82+
83+
return;
84+
}
85+
86+
PYBIND11_MODULE(_use_kernel, m)
87+
{
88+
m.def("submit_custom_kernel", &submit_custom_kernel,
89+
"Submit given kernel with arguments (int *, int *) to queue",
90+
py::arg("queue"), py::arg("kernel"), py::arg("src"), py::arg("dst"),
91+
py::arg("depends") = py::list());
92+
}

0 commit comments

Comments
 (0)