Skip to content

Commit f1f9616

Browse files
authored
Fix MLD level mask generation to support 64-bit masks. (#6123)
The generation of level masks for compactly storing partition cells supports sizes that can be stored in 64 bits. The current implementation fails if the total bit sum is 64 bits exactly. A bit shift mechanism is used that is undefined when the shift size is equal to the bit size of the underlying type. This generates an incorrect mask value. We fix this by adding a special case for a 64 bit offset. Given this code is called at most |level| times, there will be no effect on performance. We also update the assertions to reflect 64 bit masks are now supported.
1 parent 8e100ea commit f1f9616

File tree

3 files changed

+68
-5
lines changed

3 files changed

+68
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- FIXED: Fixed Boost link flags in pkg-config file. [#6083](https://github.com/Project-OSRM/osrm-backend/pull/6083)
99
- Routing:
1010
- FIXED: Fix generation of inefficient MLD partitions [#6084](https://github.com/Project-OSRM/osrm-backend/pull/6084)
11+
- FIXED: Fix MLD level mask generation to support 64-bit masks. [#6123](https://github.com/Project-OSRM/osrm-backend/pull/6123)
1112

1213
# 5.25.0
1314
- Changes from 5.24.0

include/partitioner/multi_level_partition.hpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,12 @@ template <storage::Ownership Ownership> class MultiLevelPartitionImpl final
186186
auto bits = static_cast<std::uint64_t>(std::ceil(std::log2(num_cells + 1)));
187187
offsets[lidx++] = sum_bits;
188188
sum_bits += bits;
189-
if (sum_bits > 64)
189+
if (sum_bits > NUM_PARTITION_BITS)
190190
{
191191
throw util::exception(
192192
"Can't pack the partition information at level " + std::to_string(lidx) +
193-
" into a 64bit integer. Would require " + std::to_string(sum_bits) + " bits.");
193+
" into a " + std::to_string(NUM_PARTITION_BITS) +
194+
"bit integer. Would require " + std::to_string(sum_bits) + " bits.");
194195
}
195196
}
196197
// sentinel
@@ -211,11 +212,15 @@ template <storage::Ownership Ownership> class MultiLevelPartitionImpl final
211212
[&](const auto offset, const auto next_offset) {
212213
// create mask that has `bits` ones at its LSBs.
213214
// 000011
214-
BOOST_ASSERT(offset < NUM_PARTITION_BITS);
215+
BOOST_ASSERT(offset <= NUM_PARTITION_BITS);
215216
PartitionID mask = (1ULL << offset) - 1ULL;
216217
// 001111
217-
BOOST_ASSERT(next_offset < NUM_PARTITION_BITS);
218-
PartitionID next_mask = (1ULL << next_offset) - 1ULL;
218+
BOOST_ASSERT(next_offset <= NUM_PARTITION_BITS);
219+
// Check offset for shift overflow. Offsets are strictly increasing,
220+
// so we only need the check on the last mask.
221+
PartitionID next_mask = next_offset == NUM_PARTITION_BITS
222+
? -1ULL
223+
: (1ULL << next_offset) - 1ULL;
219224
// 001100
220225
masks[lidx++] = next_mask ^ mask;
221226
});

unit_tests/partitioner/multi_level_partition.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include <boost/numeric/conversion/cast.hpp>
22
#include <boost/test/unit_test.hpp>
33

4+
#include "util/exception.hpp"
5+
#include "util/for_each_indexed.hpp"
46
#include <util/integer_range.hpp>
57
#include <util/msb.hpp>
68

@@ -230,4 +232,59 @@ BOOST_AUTO_TEST_CASE(large_cell_number)
230232
}
231233
}
232234

235+
BOOST_AUTO_TEST_CASE(cell_64_bits)
236+
{
237+
// bits = ceil(log2(2458529 + 1)) + ceil(log2(258451 + 1)) + ceil(log2(16310 + 1)) +
238+
// ceil(log2(534 + 1))
239+
// = 22 + 18 + 14 + 10
240+
// = 64
241+
const size_t NUM_PARTITIONS = 2458529;
242+
const std::vector<size_t> level_cells = {NUM_PARTITIONS, 258451, 16310, 534};
243+
std::vector<std::vector<CellID>> levels(level_cells.size(),
244+
std::vector<CellID>(level_cells[0]));
245+
std::vector<uint32_t> levels_to_num_cells(level_cells.size());
246+
247+
const auto set_level_cells = [&](size_t level, auto const num_cells) {
248+
for (auto val : util::irange<size_t>(0ULL, NUM_PARTITIONS))
249+
{
250+
levels[level][val] = std::min(val, num_cells - 1);
251+
}
252+
levels_to_num_cells[level] = num_cells;
253+
};
254+
util::for_each_indexed(level_cells.cbegin(), level_cells.cend(), set_level_cells);
255+
256+
MultiLevelPartition mlp{levels, levels_to_num_cells};
257+
258+
BOOST_REQUIRE_EQUAL(mlp.GetNumberOfCells(1), level_cells[0]);
259+
BOOST_REQUIRE_EQUAL(mlp.GetNumberOfCells(2), level_cells[1]);
260+
BOOST_REQUIRE_EQUAL(mlp.GetNumberOfCells(3), level_cells[2]);
261+
BOOST_REQUIRE_EQUAL(mlp.GetNumberOfCells(4), level_cells[3]);
262+
}
263+
264+
BOOST_AUTO_TEST_CASE(cell_overflow_bits)
265+
{
266+
// bits = ceil(log2(4194304 + 1)) + ceil(log2(262144 + 1)) + ceil(log2(16384 + 1)) +
267+
// ceil(log2(1024 + 1))
268+
// = 23 + 19 + 15 + 11
269+
// = 68
270+
const size_t NUM_PARTITIONS = 4194304;
271+
const std::vector<size_t> level_cells = {NUM_PARTITIONS, 262144, 16384, 1024};
272+
std::vector<std::vector<CellID>> levels(level_cells.size(),
273+
std::vector<CellID>(level_cells[0]));
274+
std::vector<uint32_t> levels_to_num_cells(level_cells.size());
275+
276+
const auto set_level_cells = [&](size_t level, auto const num_cells) {
277+
for (auto val : util::irange<size_t>(0ULL, NUM_PARTITIONS))
278+
{
279+
levels[level][val] = std::min(val, num_cells - 1);
280+
}
281+
levels_to_num_cells[level] = num_cells;
282+
};
283+
util::for_each_indexed(level_cells.cbegin(), level_cells.cend(), set_level_cells);
284+
285+
BOOST_REQUIRE_EXCEPTION(MultiLevelPartition(levels, levels_to_num_cells),
286+
util::exception,
287+
[](auto) { return true; });
288+
}
289+
233290
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)