Skip to content

Commit b180d59

Browse files
Added new example of Python object exposing __sycl_usm_array_interface__ (#596)
Native Pybind11-generated extension implemented Python type bound to DMatrix C++ class which allocates USM memory using sycl::usm_allocator. The Python object implements __sycl_usm_array_interface__, which allows dpctl.memory.as_usm_memory to create a view into that native USM allocation. The example.py modifies that memory from Python, and uses object's own .tolist() method to retrieve the memory using C++, demonstrating that values changed. Also added license missing headers to .cpp and .hpp files from other examples
1 parent 18b56cf commit b180d59

File tree

9 files changed

+417
-0
lines changed

9 files changed

+417
-0
lines changed

examples/cython/sycl_buffer/use_sycl_buffer.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
//=- use_sycl_buffer.cpp - Example of SYCL code to be called from Cython =//
2+
//
3+
// Data Parallel Control (dpctl)
4+
//
5+
// Copyright 2020-2021 Intel Corporation
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an "AS IS" BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
//===----------------------------------------------------------------------===//
20+
///
21+
/// \file
22+
/// This file implements SYCL code to compute columnwise total of a matrix,
23+
/// provided as host C-contiguous allocation. SYCL kernels access this memory
24+
/// using `sycl::buffer`. Two routines are provided. One solves the task by
25+
/// calling BLAS function GEMV from Intel(R) Math Kernel Library, the other
26+
/// performs the computation using DPC++ reduction group function and atomics.
27+
///
28+
//===----------------------------------------------------------------------===//
29+
130
#include "use_sycl_buffer.h"
231
#include "dpctl_sycl_types.h"
332
#include <CL/sycl.hpp>

examples/cython/sycl_direct_linkage/sycl_function.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
//=- use_sycl_buffer.cpp - Example of SYCL code to be called from Cython =//
2+
//
3+
// Data Parallel Control (dpctl)
4+
//
5+
// Copyright 2020-2021 Intel Corporation
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an "AS IS" BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
//===----------------------------------------------------------------------===//
20+
///
21+
/// \file
22+
/// This file implements SYCL code to compute columnwise total of a matrix,
23+
/// provided as host C-contiguous allocation. SYCL kernels access this memory
24+
/// using `sycl::buffer`. The routine solves the task by calling BLAS function
25+
// GEMV from Intel(R) Math Kernel Library.
26+
///
27+
//===----------------------------------------------------------------------===//
28+
129
#include "sycl_function.hpp"
230
#include "mkl.h"
331
#include <CL/sycl.hpp>

examples/cython/usm_memory/sycl_blackscholes.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
//=- sycl_blackscholes.cpp - Example of SYCL code to be called from Cython =//
2+
//
3+
// Data Parallel Control (dpctl)
4+
//
5+
// Copyright 2020-2021 Intel Corporation
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an "AS IS" BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
//===----------------------------------------------------------------------===//
20+
///
21+
/// \file
22+
/// This file implements SYCL code to price European vanilla options using
23+
/// Black-Scholes formula, as well as code to generate option parameters using
24+
/// SYCL device random number generation library from Intel(R) Math Kernel
25+
/// Library.
26+
///
27+
//===----------------------------------------------------------------------===//
28+
129
#include "sycl_blackscholes.hpp"
230
#include "dpctl_sycl_types.h"
331
#include <CL/sycl.hpp>

examples/cython/usm_memory/sycl_blackscholes.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
//=- sycl_blackscholes.hpp - Example of SYCL code to be called from Cython =//
2+
//
3+
// Data Parallel Control (dpctl)
4+
//
5+
// Copyright 2020-2021 Intel Corporation
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an "AS IS" BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
//===----------------------------------------------------------------------===//
20+
///
21+
/// \file
22+
/// This file exports C++ functions to be called from Cython-generated
23+
/// extensions.
24+
///
25+
//===----------------------------------------------------------------------===//
26+
127
#include "dpctl_sycl_types.h"
228
#include <CL/sycl.hpp>
329

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Exposing USM allocations made by native code to dpctl
2+
3+
This extension demonstrates how a Python object backed by
4+
a native class, which allocates USM memory, can expose it
5+
to dpctl.memory entities using `__sycl_usm_array_interface__`.
6+
7+
8+
# Building extension
9+
10+
```
11+
source /opt/intel/oneapi/compiler/latest/env/vars.sh
12+
CXX=dpcpp CC=dpcpp python setup.py build_ext --inplace
13+
python example.py
14+
```
15+
16+
# Sample output
17+
18+
```
19+
(idp) [12:43:20 ansatnuc04 external_usm_allocation]$ python example.py
20+
<external_usm_alloc.DMatrix object at 0x7f2b98b4cef0>
21+
{'data': [94846745444352, True], 'shape': (5, 5), 'strides': None, 'version': 1, 'typestr': '|f8', 'syclobj': <capsule object "SyclQueueRef" at 0x7f2b9b941d80>}
22+
shared
23+
24+
[1.0, 1.0, 1.0, 2.0, 2.0]
25+
[1.0, 0.0, 1.0, 2.0, 2.0]
26+
[1.0, 1.0, 0.0, 2.0, 2.0]
27+
[0.0, 0.0, 0.0, 3.0, -1.0]
28+
[0.0, 0.0, 0.0, -1.0, 5.0]
29+
```
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//==- _usm_alloc_example.cpp - Example of Pybind11 extension exposing --===//
2+
// native USM allocation to Python in such a way that dpctl.memory
3+
// can form views into it.
4+
//
5+
// Data Parallel Control (dpctl)
6+
//
7+
// Copyright 2020-2021 Intel Corporation
8+
//
9+
// Licensed under the Apache License, Version 2.0 (the "License");
10+
// you may not use this file except in compliance with the License.
11+
// You may obtain a copy of the License at
12+
//
13+
// http://www.apache.org/licenses/LICENSE-2.0
14+
//
15+
// Unless required by applicable law or agreed to in writing, software
16+
// distributed under the License is distributed on an "AS IS" BASIS,
17+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
// See the License for the specific language governing permissions and
19+
// limitations under the License.
20+
//
21+
//===----------------------------------------------------------------------===//
22+
///
23+
/// \file
24+
/// This file implements Pybind11-generated extension that creates Python type
25+
/// backed-up by C++ class DMatrix, which creates a USM allocation associated
26+
/// with a given dpctl.SyclQueue. The Python object of this type implements
27+
/// __sycl_usm_array_interface__, allowing dpctl.memory.as_usm_memory to form
28+
/// a view into this allocation, and modify it from Python.
29+
///
30+
/// The DMatrix type object also implements `.tolist()` method which copies
31+
/// content of the object into list of lists of Python floats.
32+
///
33+
//===----------------------------------------------------------------------===//
34+
#include <CL/sycl.hpp>
35+
36+
// clang-format off
37+
#include "dpctl_sycl_types.h"
38+
#include "../_sycl_queue.h"
39+
#include "../_sycl_queue_api.h"
40+
// clang-format on
41+
42+
#include "pybind11/pybind11.h"
43+
#include "pybind11/stl.h"
44+
45+
namespace py = pybind11;
46+
47+
struct DMatrix
48+
{
49+
using alloc_t = sycl::usm_allocator<double, sycl::usm::alloc::shared>;
50+
using vec_t = std::vector<double, alloc_t>;
51+
52+
DMatrix(sycl::queue &q, size_t rows, size_t columns)
53+
: n_(rows), m_(columns), q_(q), alloc_(q), vec_(n_ * m_, alloc_)
54+
{
55+
}
56+
~DMatrix(){};
57+
DMatrix(const DMatrix &) = default;
58+
DMatrix(DMatrix &&) = default;
59+
60+
size_t get_n() const
61+
{
62+
return n_;
63+
}
64+
size_t get_m() const
65+
{
66+
return m_;
67+
}
68+
vec_t &get_vector()
69+
{
70+
return vec_;
71+
}
72+
sycl::queue get_queue() const
73+
{
74+
return q_;
75+
}
76+
77+
double get_element(size_t i, size_t j)
78+
{
79+
return vec_.at(i * m_ + j);
80+
}
81+
82+
private:
83+
size_t n_;
84+
size_t m_;
85+
sycl::queue q_;
86+
alloc_t alloc_;
87+
vec_t vec_;
88+
};
89+
90+
DMatrix create_matrix(py::object queue, size_t n, size_t m)
91+
{
92+
PyObject *queue_ptr = queue.ptr();
93+
if (PyObject_TypeCheck(queue_ptr, &PySyclQueueType)) {
94+
DPCTLSyclQueueRef QRef =
95+
get_queue_ref(reinterpret_cast<PySyclQueueObject *>(queue_ptr));
96+
sycl::queue *q = reinterpret_cast<sycl::queue *>(QRef);
97+
98+
return DMatrix(*q, n, m);
99+
}
100+
else {
101+
throw std::runtime_error("expected dpctl.SyclQueue as argument");
102+
}
103+
}
104+
105+
py::dict construct_sua_iface(DMatrix &m)
106+
{
107+
// need "version", "data", "shape", "typestr", "syclobj"
108+
py::tuple shape = py::make_tuple(m.get_n(), m.get_m());
109+
py::list data_entry(2);
110+
data_entry[0] = reinterpret_cast<size_t>(m.get_vector().data());
111+
data_entry[1] = true;
112+
auto syclobj = py::capsule(
113+
reinterpret_cast<void *>(new sycl::queue(m.get_queue())),
114+
"SyclQueueRef", [](PyObject *cap) {
115+
if (cap) {
116+
auto name = PyCapsule_GetName(cap);
117+
std::string name_s(name);
118+
if (name_s == "SyclQueueRef" or name_s == "used_SyclQueueRef") {
119+
void *p = PyCapsule_GetPointer(cap, name);
120+
delete reinterpret_cast<sycl::queue *>(p);
121+
}
122+
}
123+
});
124+
py::dict iface;
125+
iface["data"] = data_entry;
126+
iface["shape"] = shape;
127+
iface["strides"] = py::none();
128+
iface["version"] = 1;
129+
iface["typestr"] = "|f8";
130+
iface["syclobj"] = syclobj;
131+
132+
return iface;
133+
}
134+
135+
py::list tolist(DMatrix &m)
136+
{
137+
size_t rows_count = m.get_n();
138+
size_t cols_count = m.get_m();
139+
py::list rows(rows_count);
140+
for (size_t i = 0; i < rows_count; ++i) {
141+
py::list row_i(cols_count);
142+
for (size_t j = 0; j < cols_count; ++j) {
143+
row_i[j] = m.get_element(i, j);
144+
}
145+
rows[i] = row_i;
146+
}
147+
return rows;
148+
}
149+
150+
PYBIND11_MODULE(external_usm_alloc, m)
151+
{
152+
// Import the dpctl._sycl_queue extension
153+
import_dpctl___sycl_queue();
154+
155+
py::class_<DMatrix> dm(m, "DMatrix");
156+
dm.def(py::init(&create_matrix),
157+
"DMatrix(dpctl.SyclQueue, n_rows, n_cols)");
158+
dm.def_property("__sycl_usm_array_interface__", &construct_sua_iface,
159+
nullptr);
160+
dm.def("tolist", &tolist, "Return matrix a Python list of lists");
161+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Data Parallel Control (dpctl)
2+
#
3+
# Copyright 2020-2021 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 external_usm_alloc as eua
20+
import numpy as np
21+
22+
import dpctl
23+
import dpctl.memory as dpm
24+
25+
q = dpctl.SyclQueue("gpu")
26+
matr = eua.DMatrix(q, 5, 5)
27+
28+
print(matr)
29+
print(matr.__sycl_usm_array_interface__)
30+
31+
blob = dpm.as_usm_memory(matr)
32+
33+
print(blob.get_usm_type())
34+
35+
Xh = np.array(
36+
[
37+
[1, 1, 1, 2, 2],
38+
[1, 0, 1, 2, 2],
39+
[1, 1, 0, 2, 2],
40+
[0, 0, 0, 3, -1],
41+
[0, 0, 0, -1, 5],
42+
],
43+
dtype="d",
44+
)
45+
host_bytes_view = Xh.reshape((-1)).view(np.ubyte)
46+
47+
blob.copy_from_host(host_bytes_view)
48+
49+
print("")
50+
list_of_lists = matr.tolist()
51+
for row in list_of_lists:
52+
print(row)

0 commit comments

Comments
 (0)