Skip to content

Commit e316985

Browse files
authored
Merge pull request #47175 from makortel/resourceDescription
Add HardwareResourcesDescription class
2 parents 14fd2e0 + 8cb6597 commit e316985

File tree

7 files changed

+689
-1
lines changed

7 files changed

+689
-1
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#ifndef DataFormats_Provenance_interface_HardwareResourcesDescription_h
2+
#define DataFormats_Provenance_interface_HardwareResourcesDescription_h
3+
4+
#include <iosfwd>
5+
#include <string>
6+
#include <string_view>
7+
#include <vector>
8+
9+
namespace edm {
10+
struct HardwareResourcesDescription {
11+
HardwareResourcesDescription() = default;
12+
explicit HardwareResourcesDescription(std::string_view serialized);
13+
14+
std::string serialize() const;
15+
16+
bool operator==(HardwareResourcesDescription const& other) const;
17+
18+
std::string microarchitecture;
19+
std::vector<std::string> cpuModels;
20+
std::vector<std::string> selectedAccelerators;
21+
std::vector<std::string> gpuModels;
22+
};
23+
24+
std::ostream& operator<<(std::ostream& os, HardwareResourcesDescription const& rd);
25+
} // namespace edm
26+
27+
#endif
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include "DataFormats/Provenance/interface/HardwareResourcesDescription.h"
2+
#include "FWCore/Utilities/interface/EDMException.h"
3+
#include "FWCore/Utilities/interface/compactStringSerializer.h"
4+
5+
#include <iterator>
6+
#include <ostream>
7+
8+
namespace edm {
9+
HardwareResourcesDescription::HardwareResourcesDescription(std::string_view serialized) {
10+
// allowing empty input is mostly for backwards compatibility
11+
if (not serialized.empty()) {
12+
auto ret = edm::compactString::deserialize(serialized,
13+
microarchitecture,
14+
std::back_inserter(cpuModels),
15+
std::back_inserter(selectedAccelerators),
16+
std::back_inserter(gpuModels));
17+
// not comparing against serialized.size() to allow serialized
18+
// to have more content (for kind of forward compatibility)
19+
if (ret == 0) {
20+
throw Exception(errors::EventCorruption) << "Failed to deserialize HardwareResourcesDescription string format";
21+
}
22+
}
23+
}
24+
25+
std::string HardwareResourcesDescription::serialize() const {
26+
return edm::compactString::serialize(microarchitecture, cpuModels, selectedAccelerators, gpuModels);
27+
}
28+
29+
bool HardwareResourcesDescription::operator==(HardwareResourcesDescription const& other) const {
30+
return microarchitecture == other.microarchitecture and std::ranges::equal(cpuModels, other.cpuModels) and
31+
std::ranges::equal(selectedAccelerators, other.selectedAccelerators) and
32+
std::ranges::equal(gpuModels, other.gpuModels);
33+
}
34+
35+
std::ostream& operator<<(std::ostream& os, HardwareResourcesDescription const& rd) {
36+
auto printContainer = [&os](std::string_view header, std::vector<std::string> const& cont) {
37+
os << header << ": " << cont.front();
38+
for (auto it = cont.begin() + 1; it != cont.end(); ++it) {
39+
os << ", " << *it;
40+
}
41+
};
42+
43+
os << "uarch: " << rd.microarchitecture << "\n";
44+
if (not rd.cpuModels.empty()) {
45+
printContainer("CPU models", rd.cpuModels);
46+
os << "\n";
47+
}
48+
if (not rd.selectedAccelerators.empty()) {
49+
printContainer("Selected accelerators", rd.selectedAccelerators);
50+
os << "\n";
51+
}
52+
if (not rd.gpuModels.empty()) {
53+
printContainer("GPU models", rd.gpuModels);
54+
}
55+
return os;
56+
}
57+
} // namespace edm

DataFormats/Provenance/test/BuildFile.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<use name="DataFormats/Provenance"/>
44
</bin>
55

