Skip to content

Commit

Permalink
cmd/margin: add a new --predict option.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
apenwarr committed Aug 26, 2010
1 parent baa7e47 commit 7bb9af2
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 13 deletions.
48 changes: 36 additions & 12 deletions cmd/margin-cmd.py
@@ -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:])
Expand All @@ -14,16 +17,37 @@
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
5 changes: 4 additions & 1 deletion lib/bup/git.py
Expand Up @@ -271,6 +271,9 @@ def __del__(self):
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:
Expand Down Expand Up @@ -407,7 +410,7 @@ def __del__(self):
self.close()

def _make_objcache(self):
if not self.objcache:
if self.objcache == None:
if self.objcache_maker:
self.objcache = self.objcache_maker()
else:
Expand Down

0 comments on commit 7bb9af2

Please sign in to comment.