]> arthur.barton.de Git - bup.git/commitdiff
cmd/margin: add a new --predict option.
authorAvery Pennarun <apenwarr@gmail.com>
Thu, 26 Aug 2010 03:40:34 +0000 (20:40 -0700)
committerAvery Pennarun <apenwarr@gmail.com>
Thu, 26 Aug 2010 04:23:41 +0000 (21:23 -0700)
When --predict is given, it tries to guess the offset in the indexfile of
each hash, based on assumption that the hashes are distributed evenly
throughout the file.  Then it prints the maximum amount by which this guess
deviates from reality.

I was hoping the results would show that the maximum deviation in a typical
midx was less than a page's worth of hashes; that would mean the toplevel
lookup table could be redundant, which means fewer pages hit in the
common case.  No such luck, unfortunately; with 1.6 million objects, my
maximum deviation was 913 hashes (about 18 kbytes, or 5 pages).

By comparison, midx files should hit about 2 pages in the common case (1
lookup table + 1 data page).  Or 3 pages if we're unlucky and the search
spans two data pages.

Signed-off-by: Avery Pennarun <apenwarr@gmail.com>
cmd/margin-cmd.py
lib/bup/git.py

index cb059a3809687593222a3e91def7f1d7b987ba6b..e61d0573aba00fde59c4583f8236b0b38f78abe5 100755 (executable)
@@ -1,11 +1,14 @@
 #!/usr/bin/env python
-import sys
+import sys, struct
 from bup import options, git, _helpers
 from bup.helpers import *
 
 
 optspec = """
 bup margin
+--
+predict    Guess object offsets and report the maximum deviation
+ignore-midx  Don't use midx files; use only plain pack idx files.
 """
 o = options.Options('bup margin', optspec)
 (opt, flags, extra) = o.parse(sys.argv[1:])
@@ -14,16 +17,37 @@ if extra:
     o.fatal("no arguments expected")
 
 git.check_repo_or_die()
-#git.ignore_midx = 1
+git.ignore_midx = opt.ignore_midx
 
 mi = git.PackIdxList(git.repo('objects/pack'))
-last = '\0'*20
-longmatch = 0
-for i in mi:
-    if i == last:
-        continue
-    #assert(str(i) >= last)
-    pm = _helpers.bitmatch(last, i)
-    longmatch = max(longmatch, pm)
-    last = i
-print longmatch
+
+def do_predict(ix):
+    total = len(ix)
+    maxdiff = 0
+    for count,i in enumerate(ix):
+        prefix = struct.unpack('!Q', i[:8])[0]
+        expected = prefix * total / (1<<64)
+        diff = count - expected
+        maxdiff = max(maxdiff, abs(diff))
+    print '%d of %d (%.3f%%) ' % (maxdiff, len(ix), maxdiff*100.0/len(ix))
+    sys.stdout.flush()
+    assert(count+1 == len(ix))
+
+if opt.predict:
+    if opt.ignore_midx:
+        for pack in mi.packs:
+            do_predict(pack)
+    else:
+        do_predict(mi)
+else:
+    # default mode: find longest matching prefix
+    last = '\0'*20
+    longmatch = 0
+    for i in mi:
+        if i == last:
+            continue
+        #assert(str(i) >= last)
+        pm = _helpers.bitmatch(last, i)
+        longmatch = max(longmatch, pm)
+        last = i
+    print longmatch
index 87351a1caa53e88dd42745b10ae0fd8139b3c5e5..3946ec96aefcd7e190ec56f29f214fbe84c5fe76 100644 (file)
@@ -271,6 +271,9 @@ class PackIdxList:
     def __iter__(self):
         return iter(idxmerge(self.packs))
 
+    def __len__(self):
+        return sum(len(pack) for pack in self.packs)
+
     def exists(self, hash):
         """Return nonempty if the object exists in the index files."""
         if hash in self.also:
@@ -407,7 +410,7 @@ class PackWriter:
         self.close()
 
     def _make_objcache(self):
-        if not self.objcache:
+        if self.objcache == None:
             if self.objcache_maker:
                 self.objcache = self.objcache_maker()
             else: