Rewrite packed vector to also allow random access
This fixes issues #3952. The new approach pre-computes masks for fast access. Since elements can potentially span multiple words we need masks and offsets for each upper and lower word. Due to a bug in the C++14 standart the mask computation is not recognized as constexpr, but would work on C++17.
This commit is contained in:
parent
26a208529e
commit
6bd724fe24
@ -300,25 +300,21 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
|||||||
new SharedGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
|
new SharedGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeNodeInformationPointers(storage::DataLayout &data_layout, char *memory_block)
|
void InitializeNodeInformationPointers(storage::DataLayout &layout, char *memory_ptr)
|
||||||
{
|
{
|
||||||
const auto coordinate_list_ptr = data_layout.GetBlockPtr<util::Coordinate>(
|
const auto coordinate_list_ptr =
|
||||||
memory_block, storage::DataLayout::COORDINATE_LIST);
|
layout.GetBlockPtr<util::Coordinate>(memory_ptr, storage::DataLayout::COORDINATE_LIST);
|
||||||
m_coordinate_list.reset(coordinate_list_ptr,
|
m_coordinate_list.reset(coordinate_list_ptr,
|
||||||
data_layout.num_entries[storage::DataLayout::COORDINATE_LIST]);
|
layout.num_entries[storage::DataLayout::COORDINATE_LIST]);
|
||||||
|
|
||||||
for (unsigned i = 0; i < m_coordinate_list.size(); ++i)
|
const auto osmnodeid_ptr = layout.GetBlockPtr<extractor::PackedOSMIDsView::block_type>(
|
||||||
{
|
memory_ptr, storage::DataLayout::OSM_NODE_ID_LIST);
|
||||||
BOOST_ASSERT(GetCoordinateOfNode(i).IsValid());
|
m_osmnodeid_list = extractor::PackedOSMIDsView(
|
||||||
}
|
util::vector_view<extractor::PackedOSMIDsView::block_type>(
|
||||||
|
osmnodeid_ptr, layout.num_entries[storage::DataLayout::OSM_NODE_ID_LIST]),
|
||||||
const auto osmnodeid_list_ptr = data_layout.GetBlockPtr<std::uint64_t>(
|
// We (ab)use the number of coordinates here because we know we have the same amount of
|
||||||
memory_block, storage::DataLayout::OSM_NODE_ID_LIST);
|
// ids
|
||||||
m_osmnodeid_list.reset(osmnodeid_list_ptr,
|
layout.num_entries[storage::DataLayout::COORDINATE_LIST]);
|
||||||
data_layout.num_entries[storage::DataLayout::OSM_NODE_ID_LIST]);
|
|
||||||
// We (ab)use the number of coordinates here because we know we have the same amount of ids
|
|
||||||
m_osmnodeid_list.set_number_of_entries(
|
|
||||||
data_layout.num_entries[storage::DataLayout::COORDINATE_LIST]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeEdgeBasedNodeDataInformationPointers(storage::DataLayout &layout,
|
void InitializeEdgeBasedNodeDataInformationPointers(storage::DataLayout &layout,
|
||||||
@ -544,7 +540,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
|||||||
|
|
||||||
OSMNodeID GetOSMNodeIDOfNode(const NodeID id) const override final
|
OSMNodeID GetOSMNodeIDOfNode(const NodeID id) const override final
|
||||||
{
|
{
|
||||||
return m_osmnodeid_list.at(id);
|
return m_osmnodeid_list[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<NodeID> GetUncompressedForwardGeometry(const EdgeID id) const override final
|
std::vector<NodeID> GetUncompressedForwardGeometry(const EdgeID id) const override final
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
#ifndef PACKED_VECTOR_HPP
|
#ifndef PACKED_VECTOR_HPP
|
||||||
#define PACKED_VECTOR_HPP
|
#define PACKED_VECTOR_HPP
|
||||||
|
|
||||||
|
#include "util/integer_range.hpp"
|
||||||
#include "util/typedefs.hpp"
|
#include "util/typedefs.hpp"
|
||||||
#include "util/vector_view.hpp"
|
#include "util/vector_view.hpp"
|
||||||
|
|
||||||
#include "storage/io_fwd.hpp"
|
#include "storage/io_fwd.hpp"
|
||||||
#include "storage/shared_memory_ownership.hpp"
|
#include "storage/shared_memory_ownership.hpp"
|
||||||
|
|
||||||
|
#include <boost/iterator/iterator_facade.hpp>
|
||||||
|
#include <boost/iterator/reverse_iterator.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -31,137 +36,410 @@ inline void write(storage::io::FileWriter &writer,
|
|||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template <typename WordT, typename T>
|
||||||
|
inline T get_lower_half_value(WordT word,
|
||||||
|
WordT mask,
|
||||||
|
std::uint8_t offset,
|
||||||
|
typename std::enable_if_t<std::is_integral<T>::value> * = 0)
|
||||||
|
{
|
||||||
|
return static_cast<T>((word & mask) >> offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename WordT, typename T>
|
||||||
|
inline T
|
||||||
|
get_lower_half_value(WordT word, WordT mask, std::uint8_t offset, typename T::value_type * = 0)
|
||||||
|
{
|
||||||
|
return T{static_cast<typename T::value_type>((word & mask) >> offset)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename WordT, typename T>
|
||||||
|
inline T get_upper_half_value(WordT word,
|
||||||
|
WordT mask,
|
||||||
|
std::uint8_t offset,
|
||||||
|
typename std::enable_if_t<std::is_integral<T>::value> * = 0)
|
||||||
|
{
|
||||||
|
return static_cast<T>((word & mask) << offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename WordT, typename T>
|
||||||
|
inline T
|
||||||
|
get_upper_half_value(WordT word, WordT mask, std::uint8_t offset, typename T::value_type * = 0)
|
||||||
|
{
|
||||||
|
static_assert(std::is_unsigned<WordT>::value, "Only unsigned word types supported for now.");
|
||||||
|
return T{static_cast<typename T::value_type>((word & mask) << offset)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename WordT, typename T>
|
||||||
|
inline WordT set_lower_value(WordT word, WordT mask, std::uint8_t offset, T value)
|
||||||
|
{
|
||||||
|
static_assert(std::is_unsigned<WordT>::value, "Only unsigned word types supported for now.");
|
||||||
|
return (word & ~mask) | ((static_cast<WordT>(value) << offset) & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename WordT, typename T>
|
||||||
|
inline WordT set_upper_value(WordT word, WordT mask, std::uint8_t offset, T value)
|
||||||
|
{
|
||||||
|
static_assert(std::is_unsigned<WordT>::value, "Only unsigned word types supported for now.");
|
||||||
|
return (word & ~mask) | ((static_cast<WordT>(value) >> offset) & mask);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, std::size_t Bits, storage::Ownership Ownership> class PackedVector
|
template <typename T, std::size_t Bits, storage::Ownership Ownership> class PackedVector
|
||||||
{
|
{
|
||||||
|
using WordT = std::uint64_t;
|
||||||
|
|
||||||
// This fails for all strong typedef types
|
// This fails for all strong typedef types
|
||||||
// static_assert(std::is_integral<T>::value, "T must be an integral type.");
|
// static_assert(std::is_integral<T>::value, "T must be an integral type.");
|
||||||
static_assert(sizeof(T) <= sizeof(std::uint64_t), "Maximum size of type T is 8 bytes");
|
static_assert(sizeof(T) <= sizeof(WordT), "Maximum size of type T is 8 bytes");
|
||||||
static_assert(Bits > 0, "Minimum number of bits is 0.");
|
static_assert(Bits > 0, "Minimum number of bits is 0.");
|
||||||
static_assert(Bits <= sizeof(std::uint64_t) * CHAR_BIT, "Maximum number of bits is 64.");
|
static_assert(Bits <= sizeof(WordT) * CHAR_BIT, "Maximum number of bits is 64.");
|
||||||
|
|
||||||
static const constexpr std::size_t ELEMSIZE = sizeof(std::uint64_t) * CHAR_BIT;
|
static constexpr std::size_t WORD_BITS = sizeof(WordT) * CHAR_BIT;
|
||||||
static const constexpr std::size_t PACKSIZE = Bits * ELEMSIZE;
|
// number of elements per block, use the number of bits so we make sure
|
||||||
|
// we can devide the total number of bits by the element bis
|
||||||
|
public:
|
||||||
|
static constexpr std::size_t BLOCK_ELEMENTS = WORD_BITS;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// number of words per block
|
||||||
|
static constexpr std::size_t BLOCK_WORDS = (Bits * BLOCK_ELEMENTS) / WORD_BITS;
|
||||||
|
|
||||||
|
// C++14 does not allow operator[] to be constexpr, this is fixed in C++17.
|
||||||
|
static /* constexpr */ std::array<WordT, BLOCK_ELEMENTS> initialize_lower_mask()
|
||||||
|
{
|
||||||
|
std::array<WordT, BLOCK_ELEMENTS> lower_mask{};
|
||||||
|
|
||||||
|
const WordT mask = (1ULL << Bits) - 1;
|
||||||
|
auto offset = 0;
|
||||||
|
for (auto element_index = 0u; element_index < BLOCK_ELEMENTS; element_index++)
|
||||||
|
{
|
||||||
|
auto local_offset = offset % WORD_BITS;
|
||||||
|
lower_mask[element_index] = mask << local_offset;
|
||||||
|
offset += Bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lower_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static /* constexpr */ std::array<WordT, BLOCK_ELEMENTS> initialize_upper_mask()
|
||||||
|
{
|
||||||
|
std::array<WordT, BLOCK_ELEMENTS> upper_mask{};
|
||||||
|
|
||||||
|
const WordT mask = (1ULL << Bits) - 1;
|
||||||
|
auto offset = 0;
|
||||||
|
for (auto element_index = 0u; element_index < BLOCK_ELEMENTS; element_index++)
|
||||||
|
{
|
||||||
|
auto local_offset = offset % WORD_BITS;
|
||||||
|
// check we sliced off bits
|
||||||
|
if (local_offset + Bits > WORD_BITS)
|
||||||
|
{
|
||||||
|
upper_mask[element_index] = mask >> (WORD_BITS - local_offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upper_mask[element_index] = 0;
|
||||||
|
}
|
||||||
|
offset += Bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return upper_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static /* constexpr */ std::array<std::uint8_t, BLOCK_ELEMENTS> initialize_lower_offset()
|
||||||
|
{
|
||||||
|
std::array<std::uint8_t, WORD_BITS> lower_offset{};
|
||||||
|
|
||||||
|
auto offset = 0;
|
||||||
|
for (auto element_index = 0u; element_index < BLOCK_ELEMENTS; element_index++)
|
||||||
|
{
|
||||||
|
auto local_offset = offset % WORD_BITS;
|
||||||
|
lower_offset[element_index] = local_offset;
|
||||||
|
offset += Bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lower_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static /* constexpr */ std::array<std::uint8_t, BLOCK_ELEMENTS> initialize_upper_offset()
|
||||||
|
{
|
||||||
|
std::array<std::uint8_t, BLOCK_ELEMENTS> upper_offset{};
|
||||||
|
|
||||||
|
auto offset = 0;
|
||||||
|
for (auto element_index = 0u; element_index < BLOCK_ELEMENTS; element_index++)
|
||||||
|
{
|
||||||
|
auto local_offset = offset % WORD_BITS;
|
||||||
|
// check we sliced off bits
|
||||||
|
if (local_offset + Bits > WORD_BITS)
|
||||||
|
{
|
||||||
|
upper_offset[element_index] = WORD_BITS - local_offset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upper_offset[element_index] = Bits;
|
||||||
|
}
|
||||||
|
offset += Bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return upper_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static /* constexpr */ std::array<std::uint8_t, BLOCK_ELEMENTS> initialize_word_offset()
|
||||||
|
{
|
||||||
|
std::array<std::uint8_t, BLOCK_ELEMENTS> word_offset{};
|
||||||
|
|
||||||
|
auto offset = 0;
|
||||||
|
for (auto element_index = 0u; element_index < BLOCK_ELEMENTS; element_index++)
|
||||||
|
{
|
||||||
|
word_offset[element_index] = offset / WORD_BITS;
|
||||||
|
offset += Bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return word_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now we need to call these on object creation
|
||||||
|
void initialize()
|
||||||
|
{
|
||||||
|
lower_mask = initialize_lower_mask();
|
||||||
|
upper_mask = initialize_upper_mask();
|
||||||
|
lower_offset = initialize_lower_offset();
|
||||||
|
upper_offset = initialize_upper_offset();
|
||||||
|
word_offset = initialize_word_offset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// mask for the lower/upper word of a record
|
||||||
|
// TODO: With C++17 these could be constexpr
|
||||||
|
/* static constexpr */ std::array<WordT, BLOCK_ELEMENTS>
|
||||||
|
lower_mask /* = initialize_lower_mask()*/;
|
||||||
|
/* static constexpr */ std::array<WordT, BLOCK_ELEMENTS>
|
||||||
|
upper_mask /* = initialize_upper_mask()*/;
|
||||||
|
/* static constexpr */ std::array<std::uint8_t, BLOCK_ELEMENTS>
|
||||||
|
lower_offset /* = initialize_lower_offset()*/;
|
||||||
|
/* static constexpr */ std::array<std::uint8_t, BLOCK_ELEMENTS>
|
||||||
|
upper_offset /* = initialize_upper_offset()*/;
|
||||||
|
// in which word of the block is the element
|
||||||
|
/* static constexpr */ std::array<std::uint8_t, BLOCK_ELEMENTS> word_offset =
|
||||||
|
initialize_word_offset();
|
||||||
|
|
||||||
|
struct InternalIndex
|
||||||
|
{
|
||||||
|
// index to the word that contains the lower
|
||||||
|
// part of the value
|
||||||
|
// note: upper_word == lower_word + 1
|
||||||
|
std::size_t lower_word;
|
||||||
|
// index to the element of the block
|
||||||
|
std::uint8_t element;
|
||||||
|
|
||||||
|
bool operator==(const InternalIndex &other) const
|
||||||
|
{
|
||||||
|
return std::tie(lower_word, element) == std::tie(other.lower_word, other.element);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
using block_type = WordT;
|
||||||
|
|
||||||
/**
|
class internal_reference
|
||||||
* Returns the size of the packed vector datastructure with `elements` packed elements (the size
|
|
||||||
* of
|
|
||||||
* its underlying uint64 vector)
|
|
||||||
*/
|
|
||||||
inline static std::size_t elements_to_blocks(std::size_t elements)
|
|
||||||
{
|
{
|
||||||
return std::ceil(static_cast<double>(elements) * Bits / ELEMSIZE);
|
public:
|
||||||
|
internal_reference(PackedVector &container, const InternalIndex internal_index)
|
||||||
|
: container(container), internal_index(internal_index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal_reference &operator=(const value_type value)
|
||||||
|
{
|
||||||
|
container.set_value(internal_index, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T() const { return container.get_value(internal_index); }
|
||||||
|
|
||||||
|
bool operator==(const internal_reference &other) const
|
||||||
|
{
|
||||||
|
return &container == &other.container && internal_index == other.internal_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::ostream &operator<<(std::ostream &os, const internal_reference &rhs)
|
||||||
|
{
|
||||||
|
return os << static_cast<T>(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PackedVector &container;
|
||||||
|
const InternalIndex internal_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename DataT, typename ContainerT, typename ReferenceT = internal_reference>
|
||||||
|
class iterator_impl
|
||||||
|
: public boost::iterator_facade<iterator_impl<DataT, ContainerT, ReferenceT>,
|
||||||
|
DataT,
|
||||||
|
boost::random_access_traversal_tag,
|
||||||
|
ReferenceT>
|
||||||
|
{
|
||||||
|
typedef boost::iterator_facade<iterator_impl<DataT, ContainerT, ReferenceT>,
|
||||||
|
DataT,
|
||||||
|
boost::random_access_traversal_tag,
|
||||||
|
ReferenceT>
|
||||||
|
base_t;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef typename base_t::value_type value_type;
|
||||||
|
typedef typename base_t::difference_type difference_type;
|
||||||
|
typedef typename base_t::reference reference;
|
||||||
|
typedef std::random_access_iterator_tag iterator_category;
|
||||||
|
|
||||||
|
explicit iterator_impl()
|
||||||
|
: container(nullptr), index(std::numeric_limits<std::size_t>::max())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit iterator_impl(ContainerT *container, const std::size_t index)
|
||||||
|
: container(container), index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void increment() { ++index; }
|
||||||
|
void decrement() { --index; }
|
||||||
|
void advance(difference_type offset) { index += offset; }
|
||||||
|
bool equal(const iterator_impl &other) const { return index == other.index; }
|
||||||
|
auto dereference() const { return (*container)[index]; }
|
||||||
|
difference_type distance_to(const iterator_impl &other) const
|
||||||
|
{
|
||||||
|
return other.index - index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ContainerT *container;
|
||||||
|
std::size_t index;
|
||||||
|
|
||||||
|
friend class ::boost::iterator_core_access;
|
||||||
|
};
|
||||||
|
|
||||||
|
using iterator = iterator_impl<T, PackedVector>;
|
||||||
|
using const_iterator = iterator_impl<const T, const PackedVector, T>;
|
||||||
|
using reverse_iterator = boost::reverse_iterator<iterator>;
|
||||||
|
|
||||||
|
PackedVector(std::initializer_list<T> list)
|
||||||
|
{
|
||||||
|
initialize();
|
||||||
|
reserve(list.size());
|
||||||
|
for (const auto value : list)
|
||||||
|
push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_back(T data)
|
PackedVector() { initialize(); };
|
||||||
|
PackedVector(const PackedVector &) = default;
|
||||||
|
PackedVector(PackedVector &&) = default;
|
||||||
|
PackedVector &operator=(const PackedVector &) = default;
|
||||||
|
PackedVector &operator=(PackedVector &&) = default;
|
||||||
|
|
||||||
|
PackedVector(std::size_t size)
|
||||||
{
|
{
|
||||||
std::uint64_t node_id = static_cast<std::uint64_t>(data);
|
initialize();
|
||||||
|
resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
// mask incoming values, just in case they are > bitsize
|
PackedVector(std::size_t size, T initial_value)
|
||||||
const std::uint64_t incoming_mask = static_cast<std::uint64_t>(pow(2, Bits)) - 1;
|
{
|
||||||
node_id = node_id & incoming_mask;
|
initialize();
|
||||||
|
resize(size);
|
||||||
|
fill(initial_value);
|
||||||
|
}
|
||||||
|
|
||||||
const std::size_t available = (PACKSIZE - Bits * num_elements) % ELEMSIZE;
|
PackedVector(util::ViewOrVector<std::uint64_t, Ownership> vec_, std::size_t num_elements)
|
||||||
|
: vec(std::move(vec_)), num_elements(num_elements)
|
||||||
|
{
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
if (available == 0)
|
// forces the efficient read-only lookup
|
||||||
{
|
auto peek(const std::size_t index) const { return operator[](index); }
|
||||||
// insert ID at the left side of this element
|
|
||||||
std::uint64_t at_left = node_id << (ELEMSIZE - Bits);
|
|
||||||
|
|
||||||
add_last_elem(at_left);
|
auto operator[](const std::size_t index) const { return get_value(get_internal_index(index)); }
|
||||||
}
|
|
||||||
else if (available >= Bits)
|
|
||||||
{
|
|
||||||
// insert ID somewhere in the middle of this element; ID can be contained
|
|
||||||
// entirely within one element
|
|
||||||
const std::uint64_t shifted = node_id << (available - Bits);
|
|
||||||
|
|
||||||
replace_last_elem(vec_back() | shifted);
|
auto operator[](const std::size_t index)
|
||||||
}
|
{
|
||||||
|
return internal_reference{*this, get_internal_index(index)};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto at(std::size_t index) const
|
||||||
|
{
|
||||||
|
if (index < num_elements)
|
||||||
|
return operator[](index);
|
||||||
else
|
else
|
||||||
|
throw std::out_of_range(std::to_string(index) + " is bigger then container size " +
|
||||||
|
std::to_string(num_elements));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto at(std::size_t index)
|
||||||
|
{
|
||||||
|
if (index < num_elements)
|
||||||
|
return operator[](index);
|
||||||
|
else
|
||||||
|
throw std::out_of_range(std::to_string(index) + " is bigger then container size " +
|
||||||
|
std::to_string(num_elements));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin() { return iterator(this, 0); }
|
||||||
|
|
||||||
|
auto end() { return iterator(this, num_elements); }
|
||||||
|
|
||||||
|
auto begin() const { return const_iterator(this, 0); }
|
||||||
|
|
||||||
|
auto end() const { return const_iterator(this, num_elements); }
|
||||||
|
|
||||||
|
auto cbegin() const { return const_iterator(this, 0); }
|
||||||
|
|
||||||
|
auto cend() const { return const_iterator(this, num_elements); }
|
||||||
|
|
||||||
|
auto rbegin() { return reverse_iterator(end()); }
|
||||||
|
|
||||||
|
auto rend() { return reverse_iterator(begin()); }
|
||||||
|
|
||||||
|
auto front() const { return operator[](0); }
|
||||||
|
auto back() const { return operator[](num_elements - 1); }
|
||||||
|
auto front() { return operator[](0); }
|
||||||
|
auto back() { return operator[](num_elements - 1); }
|
||||||
|
|
||||||
|
// Since we only allow passing by value anyway this is just an alias
|
||||||
|
template <class... Args> void emplace_back(Args... args)
|
||||||
|
{
|
||||||
|
push_back(T{std::forward<Args>(args)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(const T value)
|
||||||
|
{
|
||||||
|
auto internal_index = get_internal_index(num_elements);
|
||||||
|
|
||||||
|
while (internal_index.lower_word + 1 >= vec.size())
|
||||||
{
|
{
|
||||||
// ID will be split between the end of this element and the beginning
|
allocate_blocks(1);
|
||||||
// of the next element
|
|
||||||
const std::uint64_t left = node_id >> (Bits - available);
|
|
||||||
|
|
||||||
std::uint64_t right = node_id << (ELEMSIZE - (Bits - available));
|
|
||||||
|
|
||||||
replace_last_elem(vec_back() | left);
|
|
||||||
add_last_elem(right);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_value(internal_index, value);
|
||||||
num_elements++;
|
num_elements++;
|
||||||
}
|
|
||||||
|
|
||||||
T operator[](const std::size_t index) const { return at(index); }
|
BOOST_ASSERT(static_cast<T>(back()) == value);
|
||||||
|
|
||||||
T at(const std::size_t a_index) const
|
|
||||||
{
|
|
||||||
BOOST_ASSERT(a_index < num_elements);
|
|
||||||
|
|
||||||
const std::size_t pack_group = trunc(a_index / ELEMSIZE);
|
|
||||||
const std::size_t pack_index = (a_index + ELEMSIZE) % ELEMSIZE;
|
|
||||||
const std::size_t left_index = (PACKSIZE - Bits * pack_index) % ELEMSIZE;
|
|
||||||
|
|
||||||
const bool back_half = pack_index >= Bits;
|
|
||||||
const std::size_t index =
|
|
||||||
pack_group * Bits + trunc(pack_index / Bits) + trunc((pack_index - back_half) / 2);
|
|
||||||
|
|
||||||
BOOST_ASSERT(index < vec.size());
|
|
||||||
const std::uint64_t elem = static_cast<std::uint64_t>(vec.at(index));
|
|
||||||
|
|
||||||
if (left_index == 0)
|
|
||||||
{
|
|
||||||
// ID is at the far left side of this element
|
|
||||||
return T{elem >> (ELEMSIZE - Bits)};
|
|
||||||
}
|
|
||||||
else if (left_index >= Bits)
|
|
||||||
{
|
|
||||||
// ID is entirely contained within this element
|
|
||||||
const std::uint64_t at_right = elem >> (left_index - Bits);
|
|
||||||
const std::uint64_t left_mask = static_cast<std::uint64_t>(pow(2, Bits)) - 1;
|
|
||||||
return T{at_right & left_mask};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// ID is split between this and the next element
|
|
||||||
const std::uint64_t left_mask = static_cast<std::uint64_t>(pow(2, left_index)) - 1;
|
|
||||||
const std::uint64_t left_side = (elem & left_mask) << (Bits - left_index);
|
|
||||||
|
|
||||||
BOOST_ASSERT(index < vec.size() - 1);
|
|
||||||
const std::uint64_t next_elem = static_cast<std::uint64_t>(vec.at(index + 1));
|
|
||||||
|
|
||||||
const std::uint64_t right_side = next_elem >> (ELEMSIZE - (Bits - left_index));
|
|
||||||
return T{left_side | right_side};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t size() const { return num_elements; }
|
std::size_t size() const { return num_elements; }
|
||||||
|
|
||||||
|
void resize(std::size_t elements)
|
||||||
|
{
|
||||||
|
num_elements = elements;
|
||||||
|
auto num_blocks = std::ceil(static_cast<double>(elements) / BLOCK_ELEMENTS);
|
||||||
|
vec.resize(num_blocks * BLOCK_WORDS + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t capacity() const { return (vec.capacity() / BLOCK_WORDS) * BLOCK_ELEMENTS; }
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
template <bool enabled = (Ownership == storage::Ownership::View)>
|
||||||
void reserve(typename std::enable_if<!enabled, std::size_t>::type capacity)
|
void reserve(typename std::enable_if<!enabled, std::size_t>::type capacity)
|
||||||
{
|
{
|
||||||
vec.reserve(elements_to_blocks(capacity));
|
auto num_blocks = std::ceil(static_cast<double>(capacity) / BLOCK_ELEMENTS);
|
||||||
}
|
vec.reserve(num_blocks * BLOCK_WORDS + 1);
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
|
||||||
void reset(typename std::enable_if<enabled, std::uint64_t>::type *ptr,
|
|
||||||
typename std::enable_if<enabled, std::size_t>::type size)
|
|
||||||
{
|
|
||||||
vec.reset(ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
|
||||||
void set_number_of_entries(typename std::enable_if<enabled, std::size_t>::type count)
|
|
||||||
{
|
|
||||||
num_elements = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t capacity() const
|
|
||||||
{
|
|
||||||
return std::floor(static_cast<double>(vec.capacity()) * ELEMSIZE / Bits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
friend void serialization::read<T, Bits, Ownership>(storage::io::FileReader &reader,
|
friend void serialization::read<T, Bits, Ownership>(storage::io::FileReader &reader,
|
||||||
@ -171,48 +449,67 @@ template <typename T, std::size_t Bits, storage::Ownership Ownership> class Pack
|
|||||||
const PackedVector &vec);
|
const PackedVector &vec);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void allocate_blocks(std::size_t num_blocks)
|
||||||
|
{
|
||||||
|
vec.resize(vec.size() + num_blocks * BLOCK_WORDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline InternalIndex get_internal_index(const std::size_t index) const
|
||||||
|
{
|
||||||
|
const auto block_offset = BLOCK_WORDS * (index / BLOCK_ELEMENTS);
|
||||||
|
const std::uint8_t element_index = index % BLOCK_ELEMENTS;
|
||||||
|
const auto lower_word_index = block_offset + word_offset[element_index];
|
||||||
|
|
||||||
|
return InternalIndex{lower_word_index, element_index};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void fill(const T value)
|
||||||
|
{
|
||||||
|
for (auto block_index : util::irange<std::size_t>(0, vec.size() / BLOCK_WORDS))
|
||||||
|
{
|
||||||
|
const auto block_offset = block_index * BLOCK_WORDS;
|
||||||
|
|
||||||
|
for (auto element_index : util::irange<std::uint8_t>(0, BLOCK_ELEMENTS))
|
||||||
|
{
|
||||||
|
const auto lower_word_index = block_offset + word_offset[element_index];
|
||||||
|
set_value({lower_word_index, element_index}, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T get_value(const InternalIndex internal_index) const
|
||||||
|
{
|
||||||
|
const auto lower_word = vec[internal_index.lower_word];
|
||||||
|
// note this can actually already be a word of the next block however in
|
||||||
|
// that case the upper mask will be 0.
|
||||||
|
// we make sure to have a sentinel element to avoid out-of-bounds errors.
|
||||||
|
const auto upper_word = vec[internal_index.lower_word + 1];
|
||||||
|
const auto value = get_lower_half_value<WordT, T>(lower_word,
|
||||||
|
lower_mask[internal_index.element],
|
||||||
|
lower_offset[internal_index.element]) |
|
||||||
|
get_upper_half_value<WordT, T>(upper_word,
|
||||||
|
upper_mask[internal_index.element],
|
||||||
|
upper_offset[internal_index.element]);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void set_value(const InternalIndex internal_index, const T value)
|
||||||
|
{
|
||||||
|
auto &lower_word = vec[internal_index.lower_word];
|
||||||
|
auto &upper_word = vec[internal_index.lower_word + 1];
|
||||||
|
|
||||||
|
lower_word = set_lower_value<WordT, T>(lower_word,
|
||||||
|
lower_mask[internal_index.element],
|
||||||
|
lower_offset[internal_index.element],
|
||||||
|
value);
|
||||||
|
upper_word = set_upper_value<WordT, T>(upper_word,
|
||||||
|
upper_mask[internal_index.element],
|
||||||
|
upper_offset[internal_index.element],
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
|
||||||
util::ViewOrVector<std::uint64_t, Ownership> vec;
|
util::ViewOrVector<std::uint64_t, Ownership> vec;
|
||||||
|
|
||||||
std::uint64_t num_elements = 0;
|
std::uint64_t num_elements = 0;
|
||||||
|
|
||||||
signed cursor = -1;
|
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
|
||||||
void replace_last_elem(typename std::enable_if<enabled, std::uint64_t>::type last_elem)
|
|
||||||
{
|
|
||||||
vec[cursor] = last_elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
|
||||||
void replace_last_elem(typename std::enable_if<!enabled, std::uint64_t>::type last_elem)
|
|
||||||
{
|
|
||||||
vec.back() = last_elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
|
||||||
void add_last_elem(typename std::enable_if<enabled, std::uint64_t>::type last_elem)
|
|
||||||
{
|
|
||||||
vec[cursor + 1] = last_elem;
|
|
||||||
cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
|
||||||
void add_last_elem(typename std::enable_if<!enabled, std::uint64_t>::type last_elem)
|
|
||||||
{
|
|
||||||
vec.push_back(last_elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
|
||||||
std::uint64_t vec_back(typename std::enable_if<enabled>::type * = nullptr)
|
|
||||||
{
|
|
||||||
return vec[cursor];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool enabled = (Ownership == storage::Ownership::View)>
|
|
||||||
std::uint64_t vec_back(typename std::enable_if<!enabled>::type * = nullptr)
|
|
||||||
{
|
|
||||||
return vec.back();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
file(GLOB RTreeBenchmarkSources static_rtree.cpp)
|
file(GLOB RTreeBenchmarkSources static_rtree.cpp)
|
||||||
file(GLOB MatchBenchmarkSources match.cpp)
|
file(GLOB MatchBenchmarkSources match.cpp)
|
||||||
file(GLOB AliasBenchmarkSources alias.cpp)
|
file(GLOB AliasBenchmarkSources alias.cpp)
|
||||||
|
file(GLOB PackedVectorBenchmarkSources packed_vector.cpp)
|
||||||
|
|
||||||
add_executable(rtree-bench
|
add_executable(rtree-bench
|
||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
@ -40,8 +41,21 @@ target_link_libraries(alias-bench
|
|||||||
${TBB_LIBRARIES}
|
${TBB_LIBRARIES}
|
||||||
${MAYBE_SHAPEFILE})
|
${MAYBE_SHAPEFILE})
|
||||||
|
|
||||||
|
add_executable(packedvector-bench
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
${PackedVectorBenchmarkSources}
|
||||||
|
$<TARGET_OBJECTS:UTIL>)
|
||||||
|
|
||||||
|
target_link_libraries(packedvector-bench
|
||||||
|
${BOOST_BASE_LIBRARIES}
|
||||||
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
|
${TBB_LIBRARIES}
|
||||||
|
${MAYBE_SHAPEFILE})
|
||||||
|
|
||||||
|
|
||||||
add_custom_target(benchmarks
|
add_custom_target(benchmarks
|
||||||
DEPENDS
|
DEPENDS
|
||||||
rtree-bench
|
rtree-bench
|
||||||
|
packedvector-bench
|
||||||
match-bench
|
match-bench
|
||||||
alias-bench)
|
alias-bench)
|
||||||
|
81
src/benchmarks/packed_vector.cpp
Normal file
81
src/benchmarks/packed_vector.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include "util/packed_vector.hpp"
|
||||||
|
#include "util/integer_range.hpp"
|
||||||
|
#include "util/log.hpp"
|
||||||
|
#include "util/timing_util.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <numeric>
|
||||||
|
#include <random>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace osrm;
|
||||||
|
|
||||||
|
struct Measurement
|
||||||
|
{
|
||||||
|
double random_write_ms;
|
||||||
|
double random_read_ms;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma optimize("", off)
|
||||||
|
template <class T> void dont_optimize_away(T &&datum) { T local = datum; }
|
||||||
|
#pragma optimize("", on)
|
||||||
|
#else
|
||||||
|
template <class T> void dont_optimize_away(T &&datum) { asm volatile("" : "+r"(datum)); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <std::size_t num_rounds, std::size_t num_entries, typename VectorT>
|
||||||
|
auto measure_random_access()
|
||||||
|
{
|
||||||
|
std::vector<std::size_t> indices(num_entries);
|
||||||
|
std::iota(indices.begin(), indices.end(), 0);
|
||||||
|
std::mt19937 g(1337);
|
||||||
|
std::shuffle(indices.begin(), indices.end(), g);
|
||||||
|
|
||||||
|
VectorT vector(num_entries);
|
||||||
|
|
||||||
|
TIMER_START(write);
|
||||||
|
for (auto round : util::irange<std::size_t>(0, num_rounds))
|
||||||
|
{
|
||||||
|
for (auto idx : util::irange<std::size_t>(0, num_entries))
|
||||||
|
{
|
||||||
|
vector[indices[idx]] = idx + round;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TIMER_STOP(write);
|
||||||
|
|
||||||
|
TIMER_START(read);
|
||||||
|
auto sum = 0;
|
||||||
|
for (auto round : util::irange<std::size_t>(0, num_rounds))
|
||||||
|
{
|
||||||
|
sum = round;
|
||||||
|
for (auto idx : util::irange<std::size_t>(0, num_entries))
|
||||||
|
{
|
||||||
|
sum += vector[indices[idx]];
|
||||||
|
}
|
||||||
|
dont_optimize_away(sum);
|
||||||
|
}
|
||||||
|
TIMER_STOP(read);
|
||||||
|
|
||||||
|
return Measurement{TIMER_MSEC(write), TIMER_MSEC(read)};
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int, char **)
|
||||||
|
{
|
||||||
|
util::LogPolicy::GetInstance().Unmute();
|
||||||
|
|
||||||
|
auto result_plain = measure_random_access<10000, 1000000, std::vector<std::uint32_t>>();
|
||||||
|
auto result_packed =
|
||||||
|
measure_random_access<10000, 1000000, util::PackedVector<std::uint32_t, 22>>();
|
||||||
|
|
||||||
|
auto write_slowdown = result_packed.random_write_ms / result_plain.random_write_ms;
|
||||||
|
auto read_slowdown = result_packed.random_read_ms / result_plain.random_read_ms;
|
||||||
|
util::Log() << "random write: std::vector " << result_plain.random_write_ms
|
||||||
|
<< " ms, util::packed_vector " << result_packed.random_write_ms << " ms. "
|
||||||
|
<< write_slowdown;
|
||||||
|
util::Log() << "random read: std::vector " << result_plain.random_read_ms
|
||||||
|
<< " ms, util::packed_vector " << result_packed.random_read_ms << " ms. "
|
||||||
|
<< read_slowdown;
|
||||||
|
}
|
@ -329,11 +329,14 @@ void Storage::PopulateLayout(DataLayout &layout)
|
|||||||
io::FileReader node_file(config.nodes_data_path, io::FileReader::VerifyFingerprint);
|
io::FileReader node_file(config.nodes_data_path, io::FileReader::VerifyFingerprint);
|
||||||
const auto coordinate_list_size = node_file.ReadElementCount64();
|
const auto coordinate_list_size = node_file.ReadElementCount64();
|
||||||
layout.SetBlockSize<util::Coordinate>(DataLayout::COORDINATE_LIST, coordinate_list_size);
|
layout.SetBlockSize<util::Coordinate>(DataLayout::COORDINATE_LIST, coordinate_list_size);
|
||||||
|
node_file.Skip<util::Coordinate>(coordinate_list_size);
|
||||||
|
// skip number of elements
|
||||||
|
node_file.Skip<std::uint64_t>(1);
|
||||||
|
const auto num_id_blocks = node_file.ReadElementCount64();
|
||||||
// we'll read a list of OSM node IDs from the same data, so set the block size for the same
|
// we'll read a list of OSM node IDs from the same data, so set the block size for the same
|
||||||
// number of items:
|
// number of items:
|
||||||
layout.SetBlockSize<std::uint64_t>(
|
layout.SetBlockSize<extractor::PackedOSMIDsView::block_type>(DataLayout::OSM_NODE_ID_LIST,
|
||||||
DataLayout::OSM_NODE_ID_LIST,
|
num_id_blocks);
|
||||||
extractor::PackedOSMIDsView::elements_to_blocks(coordinate_list_size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load geometries sizes
|
// load geometries sizes
|
||||||
@ -703,11 +706,15 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr)
|
|||||||
const auto coordinates_ptr =
|
const auto coordinates_ptr =
|
||||||
layout.GetBlockPtr<util::Coordinate, true>(memory_ptr, DataLayout::COORDINATE_LIST);
|
layout.GetBlockPtr<util::Coordinate, true>(memory_ptr, DataLayout::COORDINATE_LIST);
|
||||||
const auto osmnodeid_ptr =
|
const auto osmnodeid_ptr =
|
||||||
layout.GetBlockPtr<std::uint64_t, true>(memory_ptr, DataLayout::OSM_NODE_ID_LIST);
|
layout.GetBlockPtr<extractor::PackedOSMIDsView::block_type, true>(
|
||||||
|
memory_ptr, DataLayout::OSM_NODE_ID_LIST);
|
||||||
util::vector_view<util::Coordinate> coordinates(
|
util::vector_view<util::Coordinate> coordinates(
|
||||||
coordinates_ptr, layout.num_entries[DataLayout::COORDINATE_LIST]);
|
coordinates_ptr, layout.num_entries[DataLayout::COORDINATE_LIST]);
|
||||||
extractor::PackedOSMIDsView osm_node_ids;
|
extractor::PackedOSMIDsView osm_node_ids(
|
||||||
osm_node_ids.reset(osmnodeid_ptr, layout.num_entries[DataLayout::OSM_NODE_ID_LIST]);
|
util::vector_view<extractor::PackedOSMIDsView::block_type>(
|
||||||
|
osmnodeid_ptr, layout.num_entries[DataLayout::OSM_NODE_ID_LIST]),
|
||||||
|
layout.num_entries[DataLayout::OSM_NODE_ID_LIST] *
|
||||||
|
extractor::PackedOSMIDsView::BLOCK_ELEMENTS);
|
||||||
|
|
||||||
extractor::files::readNodes(config.nodes_data_path, coordinates, osm_node_ids);
|
extractor::files::readNodes(config.nodes_data_path, coordinates, osm_node_ids);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
#include "util/packed_vector.hpp"
|
#include "util/packed_vector.hpp"
|
||||||
#include "util/typedefs.hpp"
|
#include "util/typedefs.hpp"
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
#include <boost/test/test_case_template.hpp>
|
#include <boost/test/test_case_template.hpp>
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(packed_vector_test)
|
BOOST_AUTO_TEST_SUITE(packed_vector_test)
|
||||||
|
|
||||||
using namespace osrm;
|
using namespace osrm;
|
||||||
@ -16,10 +22,15 @@ BOOST_AUTO_TEST_CASE(insert_and_retrieve_packed_test)
|
|||||||
std::vector<OSMNodeID> original_ids;
|
std::vector<OSMNodeID> original_ids;
|
||||||
|
|
||||||
const constexpr std::size_t num_test_cases = 399;
|
const constexpr std::size_t num_test_cases = 399;
|
||||||
|
const constexpr std::uint64_t max_id = (1ULL << 33) - 1;
|
||||||
|
|
||||||
|
std::mt19937 rng;
|
||||||
|
rng.seed(1337);
|
||||||
|
std::uniform_int_distribution<std::mt19937::result_type> dist(0, max_id);
|
||||||
|
|
||||||
for (std::size_t i = 0; i < num_test_cases; i++)
|
for (std::size_t i = 0; i < num_test_cases; i++)
|
||||||
{
|
{
|
||||||
OSMNodeID r{static_cast<std::uint64_t>(rand() % 2147483647)}; // max 33-bit uint
|
OSMNodeID r{static_cast<std::uint64_t>(dist(rng))}; // max 33-bit uint
|
||||||
|
|
||||||
packed_ids.push_back(r);
|
packed_ids.push_back(r);
|
||||||
original_ids.push_back(r);
|
original_ids.push_back(r);
|
||||||
@ -44,4 +55,152 @@ BOOST_AUTO_TEST_CASE(packed_vector_capacity_test)
|
|||||||
BOOST_CHECK(packed_vec.capacity() >= 100);
|
BOOST_CHECK(packed_vec.capacity() >= 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(packed_vector_resize_test)
|
||||||
|
{
|
||||||
|
PackedVector<std::uint32_t, 33> packed_vec(100);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(packed_vec.size(), 100);
|
||||||
|
packed_vec[99] = 1337;
|
||||||
|
packed_vec[0] = 42;
|
||||||
|
BOOST_CHECK_EQUAL(packed_vec[99], 1337u);
|
||||||
|
BOOST_CHECK_EQUAL(packed_vec[0], 42u);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(packed_vector_iterator_test)
|
||||||
|
{
|
||||||
|
PackedVector<std::uint32_t, 33> packed_vec(100);
|
||||||
|
|
||||||
|
std::iota(packed_vec.begin(), packed_vec.end(), 0);
|
||||||
|
|
||||||
|
BOOST_CHECK(std::is_sorted(packed_vec.begin(), packed_vec.end()));
|
||||||
|
|
||||||
|
auto idx = 0;
|
||||||
|
for (auto value : packed_vec)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(packed_vec[idx], value);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
BOOST_CHECK_EQUAL(idx, packed_vec.size());
|
||||||
|
|
||||||
|
auto range = boost::make_iterator_range(packed_vec.cbegin(), packed_vec.cend());
|
||||||
|
BOOST_CHECK_EQUAL(range.size(), packed_vec.size());
|
||||||
|
for (auto idx : util::irange<std::size_t>(0, packed_vec.size()))
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(packed_vec[idx], range[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reverse_range = boost::adaptors::reverse(
|
||||||
|
boost::make_iterator_range(packed_vec.cbegin(), packed_vec.cend()));
|
||||||
|
BOOST_CHECK_EQUAL(reverse_range.size(), packed_vec.size());
|
||||||
|
for (auto idx : util::irange<std::size_t>(0, packed_vec.size()))
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(packed_vec[packed_vec.size() - 1 - idx], reverse_range[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mut_range = boost::make_iterator_range(packed_vec.begin(), packed_vec.end());
|
||||||
|
BOOST_CHECK_EQUAL(range.size(), packed_vec.size());
|
||||||
|
for (auto idx : util::irange<std::size_t>(0, packed_vec.size()))
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(packed_vec[idx], mut_range[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mut_reverse_range =
|
||||||
|
boost::adaptors::reverse(boost::make_iterator_range(packed_vec.begin(), packed_vec.end()));
|
||||||
|
BOOST_CHECK_EQUAL(reverse_range.size(), packed_vec.size());
|
||||||
|
for (auto idx : util::irange<std::size_t>(0, packed_vec.size()))
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(packed_vec[packed_vec.size() - 1 - idx], mut_reverse_range[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(packed_vector_10bit_small_test)
|
||||||
|
{
|
||||||
|
PackedVector<std::uint32_t, 10> vector = {10, 5, 8, 12, 254, 4, (1 << 10) - 1, 6};
|
||||||
|
std::vector<std::uint32_t> reference = {10, 5, 8, 12, 254, 4, (1 << 10) - 1, 6};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(vector[0], reference[0]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[1], reference[1]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[2], reference[2]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[3], reference[3]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[4], reference[4]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[5], reference[5]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[6], reference[6]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[7], reference[7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(packed_vector_33bit_small_test)
|
||||||
|
{
|
||||||
|
std::vector<std::uint64_t> reference = {1597322404,
|
||||||
|
1939964443,
|
||||||
|
2112255763,
|
||||||
|
1432114613,
|
||||||
|
1067854538,
|
||||||
|
352118606,
|
||||||
|
1782436840,
|
||||||
|
1909002904,
|
||||||
|
165344818};
|
||||||
|
|
||||||
|
PackedVector<std::uint64_t, 33> vector = {1597322404,
|
||||||
|
1939964443,
|
||||||
|
2112255763,
|
||||||
|
1432114613,
|
||||||
|
1067854538,
|
||||||
|
352118606,
|
||||||
|
1782436840,
|
||||||
|
1909002904,
|
||||||
|
165344818};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(vector[0], reference[0]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[1], reference[1]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[2], reference[2]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[3], reference[3]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[4], reference[4]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[5], reference[5]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[6], reference[6]);
|
||||||
|
BOOST_CHECK_EQUAL(vector[7], reference[7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(values_overflow)
|
||||||
|
{
|
||||||
|
const std::uint64_t mask = (1ull << 42) - 1;
|
||||||
|
PackedVector<std::uint64_t, 42> vector(52, 0);
|
||||||
|
|
||||||
|
for (auto it = vector.begin(); it != vector.end(); ++it)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(*it, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t value = 1;
|
||||||
|
for (auto it = vector.begin(); it != vector.end(); ++it)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(*it, 0);
|
||||||
|
*it = value;
|
||||||
|
BOOST_CHECK_EQUAL(*it, value & mask);
|
||||||
|
value <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = vector.rbegin(); it != vector.rend(); ++it)
|
||||||
|
{
|
||||||
|
value >>= 1;
|
||||||
|
BOOST_CHECK_EQUAL(*it, value & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = vector.cbegin(); it != vector.cend(); ++it)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(*it, value & mask);
|
||||||
|
value <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(packed_vector_33bit_continious)
|
||||||
|
{
|
||||||
|
PackedVector<std::uint64_t, 33> vector;
|
||||||
|
|
||||||
|
for (std::uint64_t i : osrm::util::irange(0, 400))
|
||||||
|
{
|
||||||
|
vector.push_back(i);
|
||||||
|
BOOST_CHECK_EQUAL(vector.back(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
Loading…
Reference in New Issue
Block a user