Skip to content

Commit 5e1658e

Browse files
authored
Merge pull request #2026 from Idclip/leaf_val_iters
Improved performance of LeafNode ValueIters
2 parents 67964d1 + 7b8f054 commit 5e1658e

File tree

3 files changed

+70
-10
lines changed

3 files changed

+70
-10
lines changed

openvdb/openvdb/Platform.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,29 @@
9292
#define OPENVDB_UNLIKELY(x) (x)
9393
#endif
9494

95+
/// Macros for assume builtins. Note that we currently don't simply apply these
96+
/// in place of asserts (when asserts are disabled) - they should be only be
97+
/// applied with an assert once profiled
98+
#ifdef __has_cpp_attribute
99+
#if __has_cpp_attribute(assume) >= 202207L
100+
#define OPENVDB_ASSUME(...) [[assume(__VA_ARGS__)]]
101+
#endif
102+
#endif
103+
#ifndef OPENVDB_ASSUME
104+
#if defined(__clang__)
105+
#define OPENVDB_ASSUME(...) __builtin_assume(__VA_ARGS__);
106+
#elif defined(_MSC_VER)
107+
#define OPENVDB_ASSUME(...) __assume(__VA_ARGS__);
108+
#elif defined(__GNUC__)
109+
#if __GNUC__ >= 13
110+
#define OPENVDB_ASSUME(...) __attribute__((__assume__(__VA_ARGS__)))
111+
#endif
112+
#endif
113+
#endif
114+
#ifndef OPENVDB_ASSUME
115+
#define OPENVDB_ASSUME(...)
116+
#endif
117+
95118
/// Force inline function macros. These macros do not necessary guarantee that
96119
/// the decorated function will be inlined, but provide the strongest vendor
97120
/// annotations to that end.

openvdb/openvdb/tree/LeafNode.h

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -219,31 +219,60 @@ class LeafNode
219219
public SparseIteratorBase<
220220
MaskIterT, ValueIter<MaskIterT, NodeT, ValueT, TagT>, NodeT, ValueT>
221221
{
222+
using ValueType = std::conditional_t<std::is_const_v<NodeT>, ValueT,
223+
std::remove_const_t<ValueT>>;
222224
using BaseT = SparseIteratorBase<MaskIterT, ValueIter, NodeT, ValueT>;
223225

224226
ValueIter() {}
225-
ValueIter(const MaskIterT& iter, NodeT* parent): BaseT(iter, parent) {}
227+
ValueIter(const MaskIterT& iter, NodeT* parent)
228+
: BaseT(iter, parent)
229+
// Unlike other value iterators, cache the buffer data as part of
230+
// the iterators members to avoid the cost of going through the
231+
// leaf buffer atomic/checking API
232+
, mData([&]() { OPENVDB_ASSERT(parent); return parent->buffer().data(); }()) {}
226233

227-
ValueT& getItem(Index pos) const { return this->parent().getValue(pos); }
228-
ValueT& getValue() const { return this->parent().getValue(this->pos()); }
234+
ValueT& getItem(Index pos) const { return mData[pos]; }
235+
ValueT& getValue() const { return this->getItem(this->pos()); }
229236

230237
// Note: setItem() can't be called on const iterators.
231238
void setItem(Index pos, const ValueT& value) const
232239
{
233-
this->parent().setValueOnly(pos, value);
240+
if constexpr (std::is_const_v<NodeT>) {
241+
static_assert(!std::is_const_v<NodeT>,
242+
"ValueIter::setItem cannot be called on const iterators");
243+
}
244+
else {
245+
OPENVDB_ASSERT(pos < SIZE);
246+
OPENVDB_ASSUME(pos < SIZE);
247+
mData[pos] = value;
248+
}
234249
}
235250
// Note: setValue() can't be called on const iterators.
236-
void setValue(const ValueT& value) const
237-
{
238-
this->parent().setValueOnly(this->pos(), value);
239-
}
251+
void setValue(const ValueT& value) const { this->setItem(this->pos(), value); }
240252

241253
// Note: modifyItem() can't be called on const iterators.
242254
template<typename ModifyOp>
243-
void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); }
255+
void modifyItem(Index n, const ModifyOp& op) const
256+
{
257+
if constexpr (std::is_const_v<NodeT>) {
258+
static_assert(!std::is_const_v<NodeT>,
259+
"ValueIter::modifyItem cannot be called on const iterators");
260+
}
261+
else {
262+
OPENVDB_ASSERT(n < SIZE);
263+
OPENVDB_ASSUME(n < SIZE);
264+
op(mData[n]);
265+
this->parent().setValueOn(n);
266+
}
267+
}
244268
// Note: modifyValue() can't be called on const iterators.
245269
template<typename ModifyOp>
246-
void modifyValue(const ModifyOp& op) const { this->parent().modifyValue(this->pos(), op); }
270+
void modifyValue(const ModifyOp& op) const
271+
{
272+
this->modifyItem(this->pos(), op);
273+
}
274+
private:
275+
ValueType* mData;
247276
};
248277

249278
/// Leaf nodes have no children, so their child iterators have no get/set accessors.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
OpenVDB:
2+
- Improvements:
3+
- Significantly improved the performance of all LeafNode ValueIterators,
4+
up to 5x on some platforms and up to 10x when delay loading is enabled.
5+
Construction of a ValueIterator from a leaf node now requests the leaf
6+
buffers ahead of iteration to avoid potentially expensive API calls.
7+
- Added OPENVDB_ASSUME macros to mimic builtin assume and C++23 assume
8+
attributes.

0 commit comments

Comments
 (0)