QIODevice: implement a "zero-copy" strategy for buffered writes

It works as follows:

  - user calls write(const QByteArray &);
  - this function keeps a pointer to the chunk and calls a regular
    write(data, len);
  - write(data, len) calls a virtual writeData();
  - subclass calls a new QIODevicePrivate::write();
  - QIODevicePrivate::write() makes a shallow copy of
    the byte array.

Proposed solution is fully compatible with existing subclasses.
By replacing a call to d->writeBuffer.append() with d->write(),
subclasses can improve their performance.

Bump the TypeInformationVersion field in qtHookData, to notify the
Qt Creator developers that the offset of QFilePrivate::fileName was
changed and dumpers should be adapted.

Change-Id: I24713386cc74a9f37e5223c617e4b1ba97f968dc
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Alex Trotsenko 2020-08-11 19:30:15 +03:00
parent 729d102b1c
commit a41c61fb2d
8 changed files with 40 additions and 10 deletions

View File

@ -67,7 +67,7 @@ quintptr Q_CORE_EXPORT qtHookData[] = {
// The required sizes and offsets are tested in tests/auto/other/toolsupport. // The required sizes and offsets are tested in tests/auto/other/toolsupport.
// When this fails and the change was intentional, adjust the test and // When this fails and the change was intentional, adjust the test and
// adjust this value here. // adjust this value here.
18 19
}; };
static_assert(QHooks::LastHookIndex == sizeof(qtHookData) / sizeof(qtHookData[0])); static_assert(QHooks::LastHookIndex == sizeof(qtHookData) / sizeof(qtHookData[0]));

View File

@ -158,13 +158,14 @@ static void checkWarnMessage(const QIODevice *device, const char *function, cons
QIODevicePrivate::QIODevicePrivate() QIODevicePrivate::QIODevicePrivate()
: openMode(QIODevice::NotOpen), : openMode(QIODevice::NotOpen),
pos(0), devicePos(0), pos(0), devicePos(0),
transactionPos(0),
readChannelCount(0), readChannelCount(0),
writeChannelCount(0), writeChannelCount(0),
currentReadChannel(0), currentReadChannel(0),
currentWriteChannel(0), currentWriteChannel(0),
readBufferChunkSize(QIODEVICE_BUFFERSIZE), readBufferChunkSize(QIODEVICE_BUFFERSIZE),
writeBufferChunkSize(0), writeBufferChunkSize(0),
transactionPos(0), currentWriteChunk(nullptr),
transactionStarted(false) transactionStarted(false)
, baseReadLineDataCalled(false) , baseReadLineDataCalled(false)
, accessMode(Unset) , accessMode(Unset)
@ -1750,7 +1751,34 @@ qint64 QIODevice::write(const char *data)
qint64 QIODevice::write(const QByteArray &data) qint64 QIODevice::write(const QByteArray &data)
{ {
return write(data.constData(), data.size()); Q_D(QIODevice);
// Keep the chunk pointer for further processing in
// QIODevicePrivate::write(). To reduce fragmentation,
// the chunk size must be sufficiently large.
if (data.size() >= QRINGBUFFER_CHUNKSIZE)
d->currentWriteChunk = &data;
const qint64 ret = write(data.constData(), data.size());
d->currentWriteChunk = nullptr;
return ret;
}
/*!
\internal
*/
void QIODevicePrivate::write(const char *data, qint64 size)
{
if (currentWriteChunk != nullptr
&& currentWriteChunk->constData() == data
&& currentWriteChunk->size() == size) {
// We are called from write(const QByteArray &) overload.
// So, we can make a shallow copy of chunk.
writeBuffer.append(*currentWriteChunk);
} else {
writeBuffer.append(data, size);
}
} }
/*! /*!

View File

@ -124,13 +124,14 @@ public:
QRingBufferRef writeBuffer; QRingBufferRef writeBuffer;
qint64 pos; qint64 pos;
qint64 devicePos; qint64 devicePos;
qint64 transactionPos;
int readChannelCount; int readChannelCount;
int writeChannelCount; int writeChannelCount;
int currentReadChannel; int currentReadChannel;
int currentWriteChannel; int currentWriteChannel;
int readBufferChunkSize; int readBufferChunkSize;
int writeBufferChunkSize; int writeBufferChunkSize;
qint64 transactionPos; const QByteArray *currentWriteChunk;
bool transactionStarted; bool transactionStarted;
bool baseReadLineDataCalled; bool baseReadLineDataCalled;
@ -175,6 +176,7 @@ public:
virtual qint64 peek(char *data, qint64 maxSize); virtual qint64 peek(char *data, qint64 maxSize);
virtual QByteArray peek(qint64 maxSize); virtual QByteArray peek(qint64 maxSize);
qint64 skipByReading(qint64 maxSize); qint64 skipByReading(qint64 maxSize);
void write(const char *data, qint64 size);
#ifdef QT_NO_QOBJECT #ifdef QT_NO_QOBJECT
QIODevice *q_ptr; QIODevice *q_ptr;

View File

@ -1899,7 +1899,7 @@ qint64 QProcess::writeData(const char *data, qint64 len)
} }
#endif #endif
d->writeBuffer.append(data, len); d->write(data, len);
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (!d->stdinWriteTrigger->isActive()) if (!d->stdinWriteTrigger->isActive())
d->stdinWriteTrigger->start(); d->stdinWriteTrigger->start();

View File

@ -2542,7 +2542,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
// We just write to our write buffer and enable the write notifier // We just write to our write buffer and enable the write notifier
// The write notifier then flush()es the buffer. // The write notifier then flush()es the buffer.
d->writeBuffer.append(data, size); d->write(data, size);
qint64 written = size; qint64 written = size;
if (d->socketEngine && !d->writeBuffer.isEmpty()) if (d->socketEngine && !d->writeBuffer.isEmpty())

View File

@ -214,7 +214,7 @@ qint64 QLocalSocket::writeData(const char *data, qint64 len)
Q_D(QLocalSocket); Q_D(QLocalSocket);
if (len == 0) if (len == 0)
return 0; return 0;
d->writeBuffer.append(data, len); d->write(data, len);
if (!d->pipeWriter) { if (!d->pipeWriter) {
d->pipeWriter = new QWindowsPipeWriter(d->handle, this); d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
connect(d->pipeWriter, &QWindowsPipeWriter::bytesWritten, connect(d->pipeWriter, &QWindowsPipeWriter::bytesWritten,

View File

@ -1819,7 +1819,7 @@ qint64 QSslSocket::writeData(const char *data, qint64 len)
if (d->mode == UnencryptedMode && !d->autoStartHandshake) if (d->mode == UnencryptedMode && !d->autoStartHandshake)
return d->plainSocket->write(data, len); return d->plainSocket->write(data, len);
d->writeBuffer.append(data, len); d->write(data, len);
// make sure we flush to the plain socket's buffer // make sure we flush to the plain socket's buffer
if (!d->flushTriggered) { if (!d->flushTriggered) {

View File

@ -126,9 +126,9 @@ void tst_toolsupport::offsets_data()
#ifdef Q_PROCESSOR_X86 #ifdef Q_PROCESSOR_X86
// x86 32-bit has weird alignment rules. Refer to QtPrivate::AlignOf in // x86 32-bit has weird alignment rules. Refer to QtPrivate::AlignOf in
// qglobal.h for more details. // qglobal.h for more details.
data << 184 << 288; data << 188 << 296;
#else #else
data << 188 << 288; data << 192 << 296;
#endif #endif
} }
#endif #endif