summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qarraydata.cpp
diff options
context:
space:
mode:
authorAurélien Brooke <aurelien@bahiasoft.fr>2025-01-24 17:09:58 +0100
committerAurélien Brooke <aurelien@bahiasoft.fr>2025-04-09 13:49:11 +0200
commit03d5daf9437d8b46db2e89e3a9763ea701fa681c (patch)
treef4f639b3f30e72ad15951b501435f7912eb13ce8 /src/corelib/tools/qarraydata.cpp
parentfc277e3ff655780622bcbc0b664efc69fbc33537 (diff)
Add jemalloc support
Large graphical Qt applications heavily rely on heap allocations. Jemalloc is a general-purpose malloc(3) implementation designed to reduce heap fragmentation and improve scalability. It also provides extensive tuning options. Add a -jemalloc configure option, disabled by default. When enabled, Qt and user code link to jemalloc, overriding the system's default malloc(). Add cooperation with jemalloc for some Qt key classes: QArrayData (used by QByteArray, QString and QList<T>), QBindingStoragePrivate, QDataBuffer (used by the Qt Quick renderer), QDistanceFieldData, QImageData, QObjectPrivate::TaggedSignalVector, QVarLengthArray. This cooperation relies on two jemalloc-specific optimizations: 1. Efficient allocation via fittedMalloc(): Determine the actual allocation size using nallocx(), then adjust the container’s capacity to match. This minimizes future reallocations. Note: we round allocSize to a multiple of sizeof(T) to ensure that we can later recompute the exact allocation size during deallocation. 2. Optimized deallocation via sizedFree(): Use sdallocx(), which is faster than free when the allocation size is known, as it avoids internal size lookups. Adapt the QVarLengthArray auto tests on capacity. Non-standard functions docs are at https://jemalloc.net/jemalloc.3.html [ChangeLog][QtCore] Added optional support for the jemalloc allocator, and optimized memory allocations and deallocations in core Qt classes to cooperate with it. Change-Id: I6166e64e66876dee22662d3f3ea3e42a6647cfeb Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/tools/qarraydata.cpp')
-rw-r--r--src/corelib/tools/qarraydata.cpp49
1 files changed, 35 insertions, 14 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp
index 5452fddaa89..37d6dea35f9 100644
--- a/src/corelib/tools/qarraydata.cpp
+++ b/src/corelib/tools/qarraydata.cpp
@@ -2,6 +2,7 @@
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include <QtCore/qalloc.h>
#include <QtCore/qarraydata.h>
#include <QtCore/private/qnumeric_p.h>
#include <QtCore/private/qtools_p.h>
@@ -101,12 +102,34 @@ qCalculateGrowingBlockSize(qsizetype elementCount, qsizetype elementSize, qsizet
} else {
bytes = qsizetype(morebytes);
}
+ size_t fittedBytes = QtPrivate::expectedAllocSize(bytes, alignof(std::max_align_t));
+ if (fittedBytes != 0)
+ bytes = fittedBytes;
result.elementCount = (bytes - headerSize) / elementSize;
result.size = result.elementCount * elementSize + headerSize;
return result;
}
+using QtPrivate::AlignedQArrayData;
+
+static qsizetype calculateHeaderSize(qsizetype alignment)
+{
+ qsizetype headerSize = sizeof(AlignedQArrayData);
+ const qsizetype headerAlignment = alignof(AlignedQArrayData);
+
+ if (alignment > headerAlignment) {
+ // Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding
+ // bytes so we can properly align the data array. This assumes malloc is
+ // able to provide appropriate alignment for the header -- as it should!
+ // Effectively, we allocate one QTypedArrayData<T>::AlignmentDummy.
+ headerSize += alignment - headerAlignment;
+ }
+ Q_ASSERT(headerSize > 0);
+
+ return headerSize;
+}
+
/*
Calculate the byte size for a block of \a capacity objects of size \a
objectSize, with a header of size \a headerSize. If the \a option is
@@ -140,7 +163,6 @@ struct AllocationResult {
QArrayData *header;
};
}
-using QtPrivate::AlignedQArrayData;
static inline AllocationResult
allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity,
@@ -149,16 +171,7 @@ allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity,
if (capacity == 0)
return {};
- qsizetype headerSize = sizeof(AlignedQArrayData);
- const qsizetype headerAlignment = alignof(AlignedQArrayData);
-
- if (alignment > headerAlignment) {
- // Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding
- // bytes so we can properly align the data array. This assumes malloc is
- // able to provide appropriate alignment for the header -- as it should!
- // Effectively, we allocate one QTypedArrayData<T>::AlignmentDummy.
- headerSize += alignment - headerAlignment;
- }
+ const qsizetype headerSize = calculateHeaderSize(alignment);
Q_ASSERT(headerSize > 0);
auto blockSize = calculateBlockSize(capacity, objectSize, headerSize, option);
@@ -248,10 +261,18 @@ void QArrayData::deallocate(QArrayData *data, qsizetype objectSize,
// Alignment is a power of two
Q_ASSERT(alignment >= qsizetype(alignof(QArrayData))
&& !(alignment & (alignment - 1)));
- Q_UNUSED(objectSize);
- Q_UNUSED(alignment);
- ::free(data);
+ const qsizetype capacity = data->alloc;
+ const qsizetype headerSize = calculateHeaderSize(alignment);
+ Q_ASSERT(headerSize > 0);
+ const auto blockSize = calculateBlockSize(capacity, objectSize,
+ headerSize, QArrayData::KeepSize);
+ const qsizetype allocSize = blockSize.size;
+
+ if (Q_LIKELY(allocSize > 0))
+ QtPrivate::sizedFree(data, size_t(allocSize));
+ else // something went wrong, fallback to slow free()
+ free(data);
}
QT_END_NAMESPACE