diff options
Diffstat (limited to 'tests/benchmarks/corelib/io/qfile/tst_bench_qfile.cpp')
-rw-r--r-- | tests/benchmarks/corelib/io/qfile/tst_bench_qfile.cpp | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/tests/benchmarks/corelib/io/qfile/tst_bench_qfile.cpp b/tests/benchmarks/corelib/io/qfile/tst_bench_qfile.cpp new file mode 100644 index 0000000000..b805726f5f --- /dev/null +++ b/tests/benchmarks/corelib/io/qfile/tst_bench_qfile.cpp @@ -0,0 +1,593 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QDebug> +#include <QTemporaryFile> +#include <QString> +#include <QDirIterator> + +#include <private/qfsfileengine_p.h> + +#include <qtest.h> + +#include <stdio.h> + +#ifdef Q_OS_WIN +# include <qt_windows.h> +#endif + +#if defined(Q_OS_QNX) && defined(open) +#undef open +#endif + +#define BUFSIZE 1024*512 +#define FACTOR 1024*512 +#define TF_SIZE FACTOR*81 + +// 10 predefined (but random() seek positions +// hardcoded to be comparable over several runs +const int seekpos[] = {int(TF_SIZE*0.52), + int(TF_SIZE*0.23), + int(TF_SIZE*0.73), + int(TF_SIZE*0.77), + int(TF_SIZE*0.80), + int(TF_SIZE*0.12), + int(TF_SIZE*0.53), + int(TF_SIZE*0.21), + int(TF_SIZE*0.27), + int(TF_SIZE*0.78)}; + +const int sp_size = sizeof(seekpos)/sizeof(int); + +class tst_qfile: public QObject +{ +Q_ENUMS(BenchmarkType) +Q_OBJECT +public: + enum BenchmarkType { + QFileBenchmark = 1, +#ifdef QT_BUILD_INTERNAL + QFSFileEngineBenchmark, +#endif + Win32Benchmark, + PosixBenchmark, + QFileFromPosixBenchmark + }; +private slots: + void initTestCase(); + + void open_data(); + void open(); + void seek_data(); + void seek(); + + void readSmallFiles_QFile() { readSmallFiles(); } + void readSmallFiles_QFSFileEngine() { readSmallFiles(); } + void readSmallFiles_posix() { readSmallFiles(); } + void readSmallFiles_Win32() { readSmallFiles(); } + + void readSmallFiles_QFile_data(); + void readSmallFiles_QFSFileEngine_data(); + void readSmallFiles_posix_data(); + void readSmallFiles_Win32_data(); + + void readBigFile_QFile_data(); + void readBigFile_QFSFileEngine_data(); + void readBigFile_posix_data(); + void readBigFile_Win32_data(); + + void readBigFile_QFile() { readBigFile(); } + void readBigFile_QFSFileEngine() { readBigFile(); } + void readBigFile_posix() { readBigFile(); } + void readBigFile_Win32() { readBigFile(); } + +private: + void readFile_data(BenchmarkType type, QIODevice::OpenModeFlag t, QIODevice::OpenModeFlag b); + void readBigFile(); + void readSmallFiles(); + + class TestDataDir : public QTemporaryDir + { + void createFile(); + void createSmallFiles(); + public: + TestDataDir() : QTemporaryDir(), fail(errorString().toLocal8Bit()) + { + if (fail.isEmpty() && !QTemporaryDir::isValid()) + fail = "Failed to create temporary directory for data"; + if (isValid()) + createSmallFiles(); + if (isValid()) + createFile(); + if (isValid()) + QTest::qSleep(2000); // let IO settle + } + bool isValid() { return QTemporaryDir::isValid() && fail.isEmpty(); } + QByteArray fail; + QString filename; + } tempDir; +}; + +Q_DECLARE_METATYPE(tst_qfile::BenchmarkType) +Q_DECLARE_METATYPE(QIODevice::OpenMode) +Q_DECLARE_METATYPE(QIODevice::OpenModeFlag) + +/* None of the tests modify the test data in tempDir, so it's OK to only create + * and tear down the directory once. + */ +void tst_qfile::TestDataDir::createFile() +{ + QFile tmpFile(filePath("testFile")); + if (!tmpFile.open(QIODevice::WriteOnly)) { + fail = "Unable to prepare files for test"; + return; + } +#if 0 // Varied data, rather than filling with '\0' bytes: + for (int row = 0; row < FACTOR; ++row) { + tmpFile.write(QByteArray().fill('0' + row % ('0' - 'z'), 80)); + tmpFile.write("\n"); + } +#else + tmpFile.seek(FACTOR * 80); + tmpFile.putChar('\n'); +#endif + filename = tmpFile.fileName(); + tmpFile.close(); +} + +void tst_qfile::TestDataDir::createSmallFiles() +{ + for (int i = 0; i < 1000; ++i) { + QFile f(filePath(QString::number(i))); + if (!f.open(QIODevice::WriteOnly)) { + fail = "Unable to prepare small files for test"; + return; + } + f.seek(511); + f.putChar('\n'); + f.close(); + } +} + +void tst_qfile::initTestCase() +{ + QVERIFY2(tempDir.isValid(), tempDir.fail.constData()); +} + +void tst_qfile::readFile_data(BenchmarkType type, QIODevice::OpenModeFlag t, + QIODevice::OpenModeFlag b) +{ + QTest::addColumn<tst_qfile::BenchmarkType>("testType"); + QTest::addColumn<int>("blockSize"); + QTest::addColumn<QFile::OpenModeFlag>("textMode"); + QTest::addColumn<QFile::OpenModeFlag>("bufferedMode"); + + QByteArray flagstring; + if (t & QIODevice::Text) + flagstring += "textMode"; + if (b & QIODevice::Unbuffered) { + if (flagstring.size()) + flagstring += ' '; + flagstring += "unbuffered"; + } + if (flagstring.isEmpty()) + flagstring = "none"; + + const int kbs[] = {1, 2, 8, 16, 32, 512}; + for (int kb : kbs) { + const int size = 1024 * kb; + QTest::addRow("BS: %d, Flags: %s", size, flagstring.constData()) + << type << size << t << b; + } +} + +void tst_qfile::readBigFile_QFile_data() +{ + readFile_data(QFileBenchmark, QIODevice::NotOpen, QIODevice::NotOpen); + readFile_data(QFileBenchmark, QIODevice::NotOpen, QIODevice::Unbuffered); + readFile_data(QFileBenchmark, QIODevice::Text, QIODevice::NotOpen); + readFile_data(QFileBenchmark, QIODevice::Text, QIODevice::Unbuffered); + +} + +void tst_qfile::readBigFile_QFSFileEngine_data() +{ +#ifdef QT_BUILD_INTERNAL + // Support for buffering dropped at 5.10, so only test Unbuffered + readFile_data(QFSFileEngineBenchmark, QIODevice::NotOpen, QIODevice::Unbuffered); + readFile_data(QFSFileEngineBenchmark, QIODevice::Text, QIODevice::Unbuffered); +#else + QSKIP("This test requires -developer-build."); +#endif +} + +void tst_qfile::readBigFile_posix_data() +{ + readFile_data(PosixBenchmark, QIODevice::NotOpen, QIODevice::NotOpen); +} + +void tst_qfile::readBigFile_Win32_data() +{ +#ifdef Q_OS_WIN + readFile_data(Win32Benchmark, QIODevice::NotOpen, QIODevice::NotOpen); +#else + QSKIP("This is Windows only benchmark."); +#endif +} + +void tst_qfile::readBigFile() +{ + QFETCH(tst_qfile::BenchmarkType, testType); + QFETCH(int, blockSize); + QFETCH(QFile::OpenModeFlag, textMode); + QFETCH(QFile::OpenModeFlag, bufferedMode); + + char buffer[BUFSIZE]; + switch (testType) { + case QFileBenchmark: { + QFile file(tempDir.filename); + file.open(QIODevice::ReadOnly|textMode|bufferedMode); + QBENCHMARK { + while(!file.atEnd()) + file.read(blockSize); + file.reset(); + } + file.close(); + } + break; +#ifdef QT_BUILD_INTERNAL + case QFSFileEngineBenchmark: { + QFSFileEngine fse(tempDir.filename); + fse.open(QIODevice::ReadOnly | textMode | bufferedMode, std::nullopt); + QBENCHMARK { + //qWarning() << fse.supportsExtension(QAbstractFileEngine::AtEndExtension); + while (fse.read(buffer, blockSize)) {} + fse.seek(0); + } + fse.close(); + } + break; +#endif + case PosixBenchmark: { + QByteArray data = tempDir.filename.toLocal8Bit(); + const char* cfilename = data.constData(); + FILE* cfile = ::fopen(cfilename, "rb"); + QBENCHMARK { + while(!feof(cfile)) + [[maybe_unused]] auto r = ::fread(buffer, blockSize, 1, cfile); + ::fseek(cfile, 0, SEEK_SET); + } + ::fclose(cfile); + } + break; + case QFileFromPosixBenchmark: { + // No gain in benchmarking this case + } + break; + case Win32Benchmark: { +#ifdef Q_OS_WIN + HANDLE hndl; + + // ensure we don't account string conversion + const wchar_t *cfilename = reinterpret_cast<const wchar_t *>(tempDir.filename.utf16()); + + hndl = CreateFile(cfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + Q_ASSERT(hndl); + wchar_t* nativeBuffer = new wchar_t[BUFSIZE]; + DWORD numberOfBytesRead; + + QBENCHMARK { + do { + ReadFile(hndl, nativeBuffer, blockSize, &numberOfBytesRead, NULL); + } while(numberOfBytesRead != 0); + SetFilePointer(hndl, 0, NULL, FILE_BEGIN); + } + delete[] nativeBuffer; + CloseHandle(hndl); +#else + QFAIL("Not running on a non-Windows platform!"); +#endif + } + break; + } +} + +void tst_qfile::seek_data() +{ + QTest::addColumn<tst_qfile::BenchmarkType>("testType"); + QTest::newRow("QFile") << QFileBenchmark; +#ifdef QT_BUILD_INTERNAL + QTest::newRow("QFSFileEngine") << QFSFileEngineBenchmark; +#endif + QTest::newRow("Posix FILE*") << PosixBenchmark; +#ifdef Q_OS_WIN + QTest::newRow("Win32 API") << Win32Benchmark; +#endif +} + +void tst_qfile::seek() +{ + QFETCH(tst_qfile::BenchmarkType, testType); + int i = 0; + + switch (testType) { + case QFileBenchmark: { + QFile file(tempDir.filename); + file.open(QIODevice::ReadOnly); + QBENCHMARK { + i=(i+1)%sp_size; + file.seek(seekpos[i]); + } + file.close(); + } + break; +#ifdef QT_BUILD_INTERNAL + case QFSFileEngineBenchmark: { + QFSFileEngine fse(tempDir.filename); + fse.open(QIODevice::ReadOnly | QIODevice::Unbuffered, std::nullopt); + QBENCHMARK { + i=(i+1)%sp_size; + fse.seek(seekpos[i]); + } + fse.close(); + } + break; +#endif + case PosixBenchmark: { + QByteArray data = tempDir.filename.toLocal8Bit(); + const char* cfilename = data.constData(); + FILE* cfile = ::fopen(cfilename, "rb"); + QBENCHMARK { + i=(i+1)%sp_size; + ::fseek(cfile, seekpos[i], SEEK_SET); + } + ::fclose(cfile); + } + break; + case QFileFromPosixBenchmark: { + // No gain in benchmarking this case + } + break; + case Win32Benchmark: { +#ifdef Q_OS_WIN + HANDLE hndl; + + // ensure we don't account string conversion + const wchar_t *cfilename = reinterpret_cast<const wchar_t *>(tempDir.filename.utf16()); + + hndl = CreateFile(cfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + Q_ASSERT(hndl); + QBENCHMARK { + i=(i+1)%sp_size; + SetFilePointer(hndl, seekpos[i], NULL, 0); + } + CloseHandle(hndl); +#else + QFAIL("Not running on a Windows platform!"); +#endif + } + break; + } +} + +void tst_qfile::open_data() +{ + QTest::addColumn<tst_qfile::BenchmarkType>("testType"); + QTest::newRow("QFile") << QFileBenchmark; +#ifdef QT_BUILD_INTERNAL + QTest::newRow("QFSFileEngine") << QFSFileEngineBenchmark; +#endif + QTest::newRow("Posix FILE*") << PosixBenchmark; + QTest::newRow("QFile from FILE*") << QFileFromPosixBenchmark; +#ifdef Q_OS_WIN + QTest::newRow("Win32 API") << Win32Benchmark; +#endif +} + +void tst_qfile::open() +{ + QFETCH(tst_qfile::BenchmarkType, testType); + + switch (testType) { + case QFileBenchmark: { + QBENCHMARK { + QFile file(tempDir.filename); + file.open(QIODevice::ReadOnly); + file.close(); + } + } + break; +#ifdef QT_BUILD_INTERNAL + case QFSFileEngineBenchmark: { + QBENCHMARK { + QFSFileEngine fse(tempDir.filename); + fse.open(QIODevice::ReadOnly | QIODevice::Unbuffered, std::nullopt); + fse.close(); + } + } + break; +#endif + case PosixBenchmark: { + // ensure we don't account toLocal8Bit() + QByteArray data = tempDir.filename.toLocal8Bit(); + const char* cfilename = data.constData(); + + QBENCHMARK { + FILE* cfile = ::fopen(cfilename, "rb"); + ::fclose(cfile); + } + } + break; + case QFileFromPosixBenchmark: { + // ensure we don't account toLocal8Bit() + QByteArray data = tempDir.filename.toLocal8Bit(); + const char* cfilename = data.constData(); + FILE* cfile = ::fopen(cfilename, "rb"); + + QBENCHMARK { + QFile file; + file.open(cfile, QIODevice::ReadOnly); + file.close(); + } + ::fclose(cfile); + } + break; + case Win32Benchmark: { +#ifdef Q_OS_WIN + HANDLE hndl; + + // ensure we don't account string conversion + const wchar_t *cfilename = reinterpret_cast<const wchar_t *>(tempDir.filename.utf16()); + + QBENCHMARK { + hndl = CreateFile(cfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + Q_ASSERT(hndl); + CloseHandle(hndl); + } +#else + QFAIL("Not running on a non-Windows platform!"); +#endif + } + break; + } +} + +void tst_qfile::readSmallFiles_QFile_data() +{ + readFile_data(QFileBenchmark, QIODevice::NotOpen, QIODevice::NotOpen); + readFile_data(QFileBenchmark, QIODevice::NotOpen, QIODevice::Unbuffered); + readFile_data(QFileBenchmark, QIODevice::Text, QIODevice::NotOpen); + readFile_data(QFileBenchmark, QIODevice::Text, QIODevice::Unbuffered); + +} + +void tst_qfile::readSmallFiles_QFSFileEngine_data() +{ +#ifdef QT_BUILD_INTERNAL + // Support for buffering dropped at 5.10, so only test Unbuffered + readFile_data(QFSFileEngineBenchmark, QIODevice::NotOpen, QIODevice::Unbuffered); + readFile_data(QFSFileEngineBenchmark, QIODevice::Text, QIODevice::Unbuffered); +#else + QSKIP("This test requires -developer-build."); +#endif +} + +void tst_qfile::readSmallFiles_posix_data() +{ + readFile_data(PosixBenchmark, QIODevice::NotOpen, QIODevice::NotOpen); +} + +void tst_qfile::readSmallFiles_Win32_data() +{ + +#ifdef Q_OS_WIN + readFile_data(Win32Benchmark, QIODevice::NotOpen, QIODevice::NotOpen); +#else + QSKIP("This is Windows only benchmark."); +#endif +} + +void tst_qfile::readSmallFiles() +{ + QFETCH(tst_qfile::BenchmarkType, testType); + QFETCH(int, blockSize); + QFETCH(QFile::OpenModeFlag, textMode); + QFETCH(QFile::OpenModeFlag, bufferedMode); + + QDir dir(tempDir.path()); + const QStringList files = dir.entryList(QDir::NoDotAndDotDot|QDir::NoSymLinks|QDir::Files); + char buffer[BUFSIZE]; + + switch (testType) { + case QFileBenchmark: { + QList<QFile*> fileList; + for (const QString &file : files) { + QFile *f = new QFile(tempDir.filePath(file)); + f->open(QIODevice::ReadOnly|textMode|bufferedMode); + fileList.append(f); + } + + QBENCHMARK { + for (QFile *const file : std::as_const(fileList)) { + while (!file->atEnd()) + file->read(buffer, blockSize); + } + } + + for (QFile *const file : std::as_const(fileList)) { + file->close(); + delete file; + } + } + break; +#ifdef QT_BUILD_INTERNAL + case QFSFileEngineBenchmark: { + QList<QFSFileEngine*> fileList; + for (const QString &file : files) { + QFSFileEngine *fse = new QFSFileEngine(tempDir.filePath(file)); + fse->open(QIODevice::ReadOnly | textMode | bufferedMode, std::nullopt); + fileList.append(fse); + } + + QBENCHMARK { + for (QFSFileEngine *const fse : std::as_const(fileList)) + while (fse->read(buffer, blockSize)) {} + } + + for (QFSFileEngine *const fse : std::as_const(fileList)) { + fse->close(); + delete fse; + } + } + break; +#endif + case PosixBenchmark: { + QList<FILE *> fileList; + for (const QString &file : files) + fileList.append(::fopen(QFile::encodeName(tempDir.filePath(file)).constData(), "rb")); + + QBENCHMARK { + for (FILE *const cfile : std::as_const(fileList)) { + while (!feof(cfile)) + [[maybe_unused]] auto f = ::fread(buffer, blockSize, 1, cfile); + ::fseek(cfile, 0, SEEK_SET); + } + } + + for (FILE *const cfile : std::as_const(fileList)) + ::fclose(cfile); + } + break; + case QFileFromPosixBenchmark: { + // No gain in benchmarking this case + } + break; + case Win32Benchmark: { +#ifdef Q_OS_WIN + HANDLE hndl; + + // ensure we don't account string conversion + const wchar_t *cfilename = reinterpret_cast<const wchar_t *>(tempDir.filename.utf16()); + + hndl = CreateFile(cfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + Q_ASSERT(hndl); + wchar_t* nativeBuffer = new wchar_t[BUFSIZE]; + DWORD numberOfBytesRead; + QBENCHMARK { + do { + ReadFile(hndl, nativeBuffer, blockSize, &numberOfBytesRead, NULL); + } while(numberOfBytesRead != 0); + } + delete nativeBuffer; + CloseHandle(hndl); +#else + QFAIL("Not running on a non-Windows platform!"); +#endif + } + break; + } +} + +QTEST_MAIN(tst_qfile) + +#include "tst_bench_qfile.moc" |