Skip to content

Commit 7490787

Browse files
committed
Merge pull request #103759 from Ivorforce/zero-constructible
Optimize `Array.resize` by using `memset` (through new `is_zero_constructible` type trait)
2 parents 1905749 + 75bc471 commit 7490787

19 files changed

+93
-11
lines changed

core/math/aabb.h

+3
Original file line numberDiff line numberDiff line change
@@ -495,3 +495,6 @@ AABB AABB::quantized(real_t p_unit) const {
495495
ret.quantize(p_unit);
496496
return ret;
497497
}
498+
499+
template <>
500+
struct is_zero_constructible<AABB> : std::true_type {};

core/math/audio_frame.h

+3
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,6 @@ _ALWAYS_INLINE_ AudioFrame operator*(int32_t p_scalar, const AudioFrame &p_frame
168168
_ALWAYS_INLINE_ AudioFrame operator*(int64_t p_scalar, const AudioFrame &p_frame) {
169169
return AudioFrame(p_frame.left * p_scalar, p_frame.right * p_scalar);
170170
}
171+
172+
template <>
173+
struct is_zero_constructible<AudioFrame> : std::true_type {};

core/math/face3.h

+3
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,6 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const {
236236
}
237237
return true;
238238
}
239+
240+
template <>
241+
struct is_zero_constructible<Face3> : std::true_type {};

core/math/plane.h

