BLI: refactor IndexMask for better performance and memory usage #104629

Merged
Jacques Lucke merged 254 commits from JacquesLucke/blender:index-mask-refactor into main 2023-05-24 18:11:47 +02:00
2 changed files with 82 additions and 136 deletions
Showing only changes of commit 234cddec5f - Show all commits

View File

@ -5,6 +5,7 @@
#include <array>
#include "BLI_index_range.hh"
#include "BLI_span.hh"
namespace blender {
namespace index_mask {
@ -27,6 +28,11 @@ inline const std::array<int16_t, max_chunk_size> &get_static_indices_array()
const IndexMask &get_static_index_mask_for_min_size(const int64_t min_size);
struct ChunkIteratorData {
int16_t segment_index;
int16_t index_in_segment;
};
/**
* A #Chunk contains an ordered list of segments. Each segment is an array of 16-bit integers.
*/
@ -36,11 +42,8 @@ struct Chunk {
const int16_t **segment_indices;
const int16_t *segment_sizes;
const int16_t *segment_sizes_cumulative;
};
struct ChunkIteratorData {
int16_t segment_index;
int16_t index_in_segment;
ChunkIteratorData end_data() const;
};
struct IndexMaskData {
@ -58,41 +61,23 @@ struct IndexMaskIteratorData {
ChunkIteratorData chunk_it;
};
class IndexMaskIterator {
private:
IndexMaskIteratorData data_;
const IndexMask *mask_;
ChunkIteratorData current_chunk_begin_;
ChunkIteratorData current_chunk_end_;
int16_t current_segment_begin_;
int16_t current_segment_end_;
friend IndexMask;
public:
IndexMaskIterator &operator++();
int64_t operator*() const;
friend bool operator!=(const IndexMaskIterator &a, const IndexMaskIterator &b);
friend bool operator==(const IndexMaskIterator &a, const IndexMaskIterator &b);
};
class IndexMask {
private:
IndexMaskData data_;
friend IndexMaskIterator;
public:
IndexMask();
IndexMask(int64_t size);
IndexMask(IndexRange range);
IndexMaskIterator begin() const;
IndexMaskIterator end() const;
IndexMask slice(IndexRange range) const;
template<typename Fn> void foreach_chunk(const Fn &fn);
template<typename Fn> void foreach_index_range(const Fn &fn);
template<typename Fn> void foreach_index_span(const Fn &fn);
template<typename Fn> void foreach_index(const Fn &fn);
template<typename Fn> void foreach_index_range_or_span(const Fn &fn);
const IndexMaskData &data() const;
IndexMaskData &data_for_inplace_construction();
};
@ -120,67 +105,6 @@ inline bool operator==(const IndexMaskIteratorData &a, const IndexMaskIteratorDa
return !(a != b);
}
/* -------------------------------------------------------------------- */
/** \name #IndexMaskIterator Inline Methods
* \{ */
inline IndexMaskIterator &IndexMaskIterator::operator++()
{
data_.chunk_it.index_in_segment++;
if (data_.chunk_it.index_in_segment == current_segment_end_) {
data_.chunk_it.index_in_segment = 0;
data_.chunk_it.segment_index++;
current_segment_begin_ = 0;
if (data_.chunk_it.segment_index < current_chunk_end_.segment_index) {
current_segment_end_ =
mask_->data_.chunks[data_.chunk_index].segment_sizes[data_.chunk_it.segment_index];
}
else if (current_chunk_end_.index_in_segment > 0) {
current_segment_end_ = current_chunk_end_.index_in_segment;
}
else {
data_.chunk_it.segment_index = 0;
data_.chunk_index++;
current_chunk_begin_.segment_index = 0;
current_chunk_begin_.index_in_segment = 0;
if (data_.chunk_index < mask_->data_.chunks_num - 1) {
const Chunk &chunk = mask_->data_.chunks[data_.chunk_index];
if (chunk.segments_num == 0) {
}
else {
current_chunk_end_.segment_index = std::max(chunk.segments_num - 1, 0);
current_chunk_end_.index_in_segment
}
}
else if (data_.chunk_index == mask_->data_.chunks_num - 1) {
}
else {
}
}
}
return *this;
}
inline int64_t IndexMaskIterator::operator*() const
{
const Chunk &current_chunk = mask_->data_.chunks[data_.chunk_index];
const int64_t offset = mask_->data_.chunk_offsets[data_.chunk_index];
const int16_t index =
current_chunk.segment_indices[data_.chunk_it.segment_index][data_.chunk_it.index_in_segment];
return offset + index;
}
inline bool operator!=(const IndexMaskIterator &a, const IndexMaskIterator &b)
{
return a.data_ != b.data_;
}
inline bool operator==(const IndexMaskIterator &a, const IndexMaskIterator &b)
{
return a.data_ == b.data_;
}
/* -------------------------------------------------------------------- */
/** \name #IndexMask Inline Methods
* \{ */
@ -206,50 +130,6 @@ inline IndexMask::IndexMask(const int64_t size)
data_.end_it.index_in_segment = size & chunk_mask_low;
}
inline IndexMaskIterator IndexMask::begin() const
{
IndexMaskIterator it;
it.data_.chunk_index = 0;
it.data_.chunk_it = data_.begin_it;
it.mask_ = this;
it.current_chunk_begin_ = data_.begin_it;
it.current_segment_begin_ = data_.begin_it.segment_index;
if (data_.chunks_num == 0) {
it.current_chunk_end_ = data_.end_it;
it.current_segment_end_ = 0;
}
if (data_.chunks_num == 1) {
it.current_chunk_end_ = data_.end_it;
if (data_.begin_it.segment_index == data_.end_it.segment_index) {
it.current_segment_end_ = data_.end_it.segment_index;
}
else {
const Chunk &first_chunk = data_.chunks[0];
it.current_segment_end_ = first_chunk.segment_sizes[data_.begin_it.segment_index];
}
}
else {
const Chunk &first_chunk = data_.chunks[0];
it.current_chunk_end_.segment_index = first_chunk.segments_num;
it.current_chunk_end_.index_in_segment = 0;
}
return it;
}
inline IndexMaskIterator IndexMask::end() const
{
IndexMaskIterator it;
it.data_.chunk_index = data_.chunks_num;
it.data_.chunk_it.segment_index = 0;
it.data_.chunk_it.index_in_segment = 0;
it.mask_ = this;
it.current_chunk_begin_ = it.data_.chunk_it;
it.current_chunk_end_ = it.data_.chunk_it;
it.current_segment_begin_ = 0;
it.current_segment_end_ = 0;
return it;
}
inline IndexMask IndexMask::slice(const IndexRange range) const
{
IndexMask sliced_mask = *this;
@ -266,6 +146,70 @@ inline IndexMaskData &IndexMask::data_for_inplace_construction()
return const_cast<IndexMaskData &>(data_);
}
inline ChunkIteratorData Chunk::end_data() const
{
ChunkIteratorData data;
if (this->segments_num > 0) {
data.segment_index = this->segments_num - 1;
data.index_in_segment = this->segment_sizes[0];
}
else {
data.segment_index = 0;
data.index_in_segment = 0;
}
return data;
}
template<typename Fn> inline void IndexMask::foreach_chunk(const Fn &fn)
{
if (data_.chunks_num == 0) {
return;
}
if (data_.chunks_num == 1) {
fn(0, data_.begin_it, data_.end_it);
return;
}
{
/* First chunk. */
const Chunk &chunk = data_.chunks[0];
fn(0, data_.begin_it, chunk.end_data());
}
/* Middle chunks. */
for (int64_t chunk_i = 1; chunk_i < data_.chunks_num - 1; chunk_i++) {
const Chunk &chunk = data_.chunks[chunk_i];
const ChunkIteratorData chunk_begin = {0, 0};
fn(chunk_i, chunk_begin, chunk.end_data());
}
{
/* Last chunk */
const ChunkIteratorData chunk_begin = {0, 0};
fn(data_.chunks_num - 1, chunk_begin, data_.end_it);
}
}
template<typename Fn> inline void IndexMask::foreach_index_span(const Fn &fn)
{
this->foreach_chunk([&](const int64_t chunk_i,
const ChunkIteratorData begin_it,
const ChunkIteratorData end_it) {
const int64_t offset = data_.chunk_offsets[chunk_i];
const Chunk &chunk = data_.chunks[chunk_i];
for (int16_t segment_i = begin_it.segment_index; segment_i <= end_it.segment_index;
segment_i++) {
const int16_t segment_start_i = (segment_i == begin_it.segment_index) ?
begin_it.index_in_segment :
0;
const int16_t segment_end_i = (segment_i == end_it.segment_index) ?
end_it.index_in_segment :
chunk.segment_sizes[segment_i];
const int16_t segment_size = segment_end_i - segment_start_i;
const Span<int16_t> indices{chunk.segment_indices[segment_i] + segment_start_i,
segment_size};
fn(offset, indices);
}
});
}
} // namespace index_mask
using index_mask::IndexMask;

View File

@ -11,9 +11,11 @@ namespace blender::index_mask::tests {
TEST(index_mask2, Test)
{
IndexMask mask(3);
for (const int64_t i : mask) {
std::cout << i << "\n";
}
mask.foreach_index_span([&](const int64_t offset, const Span<int16_t> indices) {
for (const int64_t i : indices) {
std::cout << (i + offset) << "\n";
}
});
}
} // namespace blender::index_mask::tests