QRingBuffer: avoid allocation in c'tor

Reduces creation time and memory consumption when the buffer is unused.
Robin's benchmark:

  QBENCHMARK {
      QFile file(filename);
  }

reports the following results (run with -median 10 -iterations 524288):

before: 0.00028 msecs per iteration (total: 149, iterations: 524288)
after:  0.00017 msecs per iteration (total: 93, iterations: 524288).

Change-Id: Ied4e7caeca794b94260b8fc59b3ba656f4719c30
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
This commit is contained in:
Alex Trotsenko 2015-07-02 10:55:02 +03:00
parent 61ad604ad4
commit c3d8ab78b8
3 changed files with 54 additions and 27 deletions

View File

@ -65,6 +65,8 @@ const char *QRingBuffer::readPointerAtPosition(qint64 pos, qint64 &length) const
void QRingBuffer::free(qint64 bytes)
{
Q_ASSERT(bytes <= bufferSize);
while (bytes > 0) {
const qint64 blockSize = buffers.first().size() - head;
@ -100,20 +102,25 @@ char *QRingBuffer::reserve(qint64 bytes)
if (bytes <= 0 || bytes >= MaxByteArraySize)
return 0;
const qint64 newSize = bytes + tail;
// if need buffer reallocation
if (newSize > buffers.last().size()) {
if (newSize > buffers.last().capacity() && (tail >= basicBlockSize
|| newSize >= MaxByteArraySize)) {
// shrink this buffer to its current size
buffers.last().resize(tail);
if (buffers.isEmpty()) {
buffers.append(QByteArray());
buffers.first().resize(qMax(basicBlockSize, int(bytes)));
} else {
const qint64 newSize = bytes + tail;
// if need buffer reallocation
if (newSize > buffers.last().size()) {
if (newSize > buffers.last().capacity() && (tail >= basicBlockSize
|| newSize >= MaxByteArraySize)) {
// shrink this buffer to its current size
buffers.last().resize(tail);
// create a new QByteArray
buffers.append(QByteArray());
++tailBuffer;
tail = 0;
// create a new QByteArray
buffers.append(QByteArray());
++tailBuffer;
tail = 0;
}
buffers.last().resize(qMax(basicBlockSize, tail + int(bytes)));
}
buffers.last().resize(qMax(basicBlockSize, tail + int(bytes)));
}
char *writePtr = buffers.last().data() + tail;
@ -134,9 +141,13 @@ char *QRingBuffer::reserveFront(qint64 bytes)
return 0;
if (head < bytes) {
buffers.first().remove(0, head);
if (tailBuffer == 0)
tail -= head;
if (buffers.isEmpty()) {
buffers.append(QByteArray());
} else {
buffers.first().remove(0, head);
if (tailBuffer == 0)
tail -= head;
}
head = qMax(basicBlockSize, int(bytes));
if (bufferSize == 0) {
@ -155,6 +166,8 @@ char *QRingBuffer::reserveFront(qint64 bytes)
void QRingBuffer::chop(qint64 bytes)
{
Q_ASSERT(bytes <= bufferSize);
while (bytes > 0) {
if (tailBuffer == 0 || tail > bytes) {
// keep a single block around if it does not exceed
@ -185,6 +198,9 @@ void QRingBuffer::chop(qint64 bytes)
void QRingBuffer::clear()
{
if (buffers.isEmpty())
return;
buffers.erase(buffers.begin() + 1, buffers.end());
buffers.first().clear();
@ -301,7 +317,10 @@ qint64 QRingBuffer::peek(char *data, qint64 maxLength, qint64 pos) const
void QRingBuffer::append(const QByteArray &qba)
{
if (tail == 0) {
buffers.last() = qba;
if (buffers.isEmpty())
buffers.append(qba);
else
buffers.last() = qba;
} else {
buffers.last().resize(tail);
buffers.append(qba);

View File

@ -54,9 +54,7 @@ class QRingBuffer
{
public:
explicit inline QRingBuffer(int growth = 4096) :
head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) {
buffers.append(QByteArray());
}
head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) { }
inline qint64 nextDataBlockSize() const {
return (tailBuffer == 0 ? tail : buffers.first().size()) - head;

View File

@ -39,11 +39,11 @@ class tst_QRingBuffer : public QObject
{
Q_OBJECT
private slots:
void constructing();
void readPointerAtPositionWriteRead();
void readPointerAtPositionEmptyRead();
void readPointerAtPositionWithHead();
void readPointerAtPositionReadTooMuch();
void sizeWhenEmpty();
void sizeWhenReservedAndChopped();
void sizeWhenReserved();
void free();
@ -57,6 +57,23 @@ private slots:
void readLine();
};
void tst_QRingBuffer::constructing()
{
QRingBuffer ringBuffer;
QCOMPARE(ringBuffer.size(), Q_INT64_C(0));
QVERIFY(ringBuffer.isEmpty());
QCOMPARE(ringBuffer.nextDataBlockSize(), Q_INT64_C(0));
QVERIFY(ringBuffer.readPointer() == Q_NULLPTR);
QCOMPARE(ringBuffer.skip(5), Q_INT64_C(0));
QCOMPARE(ringBuffer.read(), QByteArray());
QCOMPARE(ringBuffer.getChar(), -1);
QVERIFY(!ringBuffer.canReadLine());
char buf[5];
QCOMPARE(ringBuffer.peek(buf, sizeof(buf)), Q_INT64_C(0));
}
void tst_QRingBuffer::sizeWhenReserved()
{
QRingBuffer ringBuffer;
@ -74,13 +91,6 @@ void tst_QRingBuffer::sizeWhenReservedAndChopped()
QCOMPARE(ringBuffer.size(), Q_INT64_C(0));
}
void tst_QRingBuffer::sizeWhenEmpty()
{
QRingBuffer ringBuffer;
QCOMPARE(ringBuffer.size(), Q_INT64_C(0));
}
void tst_QRingBuffer::readPointerAtPositionReadTooMuch()
{
QRingBuffer ringBuffer;