From: Olivier Goffart Date: Sun, 24 Sep 2017 19:58:41 +0000 (+0200) Subject: csync_reconcile: Optimize reconcile by avoiding alocations X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~701^2~28 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=917e2e43c64fd0faf4ff15744f5d6b5e5f3019da;p=nextcloud-desktop.git csync_reconcile: Optimize reconcile by avoiding alocations Fix performence regression from commit d66c2b5faecc689c52df23d7dbc44c894b92febc For every new file we would look up every parent directories. Allocating a new QByteArreay for every parent riectory just to know if it is in the other tree is wasting lots of CPU. Use a ByteArrayRef trick, similar to QStringRef --- diff --git a/src/csync/csync_private.h b/src/csync/csync_private.h index 60d12df91..010e884e3 100644 --- a/src/csync/csync_private.h +++ b/src/csync/csync_private.h @@ -68,14 +68,44 @@ enum csync_replica_e { REMOTE_REPLICA }; + +/* + * This is a structurere similar to QStringRef + * The difference is that it keeps the QByteArray by value and not by pointer + * And it only implements a very small subset of the API that is required by csync, the API can be + * added as we need it. + */ +class ByteArrayRef +{ + QByteArray _arr; + int _begin = 0; + int _size = -1; + +public: + ByteArrayRef(const QByteArray &arr = {}, int begin = 0, int size = -1) + : _arr(arr) + , _begin(begin) + , _size(qMin(arr.size() - begin, size < 0 ? arr.size() : size)) + { + } + ByteArrayRef left(int l) const { return ByteArrayRef(_arr, _begin, l); }; + char at(int x) const { return _arr.at(_begin + x); } + int size() const { return _size; } + /* Pointer to the beginning of the data. WARNING: not null terminated */ + const char *data() const { return _arr.constData() + _begin; } + + friend bool operator==(const ByteArrayRef &a, const ByteArrayRef &b) + { return a.size() == b.size() && qstrncmp(a.data(), b.data(), a.size()) == 0; } +}; + /** * @brief csync public structure */ struct OCSYNC_EXPORT csync_s { - struct FileMapHash { uint operator()(const QByteArray &a) const { return qHash(a); } }; - class FileMap : public std::unordered_map, FileMapHash> { + struct FileMapHash { uint operator()(const ByteArrayRef &a) const { return qHashBits(a.data(), a.size()); } }; + class FileMap : public std::unordered_map, FileMapHash> { public: - csync_file_stat_t *findFile(const QByteArray &key) const { + csync_file_stat_t *findFile(const ByteArrayRef &key) const { auto it = find(key); return it != end() ? it->second.get() : nullptr; } diff --git a/src/csync/csync_reconcile.cpp b/src/csync/csync_reconcile.cpp index 2fdaa7f38..80bd91ee1 100644 --- a/src/csync/csync_reconcile.cpp +++ b/src/csync/csync_reconcile.cpp @@ -37,7 +37,8 @@ Q_LOGGING_CATEGORY(lcReconcile, "sync.csync.reconciler", QtInfoMsg) /* Check if a file is ignored because one parent is ignored. * return the node of the ignored directoy if it's the case, or NULL if it is not ignored */ -static csync_file_stat_t *_csync_check_ignored(csync_s::FileMap *tree, const QByteArray &path) { +static csync_file_stat_t *_csync_check_ignored(csync_s::FileMap *tree, const ByteArrayRef &path) +{ /* compute the size of the parent directory */ int parentlen = path.size() - 1; while (parentlen > 0 && path.at(parentlen) != '/') { @@ -46,7 +47,7 @@ static csync_file_stat_t *_csync_check_ignored(csync_s::FileMap *tree, const QBy if (parentlen <= 0) { return nullptr; } - QByteArray parentPath = path.left(parentlen); + auto parentPath = path.left(parentlen); csync_file_stat_t *fs = tree->findFile(parentPath); if (fs) { if (fs->instruction == CSYNC_INSTRUCTION_IGNORE) {