diff options
| author | Aurélien Brooke <aurelien@bahiasoft.fr> | 2025-01-24 17:09:58 +0100 |
|---|---|---|
| committer | Aurélien Brooke <aurelien@bahiasoft.fr> | 2025-04-09 13:49:11 +0200 |
| commit | 03d5daf9437d8b46db2e89e3a9763ea701fa681c (patch) | |
| tree | f4f639b3f30e72ad15951b501435f7912eb13ce8 /src/corelib/tools/qarraydata.cpp | |
| parent | fc277e3ff655780622bcbc0b664efc69fbc33537 (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.cpp | 49 |
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 |
