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>
This commit is contained in:
parent
fc277e3ff6
commit
03d5daf943
10
cmake/FindJeMalloc.cmake
Normal file
10
cmake/FindJeMalloc.cmake
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
|
||||||
|
pkg_check_modules(JeMalloc IMPORTED_TARGET "jemalloc")
|
||||||
|
|
||||||
|
if (NOT TARGET PkgConfig::JeMalloc)
|
||||||
|
set(JeMalloc_FOUND 0)
|
||||||
|
endif()
|
@ -272,6 +272,7 @@ Core options:
|
|||||||
-glib ................ Enable Glib support [no; auto on Unix]
|
-glib ................ Enable Glib support [no; auto on Unix]
|
||||||
-inotify ............. Enable inotify support
|
-inotify ............. Enable inotify support
|
||||||
-icu ................. Enable ICU support [auto]
|
-icu ................. Enable ICU support [auto]
|
||||||
|
-jemalloc ............ Enable jemalloc support and cooperation [no]
|
||||||
-pcre ................ Select used libpcre2 [system/qt/no]
|
-pcre ................ Select used libpcre2 [system/qt/no]
|
||||||
-zlib ................ Select used zlib [system/qt]
|
-zlib ................ Select used zlib [system/qt]
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ qt_internal_add_module(Core
|
|||||||
# Keep the rest alphabetical
|
# Keep the rest alphabetical
|
||||||
compat/removed_api.cpp
|
compat/removed_api.cpp
|
||||||
global/archdetect.cpp
|
global/archdetect.cpp
|
||||||
|
global/qalloc.cpp global/qalloc.h
|
||||||
global/qassert.cpp global/qassert.h
|
global/qassert.cpp global/qassert.h
|
||||||
global/qcheckedint_impl.h
|
global/qcheckedint_impl.h
|
||||||
global/qcompare_impl.h
|
global/qcompare_impl.h
|
||||||
@ -922,6 +923,11 @@ qt_internal_extend_target(Core CONDITION UNIX AND NOT MACOS AND NOT QT_FEATURE_i
|
|||||||
text/qcollator_posix.cpp
|
text/qcollator_posix.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qt_internal_extend_target(Core CONDITION QT_FEATURE_jemalloc
|
||||||
|
PUBLIC_LIBRARIES
|
||||||
|
PkgConfig::JeMalloc
|
||||||
|
)
|
||||||
|
|
||||||
qt_internal_extend_target(Core CONDITION QT_FEATURE_regularexpression
|
qt_internal_extend_target(Core CONDITION QT_FEATURE_regularexpression
|
||||||
SOURCES
|
SOURCES
|
||||||
text/qregularexpression.cpp text/qregularexpression.h
|
text/qregularexpression.cpp text/qregularexpression.h
|
||||||
|
@ -35,6 +35,7 @@ qt_find_package(ICU 50.1 COMPONENTS i18n uc data PROVIDED_TARGETS ICU::i18n ICU:
|
|||||||
if(QT_FEATURE_dlopen)
|
if(QT_FEATURE_dlopen)
|
||||||
qt_add_qmake_lib_dependency(icu libdl)
|
qt_add_qmake_lib_dependency(icu libdl)
|
||||||
endif()
|
endif()
|
||||||
|
qt_find_package(JeMalloc PROVIDED_TARGETS PkgConfig::JeMalloc MODULE_NAME core QMAKE_LIB jemalloc)
|
||||||
qt_find_package(Libsystemd PROVIDED_TARGETS PkgConfig::Libsystemd MODULE_NAME core QMAKE_LIB journald)
|
qt_find_package(Libsystemd PROVIDED_TARGETS PkgConfig::Libsystemd MODULE_NAME core QMAKE_LIB journald)
|
||||||
qt_find_package(WrapAtomic PROVIDED_TARGETS WrapAtomic::WrapAtomic MODULE_NAME core QMAKE_LIB libatomic)
|
qt_find_package(WrapAtomic PROVIDED_TARGETS WrapAtomic::WrapAtomic MODULE_NAME core QMAKE_LIB libatomic)
|
||||||
qt_find_package(Libb2 PROVIDED_TARGETS Libb2::Libb2 MODULE_NAME core QMAKE_LIB libb2)
|
qt_find_package(Libb2 PROVIDED_TARGETS Libb2::Libb2 MODULE_NAME core QMAKE_LIB libb2)
|
||||||
@ -733,6 +734,11 @@ qt_feature("ipc_posix"
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
qt_feature_definition("ipc_posix" "QT_POSIX_IPC")
|
qt_feature_definition("ipc_posix" "QT_POSIX_IPC")
|
||||||
|
qt_feature("jemalloc" PUBLIC PRIVATE
|
||||||
|
LABEL "JeMalloc"
|
||||||
|
AUTODETECT OFF
|
||||||
|
CONDITION JeMalloc_FOUND
|
||||||
|
)
|
||||||
qt_feature("journald" PRIVATE
|
qt_feature("journald" PRIVATE
|
||||||
LABEL "journald"
|
LABEL "journald"
|
||||||
AUTODETECT OFF
|
AUTODETECT OFF
|
||||||
@ -1172,6 +1178,7 @@ qt_configure_add_summary_entry(ARGS "system-doubleconversion")
|
|||||||
qt_configure_add_summary_entry(ARGS "forkfd_pidfd" CONDITION LINUX)
|
qt_configure_add_summary_entry(ARGS "forkfd_pidfd" CONDITION LINUX)
|
||||||
qt_configure_add_summary_entry(ARGS "glib")
|
qt_configure_add_summary_entry(ARGS "glib")
|
||||||
qt_configure_add_summary_entry(ARGS "icu")
|
qt_configure_add_summary_entry(ARGS "icu")
|
||||||
|
qt_configure_add_summary_entry(ARGS "jemalloc")
|
||||||
qt_configure_add_summary_entry(ARGS "timezone_tzdb")
|
qt_configure_add_summary_entry(ARGS "timezone_tzdb")
|
||||||
qt_configure_add_summary_entry(ARGS "system-libb2")
|
qt_configure_add_summary_entry(ARGS "system-libb2")
|
||||||
qt_configure_add_summary_entry(ARGS "mimetype-database")
|
qt_configure_add_summary_entry(ARGS "mimetype-database")
|
||||||
|
43
src/corelib/global/qalloc.cpp
Normal file
43
src/corelib/global/qalloc.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
#include "qalloc.h"
|
||||||
|
|
||||||
|
#include <QtCore/qalgorithms.h>
|
||||||
|
#include <QtCore/qtpreprocessorsupport.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#if QT_CONFIG(jemalloc)
|
||||||
|
#include <jemalloc/jemalloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
size_t QtPrivate::expectedAllocSize(size_t allocSize, size_t alignment) noexcept
|
||||||
|
{
|
||||||
|
Q_ASSERT(qPopulationCount(alignment) == 1);
|
||||||
|
#if QT_CONFIG(jemalloc)
|
||||||
|
return ::nallocx(allocSize, MALLOCX_ALIGN(alignment));
|
||||||
|
#endif
|
||||||
|
Q_UNUSED(allocSize);
|
||||||
|
Q_UNUSED(alignment);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtPrivate::sizedFree(void *ptr, size_t allocSize) noexcept
|
||||||
|
{
|
||||||
|
#if QT_CONFIG(jemalloc)
|
||||||
|
// jemalloc is okay with free(nullptr), as required by the standard,
|
||||||
|
// but will asssert (in debug) or invoke UB (in release) on sdallocx(nullptr, ...),
|
||||||
|
// so don't allow Qt to do that.
|
||||||
|
if (Q_LIKELY(ptr)) {
|
||||||
|
::sdallocx(ptr, allocSize, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Q_UNUSED(allocSize);
|
||||||
|
::free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
128
src/corelib/global/qalloc.h
Normal file
128
src/corelib/global/qalloc.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
#ifndef QALLOC_H
|
||||||
|
#define QALLOC_H
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// This file is not part of the Qt API. It exists purely as an
|
||||||
|
// implementation detail. This header file may change from version to
|
||||||
|
// version without notice, or even be removed.
|
||||||
|
//
|
||||||
|
// We mean it.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtCore/qtconfigmacros.h>
|
||||||
|
#include <QtCore/qtcoreexports.h>
|
||||||
|
#include <QtCore/qnumeric.h>
|
||||||
|
#include <QtCore/qtypeinfo.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace QtPrivate {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \return the size that would be allocated for the given request.
|
||||||
|
*
|
||||||
|
* Computes the actual allocation size for \a allocSize and \a alignment,
|
||||||
|
* as determined by the active allocator, without performing the allocation.
|
||||||
|
*
|
||||||
|
* In practice, it only returns nonzero when using jemalloc.
|
||||||
|
*/
|
||||||
|
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION
|
||||||
|
size_t expectedAllocSize(size_t allocSize, size_t alignment) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \brief Computes the best allocation size for the requested minimum capacity, and updates capacity.
|
||||||
|
*
|
||||||
|
* Computes the allocation size starting from \a headerSize and a requested minimum capacity in \a capacity,
|
||||||
|
* multiplied by the \a elementSize and adjusted by the \a unusedCapacity.
|
||||||
|
* The final capacity is written back into \a capacity.
|
||||||
|
* The \a headerSize and \a unusedCapacity values are not included in the final reported capacity.
|
||||||
|
*/
|
||||||
|
inline size_t fittedAllocSize(size_t headerSize, size_t *capacity,
|
||||||
|
size_t elementSize, size_t unusedCapacity, size_t alignment) noexcept
|
||||||
|
{
|
||||||
|
size_t totalCapacity = 0; // = capacity + unusedCapacity
|
||||||
|
if (Q_UNLIKELY(qAddOverflow(*capacity, unusedCapacity, &totalCapacity)))
|
||||||
|
return 0; // or handle error
|
||||||
|
|
||||||
|
size_t payloadSize = 0; // = totalCapacity * elementSize
|
||||||
|
if (Q_UNLIKELY(qMulOverflow(totalCapacity, elementSize, &payloadSize)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t allocSize = 0; // = headerSize + payloadSize
|
||||||
|
if (Q_UNLIKELY(qAddOverflow(headerSize, payloadSize, &allocSize)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (size_t fittedSize = expectedAllocSize(allocSize, alignment); fittedSize != 0) {
|
||||||
|
// no need to overflow/underflow check from fittedSize,
|
||||||
|
// since allocSize <= fittedSize <= SIZE_T_MAX
|
||||||
|
*capacity = (fittedSize - headerSize) / elementSize - unusedCapacity;
|
||||||
|
size_t newTotalCapacity = *capacity + unusedCapacity;
|
||||||
|
size_t newPayloadSize = newTotalCapacity * elementSize;
|
||||||
|
return headerSize + newPayloadSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allocSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_CC_GNU
|
||||||
|
__attribute__((malloc))
|
||||||
|
#endif
|
||||||
|
inline void *fittedMalloc(size_t headerSize, size_t *capacity,
|
||||||
|
size_t elementSize, size_t unusedCapacity) noexcept
|
||||||
|
{
|
||||||
|
size_t allocSize = fittedAllocSize(headerSize, capacity,
|
||||||
|
elementSize, unusedCapacity, alignof(std::max_align_t));
|
||||||
|
if (Q_LIKELY(allocSize != 0))
|
||||||
|
return malloc(allocSize);
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
inline void *fittedMalloc(size_t headerSize, qsizetype *capacity,
|
||||||
|
size_t elementSize, size_t unusedCapacity = 0) noexcept
|
||||||
|
{
|
||||||
|
size_t uCapacity = size_t(*capacity);
|
||||||
|
void *ptr = fittedMalloc(headerSize, &uCapacity, elementSize, unusedCapacity);
|
||||||
|
*capacity = qsizetype(uCapacity);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void *fittedRealloc(void *ptr, size_t headerSize, size_t *capacity,
|
||||||
|
size_t elementSize, size_t unusedCapacity) noexcept
|
||||||
|
{
|
||||||
|
size_t allocSize = fittedAllocSize(headerSize, capacity,
|
||||||
|
elementSize, unusedCapacity, alignof(std::max_align_t));
|
||||||
|
if (Q_LIKELY(allocSize != 0))
|
||||||
|
return realloc(ptr, allocSize);
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
inline void *fittedRealloc(void *ptr, size_t headerSize, qsizetype *capacity,
|
||||||
|
size_t elementSize, size_t unusedCapacity = 0) noexcept
|
||||||
|
{
|
||||||
|
size_t uCapacity = size_t(*capacity);
|
||||||
|
ptr = fittedRealloc(ptr, headerSize, &uCapacity, elementSize, unusedCapacity);
|
||||||
|
*capacity = qsizetype(uCapacity);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_CORE_EXPORT void sizedFree(void *ptr, size_t allocSize) noexcept;
|
||||||
|
inline void sizedFree(void *ptr, size_t capacity, size_t elementSize) noexcept
|
||||||
|
{
|
||||||
|
sizedFree(ptr, capacity * elementSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QtPrivate
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QALLOC_H
|
@ -62,6 +62,7 @@
|
|||||||
#define QT_FEATURE_itemmodel -1
|
#define QT_FEATURE_itemmodel -1
|
||||||
#define QT_FEATURE_islamiccivilcalendar -1
|
#define QT_FEATURE_islamiccivilcalendar -1
|
||||||
#define QT_FEATURE_jalalicalendar -1
|
#define QT_FEATURE_jalalicalendar -1
|
||||||
|
#define QT_FEATURE_jemalloc -1
|
||||||
#define QT_FEATURE_journald -1
|
#define QT_FEATURE_journald -1
|
||||||
#define QT_FEATURE_library -1
|
#define QT_FEATURE_library -1
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
// code lives here is that some special apps/libraries for e.g., QtJambi,
|
// code lives here is that some special apps/libraries for e.g., QtJambi,
|
||||||
// Gammaray need access to the structs in this file.
|
// Gammaray need access to the structs in this file.
|
||||||
|
|
||||||
|
#include <QtCore/qalloc.h>
|
||||||
#include <QtCore/qobject.h>
|
#include <QtCore/qobject.h>
|
||||||
#include <QtCore/private/qobject_p.h>
|
#include <QtCore/private/qobject_p.h>
|
||||||
|
|
||||||
@ -152,8 +153,9 @@ struct QObjectPrivate::ConnectionData
|
|||||||
deleteOrphaned(c);
|
deleteOrphaned(c);
|
||||||
SignalVector *v = signalVector.loadRelaxed();
|
SignalVector *v = signalVector.loadRelaxed();
|
||||||
if (v) {
|
if (v) {
|
||||||
|
const size_t allocSize = sizeof(SignalVector) + (v->allocated + 1) * sizeof(ConnectionList);
|
||||||
v->~SignalVector();
|
v->~SignalVector();
|
||||||
free(v);
|
QtPrivate::sizedFree(v, allocSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,13 +181,13 @@ struct QObjectPrivate::ConnectionData
|
|||||||
return signalVector.loadRelaxed()->at(signal);
|
return signalVector.loadRelaxed()->at(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resizeSignalVector(uint size)
|
void resizeSignalVector(size_t size)
|
||||||
{
|
{
|
||||||
SignalVector *vector = this->signalVector.loadRelaxed();
|
SignalVector *vector = this->signalVector.loadRelaxed();
|
||||||
if (vector && vector->allocated > size)
|
if (vector && vector->allocated > size)
|
||||||
return;
|
return;
|
||||||
size = (size + 7) & ~7;
|
size = (size + 7) & ~7;
|
||||||
void *ptr = malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList));
|
void *ptr = QtPrivate::fittedMalloc(sizeof(SignalVector), &size, sizeof(ConnectionList), 1);
|
||||||
auto newVector = new (ptr) SignalVector;
|
auto newVector = new (ptr) SignalVector;
|
||||||
|
|
||||||
int start = -1;
|
int start = -1;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <qscopedvaluerollback.h>
|
#include <qscopedvaluerollback.h>
|
||||||
#include <QScopeGuard>
|
#include <QScopeGuard>
|
||||||
|
#include <QtCore/qalloc.h>
|
||||||
#include <QtCore/qloggingcategory.h>
|
#include <QtCore/qloggingcategory.h>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QtCore/qmetaobject.h>
|
#include <QtCore/qmetaobject.h>
|
||||||
@ -2212,7 +2213,8 @@ struct QBindingStoragePrivate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// data has been moved, no need to call destructors on old Pairs
|
// data has been moved, no need to call destructors on old Pairs
|
||||||
free(d);
|
const size_t oldAllocSize = sizeof(QBindingStorageData) + d->size*sizeof(Pair);
|
||||||
|
QtPrivate::sizedFree(d, oldAllocSize);
|
||||||
d = newData;
|
d = newData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2269,7 +2271,8 @@ struct QBindingStoragePrivate
|
|||||||
p->~Pair();
|
p->~Pair();
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
free(d);
|
const size_t allocSize = sizeof(QBindingStorageData) + d->size*sizeof(Pair);
|
||||||
|
QtPrivate::sizedFree(d, allocSize);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ qt_commandline_option(doubleconversion TYPE enum VALUES no qt system)
|
|||||||
qt_commandline_option(glib TYPE boolean)
|
qt_commandline_option(glib TYPE boolean)
|
||||||
qt_commandline_option(icu TYPE boolean)
|
qt_commandline_option(icu TYPE boolean)
|
||||||
qt_commandline_option(inotify TYPE boolean)
|
qt_commandline_option(inotify TYPE boolean)
|
||||||
|
qt_commandline_option(jemalloc TYPE boolean)
|
||||||
qt_commandline_option(journald TYPE boolean)
|
qt_commandline_option(journald TYPE boolean)
|
||||||
qt_commandline_option(libb2 TYPE enum VALUES no qt system)
|
qt_commandline_option(libb2 TYPE enum VALUES no qt system)
|
||||||
qt_commandline_option(mimetype-database TYPE boolean)
|
qt_commandline_option(mimetype-database TYPE boolean)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Copyright (C) 2016 Intel Corporation.
|
// 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
|
// 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/qarraydata.h>
|
||||||
#include <QtCore/private/qnumeric_p.h>
|
#include <QtCore/private/qnumeric_p.h>
|
||||||
#include <QtCore/private/qtools_p.h>
|
#include <QtCore/private/qtools_p.h>
|
||||||
@ -101,12 +102,34 @@ qCalculateGrowingBlockSize(qsizetype elementCount, qsizetype elementSize, qsizet
|
|||||||
} else {
|
} else {
|
||||||
bytes = qsizetype(morebytes);
|
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.elementCount = (bytes - headerSize) / elementSize;
|
||||||
result.size = result.elementCount * elementSize + headerSize;
|
result.size = result.elementCount * elementSize + headerSize;
|
||||||
return result;
|
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
|
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
|
objectSize, with a header of size \a headerSize. If the \a option is
|
||||||
@ -140,7 +163,6 @@ struct AllocationResult {
|
|||||||
QArrayData *header;
|
QArrayData *header;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
using QtPrivate::AlignedQArrayData;
|
|
||||||
|
|
||||||
static inline AllocationResult
|
static inline AllocationResult
|
||||||
allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity,
|
allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity,
|
||||||
@ -149,16 +171,7 @@ allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity,
|
|||||||
if (capacity == 0)
|
if (capacity == 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
qsizetype headerSize = sizeof(AlignedQArrayData);
|
const qsizetype headerSize = calculateHeaderSize(alignment);
|
||||||
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);
|
Q_ASSERT(headerSize > 0);
|
||||||
|
|
||||||
auto blockSize = calculateBlockSize(capacity, objectSize, headerSize, option);
|
auto blockSize = calculateBlockSize(capacity, objectSize, headerSize, option);
|
||||||
@ -248,10 +261,18 @@ void QArrayData::deallocate(QArrayData *data, qsizetype objectSize,
|
|||||||
// Alignment is a power of two
|
// Alignment is a power of two
|
||||||
Q_ASSERT(alignment >= qsizetype(alignof(QArrayData))
|
Q_ASSERT(alignment >= qsizetype(alignof(QArrayData))
|
||||||
&& !(alignment & (alignment - 1)));
|
&& !(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
|
QT_END_NAMESPACE
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#pragma qt_sync_stop_processing
|
#pragma qt_sync_stop_processing
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QtCore/qalloc.h>
|
||||||
#include <QtCore/qcompare.h>
|
#include <QtCore/qcompare.h>
|
||||||
#include <QtCore/qcontainerfwd.h>
|
#include <QtCore/qcontainerfwd.h>
|
||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qglobal.h>
|
||||||
@ -368,7 +369,7 @@ public:
|
|||||||
if constexpr (QTypeInfo<T>::isComplex)
|
if constexpr (QTypeInfo<T>::isComplex)
|
||||||
std::destroy_n(data(), size());
|
std::destroy_n(data(), size());
|
||||||
if (data() != reinterpret_cast<T *>(this->array))
|
if (data() != reinterpret_cast<T *>(this->array))
|
||||||
free(data());
|
QtPrivate::sizedFree(data(), capacity(), sizeof(T));
|
||||||
}
|
}
|
||||||
inline QVarLengthArray<T, Prealloc> &operator=(const QVarLengthArray<T, Prealloc> &other)
|
inline QVarLengthArray<T, Prealloc> &operator=(const QVarLengthArray<T, Prealloc> &other)
|
||||||
{
|
{
|
||||||
@ -729,9 +730,9 @@ Q_INLINE_TEMPLATE QVarLengthArray<T, Prealloc>::QVarLengthArray(qsizetype asize)
|
|||||||
// resize(asize) // this requires a movable or copyable T, can't use, need to do it by hand
|
// resize(asize) // this requires a movable or copyable T, can't use, need to do it by hand
|
||||||
|
|
||||||
if (asize > Prealloc) {
|
if (asize > Prealloc) {
|
||||||
this->ptr = malloc(asize * sizeof(T));
|
|
||||||
Q_CHECK_PTR(this->ptr);
|
|
||||||
this->a = asize;
|
this->a = asize;
|
||||||
|
this->ptr = QtPrivate::fittedMalloc(0, &this->a, sizeof(T));
|
||||||
|
Q_CHECK_PTR(this->ptr);
|
||||||
}
|
}
|
||||||
if constexpr (QTypeInfo<T>::isComplex)
|
if constexpr (QTypeInfo<T>::isComplex)
|
||||||
std::uninitialized_default_construct_n(data(), asize);
|
std::uninitialized_default_construct_n(data(), asize);
|
||||||
@ -877,7 +878,7 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void
|
|||||||
void *newPtr;
|
void *newPtr;
|
||||||
qsizetype newA;
|
qsizetype newA;
|
||||||
if (aalloc > prealloc) {
|
if (aalloc > prealloc) {
|
||||||
newPtr = malloc(aalloc * sizeof(T));
|
newPtr = QtPrivate::fittedMalloc(0, &aalloc, sizeof(T));
|
||||||
guard.reset(newPtr);
|
guard.reset(newPtr);
|
||||||
Q_CHECK_PTR(newPtr); // could throw
|
Q_CHECK_PTR(newPtr); // could throw
|
||||||
// by design: in case of QT_NO_EXCEPTIONS malloc must not fail or it crashes here
|
// by design: in case of QT_NO_EXCEPTIONS malloc must not fail or it crashes here
|
||||||
@ -902,7 +903,7 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldPtr != reinterpret_cast<T *>(array) && oldPtr != data())
|
if (oldPtr != reinterpret_cast<T *>(array) && oldPtr != data())
|
||||||
free(oldPtr);
|
QtPrivate::sizedFree(oldPtr, osize, sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <qpa/qplatformpixmap.h>
|
#include <qpa/qplatformpixmap.h>
|
||||||
|
#include <qalloc.h>
|
||||||
#include <private/qcolorspace_p.h>
|
#include <private/qcolorspace_p.h>
|
||||||
#include <private/qcolortransform_p.h>
|
#include <private/qcolortransform_p.h>
|
||||||
#include <private/qmemrotate_p.h>
|
#include <private/qmemrotate_p.h>
|
||||||
@ -162,7 +163,7 @@ QImageData::~QImageData()
|
|||||||
QImagePixmapCleanupHooks::executeImageHooks((((qint64) ser_no) << 32) | ((qint64) detach_no));
|
QImagePixmapCleanupHooks::executeImageHooks((((qint64) ser_no) << 32) | ((qint64) detach_no));
|
||||||
delete paintEngine;
|
delete paintEngine;
|
||||||
if (data && own_data)
|
if (data && own_data)
|
||||||
free(data);
|
QtPrivate::sizedFree(data, nbytes);
|
||||||
data = nullptr;
|
data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <QtGui/private/qtguiglobal_p.h>
|
#include <QtGui/private/qtguiglobal_p.h>
|
||||||
|
|
||||||
|
#include "QtCore/qalloc.h"
|
||||||
#include "QtCore/qbytearray.h"
|
#include "QtCore/qbytearray.h"
|
||||||
#include "QtCore/qtypeinfo.h"
|
#include "QtCore/qtypeinfo.h"
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ public:
|
|||||||
if (res) {
|
if (res) {
|
||||||
QT_WARNING_PUSH
|
QT_WARNING_PUSH
|
||||||
QT_WARNING_DISABLE_GCC("-Walloc-size-larger-than=")
|
QT_WARNING_DISABLE_GCC("-Walloc-size-larger-than=")
|
||||||
buffer = (Type*) malloc(capacity * sizeof(Type));
|
buffer = (Type*) QtPrivate::fittedMalloc(0, &capacity, sizeof(Type));
|
||||||
QT_WARNING_POP
|
QT_WARNING_POP
|
||||||
Q_CHECK_PTR(buffer);
|
Q_CHECK_PTR(buffer);
|
||||||
} else {
|
} else {
|
||||||
@ -47,7 +48,7 @@ public:
|
|||||||
{
|
{
|
||||||
static_assert(!QTypeInfo<Type>::isComplex);
|
static_assert(!QTypeInfo<Type>::isComplex);
|
||||||
if (buffer)
|
if (buffer)
|
||||||
free(buffer);
|
QtPrivate::sizedFree(buffer, capacity, sizeof(Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void reset() { siz = 0; }
|
inline void reset() { siz = 0; }
|
||||||
@ -86,20 +87,21 @@ public:
|
|||||||
capacity = 1;
|
capacity = 1;
|
||||||
while (capacity < size)
|
while (capacity < size)
|
||||||
capacity *= 2;
|
capacity *= 2;
|
||||||
buffer = (Type*) realloc(static_cast<void*>(buffer), capacity * sizeof(Type));
|
buffer = (Type*) QtPrivate::fittedRealloc(static_cast<void*>(buffer), 0, &capacity, sizeof(Type));
|
||||||
Q_CHECK_PTR(buffer);
|
Q_CHECK_PTR(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void shrink(qsizetype size) {
|
void shrink(qsizetype size) {
|
||||||
Q_ASSERT(capacity >= size);
|
Q_ASSERT(capacity >= size);
|
||||||
capacity = size;
|
|
||||||
if (size) {
|
if (size) {
|
||||||
buffer = (Type*) realloc(static_cast<void*>(buffer), capacity * sizeof(Type));
|
capacity = size;
|
||||||
|
buffer = (Type*) QtPrivate::fittedRealloc(static_cast<void*>(buffer), 0, &capacity, sizeof(Type));
|
||||||
Q_CHECK_PTR(buffer);
|
Q_CHECK_PTR(buffer);
|
||||||
siz = std::min(siz, size);
|
siz = std::min(siz, size);
|
||||||
} else {
|
} else {
|
||||||
free(buffer);
|
QtPrivate::sizedFree(buffer, capacity, sizeof(Type));
|
||||||
|
capacity = size;
|
||||||
buffer = nullptr;
|
buffer = nullptr;
|
||||||
siz = 0;
|
siz = 0;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "qdistancefield_p.h"
|
#include "qdistancefield_p.h"
|
||||||
#include <qmath.h>
|
#include <qmath.h>
|
||||||
|
#include <QtCore/qalloc.h>
|
||||||
#include <private/qdatabuffer_p.h>
|
#include <private/qdatabuffer_p.h>
|
||||||
#include <private/qimage_p.h>
|
#include <private/qimage_p.h>
|
||||||
#include <private/qpathsimplifier_p.h>
|
#include <private/qpathsimplifier_p.h>
|
||||||
@ -826,7 +827,7 @@ QDistanceFieldData::QDistanceFieldData(const QDistanceFieldData &other)
|
|||||||
|
|
||||||
QDistanceFieldData::~QDistanceFieldData()
|
QDistanceFieldData::~QDistanceFieldData()
|
||||||
{
|
{
|
||||||
free(data);
|
QtPrivate::sizedFree(data, nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDistanceFieldData *QDistanceFieldData::create(const QSize &size)
|
QDistanceFieldData *QDistanceFieldData::create(const QSize &size)
|
||||||
|
@ -17,6 +17,7 @@ qt_internal_add_sync_header_dependencies(Bootstrap Core)
|
|||||||
|
|
||||||
qt_internal_extend_target(Bootstrap
|
qt_internal_extend_target(Bootstrap
|
||||||
SOURCES
|
SOURCES
|
||||||
|
../../corelib/global/qalloc.cpp
|
||||||
../../corelib/global/qassert.cpp
|
../../corelib/global/qassert.cpp
|
||||||
../../corelib/global/qtenvironmentvariables.cpp
|
../../corelib/global/qtenvironmentvariables.cpp
|
||||||
../../corelib/io/qabstractfileengine.cpp
|
../../corelib/io/qabstractfileengine.cpp
|
||||||
|
@ -1171,11 +1171,11 @@ void tst_QVarLengthArray::squeeze()
|
|||||||
list.resize(0);
|
list.resize(0);
|
||||||
QCOMPARE(list.capacity(), sizeOnStack);
|
QCOMPARE(list.capacity(), sizeOnStack);
|
||||||
list.resize(sizeOnHeap);
|
list.resize(sizeOnHeap);
|
||||||
QCOMPARE(list.capacity(), sizeOnHeap);
|
QCOMPARE_GE(list.capacity(), sizeOnHeap);
|
||||||
list.resize(sizeOnStack);
|
list.resize(sizeOnStack);
|
||||||
QCOMPARE(list.capacity(), sizeOnHeap);
|
QCOMPARE_GE(list.capacity(), sizeOnHeap);
|
||||||
list.resize(0);
|
list.resize(0);
|
||||||
QCOMPARE(list.capacity(), sizeOnHeap);
|
QCOMPARE_GE(list.capacity(), sizeOnHeap);
|
||||||
list.squeeze();
|
list.squeeze();
|
||||||
QCOMPARE(list.capacity(), sizeOnStack);
|
QCOMPARE(list.capacity(), sizeOnStack);
|
||||||
list.resize(sizeOnStack);
|
list.resize(sizeOnStack);
|
||||||
@ -1183,7 +1183,7 @@ void tst_QVarLengthArray::squeeze()
|
|||||||
QCOMPARE(list.capacity(), sizeOnStack);
|
QCOMPARE(list.capacity(), sizeOnStack);
|
||||||
list.resize(sizeOnHeap);
|
list.resize(sizeOnHeap);
|
||||||
list.squeeze();
|
list.squeeze();
|
||||||
QCOMPARE(list.capacity(), sizeOnHeap);
|
QCOMPARE_GE(list.capacity(), sizeOnHeap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QVarLengthArray::operators()
|
void tst_QVarLengthArray::operators()
|
||||||
@ -1545,14 +1545,14 @@ void tst_QVarLengthArray::reserve()
|
|||||||
|
|
||||||
arr.reserve(150);
|
arr.reserve(150);
|
||||||
// Allocate memory on heap, as we reserve more than pre-allocated
|
// Allocate memory on heap, as we reserve more than pre-allocated
|
||||||
QCOMPARE(arr.capacity(), 150);
|
QCOMPARE_GE(arr.capacity(), 150);
|
||||||
QCOMPARE(arr.size(), 0);
|
QCOMPARE(arr.size(), 0);
|
||||||
const auto *heapPtr = arr.constData();
|
const auto *heapPtr = arr.constData();
|
||||||
QVERIFY(heapPtr != stackPtr);
|
QVERIFY(heapPtr != stackPtr);
|
||||||
|
|
||||||
arr.reserve(50);
|
arr.reserve(50);
|
||||||
// Nothing changed
|
// Nothing changed
|
||||||
QCOMPARE(arr.capacity(), 150);
|
QCOMPARE_GE(arr.capacity(), 150);
|
||||||
QCOMPARE(arr.constData(), heapPtr);
|
QCOMPARE(arr.constData(), heapPtr);
|
||||||
|
|
||||||
arr.squeeze();
|
arr.squeeze();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user