• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IntelPython / dpctl / 14754782295

30 Apr 2025 12:39PM UTC coverage: 86.419%. Remained the same
14754782295

Pull #2068

github

web-flow
Merge c8700ceb2 into b7a6b67c7
Pull Request #2068: Correct a path to `cl.cfg` file

3020 of 3716 branches covered (81.27%)

Branch coverage included in aggregate %.

12195 of 13890 relevant lines covered (87.8%)

6998.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.43
/libsyclinterface/source/dpctl_sycl_device_manager.cpp
1
//===-------- dpctl_sycl_device_manager.cpp - helpers for sycl devices ------=//
2
//
3
//                      Data Parallel Control (dpctl)
4
//
5
// Copyright 2020-2025 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 the functions declared in dpctl_sycl_device_manager.h.
23
///
24
//===----------------------------------------------------------------------===//
25

26
#include "dpctl_sycl_device_manager.h"
27
#include "dpctl_error_handlers.h"
28
#include "dpctl_string_utils.hpp"
29
#include "dpctl_sycl_enum_types.h"
30
#include "dpctl_sycl_type_casters.hpp"
31
#include "dpctl_utils_helper.h"
32
#include <Config/dpctl_config.h> /* Config */
33
#include <iomanip>
34
#include <iostream>
35
#include <sstream>
36
#include <stddef.h>
37
#include <sycl/sycl.hpp> /* SYCL headers   */
38
#include <unordered_map>
39
#include <utility>
40
#include <vector>
41

42
using namespace sycl;
43

44
namespace
45
{
46

47
static_assert(__SYCL_COMPILER_VERSION >= __SYCL_COMPILER_VERSION_REQUIRED,
48
              "The compiler does not meet minimum version requirement");
49

50
using namespace dpctl::syclinterface;
51

52
/*
53
 * Helper function to print the metadata for a sycl::device.
54
 */
55
std::string get_device_info_str(const device &Device)
56
{
22✔
57
    std::stringstream ss;
22✔
58
    static constexpr const char *_endl = "\n";
22✔
59

60
    ss << std::setw(4) << " " << std::left << std::setw(16) << "Name"
22✔
61
       << Device.get_info<info::device::name>() << _endl << std::setw(4) << " "
22✔
62
       << std::left << std::setw(16) << "Driver version"
22✔
63
       << Device.get_info<info::device::driver_version>() << _endl
22✔
64
       << std::setw(4) << " " << std::left << std::setw(16) << "Vendor"
22✔
65
       << Device.get_info<info::device::vendor>() << _endl << std::setw(4)
22✔
66
       << " " << std::left << std::setw(16) << "Filter string"
22✔
67
       << DPCTL_GetDeviceFilterString(Device) << _endl;
22✔
68

69
    return ss.str();
22✔
70
}
22✔
71

72
/*!
73
 * @brief Canonicalizes a device identifier bit flag to have a valid (i.e., not
74
 * UNKNOWN) backend and device type bits.
75
 *
76
 * The device id is bit flag that indicates the backend and device type, both
77
 * of which are optional, that are to be queried. The function makes sure if a
78
 * device identifier only provides a device type value the backend is set to
79
 * DPCTL_ALL_BACKENDS. Similarly, if only backend is provided the device type
80
 * is set to DPCTL_ALL.
81
 *
82
 * @param    device_id     A bit flag storing a backend and a device type value.
83
 * @return   Canonicalized bit flag that makes sure neither backend nor device
84
 * type is UNKNOWN (0). For cases where the input device id does not provide
85
 * either one of the values, we set the value to ALL.
86
 */
87
int to_canonical_device_id(int device_id)
88
{ // If the identifier is 0 (UNKNOWN_DEVICE) return 0.
1,657✔
89
    if (!device_id)
1,657!
90
        return 0;
×
91

92
    // Check if the device identifier has a backend specified. If not, then
93
    // toggle all backend specifier bits, i.e. set the backend to
94
    // DPCTL_ALL_BACKENDS.
95
    if (!(device_id & DPCTL_ALL_BACKENDS))
1,657✔
96
        device_id |= DPCTL_ALL_BACKENDS;
5✔
97

98
    // Check if a device type was specified. If not, set device type to ALL.
99
    if (!(device_id & ~DPCTL_ALL_BACKENDS))
1,657✔
100
        device_id |= DPCTL_ALL;
10✔
101

102
    return device_id;
1,657✔
103
}
1,657✔
104

105
struct DeviceCacheBuilder
106
{
107
    using DeviceCache = std::unordered_map<device, context>;
108
    /* This function implements a workaround to the current lack of a
109
     * default context per root device in DPC++. The map stores a "default"
110
     * context for each root device, and the QMgrHelper uses the map
111
     * whenever it creates a new queue for a root device. By doing so, we
112
     * avoid the performance overhead of context creation for every queue.
113
     *
114
     * The singleton pattern implemented here ensures that the map is
115
     * created once in a thread-safe manner. Since, the map is only read
116
     * post-creation we do not need any further protection to ensure
117
     * thread-safety.
118
     */
119
    static const DeviceCache &getDeviceCache()
120
    {
40,092✔
121
        static DeviceCache *cache = new DeviceCache([] {
40,092✔
122
            DeviceCache cache_l{};
2✔
123
            dpctl_default_selector mRanker;
2✔
124
            std::vector<platform> Platforms{};
2✔
125
            try {
2✔
126
                Platforms = platform::get_platforms();
2✔
127
            } catch (std::exception const &e) {
2✔
128
                error_handler(e, __FILE__, __func__, __LINE__);
×
129
                return cache_l;
×
130
            }
×
131
            for (const auto &P : Platforms) {
2✔
132
                auto Devices = P.get_devices();
2✔
133
                for (const auto &D : Devices) {
2✔
134
                    if (mRanker(D) < 0)
2!
135
                        continue;
×
136

137
                    try {
2✔
138
                        // Per https://github.com/intel/llvm/blob/sycl/sycl/doc/
139
                        // extensions/supported/sycl_ext_oneapi_default_context.asciidoc
140
                        // sycl::queue(D) would create default platform context
141
                        // for capable compiler, sycl::context(D) otherwise
142
                        auto Q = queue(D);
2✔
143
                        auto Ctx = Q.get_context();
2✔
144
                        cache_l.emplace(D, Ctx);
2✔
145
                    } catch (const std::exception &e) {
2✔
146
                        // Nothing is added to the cache_l by guarantees of
147
                        // emplace
148
                        error_handler(e, __FILE__, __func__, __LINE__);
×
149
                    }
×
150
                }
2✔
151
            }
2✔
152
            return cache_l;
2✔
153
        }());
2✔
154

155
        return *cache;
40,092✔
156
    }
40,092✔
157
};
158

159
} // namespace
160