6-
<bin name="testDataFormatsProvenanceCatch2" file="ElementID_t.cpp,Parentage_t.cpp,StoredProcessBlockHelper_t.cpp,CompactHash_t.cpp">
6+
<bin name="testDataFormatsProvenanceCatch2" file="ElementID_t.cpp,Parentage_t.cpp,StoredProcessBlockHelper_t.cpp,CompactHash_t.cpp,HardwareResourcesDescription_t.cpp">
77
<use name="DataFormats/Provenance"/>
88
<use name="catch2"/>
99
</bin>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include "catch.hpp"
2+
3+
#include "DataFormats/Provenance/interface/HardwareResourcesDescription.h"
4+
#include "FWCore/Utilities/interface/EDMException.h"
5+
6+
TEST_CASE("HardwareResourcesDescription", "[HardwareResourcesDescription]") {
7+
SECTION("Construction from empty string") {
8+
CHECK(edm::HardwareResourcesDescription("") == edm::HardwareResourcesDescription());
9+
}
10+
11+
SECTION("Default construction") {
12+
edm::HardwareResourcesDescription resources;
13+
CHECK(edm::HardwareResourcesDescription(resources.serialize()) == resources);
14+
}
15+
16+
SECTION("Microarchitecture") {
17+
edm::HardwareResourcesDescription resources;
18+
resources.microarchitecture = "x86-64-v3";
19+
CHECK(edm::HardwareResourcesDescription(resources.serialize()) == resources);
20+
}
21+
22+
SECTION("CPU models") {
23+
edm::HardwareResourcesDescription resources;
24+
resources.cpuModels = {"Intel something", "AMD something else"};
25+
CHECK(edm::HardwareResourcesDescription(resources.serialize()) == resources);
26+
}
27+
28+
SECTION("accelerators") {
29+
edm::HardwareResourcesDescription resources;
30+
resources.selectedAccelerators = {"cpu", "gpu"};
31+
CHECK(edm::HardwareResourcesDescription(resources.serialize()) == resources);
32+
}
33+
34+
SECTION("GPU models") {
35+
edm::HardwareResourcesDescription resources;
36+
resources.gpuModels = {"NVIDIA something", "NVIDIA something else"};
37+
CHECK(edm::HardwareResourcesDescription(resources.serialize()) == resources);
38+
}
39+
40+
SECTION("All fields") {
41+
edm::HardwareResourcesDescription resources;
42+
resources.microarchitecture = "x86-64-v3";
43+
resources.cpuModels = {"Intel something", "AMD something else"};
44+
resources.selectedAccelerators = {"cpu", "gpu"};
45+
resources.gpuModels = {"NVIDIA something", "NVIDIA something else"};
46+
CHECK(edm::HardwareResourcesDescription(resources.serialize()) == resources);
47+
}
48+
49+
SECTION("Serialization has additional things (forward compatibility)") {
50+
edm::HardwareResourcesDescription resources, resources2;
51+
resources.microarchitecture = "x86-64-v3";
52+
resources.cpuModels = {"Intel something", "AMD something else"};
53+
resources.selectedAccelerators = {"cpu", "gpu"};
54+
resources.gpuModels = {"NVIDIA something", "NVIDIA something else"};
55+
resources2.microarchitecture = "this";
56+
resources2.cpuModels = {"is"};
57+
resources2.selectedAccelerators = {"something"};
58+
resources2.gpuModels = {"else"};
59+
auto const serial = resources.serialize() + resources2.serialize();
60+
CHECK(edm::HardwareResourcesDescription(serial) == resources);
61+
}
62+
63+
SECTION("Error cases") {
64+
SECTION("Invalid serialized string") {
65+
CHECK_THROWS_AS(edm::HardwareResourcesDescription("foo"), edm::Exception);
66+
67+
edm::HardwareResourcesDescription resources;
68+
resources.microarchitecture = "x86-64-v3";
69+
auto serialized = resources.serialize();
70+
SECTION("Last container does not have the delimiter") {
71+
serialized.back() = ',';
72+
CHECK_THROWS_AS(edm::HardwareResourcesDescription(serialized), edm::Exception);
73+
}
74+
SECTION("Too few containers") {
75+
serialized.pop_back();
76+
CHECK_THROWS_AS(edm::HardwareResourcesDescription(serialized), edm::Exception);
77+
}
78+
}
79+
}
80+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#ifndef FWCore_Utilities_interface_compactStringSerializer_h
2+
#define FWCore_Utilities_interface_compactStringSerializer_h
3+
4+
#include <cassert>
5+
#include <iterator>
6+
#include <numeric>
7+
#include <ranges>
8+
#include <string>
9+
#include <string_view>
10+
11+
namespace edm::compactString {
12+
namespace detail {
13+
constexpr std::string_view kDelimiters = "\x1d\x1e";
14+
constexpr char kContainerDelimiter = kDelimiters[0]; // "group separator" in ASCII
15+
constexpr char kElementDelimiter = kDelimiters[1]; // "record separator" in ASCII
16+
17+
void throwIfContainsDelimiters(std::string const& str);
18+
} // namespace detail
19+
20+
/**
21+
* Following three functions serialize a sequence of strings and containers of strings
22+
*
23+
* Each top-level string or container of strings is separated with kContainerDelimeter
24+
* In case of container of strings, each element is separated with kElementDelimeter
25+
* The serialized string will end with kContainerDelimeter and a null character
26+
*
27+
* The functions throw an exception if the serialized strings
28+
* contain any of the delimeter characters. The underlying string
29+
* operations may also throw exceptions.
30+
*/
31+
inline std::string serialize(std::string arg) noexcept(false) {
32+
detail::throwIfContainsDelimiters(arg);
33+
arg += detail::kContainerDelimiter;
34+
return arg;
35+
}
36+
37+
template <typename R>
38+
requires std::ranges::input_range<R> and std::is_same_v<std::ranges::range_value_t<R>, std::string>
39+
std::string serialize(R const& arg) noexcept(false) {
40+
std::string ret;
41+
42+
if (not std::ranges::empty(arg)) {
43+
for (std::string const& elem : arg) {
44+
ret.reserve(ret.size() + elem.size() + 1);
45+
detail::throwIfContainsDelimiters(elem);
46+
ret += elem;
47+
ret += detail::kElementDelimiter;
48+
}
49+
}
50+
51+
ret += detail::kContainerDelimiter;
52+
return ret;
53+
}
54+
55+
template <typename T, typename... Args>
56+
requires(sizeof...(Args) >= 1)
57+
std::string serialize(T&& arg0, Args&&... args) noexcept(false) {
58+
return serialize(std::forward<T>(arg0)) + serialize(std::forward<Args>(args)...);
59+
}
60+
61+
/**
62+
* Following three functions deserialize a string 'input' into a
63+
* sequence of strings and containers of strings
64+
*
65+
* The 'input' string is assumed to be serialized with the
66+
* serialize() functions above.
67+
*
68+
* The output arguments following the 'input' define the schema of
69+
* the deserialization.
70+
* - std::string& for strings
71+
* - output iterator for containers of strings (e.g. std::back_inserter(vector))
72+
*
73+
* Upon success, the return value is the position in `input` for the
74+
* next possible element (i.e. the position after the
75+
* kContainerDelimiter), that is also the number of characters
76+
* consumed by the deserializatiom..
77+
*
78+
* Upon failure, returns 0 to denote the beginning of `input`. The
79+
* output arguments may have been modified.
80+
*
81+
* The functions do not explicitly throw exceptions, but underlying
82+
* operations may throw exceptions.
83+
*/
84+
inline std::string_view::size_type deserialize(std::string_view input, std::string& arg) {
85+
auto const pos = input.find_first_of(detail::kDelimiters);
86+
if (pos == std::string_view::npos or input[pos] != detail::kContainerDelimiter) {
87+
return 0;
88+
}
89+
arg = input.substr(0, pos);
90+
return pos + 1; // skip delimiter
91+
}
92+
93+
template <std::output_iterator<std::string> I>
94+
inline std::string_view::size_type deserialize(std::string_view input, I arg) {
95+
auto pos = input.find_first_of(detail::kDelimiters);
96+
// invalid input
97+
if (pos == std::string_view::npos) {
98+
return 0;
99+
}
100+
// no elements
101+
if (input[pos] == detail::kContainerDelimiter) {
102+
// invalid input for empty container
103+
if (pos != 0) {
104+
return 0;
105+
}
106+
// skip delimiter
107+
return pos + 1;
108+
}
109+
110+
std::string_view::size_type prev = 0;
111+
while (pos != std::string_view::npos and input[pos] == detail::kElementDelimiter) {
112+
*arg = std::string(input.substr(prev, pos - prev));
113+
++arg;
114+
prev = pos + 1; //skip delimiter
115+
pos = input.find_first_of(detail::kDelimiters, prev);
116+
}
117+
118+
// every container must end with kContainerDelimiter
119+
// reaching npos is an error
120+
if (pos == std::string_view::npos) {
121+
return 0;
122+
}
123+
assert(input[pos] == detail::kContainerDelimiter);
124+
125+
return pos + 1; // skip delimiter
126+
}
127+
128+
template <typename T, typename... Args>
129+
requires(sizeof...(Args) >= 1)
130+
std::string_view::size_type deserialize(std::string_view input, T&& arg0, Args&&... args) {
131+
auto pos = deserialize(input, std::forward<T>(arg0));
132+
if (pos != 0) {
133+
auto const ret = deserialize(input.substr(pos), std::forward<Args>(args)...);
134+
pos = (ret == 0) ? 0 : pos + ret;
135+
}
136+
return pos;
137+
}
138+
} // namespace edm::compactString
139+
140+
#endif
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include "FWCore/Utilities/interface/compactStringSerializer.h"
2+
#include "FWCore/Utilities/interface/Exception.h"
3+
4+
namespace edm::compactString::detail {
5+
void throwIfContainsDelimiters(std::string const& str) {
6+
auto pos = str.find_first_of(kDelimiters);
7+
if (pos != std::string::npos) {
8+
cms::Exception ex("compactString");
9+
ex << "Serialized string '" << str << "' contains ";
10+
if (str[pos] == kContainerDelimiter) {
11+
ex << "container";
12+
} else {
13+
ex << "element";
14+
}
15+
ex << " delimiter at position " << pos;
16+
throw ex;
17+
}
18+
}
19+
} // namespace edm::compactString::detail

0 commit comments

Comments
 (0)