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

celerity / celerity-runtime / 10216674169

02 Aug 2024 01:45PM UTC coverage: 94.951% (+2.1%) from 92.884%
10216674169

push

github

fknorr
Remove experimental::user_benchmarker

user_benchmarker has been obsolete ever since we moved away from
structured logging as a the profiler (CPAT) interface.

2978 of 3372 branches covered (88.32%)

Branch coverage included in aggregate %.

6557 of 6670 relevant lines covered (98.31%)

1534446.4 hits per line

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

83.1
/include/device_selection.h
1
#pragma once
2

3
#include <functional>
4
#include <string>
5
#include <type_traits>
6
#include <variant>
7
#include <vector>
8

9
#include "config.h"
10
#include "log.h"
11
#include "utils.h"
12

13
#include <sycl/sycl.hpp>
14

15
namespace celerity::detail {
16

17
// TODO these are required by distr_queue.h, but we don't want to pull all include dependencies of the pick_devices implementation into user code!
18
struct auto_select_devices {};
19
using device_selector = std::function<int(const sycl::device&)>;
20
using devices_or_selector = std::variant<auto_select_devices, std::vector<sycl::device>, device_selector>;
21

22
template <typename DeviceT>
23
void check_required_device_aspects(const DeviceT& device) {
1,177✔
24
        if(!device.has(sycl::aspect::usm_device_allocations)) { throw std::runtime_error("device does not support USM device allocations"); }
1,177✔
25
        if(!device.has(sycl::aspect::usm_host_allocations)) { throw std::runtime_error("device does not support USM host allocations"); }
1,161✔
26
}
1,160✔
27

28
template <typename DevicesOrSelector, typename PlatformT>
29
auto pick_devices(const host_config& cfg, const DevicesOrSelector& user_devices_or_selector, const std::vector<PlatformT>& platforms) {
286✔
30
        using DeviceT = typename decltype(std::declval<PlatformT&>().get_devices())::value_type;
31
        using BackendT = decltype(std::declval<DeviceT&>().get_backend());
32

33
        constexpr bool user_devices_provided = std::is_same_v<DevicesOrSelector, std::vector<DeviceT>>;
286✔
34
        constexpr bool device_selector_provided = std::is_invocable_r_v<int, DevicesOrSelector, DeviceT>;
286✔
35
        constexpr bool auto_select = std::is_same_v<auto_select_devices, DevicesOrSelector>;
286✔
36
        static_assert(user_devices_provided ^ device_selector_provided ^ auto_select,
37
            "pick_device requires either a list of devices, a selector, or the auto_select_devices tag");
38

39
        std::vector<DeviceT> selected_devices;
286✔
40
        std::string how_selected;
286✔
41

42
        if(cfg.node_count > 1) {
286✔
43
                CELERITY_WARN("Celerity detected more than one node (MPI rank) on this host, which is not recommended. Will attempt to distribute local devices evenly "
350✔
44
                              "across nodes.");
45
        }
46

47
        if constexpr(user_devices_provided) {
48
                const auto devices = user_devices_or_selector;
14✔
49
                if(devices.empty()) { throw std::runtime_error("Device selection failed: The user-provided list of devices is empty"); }
14✔
50
                auto backend = devices[0].get_backend();
13✔
51
                for(size_t i = 0; i < devices.size(); ++i) {
31✔
52
                        if(devices[i].get_backend() != backend) {
20✔
53
                                throw std::runtime_error("Device selection failed: The user-provided list of devices contains devices from different backends");
1✔
54
                        }
55
                        try {
56
                                check_required_device_aspects(devices[i]);
19✔
57
                        } catch(std::runtime_error& e) {
2!
58
                                throw std::runtime_error(fmt::format("Device selection failed: Device {} in user-provided list of devices caused error: {}", i, e.what()));
2✔
59
                        }
60
                }
61
                selected_devices = devices;
11✔
62
                how_selected = "specified by user";
11✔
63
        } else {
14✔
64
                if(std::all_of(platforms.cbegin(), platforms.cend(), [](auto& p) { return p.get_devices().empty(); })) {
544✔
65
                        throw std::runtime_error("Device selection failed: No devices available");
2✔
66
                }
67

68
                const auto select_all = [platforms](auto& selector) {
544✔
69
                        std::unordered_map<BackendT, std::vector<std::pair<DeviceT, size_t>>> scored_devices_by_backend;
274✔
70
                        for(size_t i = 0; i < platforms.size(); ++i) {
870✔
71
                                const auto devices = platforms[i].get_devices(sycl::info::device_type::all);
298✔
72
                                for(size_t j = 0; j < devices.size(); ++j) {
1,453✔
73
                                        try {
74
                                                check_required_device_aspects(devices[j]);
1,155✔
75
                                        } catch(std::runtime_error& e) {
28!
76
                                                CELERITY_TRACE("Ignoring platform {} \"{}\", device {} \"{}\": {}", i, platforms[i].template get_info<sycl::info::platform::name>(), j,
14✔
77
                                                    devices[j].template get_info<sycl::info::device::name>(), e.what());
78
                                                continue;
14✔
79
                                        }
80
                                        const auto score = selector(devices[j]);
1,141✔
81
                                        if(score < 0) continue;
1,141!
82
                                        scored_devices_by_backend[devices[j].get_backend()].push_back(std::pair{devices[j], score});
1,121✔
83
                                }
84
                        }
85
                        size_t max_score = 0;
274✔
86
                        std::vector<DeviceT> max_score_devices;
274✔
87
                        for(auto& [backend, scored_devices] : scored_devices_by_backend) {
844!
88
                                size_t sum_score = 0;
285✔
89
                                std::vector<DeviceT> devices;
285✔
90
                                for(auto& [d, score] : scored_devices) {
1,406!
91
                                        sum_score += score;
1,121✔
92
                                        devices.push_back(d);
1,121✔
93
                                }
94
                                if(sum_score > max_score) {
285!
95
                                        max_score = sum_score;
272✔
96
                                        max_score_devices = std::move(devices);
272✔
97
                                }
98
                        }
99
                        return max_score_devices;
274✔
100
                };
274✔
101

102
                if constexpr(device_selector_provided) {
103
                        how_selected = "via user-provided selector";
9✔
104
                        selected_devices = select_all(user_devices_or_selector);
9✔
105
                } else {
106
                        how_selected = "automatically selected";
261✔
107
                        // First try to find eligible GPUs
108
                        const auto selector = [](const DeviceT& d) {
1,360✔
109
                                return d.template get_info<sycl::info::device::device_type>() == sycl::info::device_type::gpu ? 1 : -1;
1,099✔
110
                        };
111
                        selected_devices = select_all(selector);
261✔
112
                        if(selected_devices.empty()) {
261✔
113
                                // If none were found, fall back to other device types
114
                                const auto selector = [](const DeviceT& d) { return 1; };
14✔
115
                                selected_devices = select_all(selector);
4✔
116
                        }
117
                }
118

119
                if(selected_devices.empty()) { throw std::runtime_error("Device selection failed: No eligible devices found"); }
270✔
120
        }
270✔
121

122
        // When running with more than one local node, attempt to distribute devices evenly
123
        if(cfg.node_count > 1) {
278✔
124
                if(selected_devices.size() >= cfg.node_count) {
175✔
125
                        const size_t quotient = selected_devices.size() / cfg.node_count;
160✔
126
                        const size_t remainder = selected_devices.size() % cfg.node_count;
160✔
127

128
                        const auto rank = cfg.local_rank;
160✔
129
                        const size_t offset = rank < remainder ? rank * (quotient + 1) : remainder * (quotient + 1) + (rank - remainder) * quotient;
160✔
130
                        const size_t count = rank < remainder ? quotient + 1 : quotient;
160✔
131

132
                        std::vector<DeviceT> subset{selected_devices.begin() + offset, selected_devices.begin() + offset + count};
480✔
133
                        selected_devices = std::move(subset);
160✔
134
                } else {
160✔
135
                        CELERITY_WARN(
15✔
136
                            "Found fewer devices ({}) than local nodes ({}), multiple nodes will use the same device(s).", selected_devices.size(), cfg.node_count);
137
                        selected_devices = {selected_devices[cfg.local_rank % selected_devices.size()]};
60!
138
                }
139
        }
140

141
        for(device_id did = 0; did < selected_devices.size(); ++did) {
1,466✔
142
                const auto platform_name = selected_devices[did].get_platform().template get_info<sycl::info::platform::name>();
594✔
143
                const auto device_name = selected_devices[did].template get_info<sycl::info::device::name>();
594✔
144
                CELERITY_INFO("Using platform \"{}\", device \"{}\" as D{} ({})", platform_name, device_name, did, how_selected);
594!
145
        }
146

147
        return selected_devices;
278✔
148
}
324✔
149

150
/*
151
template<typename T>
152
concept BackendEnumerator = requires(const T &a) {
153
    typename T::backend_type;
154
    typename T::device_type;
155
    {a.compatible_backends(std::declval<typename T::device_type>)} -> std::same_as<std::vector<T::backend_type>>;
156
    {a.available_backends()} -> std::same_as<std::vector<T::backend_type>>;
157
    {a.is_specialized(std::declval<T::backend_type>())} -> std::same_as<bool>;
158
    {a.get_priority(std::declval<T::backend_type>())} -> std::same_as<int>;
159
};
160
*/
161

162
template <typename BackendEnumerator>
163
inline auto select_backend(const BackendEnumerator& enumerator, const std::vector<typename BackendEnumerator::device_type>& devices) {
226✔
164
        using backend_type = typename BackendEnumerator::backend_type;
165

166
        const auto available_backends = enumerator.available_backends();
226✔
167

168
        std::vector<backend_type> common_backends;
226✔
169
        for(auto& device : devices) {
1,238✔
170
                auto device_backends = enumerator.compatible_backends(device);
506✔
171
                common_backends = common_backends.empty() ? std::move(device_backends) : utils::set_intersection(common_backends, device_backends);
506✔
172
        }
173

174
        assert(!common_backends.empty());
226✔
175
        std::sort(common_backends.begin(), common_backends.end(),
226✔
176
            [&](const backend_type lhs, const backend_type rhs) { return enumerator.get_priority(lhs) > enumerator.get_priority(rhs); });
10✔
177

178
        for(const auto backend : common_backends) {
229!
179
                const auto is_specialized = enumerator.is_specialized(backend);
229✔
180
                if(utils::contains(available_backends, backend)) {
229✔
181
                        if(is_specialized) {
226✔
182
                                CELERITY_DEBUG("Using {} backend for the selected devices.", backend);
1!
183
                        } else {
184
                                CELERITY_WARN("No common backend specialization available for all selected devices, falling back to {}. Performance may be degraded.", backend);
225!
185
                        }
186
                        return backend;
452✔
187
                } else if(is_specialized) {
3!
188
                        CELERITY_WARN(
3!
189
                            "All selected devices are compatible with specialized {} backend, but it has not been compiled. Performance may be degraded.", backend);
190
                }
191
        }
192
        utils::panic("no compatible backend available");
×
193
}
226✔
194

195
} // namespace celerity::detail
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

© 2026 Coveralls, Inc