161
#undef EL
162
#undef EL_SYCL_TYPE
163
#define EL Device
164
#define EL_SYCL_TYPE sycl::device
905✔
165
#include "dpctl_vector_templ.cpp"
166
#undef EL
167
#undef EL_SYCL_TYPE
168

169
DPCTLSyclContextRef
170
DPCTLDeviceMgr_GetCachedContext(__dpctl_keep const DPCTLSyclDeviceRef DRef)
171
{
40,107✔
172
    DPCTLSyclContextRef CRef = nullptr;
40,107✔
173

174
    auto Device = unwrap<device>(DRef);
40,107✔
175
    if (!Device) {
40,107✔
176
        error_handler("Cannot create device from DPCTLSyclDeviceRef"
31✔
177
                      "as input is a nullptr.",
31✔
178
                      __FILE__, __func__, __LINE__);
31✔
179
        return CRef;
31✔
180
    }
31✔
181

182
    using CacheT = typename DeviceCacheBuilder::DeviceCache;
40,076✔
183
    CacheT const &cache = DeviceCacheBuilder::getDeviceCache();
40,076✔
184

185
    if (cache.empty()) {
40,076!
186
        // an exception was caught and logged by getDeviceCache
187
        return nullptr;
×
188
    }
×
189

190
    const auto &entry = cache.find(*Device);
40,076✔
191
    if (entry != cache.end()) {
40,076✔
192
        context *ContextPtr = nullptr;
40,072✔
193
        try {
40,072✔
194
            ContextPtr = new context(entry->second);
40,072✔
195
            CRef = wrap<context>(ContextPtr);
40,072✔
196
        } catch (std::exception const &e) {
40,072✔
197
            error_handler(e, __FILE__, __func__, __LINE__);
×
198
            delete ContextPtr;
×
199
            CRef = nullptr;
×
200
        }
×
201
    }
40,072✔
202
    else {
4✔
203
        error_handler("No cached default context for device.", __FILE__,
4✔
204
                      __func__, __LINE__);
4✔
205
    }
4✔
206
    return CRef;
40,076✔
207
}
40,076✔
208