+3
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,6 @@ bool Plane::operator==(const Plane &p_plane) const {
132132
bool Plane::operator!=(const Plane &p_plane) const {
133133
return normal != p_plane.normal || d != p_plane.d;
134134
}
135+
136+
template <>
137+
struct is_zero_constructible<Plane> : std::true_type {};

core/math/rect2.h

+3
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,6 @@ struct [[nodiscard]] Rect2 {
371371
size(p_size) {
372372
}
373373
};
374+
375+
template <>
376+
struct is_zero_constructible<Rect2> : std::true_type {};

core/math/rect2i.h

+3
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,6 @@ struct [[nodiscard]] Rect2i {
236236
size(p_size) {
237237
}
238238
};
239+
240+
template <>
241+
struct is_zero_constructible<Rect2i> : std::true_type {};

core/math/vector2.h

+3
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,6 @@ _FORCE_INLINE_ Vector2 operator*(int64_t p_scalar, const Vector2 &p_vec) {
326326

327327
typedef Vector2 Size2;
328328
typedef Vector2 Point2;
329+
330+
template <>
331+
struct is_zero_constructible<Vector2> : std::true_type {};

core/math/vector2i.h

+3
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,6 @@ _FORCE_INLINE_ Vector2i operator*(double p_scalar, const Vector2i &p_vector) {
168168

169169
typedef Vector2i Size2i;
170170
typedef Vector2i Point2i;
171+
172+
template <>
173+
struct is_zero_constructible<Vector2i> : std::true_type {};

core/math/vector3.h

+3
Original file line numberDiff line numberDiff line change
@@ -549,3 +549,6 @@ Vector3 Vector3::reflect(const Vector3 &p_normal) const {
549549
#endif
550550
return 2.0f * p_normal * dot(p_normal) - *this;
551551
}
552+
553+
template <>
554+
struct is_zero_constructible<Vector3> : std::true_type {};

core/math/vector3i.h

+3
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,6 @@ bool Vector3i::operator>=(const Vector3i &p_v) const {
334334
void Vector3i::zero() {
335335
x = y = z = 0;
336336
}
337+
338+
template <>
339+
struct is_zero_constructible<Vector3i> : std::true_type {};

core/math/vector4.h

+3
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,6 @@ _FORCE_INLINE_ Vector4 operator*(int32_t p_scalar, const Vector4 &p_vec) {
302302
_FORCE_INLINE_ Vector4 operator*(int64_t p_scalar, const Vector4 &p_vec) {
303303
return p_vec * p_scalar;
304304
}
305+
306+
template <>
307+
struct is_zero_constructible<Vector4> : std::true_type {};

core/math/vector4i.h

+3
Original file line numberDiff line numberDiff line change
@@ -362,3 +362,6 @@ bool Vector4i::operator>=(const Vector4i &p_v) const {
362362
void Vector4i::zero() {
363363
x = y = z = w = 0;
364364
}
365+
366+
template <>
367+
struct is_zero_constructible<Vector4i> : std::true_type {};

core/os/memory.h

+17
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "core/templates/safe_refcount.h"
3535

3636
#include <stddef.h>
37+
#include <cstring>
3738
#include <new>
3839
#include <type_traits>
3940

@@ -195,6 +196,22 @@ T *memnew_arr_template(size_t p_elements) {
195196
return (T *)mem;
196197
}
197198

199+
// Fast alternative to a loop constructor pattern.
200+
template <bool p_ensure_zero = false, typename T>
201+
_FORCE_INLINE_ void memnew_arr_placement(T *p_start, size_t p_num) {
202+
if constexpr (std::is_trivially_constructible_v<T> && !p_ensure_zero) {
203+
// Don't need to do anything :)
204+
} else if constexpr (is_zero_constructible_v<T>) {
205+
// Can optimize with memset.
206+
memset(static_cast<void *>(p_start), 0, p_num * sizeof(T));
207+
} else {
208+
// Need to use a for loop.
209+
for (size_t i = 0; i < p_num; i++) {
210+
memnew_placement(p_start + i, T);
211+
}
212+
}
213+
}
214+
198215
/**
199216
* Wonders of having own array functions, you can actually check the length of
200217
* an allocated-with memnew_arr() array

core/string/ustring.h

+4
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,10 @@ class String {
652652
}
653653
};
654654

655+
// Zero-constructing String initializes _cowdata.ptr() to nullptr and thus empty.
656+
template <>
657+
struct is_zero_constructible<String> : std::true_type {};
658+
655659
bool operator==(const char *p_chr, const String &p_str);
656660
bool operator==(const wchar_t *p_chr, const String &p_str);
657661
bool operator!=(const char *p_chr, const String &p_str);

core/templates/cowdata.h

+5-8
Original file line numberDiff line numberDiff line change
@@ -389,14 +389,7 @@ Error CowData<T>::resize(Size p_size) {
389389
}
390390

391391
// construct the newly created elements
392-
393-
if constexpr (!std::is_trivially_constructible_v<T>) {
394-
for (Size i = *_get_size(); i < p_size; i++) {
395-
memnew_placement(&_ptr[i], T);
396-
}
397-
} else if (p_ensure_zero) {
398-
memset((void *)(_ptr + current_size), 0, (p_size - current_size) * sizeof(T));
399-
}
392+
memnew_arr_placement<p_ensure_zero>(_ptr + current_size, p_size - current_size);
400393

401394
*_get_size() = p_size;
402395

@@ -523,3 +516,7 @@ CowData<T>::CowData(std::initializer_list<T> p_init) {
523516
#if defined(__GNUC__) && !defined(__clang__)
524517
#pragma GCC diagnostic pop
525518
#endif
519+
520+
// Zero-constructing CowData initializes _ptr to nullptr (and thus empty).
521+
template <typename T>
522+
struct is_zero_constructible<CowData<T>> : std::true_type {};

core/templates/local_vector.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,7 @@ class LocalVector {
160160
CRASH_COND_MSG(!data, "Out of memory");
161161
}
162162
if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
163-
for (U i = count; i < p_size; i++) {
164-
memnew_placement(&data[i], T);
165-
}
163+
memnew_arr_placement(data + count, p_size - count);
166164
}
167165
count = p_size;
168166
}
@@ -382,3 +380,7 @@ class LocalVector {
382380

383381
template <typename T, typename U = uint32_t, bool force_trivial = false>
384382
using TightLocalVector = LocalVector<T, U, force_trivial, true>;
383+
384+
// Zero-constructing LocalVector initializes count, capacity and data to 0 and thus empty.
385+
template <typename T, typename U, bool force_trivial, bool tight>
386+
struct is_zero_constructible<LocalVector<T, U, force_trivial, tight>> : std::true_type {};

core/templates/vector.h

+4
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,7 @@ void Vector<T>::fill(T p_elem) {
335335
p[i] = p_elem;
336336
}
337337
}
338+
339+
// Zero-constructing Vector initializes CowData.ptr() to nullptr and thus empty.
340+
template <typename T>
341+
struct is_zero_constructible<Vector<T>> : std::true_type {};

core/typedefs.h

+18
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,21 @@ struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {};
325325
#define ____gd_is_defined(arg1_or_junk) __gd_take_second_arg(arg1_or_junk true, false)
326326
#define ___gd_is_defined(val) ____gd_is_defined(__GDARG_PLACEHOLDER_##val)
327327
#define GD_IS_DEFINED(x) ___gd_is_defined(x)
328+
329+
// Whether the default value of a type is just all-0 bytes.
330+
// This can most commonly be exploited by using memset for these types instead of loop-construct.
331+
// Trivially constructible types are also zero-constructible.
332+
template <typename T>
333+
struct is_zero_constructible : std::is_trivially_constructible<T> {};
334+
335+
template <typename T>
336+
struct is_zero_constructible<const T> : is_zero_constructible<T> {};
337+
338+
template <typename T>
339+
struct is_zero_constructible<volatile T> : is_zero_constructible<T> {};
340+
341+
template <typename T>
342+
struct is_zero_constructible<const volatile T> : is_zero_constructible<T> {};
343+
344+
template <typename T>
345+
inline constexpr bool is_zero_constructible_v = is_zero_constructible<T>::value;

core/variant/variant.h

+4
Original file line numberDiff line numberDiff line change
@@ -1021,3 +1021,7 @@ Array::ConstIterator &Array::ConstIterator::operator--() {
10211021
element_ptr--;
10221022
return *this;
10231023
}
1024+
1025+
// Zero-constructing Variant results in NULL.
1026+
template <>
1027+
struct is_zero_constructible<Variant> : std::true_type {};

0 commit comments

Comments
 (0)