summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Gao2018-05-23 17:39:48 -0500
committerGerrit Code Review2018-05-23 17:39:48 -0500
commit3a38310476e89acb6c44cdbef37de7821e84c636 (patch)
tree98456643446c94283add20e062d523bb20a1e97c
parent2ed14f3923a96f66ee0b0270cb0b373e690505e5 (diff)
parentfd3fd937b13dcaaee7275da21084f1ea897fb006 (diff)
downloadplatform-system-core-3a38310476e89acb6c44cdbef37de7821e84c636.tar.gz
platform-system-core-3a38310476e89acb6c44cdbef37de7821e84c636.tar.xz
platform-system-core-3a38310476e89acb6c44cdbef37de7821e84c636.zip
Merge changes I9f36cc26,I06561ad0,I42c2a8d0
* changes: adb: add benchmark script. adb: add IOVector. Revert "Revert "adb: add support for O_CLOEXEC to unique_fd pipe wrapper.""
-rw-r--r--adb/Android.bp1
-rw-r--r--adb/adb_unique_fd.h29
-rwxr-xr-xadb/benchmark_device.py120
-rw-r--r--adb/socket.h2
-rw-r--r--adb/sockets.cpp20
-rw-r--r--adb/types.h209
-rw-r--r--adb/types_test.cpp119
7 files changed, 470 insertions, 30 deletions
diff --git a/adb/Android.bp b/adb/Android.bp
index 99de54e1c..1f41e4f3f 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -122,6 +122,7 @@ libadb_test_srcs = [
122 "sysdeps_test.cpp", 122 "sysdeps_test.cpp",
123 "sysdeps/stat_test.cpp", 123 "sysdeps/stat_test.cpp",
124 "transport_test.cpp", 124 "transport_test.cpp",
125 "types_test.cpp",
125] 126]
126 127
127cc_library_host_static { 128cc_library_host_static {
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 9c02cbeec..7d2354d0e 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -28,11 +28,38 @@ struct AdbCloser {
28using unique_fd = android::base::unique_fd_impl<AdbCloser>; 28using unique_fd = android::base::unique_fd_impl<AdbCloser>;
29 29
30#if !defined(_WIN32) 30#if !defined(_WIN32)
31inline bool Pipe(unique_fd* read, unique_fd* write) { 31inline bool Pipe(unique_fd* read, unique_fd* write, int flags = 0) {
32 int pipefd[2]; 32 int pipefd[2];
33#if !defined(__APPLE__)
34 if (pipe2(pipefd, flags) != 0) {
35 return false;
36 }
37#else
38 // Darwin doesn't have pipe2. Implement it ourselves.
39 if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
40 errno = EINVAL;
41 return false;
42 }
43
33 if (pipe(pipefd) != 0) { 44 if (pipe(pipefd) != 0) {
34 return false; 45 return false;
35 } 46 }
47
48 if (flags & O_CLOEXEC) {
49 if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
50 fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
51 PLOG(FATAL) << "failed to set FD_CLOEXEC on newly created pipe";
52 }
53 }
54
55 if (flags & O_NONBLOCK) {
56 if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
57 fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
58 PLOG(FATAL) << "failed to set O_NONBLOCK on newly created pipe";
59 }
60 }
61#endif
62
36 read->reset(pipefd[0]); 63 read->reset(pipefd[0]);
37 write->reset(pipefd[1]); 64 write->reset(pipefd[1]);
38 return true; 65 return true;
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
new file mode 100755
index 000000000..00c23153c
--- /dev/null
+++ b/adb/benchmark_device.py
@@ -0,0 +1,120 @@
1#!/usr/bin/env python3
2#
3# Copyright (C) 2018 The Android Open Source Project
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
18import os
19import statistics
20import time
21
22import adb
23
24def lock_min(device):
25 device.shell_nocheck(["""
26 for x in /sys/devices/system/cpu/cpu?/cpufreq; do
27 echo userspace > $x/scaling_governor
28 cat $x/scaling_min_freq > $x/scaling_setspeed
29 done
30 """])
31
32def lock_max(device):
33 device.shell_nocheck(["""
34 for x in /sys/devices/system/cpu/cpu?/cpufreq; do
35 echo userspace > $x/scaling_governor
36 cat $x/scaling_max_freq > $x/scaling_setspeed
37 done
38 """])
39
40def unlock(device):
41 device.shell_nocheck(["""
42 for x in /sys/devices/system/cpu/cpu?/cpufreq; do
43 echo ondemand > $x/scaling_governor
44 echo sched > $x/scaling_governor
45 echo schedutil > $x/scaling_governor
46 done
47 """])
48
49def harmonic_mean(xs):
50 return 1.0 / statistics.mean([1.0 / x for x in xs])
51
52def analyze(name, speeds):
53 median = statistics.median(speeds)
54 mean = harmonic_mean(speeds)
55 stddev = statistics.stdev(speeds)
56 msg = "%s: %d runs: median %.2f MiB/s, mean %.2f MiB/s, stddev: %.2f MiB/s"
57 print(msg % (name, len(speeds), median, mean, stddev))
58
59def benchmark_push(device=None, file_size_mb=100):
60 if device == None:
61 device = adb.get_device()
62
63 lock_max(device)
64
65 remote_path = "/dev/null"
66 local_path = "/tmp/adb_benchmark_temp"
67
68 with open(local_path, "wb") as f:
69 f.truncate(file_size_mb * 1024 * 1024)
70
71 speeds = list()
72 for _ in range(0, 5):
73 begin = time.time()
74 device.push(local=local_path, remote=remote_path)
75 end = time.time()
76 speeds.append(file_size_mb / float(end - begin))
77
78 analyze("push %dMiB" % file_size_mb, speeds)
79
80def benchmark_pull(device=None, file_size_mb=100):
81 if device == None:
82 device = adb.get_device()
83
84 lock_max(device)
85
86 remote_path = "/data/local/tmp/adb_benchmark_temp"
87 local_path = "/tmp/adb_benchmark_temp"
88
89 device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
90 "count=" + str(file_size_mb)])
91 speeds = list()
92 for _ in range(0, 5):
93 begin = time.time()
94 device.pull(remote=remote_path, local=local_path)
95 end = time.time()
96 speeds.append(file_size_mb / float(end - begin))
97
98 analyze("pull %dMiB" % file_size_mb, speeds)
99
100def benchmark_shell(device=None, file_size_mb=100):
101 if device == None:
102 device = adb.get_device()
103
104 lock_max(device)
105
106 speeds = list()
107 for _ in range(0, 5):
108 begin = time.time()
109 device.shell(["dd", "if=/dev/zero", "bs=1m",
110 "count=" + str(file_size_mb)])
111 end = time.time()
112 speeds.append(file_size_mb / float(end - begin))
113
114 analyze("shell %dMiB" % file_size_mb, speeds)
115
116def main():
117 benchmark_pull()
118
119if __name__ == "__main__":
120 main()
diff --git a/adb/socket.h b/adb/socket.h
index 27e5b0534..0905aab30 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -62,7 +62,7 @@ struct asocket {
62 int fd = -1; 62 int fd = -1;
63 63
64 // queue of data waiting to be written 64 // queue of data waiting to be written
65 std::deque<Range> packet_queue; 65 IOVector packet_queue;
66 66
67 std::string smart_socket_data; 67 std::string smart_socket_data;
68 68
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 9a6dcbec5..de3215dc4 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -113,14 +113,14 @@ enum class SocketFlushResult {
113}; 113};
114 114
115static SocketFlushResult local_socket_flush_incoming(asocket* s) { 115static SocketFlushResult local_socket_flush_incoming(asocket* s) {
116 while (!s->packet_queue.empty()) { 116 if (!s->packet_queue.empty()) {
117 Range& r = s->packet_queue.front(); 117 std::vector<adb_iovec> iov = s->packet_queue.iovecs();
118 118 ssize_t rc = adb_writev(s->fd, iov.data(), iov.size());
119 int rc = adb_write(s->fd, r.data(), r.size()); 119 if (rc > 0 && static_cast<size_t>(rc) == s->packet_queue.size()) {
120 if (rc == static_cast<int>(r.size())) { 120 s->packet_queue.clear();
121 s->packet_queue.pop_front();
122 } else if (rc > 0) { 121 } else if (rc > 0) {
123 r.drop_front(rc); 122 // TODO: Implement a faster drop_front?
123 s->packet_queue.take_front(rc);
124 fdevent_add(s->fde, FDE_WRITE); 124 fdevent_add(s->fde, FDE_WRITE);
125 return SocketFlushResult::TryAgain; 125 return SocketFlushResult::TryAgain;
126 } else if (rc == -1 && errno == EAGAIN) { 126 } else if (rc == -1 && errno == EAGAIN) {
@@ -130,7 +130,6 @@ static SocketFlushResult local_socket_flush_incoming(asocket* s) {
130 // We failed to write, but it's possible that we can still read from the socket. 130 // We failed to write, but it's possible that we can still read from the socket.
131 // Give that a try before giving up. 131 // Give that a try before giving up.
132 s->has_write_error = true; 132 s->has_write_error = true;
133 break;
134 } 133 }
135 } 134 }
136 135
@@ -217,8 +216,7 @@ static bool local_socket_flush_outgoing(asocket* s) {
217static int local_socket_enqueue(asocket* s, apacket::payload_type data) { 216static int local_socket_enqueue(asocket* s, apacket::payload_type data) {
218 D("LS(%d): enqueue %zu", s->id, data.size()); 217 D("LS(%d): enqueue %zu", s->id, data.size());
219 218
220 Range r(std::move(data)); 219 s->packet_queue.append(std::move(data));
221 s->packet_queue.push_back(std::move(r));
222 switch (local_socket_flush_incoming(s)) { 220 switch (local_socket_flush_incoming(s)) {
223 case SocketFlushResult::Destroyed: 221 case SocketFlushResult::Destroyed:
224 return -1; 222 return -1;
@@ -622,7 +620,7 @@ static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
622 D("SS(%d): enqueue %zu", s->id, data.size()); 620 D("SS(%d): enqueue %zu", s->id, data.size());
623 621
624 if (s->smart_socket_data.empty()) { 622 if (s->smart_socket_data.empty()) {
625 // TODO: Make this a BlockChain? 623 // TODO: Make this an IOVector?
626 s->smart_socket_data.assign(data.begin(), data.end()); 624 s->smart_socket_data.assign(data.begin(), data.end());
627 } else { 625 } else {
628 std::copy(data.begin(), data.end(), std::back_inserter(s->smart_socket_data)); 626 std::copy(data.begin(), data.end(), std::back_inserter(s->smart_socket_data));
diff --git a/adb/types.h b/adb/types.h
index dd3e06390..c6b3f0703 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -17,11 +17,15 @@
17#pragma once 17#pragma once
18 18
19#include <algorithm> 19#include <algorithm>
20#include <deque>
21#include <type_traits>
20#include <utility> 22#include <utility>
23#include <vector>
21 24
22#include <android-base/logging.h> 25#include <android-base/logging.h>
23 26
24#include "sysdeps/memory.h" 27#include "sysdeps/memory.h"
28#include "sysdeps/uio.h"
25 29
26// Essentially std::vector<char>, except without zero initialization or reallocation. 30// Essentially std::vector<char>, except without zero initialization or reallocation.
27struct Block { 31struct Block {
@@ -130,34 +134,205 @@ struct apacket {
130 payload_type payload; 134 payload_type payload;
131}; 135};
132 136
133struct Range { 137struct IOVector {
134 explicit Range(apacket::payload_type data) : data_(std::move(data)) {} 138 using value_type = char;
139 using block_type = Block;
140 using size_type = size_t;
135 141
136 Range(const Range& copy) = delete; 142 IOVector() {}
137 Range& operator=(const Range& copy) = delete;
138 143
139 Range(Range&& move) = default; 144 explicit IOVector(std::unique_ptr<block_type> block) {
140 Range& operator=(Range&& move) = default; 145 append(std::move(block));
146 }
147
148 IOVector(const IOVector& copy) = delete;
149 IOVector(IOVector&& move) : IOVector() {
150 *this = std::move(move);
151 }
152
153 IOVector& operator=(const IOVector& copy) = delete;
154 IOVector& operator=(IOVector&& move) {
155 chain_ = std::move(move.chain_);
156 chain_length_ = move.chain_length_;
157 begin_offset_ = move.begin_offset_;
158 end_offset_ = move.end_offset_;
141 159
142 size_t size() const { return data_.size() - begin_offset_ - end_offset_; }; 160 move.chain_.clear();
161 move.chain_length_ = 0;
162 move.begin_offset_ = 0;
163 move.end_offset_ = 0;
164
165 return *this;
166 }
167
168 size_type size() const { return chain_length_ - begin_offset_ - end_offset_; }
143 bool empty() const { return size() == 0; } 169 bool empty() const { return size() == 0; }
144 170
145 void drop_front(size_t n) { 171 void clear() {
146 CHECK_GE(size(), n); 172 chain_length_ = 0;
147 begin_offset_ += n; 173 begin_offset_ = 0;
174 end_offset_ = 0;
175 chain_.clear();
176 }
177
178 // Split the first |len| bytes out of this chain into its own.
179 IOVector take_front(size_type len) {
180 IOVector head;
181
182 if (len == 0) {
183 return head;
184 }
185 CHECK_GE(size(), len);
186
187 std::shared_ptr<const block_type> first_block = chain_.front();
188 CHECK_GE(first_block->size(), begin_offset_);
189 head.append_shared(std::move(first_block));
190 head.begin_offset_ = begin_offset_;
191
192 while (head.size() < len) {
193 pop_front_block();
194 CHECK(!chain_.empty());
195
196 head.append_shared(chain_.front());
197 }
198
199 if (head.size() == len) {
200 // Head takes full ownership of the last block it took.
201 head.end_offset_ = 0;
202 begin_offset_ = 0;
203 pop_front_block();
204 } else {
205 // Head takes partial ownership of the last block it took.
206 size_t bytes_taken = head.size() - len;
207 head.end_offset_ = bytes_taken;
208 CHECK_GE(chain_.front()->size(), bytes_taken);
209 begin_offset_ = chain_.front()->size() - bytes_taken;
210 }
211
212 return head;
213 }
214
215 // Add a nonempty block to the chain.
216 // The end of the chain must be a complete block (i.e. end_offset_ == 0).
217 void append(std::unique_ptr<const block_type> block) {
218 CHECK_NE(0ULL, block->size());
219 CHECK_EQ(0ULL, end_offset_);
220 chain_length_ += block->size();
221 chain_.emplace_back(std::move(block));
222 }
223
224 void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); }
225
226 void trim_front() {
227 if (begin_offset_ == 0) {
228 return;
229 }
230
231 const block_type* first_block = chain_.front().get();
232 auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_);
233 memcpy(copy->data(), first_block->data() + begin_offset_, copy->size());
234 chain_.front() = std::move(copy);
235
236 chain_length_ -= begin_offset_;
237 begin_offset_ = 0;
238 }
239
240 private:
241 // append, except takes a shared_ptr.
242 // Private to prevent exterior mutation of blocks.
243 void append_shared(std::shared_ptr<const block_type> block) {
244 CHECK_NE(0ULL, block->size());
245 CHECK_EQ(0ULL, end_offset_);
246 chain_length_ += block->size();
247 chain_.emplace_back(std::move(block));
248 }
249
250 // Drop the front block from the chain, and update chain_length_ appropriately.
251 void pop_front_block() {
252 chain_length_ -= chain_.front()->size();
253 begin_offset_ = 0;
254 chain_.pop_front();
255 }
256
257 // Iterate over the blocks with a callback with an operator()(const char*, size_t).
258 template <typename Fn>
259 void iterate_blocks(Fn&& callback) const {
260 if (chain_.size() == 0) {
261 return;
262 }
263
264 for (size_t i = 0; i < chain_.size(); ++i) {
265 const std::shared_ptr<const block_type>& block = chain_.at(i);
266 const char* begin = block->data();
267 size_t length = block->size();
268
269 // Note that both of these conditions can be true if there's only one block.
270 if (i == 0) {
271 CHECK_GE(block->size(), begin_offset_);
272 begin += begin_offset_;
273 length -= begin_offset_;
274 }
275
276 if (i == chain_.size() - 1) {
277 CHECK_GE(length, end_offset_);
278 length -= end_offset_;
279 }
280
281 callback(begin, length);
282 }
148 } 283 }
149 284
150 void drop_end(size_t n) { 285 public:
151 CHECK_GE(size(), n); 286 // Copy all of the blocks into a single block.
152 end_offset_ += n; 287 template <typename CollectionType = block_type>
288 CollectionType coalesce() const {
289 CollectionType result;
290 if (size() == 0) {
291 return result;
292 }
293
294 result.resize(size());
295
296 size_t offset = 0;
297 iterate_blocks([&offset, &result](const char* data, size_t len) {
298 memcpy(&result[offset], data, len);
299 offset += len;
300 });
301
302 return result;
153 } 303 }
154 304
155 char* data() { return &data_[0] + begin_offset_; } 305 template <typename FunctionType>
306 auto coalesced(FunctionType&& f) const ->
307 typename std::result_of<FunctionType(const char*, size_t)>::type {
308 if (chain_.size() == 1) {
309 // If we only have one block, we can use it directly.
310 return f(chain_.front()->data() + begin_offset_, size());
311 } else {
312 // Otherwise, copy to a single block.
313 auto data = coalesce();
314 return f(data.data(), data.size());
315 }
316 }
156 317
157 apacket::payload_type::iterator begin() { return data_.begin() + begin_offset_; } 318 // Get a list of iovecs that can be used to write out all of the blocks.
158 apacket::payload_type::iterator end() { return data_.end() - end_offset_; } 319 std::vector<adb_iovec> iovecs() const {
320 std::vector<adb_iovec> result;
321 iterate_blocks([&result](const char* data, size_t len) {
322 adb_iovec iov;
323 iov.iov_base = const_cast<char*>(data);
324 iov.iov_len = len;
325 result.emplace_back(iov);
326 });
327
328 return result;
329 }
330
331 private:
332 // Total length of all of the blocks in the chain.
333 size_t chain_length_ = 0;
159 334
160 apacket::payload_type data_;
161 size_t begin_offset_ = 0; 335 size_t begin_offset_ = 0;
162 size_t end_offset_ = 0; 336 size_t end_offset_ = 0;
337 std::deque<std::shared_ptr<const block_type>> chain_;
163}; 338};
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
new file mode 100644
index 000000000..31ab90af3
--- /dev/null
+++ b/adb/types_test.cpp
@@ -0,0 +1,119 @@
1/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <gtest/gtest.h>
18
19#include "sysdeps/memory.h"
20#include "types.h"
21
22static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
23 return std::make_unique<IOVector::block_type>(string.begin(), string.end());
24}
25
26static std::unique_ptr<IOVector::block_type> create_block(char value, size_t len) {
27 auto block = std::make_unique<IOVector::block_type>();
28 block->resize(len);
29 memset(&(*block)[0], value, len);
30 return block;
31}
32
33template <typename T>
34static std::unique_ptr<IOVector::block_type> copy_block(T&& block) {
35 auto copy = std::make_unique<IOVector::block_type>();
36 copy->assign(block->begin(), block->end());
37 return copy;
38}
39
40TEST(IOVector, empty) {
41 // Empty IOVector.
42 IOVector bc;
43 CHECK_EQ(0ULL, bc.coalesce().size());
44}
45
46TEST(IOVector, single_block) {
47 // A single block.
48 auto block = create_block('x', 100);
49 IOVector bc;
50 bc.append(copy_block(block));
51 ASSERT_EQ(100ULL, bc.size());
52 auto coalesced = bc.coalesce();
53 ASSERT_EQ(*block, coalesced);
54}
55
56TEST(IOVector, single_block_split) {
57 // One block split.
58 IOVector bc;
59 bc.append(create_block("foobar"));
60 IOVector foo = bc.take_front(3);
61 ASSERT_EQ(3ULL, foo.size());
62 ASSERT_EQ(3ULL, bc.size());
63 ASSERT_EQ(*create_block("foo"), foo.coalesce());
64 ASSERT_EQ(*create_block("bar"), bc.coalesce());
65}
66
67TEST(IOVector, aligned_split) {
68 IOVector bc;
69 bc.append(create_block("foo"));
70 bc.append(create_block("bar"));
71 bc.append(create_block("baz"));
72 ASSERT_EQ(9ULL, bc.size());
73
74 IOVector foo = bc.take_front(3);
75 ASSERT_EQ(3ULL, foo.size());
76 ASSERT_EQ(*create_block("foo"), foo.coalesce());
77
78 IOVector bar = bc.take_front(3);
79 ASSERT_EQ(3ULL, bar.size());
80 ASSERT_EQ(*create_block("bar"), bar.coalesce());
81
82 IOVector baz = bc.take_front(3);
83 ASSERT_EQ(3ULL, baz.size());
84 ASSERT_EQ(*create_block("baz"), baz.coalesce());
85
86 ASSERT_EQ(0ULL, bc.size());
87}
88
89TEST(IOVector, misaligned_split) {
90 IOVector bc;
91 bc.append(create_block("foo"));
92 bc.append(create_block("bar"));
93 bc.append(create_block("baz"));
94 bc.append(create_block("qux"));
95 bc.append(create_block("quux"));
96
97 // Aligned left, misaligned right, across multiple blocks.
98 IOVector foob = bc.take_front(4);
99 ASSERT_EQ(4ULL, foob.size());
100 ASSERT_EQ(*create_block("foob"), foob.coalesce());
101
102 // Misaligned left, misaligned right, in one block.
103 IOVector a = bc.take_front(1);
104 ASSERT_EQ(1ULL, a.size());
105 ASSERT_EQ(*create_block("a"), a.coalesce());
106
107 // Misaligned left, misaligned right, across two blocks.
108 IOVector rba = bc.take_front(3);
109 ASSERT_EQ(3ULL, rba.size());
110 ASSERT_EQ(*create_block("rba"), rba.coalesce());
111
112 // Misaligned left, misaligned right, across three blocks.
113 IOVector zquxquu = bc.take_front(7);
114 ASSERT_EQ(7ULL, zquxquu.size());
115 ASSERT_EQ(*create_block("zquxquu"), zquxquu.coalesce());
116
117 ASSERT_EQ(1ULL, bc.size());
118 ASSERT_EQ(*create_block("x"), bc.coalesce());
119}