File librsync-logn-search.patch of Package librsync

From: Victor Denisov ( victordenisov ) - 2012-09-24 10:07:15 PDT
URL: http://sourceforge.net/tracker/?func=detail&aid=3571263&group_id=56125&atid=479441
Subject: performance issue resolution for large files - ID: 3571263

When files being rsynced are hundreds of Gbytes size collisions in hash table
kill librsync.  So linear collision resolution has been replaced with log n
collision resolution based on binary search.  Size of hash table is 65536
buckets. So when files size is (block_size * 65536 * t) then linear collision
resolution is t / (log t) slower than binary search resolution. If block size
is 2048 bytes then for 1TB speed up is 630 times. for 100GB - 80 times.

Index: b/search.c
===================================================================
--- a/search.c
+++ b/search.c
@@ -48,57 +48,73 @@
 #include "search.h"
 #include "checksum.h"
 
-
-#define TABLESIZE (1<<16)
+#define TABLE_SIZE (1<<16)
 #define NULL_TAG (-1)
 
-
 #define gettag2(s1,s2) (((s1) + (s2)) & 0xFFFF)
 #define gettag(sum) gettag2((sum)&0xFFFF,(sum)>>16)
 
-
-static int
-rs_compare_targets(rs_target_t const *t1, rs_target_t const *t2)
-{
-    return ((int) t1->t - (int) t2->t);
-}
-
-
 rs_result
 rs_build_hash_table(rs_signature_t * sums)
 {
-    int                     i;
+    int rs_compare_targets(void const *a1, void const *a2) {
+	rs_target_t const *t1 = a1;
+	rs_target_t const *t2 = a2;
+
+	int v = (int) t1->t - (int) t2->t;
+	if (v != 0)
+	    return v;
+
+	rs_weak_sum_t w1 = sums->block_sigs[t1->i].weak_sum;
+	rs_weak_sum_t w2 = sums->block_sigs[t2->i].weak_sum;
+
+	v = (w1 > w2) - (w1 < w2);
+	if (v != 0)
+	    return v;
+
+	return memcmp(sums->block_sigs[t1->i].strong_sum,
+		sums->block_sigs[t2->i].strong_sum,
+		sums->strong_sum_len);
+    }
+
+    int i;
 
-    sums->tag_table = calloc(TABLESIZE, sizeof sums->tag_table[0]);
+    sums->tag_table = calloc(TABLE_SIZE, sizeof(sums->tag_table[0]));
     if (!sums->tag_table)
         return RS_MEM_ERROR;
     
     if (sums->count > 0) {
 	sums->targets = calloc(sums->count, sizeof(rs_target_t));
-        if (!sums->targets)
+        if (!sums->targets) {
+	    free(sums->tag_table);
+	    sums->tag_table = NULL;
             return RS_MEM_ERROR;
+	}
 
 	for (i = 0; i < sums->count; i++) {
 	    sums->targets[i].i = i;
 	    sums->targets[i].t = gettag(sums->block_sigs[i].weak_sum);
 	}
 
-	/* FIXME: Perhaps if this operating system has comparison_fn_t
-         * like GNU, then use it in the cast.  But really does anyone
-         * care?  */
 	qsort(sums->targets, sums->count,
 	      sizeof(sums->targets[0]),
-              (int (*)(const void *, const void *)) rs_compare_targets);
+              rs_compare_targets);
     }
 
-    for (i = 0; i < TABLESIZE; i++)
-	sums->tag_table[i] = NULL_TAG;
+    for (i = 0; i < TABLE_SIZE; i++) {
+	sums->tag_table[i].l = NULL_TAG;
+	sums->tag_table[i].r = NULL_TAG;
+    }
 
     for (i = sums->count - 1; i >= 0; i--) {
-	sums->tag_table[sums->targets[i].t] = i;
+	sums->tag_table[sums->targets[i].t].l = i;
     }
 
-    rs_trace("done");
+    for (i = 0; i < sums->count; i++) {
+	sums->tag_table[sums->targets[i].t].r = i;
+    }
+
+    rs_trace("rs_build_hash_table done");
     return RS_DONE;
 }
 
@@ -119,44 +135,39 @@ rs_search_for_block(rs_weak_sum_t weak_s
                     rs_signature_t const *sig, rs_stats_t * stats,
                     rs_long_t * match_where)
 {
-    int                     hash_tag = gettag(weak_sum);
-    int                     j = sig->tag_table[hash_tag];
-    rs_strong_sum_t         strong_sum;
-    int                     got_strong = 0;
+    rs_strong_sum_t strong_sum;
+    int got_strong = 0;
+    int hash_tag = gettag(weak_sum);
+    tag_table_entry_t *bucket = &(sig->tag_table[hash_tag]);
+    int l = bucket->l;
+    int r = bucket->r + 1;
+    int v = 1;
 
-    if (j == NULL_TAG) {
+    if (l == NULL_TAG)
 	return 0;
-    }
-
-    for (; j < sig->count && sig->targets[j].t == hash_tag; j++) {
-	int                     i = sig->targets[j].i;
-	int                     token;
-
-	if (weak_sum != sig->block_sigs[i].weak_sum)
-	    continue;
 
-	token = sig->block_sigs[i].i;
-
-	rs_trace("found weak match for %08x in token %d", weak_sum, token);
-
-	if (!got_strong) {
-	    rs_calc_strong_sum(inbuf, block_len, &strong_sum);
-	    got_strong = 1;
+    while (l < r) {
+	int m = (l + r) >> 1;
+	int i = sig->targets[m].i;
+	rs_block_sig_t *b = &(sig->block_sigs[i]);
+	v = (weak_sum > b->weak_sum) - (weak_sum < b->weak_sum);
+	if (v == 0) {
+		if (!got_strong) {
+			rs_calc_strong_sum(inbuf, block_len, &strong_sum);
+			got_strong = 1;
+		}
+		v = memcmp(strong_sum, b->strong_sum, sig->strong_sum_len);
 	}
-
-	/* FIXME: Use correct dynamic sum length! */
-	if (memcmp(strong_sum, sig->block_sigs[i].strong_sum,
-                   sig->strong_sum_len) == 0) {
-	    /* XXX: This is a remnant of rsync: token number 1 is the
-	     * block at offset 0.  It would be good to clear this
-	     * up. */
+	if (0 == v) {
+	    int token = b->i;
 	    *match_where = (rs_long_t)(token - 1) * sig->block_len;
-	    return 1;
-	} else {
-	    rs_trace("this was a false positive, the strong sig doesn't match");
-	    stats->false_matches++;
+	    break;
 	}
-    }
 
-    return 0;
+	if (v > 0)
+	    l = m + 1;
+	else
+	    r = m;
+    }
+    return !v;
 }
openSUSE Build Service is sponsored by