GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
39 / 52
75.0%
Branches:
23 / 70
32.9%
Functions:
6 / 7
85.7%

device/column.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file column.cpp
3 /// @brief Allocating device columns and growing them by device copies.
4 #include "ernie/device/column.h"
5
6 #include "ernie/util/on_failure.h"
7
8 #include <algorithm>
9 #include <functional>
10 #include <limits>
11 #include <numeric>
12 #include <stdexcept>
13 #include <utility>
14
15 namespace ernie {
16 namespace {
17
18 329 std::uint64_t ElementBytes(DType type, const std::vector<std::int64_t>& element) {
19 329 return ByteSize(type) * std::accumulate(element.begin(), element.end(), std::uint64_t{1},
20 287 [](std::uint64_t product, std::int64_t extent) {
21 287 return product * static_cast<std::uint64_t>(extent);
22 329 });
23 }
24
25 } // namespace
26
27 329 DeviceColumn::DeviceColumn(Arenas& arenas, std::string_view name, DType type,
28 329 std::vector<std::int64_t> element, std::uint64_t rows)
29
1/2
✓ Branch 1 taken 329 times.
✗ Branch 2 not taken.
329 : arenas_(&arenas), name_(name), type_(type), element_(std::move(element)),
30
1/2
✓ Branch 1 taken 329 times.
✗ Branch 2 not taken.
329 rowBytes_(ElementBytes(type, element_)), rows_(rows), capacity_(rows),
31
2/8
✓ Branch 1 taken 329 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 329 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
658 range_(Allocate(rows)) {}
32
33 ✗ DeviceColumn::DeviceColumn(DeviceContext& context, std::shared_ptr<const Buffer> storage,
34 std::uint64_t offset, std::string_view name, DType type,
35 std::vector<std::int64_t> element, std::uint64_t rows,
36 ✗ std::shared_ptr<const void> owner)
37 ✗ : context_(&context), storage_(std::move(storage)), owner_(std::move(owner)), name_(name),
38
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ type_(type), element_(std::move(element)), rowBytes_(ElementBytes(type, element_)),
39
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ rows_(rows), capacity_(rows), range_{0, offset, rows * rowBytes_} {
40
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if (offset > storage_->Size() || range_.size > storage_->Size() - offset) {
41
0/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ throw std::out_of_range(name_ + "'s rows run past the end of their memory");
42 }
43
0/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ }
44
45 329 DeviceColumn::~DeviceColumn() {
46
1/2
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
329 if (arenas_ != nullptr) {
47 329 arenas_->Free(range_);
48 } else {
49
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ context_->Retire(std::move(storage_));
50
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ context_->Retire(std::move(owner_));
51 }
52
2/4
✗ Branch 2 not taken.
✓ Branch 3 taken 329 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 329 times.
329 }
53
54 565 BufferRange DeviceColumn::Allocate(std::uint64_t rows) {
55
2/4
✓ Branch 0 taken 565 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 565 times.
565 if (rowBytes_ != 0 && rows > std::numeric_limits<std::uint64_t>::max() / rowBytes_) {
56 ✗ throw DeviceError(DeviceErrorKind::Unsupported,
57
0/6
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ std::to_string(rows) + " rows of " + name_ + " overflow");
58 }
59 // Arenas hand out no empty ranges.
60
2/2
✓ Branch 0 taken 283 times.
✓ Branch 1 taken 282 times.
848 return arenas_->Allocate(std::max<std::uint64_t>(rowBytes_ * rows, 1));
61 }
62
63 477 Ticket DeviceColumn::Resize(std::uint64_t rows, Queue& queue, std::span<const Ticket> waits) {
64
2/2
✓ Branch 0 taken 241 times.
✓ Branch 1 taken 236 times.
477 if (rows <= capacity_) {
65 241 rows_ = rows;
66 241 return {};
67 }
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 236 times.
236 if (arenas_ == nullptr) {
69
0/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ throw std::logic_error(name_ + " is another API's memory, which Ernie cannot grow");
70 }
71 // Nothing changes until the new range holds the rows, so a failure leaves the column whole.
72
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 235 times.
236 const std::uint64_t capacity = std::max(rows, 2 * capacity_);
73 236 const BufferRange grown = Allocate(capacity);
74
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 210 times.
236 const OnFailure freeGrown([&] { arenas_->Free(grown); });
75 236 const std::uint64_t kept = rowBytes_ * rows_;
76 236 Ticket ticket;
77
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 210 times.
236 if (kept > 0) {
78
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 ticket = queue.Submit(
79
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
52 [&](VkCommandBuffer commands) {
80 26 const VkBufferCopy region{range_.offset, grown.offset, kept};
81 26 CopyBuffer(commands, arenas_->Storage(range_).Handle(),
82 26 arenas_->Storage(grown).Handle(), std::span(&region, 1));
83 26 },
84 waits);
85 }
86
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 arenas_->Free(std::exchange(range_, grown));
87 236 capacity_ = capacity;
88 236 rows_ = rows;
89 236 return ticket;
90 236 }
91
92 } // namespace ernie
93