Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/include/OpenImageIO/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,79 @@ footprint(const std::vector<T>& vec)
} // namespace pvt


/// Allocator adaptor that interposes construct() calls to convert value
/// initialization into default initialization.
///
/// This is a way to achieve a std::vector whose resize does not force a value
/// initialization of every allocated element. Put in more plain terms, the
/// following:
///
/// std::vector<int> v(Nlarge);
///
/// will zero-initialize the Nlarge elements, which may be a cost we do not
/// wish to pay, particularly when allocating POD types that we are going to
/// write over anyway. Sometimes we do the following instead:
///
/// std::unique_ptr<int[]> v (new int[Nlarge]);
///
/// which does not zero-initialize the elements. But it's more awkward, and
/// lacks the methods you get automatically with vectors.
///
/// But you will get a lack of forced value initialization if you use a
/// std::vector with a special allocator that does default initialization.
/// This is such an allocator, so the following:
///
/// std::vector<T, default_init_allocator<T>> v(Nlarge);
///
/// will have the same performance characteristics as the new[] version.
///
/// For details:
/// https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
///
template<typename T, typename A = std::allocator<T>>
class default_init_allocator : public A {
typedef std::allocator_traits<A> a_t;

public:
template<typename U> struct rebind {
using other
= default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
};

using A::A;

template<typename U>
void
construct(U* ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
{
::new (static_cast<void*>(ptr)) U;
}
template<typename U, typename... Args>
void construct(U* ptr, Args&&... args)
{
a_t::construct(static_cast<A&>(*this), ptr,
std::forward<Args>(args)...);
}
};


/// Type alias for a std::vector that uses the default_init_allocator.
///
/// Consider using a `default_init_vector<T>` instead of `std::vector<T>` when
/// all of the following are true:
///
/// * The use is entirely internal to OIIO (since at present, this type is
/// not defined in any public header files).
/// * The type T is POD (plain old data) or trivially constructible.
/// * The vector is likely to be large enough that the cost of default
/// initialization is worth trying to avoid.
/// * After allocation, the vector will be filled with data before any reads
/// are attempted, so the default initialization is not needed.
///
template<typename T>
using default_init_vector = std::vector<T, default_init_allocator<T>>;


OIIO_NAMESPACE_3_1_END


Expand All @@ -137,5 +210,7 @@ namespace pvt {
using v3_1::pvt::footprint;
using v3_1::pvt::heapsize;
} // namespace pvt
using v3_1::default_init_allocator;
using v3_1::default_init_vector;
#endif
OIIO_NAMESPACE_END
74 changes: 0 additions & 74 deletions src/include/imageio_pvt.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,80 +282,6 @@ device_free(void* mem);



/// Allocator adaptor that interposes construct() calls to convert value
/// initialization into default initialization.
///
/// This is a way to achieve a std::vector whose resize does not force a value
/// initialization of every allocated element. Put in more plain terms, the
/// following:
///
/// std::vector<int> v(Nlarge);
///
/// will zero-initialize the Nlarge elements, which may be a cost we do not
/// wish to pay, particularly when allocating POD types that we are going to
/// write over anyway. Sometimes we do the following instead:
///
/// std::unique_ptr<int[]> v (new int[Nlarge]);
///
/// which does not zero-initialize the elements. But it's more awkward, and
/// lacks the methods you get automatically with vectors.
///
/// But you will get a lack of forced value initialization if you use a
/// std::vector with a special allocator that does default initialization.
/// This is such an allocator, so the following:
///
/// std::vector<T, default_init_allocator<T>> v(Nlarge);
///
/// will have the same performance characteristics as the new[] version.
///
/// For details:
/// https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
///
template<typename T, typename A = std::allocator<T>>
class default_init_allocator : public A {
typedef std::allocator_traits<A> a_t;

public:
template<typename U> struct rebind {
using other
= default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
};

using A::A;

template<typename U>
void
construct(U* ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
{
::new (static_cast<void*>(ptr)) U;
}
template<typename U, typename... Args>
void construct(U* ptr, Args&&... args)
{
a_t::construct(static_cast<A&>(*this), ptr,
std::forward<Args>(args)...);
}
};


/// Type alias for a std::vector that uses the default_init_allocator.
///
/// Consider using a `default_init_vector<T>` instead of `std::vector<T>` when
/// all of the following are true:
///
/// * The use is entirely internal to OIIO (since at present, this type is
/// not defined in any public header files).
/// * The type T is POD (plain old data) or trivially constructible.
/// * The vector is likely to be large enough that the cost of default
/// initialization is worth trying to avoid.
/// * After allocation, the vector will be filled with data before any reads
/// are attempted, so the default initialization is not needed.
///
template<typename T>
using default_init_vector = std::vector<T, default_init_allocator<T>>;



OIIO_NAMESPACE_END

#endif // OPENIMAGEIO_IMAGEIO_PVT_H
Loading