tests/test-tools.t
author Gregory Szorc <gregory.szorc@gmail.com>
Mon, 22 Aug 2016 21:48:50 -0700
changeset 29830 92ac2baaea86
parent 26950 6686ae524f94
child 32940 75be14993fda
permissions -rw-r--r--
revlog: use an LRU cache for delta chain bases Profiling using statprof revealed a hotspot during changegroup application calculating delta chain bases on generaldelta repos. Essentially, revlog._addrevision() was performing a lot of redundant work tracing the delta chain as part of determining when the chain distance was acceptable. This was most pronounced when adding revisions to manifests, which can have delta chains thousands of revisions long. There was a delta chain base cache on revlogs before, but it only captured a single revision. This was acceptable before generaldelta, when _addrevision would build deltas from the previous revision and thus we'd pretty much guarantee a cache hit when resolving the delta chain base on a subsequent _addrevision call. However, it isn't suitable for generaldelta because parent revisions aren't necessarily the last processed revision. This patch converts the delta chain base cache to an LRU dict cache. The cache can hold multiple entries, so generaldelta repos have a higher chance of getting a cache hit. The impact of this change when processing changegroup additions is significant. On a generaldelta conversion of the "mozilla-unified" repo (which contains heads of the main Firefox repositories in chronological order - this means there are lots of transitions between heads in revlog order), this change has the following impact when performing an `hg unbundle` of an uncompressed bundle of the repo: before: 5:42 CPU time after: 4:34 CPU time Most of this time is saved when applying the changelog and manifest revlogs: before: 2:30 CPU time after: 1:17 CPU time That nearly a 50% reduction in CPU time applying changesets and manifests! Applying a gzipped bundle of the same repo (effectively simulating a `hg clone` over HTTP) showed a similar speedup: before: 5:53 CPU time after: 4:46 CPU time Wall time improvements were basically the same as CPU time. I didn't measure explicitly, but it feels like most of the time is saved when processing manifests. This makes sense, as large manifests tend to have very long delta chains and thus benefit the most from this cache. So, this change effectively makes changegroup application (which is used by `hg unbundle`, `hg clone`, `hg pull`, `hg unshelve`, and various other commands) significantly faster when delta chains are long (which can happen on repos with large numbers of files and thus large manifests). In theory, this change can result in more memory utilization. However, we're caching a dict of ints. At most we have 200 ints + Python object overhead per revlog. And, the cache is really only populated when performing read-heavy operations, such as adding changegroups or scanning an individual revlog. For memory bloat to be an issue, we'd need to scan/read several revisions from several revlogs all while having active references to several revlogs. I don't think there are many operations that do this, so I don't think memory bloat from the cache will be an issue.

Tests of the file helper tool

  $ f -h
  ?sage: f [options] [filenames] (glob)
  
  ?ptions: (glob)
    -h, --help            show this help message and exit
    -t, --type            show file type (file or directory)
    -m, --mode            show file mode
    -l, --links           show number of links
    -s, --size            show size of file
    -n NEWER, --newer=NEWER
                          check if file is newer (or same)
    -r, --recurse         recurse into directories
    -S, --sha1            show sha1 hash of the content
    -M, --md5             show md5 hash of the content
    -D, --dump            dump file content
    -H, --hexdump         hexdump file content
    -B BYTES, --bytes=BYTES
                          number of characters to dump
    -L LINES, --lines=LINES
                          number of lines to dump
    -q, --quiet           no default output

  $ mkdir dir
  $ cd dir

  $ f --size
  size=0

  $ echo hello | f --md5 --size
  size=6, md5=b1946ac92492d2347c6235b4d2611184

  $ f foo
  foo: file not found

  $ echo foo > foo
  $ f foo
  foo:

#if symlink
  $ f foo --mode
  foo: mode=644
#endif

#if no-windows
  $ python $TESTDIR/seq.py 10 > bar
#else
Convert CRLF -> LF for consistency
  $ python $TESTDIR/seq.py 10 | sed "s/$//" > bar
#endif

#if unix-permissions symlink
  $ chmod +x bar
  $ f bar --newer foo --mode --type --size --dump --links --bytes 7
  bar: file, size=21, mode=755, links=1, newer than foo
  >>>
  1
  2
  3
  4
  <<< no trailing newline
#endif

#if unix-permissions
  $ ln bar baz
  $ f bar -n baz -l --hexdump -t --sha1 --lines=9 -B 20
  bar: file, links=2, newer than baz, sha1=612ca68d0305c821750a
  0000: 31 0a 32 0a 33 0a 34 0a 35 0a 36 0a 37 0a 38 0a |1.2.3.4.5.6.7.8.|
  0010: 39 0a                                           |9.|
  $ rm baz
#endif

#if unix-permissions symlink
  $ ln -s yadda l
  $ f . --recurse -MStmsB4
  .: directory with 3 files, mode=755
  ./bar: file, size=21, mode=755, md5=3b03, sha1=612c
  ./foo: file, size=4, mode=644, md5=d3b0, sha1=f1d2
  ./l: link, size=5, md5=2faa, sha1=af93
#endif

  $ f --quiet bar -DL 3
  1
  2
  3

  $ cd ..

Yadda is a symlink
#if symlink
  $ f -qr dir -HB 17
  dir: directory with 3 files
  dir/bar:
  0000: 31 0a 32 0a 33 0a 34 0a 35 0a 36 0a 37 0a 38 0a |1.2.3.4.5.6.7.8.|
  0010: 39                                              |9|
  dir/foo:
  0000: 66 6f 6f 0a                                     |foo.|
  dir/l:
  0000: 79 61 64 64 61                                  |yadda|
#else
  $ f -qr dir -HB 17
  dir: directory with 2 files (esc)
  dir/bar: (glob)
  0000: 31 0a 32 0a 33 0a 34 0a 35 0a 36 0a 37 0a 38 0a |1.2.3.4.5.6.7.8.|
  0010: 39                                              |9|
  dir/foo: (glob)
  0000: 66 6f 6f 0a                                     |foo.|
#endif