209
__dpctl_give DPCTLDeviceVectorRef
210
DPCTLDeviceMgr_GetDevices(int device_identifier)
211
{
360✔
212
    using vecTy = std::vector<DPCTLSyclDeviceRef>;
360✔
213
    vecTy *Devices = nullptr;
360✔
214

215
    device_identifier = to_canonical_device_id(device_identifier);
360✔
216

217
    try {
360✔
218
        Devices = new std::vector<DPCTLSyclDeviceRef>();
360✔
219
    } catch (std::exception const &e) {
360✔
220
        delete Devices;
×
221
        error_handler(e, __FILE__, __func__, __LINE__);
×
222
        return nullptr;
×
223
    }
×
224

225
    if (!device_identifier)
360!
226
        return wrap<vecTy>(Devices);
×
227

228
    std::vector<device> root_devices;
360✔
229
    try {
360✔
230
        root_devices = device::get_devices();
360✔
231
    } catch (std::exception const &e) {
360✔
232
        delete Devices;
×
233
        error_handler(e, __FILE__, __func__, __LINE__);
×
234
        return nullptr;
×
235
    }
×
236
    dpctl_default_selector mRanker;
360✔
237

238
    for (const auto &root_device : root_devices) {
360✔
239
        if (mRanker(root_device) < 0)
360!
240
            continue;
×
241
        auto Bty(DPCTL_SyclBackendToDPCTLBackendType(
360✔
242
            root_device.get_platform().get_backend()));
360✔
243
        auto Dty(DPCTL_SyclDeviceTypeToDPCTLDeviceType(
360✔
244
            root_device.get_info<info::device::device_type>()));
360✔
245
        if ((device_identifier & Bty) && (device_identifier & Dty)) {
360✔
246
            Devices->emplace_back(wrap<device>(new device(root_device)));
344✔
247
        }
344✔
248
    }
360✔
249
    // the wrap function is defined inside dpctl_vector_templ.cpp
250
    return wrap<vecTy>(Devices);
360✔
251
}
360✔
252

253
__dpctl_give const char *
254
DPCTLDeviceMgr_GetDeviceInfoStr(__dpctl_keep const DPCTLSyclDeviceRef DRef)
255
{
19✔
256
    const char *cstr_info = nullptr;
19✔
257
    auto D = unwrap<device>(DRef);
19✔
258
    if (D) {
19!
259
        try {
19✔
260
            auto infostr = get_device_info_str(*D);
19✔
261
            cstr_info = dpctl::helper::cstring_from_string(infostr);
19✔
262
        } catch (std::exception const &e) {
19✔
263
            error_handler(e, __FILE__, __func__, __LINE__);
×
264
        }
×
265
    }
19✔
266
    return cstr_info;
19✔
267
}
19✔
268

269
int DPCTLDeviceMgr_GetPositionInDevices(__dpctl_keep DPCTLSyclDeviceRef DRef,
270
                                        int device_identifier)
271
{
1,282✔
272
    constexpr int not_found = -1;
1,282✔
273
    if (!DRef) {
1,282✔
274
        return not_found;
1✔
275
    }
1✔
276

277
    device_identifier = to_canonical_device_id(device_identifier);
1,281✔
278
    if (!device_identifier)
1,281!
279
        return not_found;
×
280

281
    const auto &root_devices = device::get_devices();
1,281✔
282
    dpctl_default_selector mRanker;
1,281✔
283
    int index = not_found;
1,281✔
284
    const auto &reference_device = *(unwrap<device>(DRef));
1,281✔
285

286
    for (const auto &root_device : root_devices) {
1,281!
287
        if (mRanker(root_device) < 0)
1,281!
288
            continue;
×
289
        auto Bty(DPCTL_SyclBackendToDPCTLBackendType(
1,281✔
290
            root_device.get_platform().get_backend()));
1,281✔
291
        auto Dty(DPCTL_SyclDeviceTypeToDPCTLDeviceType(
1,281✔
292
            root_device.get_info<info::device::device_type>()));
1,281✔
293
        if ((device_identifier & Bty) && (device_identifier & Dty)) {
1,281!
294
            ++index;
1,281✔
295
            if (root_device == reference_device)
1,281!
296
                return index;
1,281✔
297
        }
1,281✔
298
    }
1,281✔
299
    return not_found;
×
300
}
1,281✔
301

