csync_reconcile: Optimize reconcile by avoiding alocations

Fix performence regression from commit d66c2b5fae
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
This commit is contained in:
Olivier Goffart 2017-09-24 21:58:41 +02:00 committed by Olivier Goffart
parent a2ce739192
commit 041c5e6d44
2 changed files with 36 additions and 5 deletions

View File

@ -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<QByteArray, std::unique_ptr<csync_file_stat_t>, FileMapHash> {
struct FileMapHash { uint operator()(const ByteArrayRef &a) const { return qHashBits(a.data(), a.size()); } };
class FileMap : public std::unordered_map<ByteArrayRef, std::unique_ptr<csync_file_stat_t>, 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;
}

View File

@ -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) {