BLI: refactor IndexMask for better performance and memory usage #104629
|
@ -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 ¤t_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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue