csync_reconcile: Optimize reconcile by avoiding alocations
authorOlivier Goffart <ogoffart@woboq.com>
Sun, 24 Sep 2017 19:58:41 +0000 (21:58 +0200)
committerRoeland Jago Douma <roeland@famdouma.nl>
Thu, 5 Oct 2017 20:01:37 +0000 (22:01 +0200)
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

src/csync/csync_private.h
src/csync/csync_reconcile.cpp

index 60d12df91ef16c6977e48acf9f470a17c55e8206..010e884e3959485fbda178ef46f02526a2b9587e 100644 (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;
       }
index 2fdaa7f38ad063e20b0a2c0988895c0606c2767a..80bd91ee1907c2d63d7601acd9aa78b9402817fe 100644 (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) {