302
/*!
303
 * Returns the number of available devices for a specific backend and device
304
 * type combination.
305
 */
306
size_t DPCTLDeviceMgr_GetNumDevices(int device_identifier)
307
{
16✔
308
    size_t nDevices = 0;
16✔
309
    using CacheT = typename DeviceCacheBuilder::DeviceCache;
16✔
310
    CacheT const &cache = DeviceCacheBuilder::getDeviceCache();
16✔
311

312
    if (cache.empty()) {
16!
313
        // an exception was caught and logged by getDeviceCache
314
        return 0;
×
315
    }
×
316

317
    device_identifier = to_canonical_device_id(device_identifier);
16✔
318
    if (!device_identifier)
16!
319
        return 0;
×
320

321
    dpctl_default_selector mRanker;
16✔
322
    for (const auto &entry : cache) {
16✔
323
        if (mRanker(entry.first) < 0)
16!
324
            continue;
×
325
        auto Bty(DPCTL_SyclBackendToDPCTLBackendType(
16✔
326
            entry.first.get_platform().get_backend()));
16✔
327
        auto Dty(DPCTL_SyclDeviceTypeToDPCTLDeviceType(
16✔
328
            entry.first.get_info<info::device::device_type>()));
16✔
329
        if ((device_identifier & Bty) && (device_identifier & Dty))
16✔
330
            ++nDevices;
8✔
331
    }
16✔
332
    return nDevices;
16✔
333
}
16✔
334

335
/*!
336
 * Prints some of the device info metadata for the device corresponding to the
337
 * specified sycl::queue. Currently, device name, driver version, device
338
 * vendor, and device profile are printed out. More attributed may be added
339
 * later.
340
 */
341
void DPCTLDeviceMgr_PrintDeviceInfo(__dpctl_keep const DPCTLSyclDeviceRef DRef)
342
{
4✔
343
    auto Device = unwrap<device>(DRef);
4✔
344
    if (Device)
4✔
345
        std::cout << get_device_info_str(*Device);
3✔
346
    else
1✔
347
        error_handler("Device is not valid (NULL). Cannot print device info.",
1✔
348
                      __FILE__, __func__, __LINE__);
1✔
349
}
4✔
350

351
int64_t DPCTLDeviceMgr_GetRelativeId(__dpctl_keep const DPCTLSyclDeviceRef DRef)
352
{
11✔
353
    auto Device = unwrap<device>(DRef);
11✔
354

355
    if (Device)
11✔
356
        return DPCTL_GetRelativeDeviceId(*Device);
10✔
357

358
    return -1;
1✔
359
}
11✔
360

361
/*!
362
 * Returns a list of the available composite devices, or an empty list if
363
 * there are none.
364
 */
365
__dpctl_give DPCTLDeviceVectorRef DPCTLDeviceMgr_GetCompositeDevices()
366
{
5✔
367
    using vecTy = std::vector<DPCTLSyclDeviceRef>;
5✔
368
    vecTy *Devices = nullptr;
5✔
369

370
    try {
5✔
371
        Devices = new std::vector<DPCTLSyclDeviceRef>();
5✔
372
    } catch (std::exception const &e) {
5✔
373
        delete Devices;
×
374
        error_handler(e, __FILE__, __func__, __LINE__);
×
375
        return nullptr;
×
376
    }
×
377

378
    try {
5✔
379
        auto composite_devices =
5✔
380
            ext::oneapi::experimental::get_composite_devices();
5✔
381
        Devices->reserve(composite_devices.size());
5✔
382
        for (const auto &CDev : composite_devices) {
5!
383
            Devices->emplace_back(wrap<device>(new device(std::move(CDev))));
×
384
        }
×
385
        return wrap<vecTy>(Devices);
5✔
386
    } catch (std::exception const &e) {
5✔
387
        delete Devices;
×
388
        error_handler(e, __FILE__, __func__, __LINE__);
×
389
        return nullptr;
×
390
    }
×
391
}
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc