Skip to content

Commit a543661

Browse files
authored
api(memory.h): default_init_allocator/default_init_vector (#5169)
Move this from internal-only in imageio_pvt.h to be public in memory.h. It's useful and we've used it in limited ways for a while without needing any changes. It seems ready for prime-time (for example, allowing it to be used in reader/writer plugins). None of the logic changes, it's just moving from a private header to a public header and out of the 'pvt' sub-namespace. Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 364abed commit a543661

2 files changed

Lines changed: 75 additions & 74 deletions

File tree

src/include/OpenImageIO/memory.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,79 @@ footprint(const std::vector<T>& vec)
127127
} // namespace pvt
128128

129129

130+
/// Allocator adaptor that interposes construct() calls to convert value
131+
/// initialization into default initialization.
132+
///
133+
/// This is a way to achieve a std::vector whose resize does not force a value
134+
/// initialization of every allocated element. Put in more plain terms, the
135+
/// following:
136+
///
137+
/// std::vector<int> v(Nlarge);
138+
///
139+
/// will zero-initialize the Nlarge elements, which may be a cost we do not
140+
/// wish to pay, particularly when allocating POD types that we are going to
141+
/// write over anyway. Sometimes we do the following instead:
142+
///
143+
/// std::unique_ptr<int[]> v (new int[Nlarge]);
144+
///
145+
/// which does not zero-initialize the elements. But it's more awkward, and
146+
/// lacks the methods you get automatically with vectors.
147+
///
148+
/// But you will get a lack of forced value initialization if you use a
149+
/// std::vector with a special allocator that does default initialization.
150+
/// This is such an allocator, so the following:
151+
///
152+
/// std::vector<T, default_init_allocator<T>> v(Nlarge);
153+
///
154+
/// will have the same performance characteristics as the new[] version.
155+
///
156+
/// For details:
157+
/// https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
158+
///
159+
template<typename T, typename A = std::allocator<T>>
160+
class default_init_allocator : public A {
161+
typedef std::allocator_traits<A> a_t;
162+
163+
public:
164+
template<typename U> struct rebind {
165+
using other
166+
= default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
167+
};
168+
169+
using A::A;
170+
171+
template<typename U>
172+
void
173+
construct(U* ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
174+
{
175+
::new (static_cast<void*>(ptr)) U;
176+
}
177+
template<typename U, typename... Args>
178+
void construct(U* ptr, Args&&... args)
179+
{
180+
a_t::construct(static_cast<A&>(*this), ptr,
181+
std::forward<Args>(args)...);
182+
}
183+
};
184+
185+
186+
/// Type alias for a std::vector that uses the default_init_allocator.
187+
///
188+
/// Consider using a `default_init_vector<T>` instead of `std::vector<T>` when
189+
/// all of the following are true:
190+
///
191+
/// * The use is entirely internal to OIIO (since at present, this type is
192+
/// not defined in any public header files).
193+
/// * The type T is POD (plain old data) or trivially constructible.
194+
/// * The vector is likely to be large enough that the cost of default
195+
/// initialization is worth trying to avoid.
196+
/// * After allocation, the vector will be filled with data before any reads
197+
/// are attempted, so the default initialization is not needed.
198+
///
199+
template<typename T>
200+
using default_init_vector = std::vector<T, default_init_allocator<T>>;
201+
202+
130203
OIIO_NAMESPACE_3_1_END
131204

132205

@@ -137,5 +210,7 @@ namespace pvt {
137210
using v3_1::pvt::footprint;
138211
using v3_1::pvt::heapsize;
139212
} // namespace pvt
213+
using v3_1::default_init_allocator;
214+
using v3_1::default_init_vector;
140215
#endif
141216
OIIO_NAMESPACE_END

src/include/imageio_pvt.h

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -282,80 +282,6 @@ device_free(void* mem);
282282

283283

284284

285-
/// Allocator adaptor that interposes construct() calls to convert value
286-
/// initialization into default initialization.
287-
///
288-
/// This is a way to achieve a std::vector whose resize does not force a value
289-
/// initialization of every allocated element. Put in more plain terms, the
290-
/// following:
291-
///
292-
/// std::vector<int> v(Nlarge);
293-
///
294-
/// will zero-initialize the Nlarge elements, which may be a cost we do not
295-
/// wish to pay, particularly when allocating POD types that we are going to
296-
/// write over anyway. Sometimes we do the following instead:
297-
///
298-
/// std::unique_ptr<int[]> v (new int[Nlarge]);
299-
///
300-
/// which does not zero-initialize the elements. But it's more awkward, and
301-
/// lacks the methods you get automatically with vectors.
302-
///
303-
/// But you will get a lack of forced value initialization if you use a
304-
/// std::vector with a special allocator that does default initialization.
305-
/// This is such an allocator, so the following:
306-
///
307-
/// std::vector<T, default_init_allocator<T>> v(Nlarge);
308-
///
309-
/// will have the same performance characteristics as the new[] version.
310-
///
311-
/// For details:
312-
/// https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
313-
///
314-
template<typename T, typename A = std::allocator<T>>
315-
class default_init_allocator : public A {
316-
typedef std::allocator_traits<A> a_t;
317-
318-
public:
319-
template<typename U> struct rebind {
320-
using other
321-
= default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
322-
};
323-
324-
using A::A;
325-
326-
template<typename U>
327-
void
328-
construct(U* ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
329-
{
330-
::new (static_cast<void*>(ptr)) U;
331-
}
332-
template<typename U, typename... Args>
333-
void construct(U* ptr, Args&&... args)
334-
{
335-
a_t::construct(static_cast<A&>(*this), ptr,
336-
std::forward<Args>(args)...);
337-
}
338-
};
339-
340-
341-
/// Type alias for a std::vector that uses the default_init_allocator.
342-
///
343-
/// Consider using a `default_init_vector<T>` instead of `std::vector<T>` when
344-
/// all of the following are true:
345-
///
346-
/// * The use is entirely internal to OIIO (since at present, this type is
347-
/// not defined in any public header files).
348-
/// * The type T is POD (plain old data) or trivially constructible.
349-
/// * The vector is likely to be large enough that the cost of default
350-
/// initialization is worth trying to avoid.
351-
/// * After allocation, the vector will be filled with data before any reads
352-
/// are attempted, so the default initialization is not needed.
353-
///
354-
template<typename T>
355-
using default_init_vector = std::vector<T, default_init_allocator<T>>;
356-
357-
358-
359285
OIIO_NAMESPACE_END
360286

361287
#endif // OPENIMAGEIO_IMAGEIO_PVT_H

0 commit comments

Comments
 (0)