Mercurial > hg
view i18n/hggettext @ 39568:842cd0bdda75
util: teach lrucachedict to enforce a max total cost
Now that lrucachedict entries can have a numeric cost associated
with them and we can easily pop the oldest item in the cache, it
now becomes relatively trivial to implement support for enforcing
a high water mark on the total cost of items in the cache.
This commit teaches lrucachedict instances to have a max cost
associated with them. When items are inserted, we pop old items
until enough "cost" frees up to make room for the new item.
This feature is close to zero cost when not used (modulo the insertion
regressed introduced by the previous commit):
$ ./hg perflrucachedict --size 4 --gets 1000000 --sets 1000000 --mixed 1000000
! gets
! wall 0.607444 comb 0.610000 user 0.610000 sys 0.000000 (best of 17)
! wall 0.601653 comb 0.600000 user 0.600000 sys 0.000000 (best of 17)
! inserts
! wall 0.678261 comb 0.680000 user 0.680000 sys 0.000000 (best of 14)
! wall 0.685042 comb 0.680000 user 0.680000 sys 0.000000 (best of 15)
! sets
! wall 0.808770 comb 0.800000 user 0.800000 sys 0.000000 (best of 13)
! wall 0.834241 comb 0.830000 user 0.830000 sys 0.000000 (best of 12)
! mixed
! wall 0.782441 comb 0.780000 user 0.780000 sys 0.000000 (best of 13)
! wall 0.803804 comb 0.800000 user 0.800000 sys 0.000000 (best of 13)
$ hg perflrucachedict --size 1000 --gets 1000000 --sets 1000000 --mixed 1000000
! init
! wall 0.006952 comb 0.010000 user 0.010000 sys 0.000000 (best of 418)
! gets
! wall 0.613350 comb 0.610000 user 0.610000 sys 0.000000 (best of 17)
! wall 0.617415 comb 0.620000 user 0.620000 sys 0.000000 (best of 17)
! inserts
! wall 0.701270 comb 0.700000 user 0.700000 sys 0.000000 (best of 15)
! wall 0.700516 comb 0.700000 user 0.700000 sys 0.000000 (best of 15)
! sets
! wall 0.825720 comb 0.830000 user 0.830000 sys 0.000000 (best of 13)
! wall 0.837946 comb 0.840000 user 0.830000 sys 0.010000 (best of 12)
! mixed
! wall 0.821644 comb 0.820000 user 0.820000 sys 0.000000 (best of 13)
! wall 0.850559 comb 0.850000 user 0.850000 sys 0.000000 (best of 12)
I reckon the slight slowdown on insert is due to added if checks.
For caches with total cost limiting enabled:
$ hg perflrucachedict --size 4 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 100
! gets w/ cost limit
! wall 0.598737 comb 0.590000 user 0.590000 sys 0.000000 (best of 17)
! inserts w/ cost limit
! wall 1.694282 comb 1.700000 user 1.700000 sys 0.000000 (best of 6)
! mixed w/ cost limit
! wall 1.157655 comb 1.150000 user 1.150000 sys 0.000000 (best of 9)
$ hg perflrucachedict --size 1000 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 10000
! gets w/ cost limit
! wall 0.598526 comb 0.600000 user 0.600000 sys 0.000000 (best of 17)
! inserts w/ cost limit
! wall 37.838315 comb 37.840000 user 37.840000 sys 0.000000 (best of 3)
! mixed w/ cost limit
! wall 18.060198 comb 18.060000 user 18.060000 sys 0.000000 (best of 3)
$ hg perflrucachedict --size 1000 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 10000 --mixedgetfreq 90
! gets w/ cost limit
! wall 0.600024 comb 0.600000 user 0.600000 sys 0.000000 (best of 17)
! inserts w/ cost limit
! wall 37.154547 comb 37.120000 user 37.120000 sys 0.000000 (best of 3)
! mixed w/ cost limit
! wall 4.381602 comb 4.380000 user 4.370000 sys 0.010000 (best of 3)
The functions we're benchmarking are slightly different, which could
move numbers by a few milliseconds. But the slowdown on insert is too
great to be explained by that. The slowness is due to insert heavy
operations needing to call popoldest() repeatedly when the cache is
at capacity. The next commit will address this.
Differential Revision: https://phab.mercurial-scm.org/D4503
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Thu, 06 Sep 2018 14:04:46 -0700 |
parents | 617ae7e33a65 |
children | 47ef023d0165 |
line wrap: on
line source
#!/usr/bin/env python # # hggettext - carefully extract docstrings for Mercurial # # Copyright 2009 Matt Mackall <mpm@selenic.com> and others # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. # The normalize function is taken from pygettext which is distributed # with Python under the Python License, which is GPL compatible. """Extract docstrings from Mercurial commands. Compared to pygettext, this script knows about the cmdtable and table dictionaries used by Mercurial, and will only extract docstrings from functions mentioned therein. Use xgettext like normal to extract strings marked as translatable and join the message cataloges to get the final catalog. """ from __future__ import absolute_import, print_function import inspect import os import re import sys def escape(s): # The order is important, the backslash must be escaped first # since the other replacements introduce new backslashes # themselves. s = s.replace('\\', '\\\\') s = s.replace('\n', '\\n') s = s.replace('\r', '\\r') s = s.replace('\t', '\\t') s = s.replace('"', '\\"') return s def normalize(s): # This converts the various Python string types into a format that # is appropriate for .po files, namely much closer to C style. lines = s.split('\n') if len(lines) == 1: s = '"' + escape(s) + '"' else: if not lines[-1]: del lines[-1] lines[-1] = lines[-1] + '\n' lines = map(escape, lines) lineterm = '\\n"\n"' s = '""\n"' + lineterm.join(lines) + '"' return s def poentry(path, lineno, s): return ('#: %s:%d\n' % (path, lineno) + 'msgid %s\n' % normalize(s) + 'msgstr ""\n') doctestre = re.compile(r'^ +>>> ', re.MULTILINE) def offset(src, doc, name, lineno, default): """Compute offset or issue a warning on stdout.""" # remove doctest part, in order to avoid backslash mismatching m = doctestre.search(doc) if m: doc = doc[:m.start()] # Backslashes in doc appear doubled in src. end = src.find(doc.replace('\\', '\\\\')) if end == -1: # This can happen if the docstring contains unnecessary escape # sequences such as \" in a triple-quoted string. The problem # is that \" is turned into " and so doc wont appear in src. sys.stderr.write("%s:%d:warning:" " unknown docstr offset, assuming %d lines\n" % (name, lineno, default)) return default else: return src.count('\n', 0, end) def importpath(path): """Import a path like foo/bar/baz.py and return the baz module.""" if path.endswith('.py'): path = path[:-3] if path.endswith('/__init__'): path = path[:-9] path = path.replace('/', '.') mod = __import__(path) for comp in path.split('.')[1:]: mod = getattr(mod, comp) return mod def docstrings(path): """Extract docstrings from path. This respects the Mercurial cmdtable/table convention and will only extract docstrings from functions mentioned in these tables. """ mod = importpath(path) if not path.startswith('mercurial/') and mod.__doc__: with open(path) as fobj: src = fobj.read() lineno = 1 + offset(src, mod.__doc__, path, 1, 7) print(poentry(path, lineno, mod.__doc__)) functions = list(getattr(mod, 'i18nfunctions', [])) functions = [(f, True) for f in functions] cmdtable = getattr(mod, 'cmdtable', {}) if not cmdtable: # Maybe we are processing mercurial.commands? cmdtable = getattr(mod, 'table', {}) functions.extend((c[0], False) for c in cmdtable.itervalues()) for func, rstrip in functions: if func.__doc__: docobj = func # this might be a proxy to provide formatted doc func = getattr(func, '_origfunc', func) funcmod = inspect.getmodule(func) extra = '' if funcmod.__package__ == funcmod.__name__: extra = '/__init__' actualpath = '%s%s.py' % (funcmod.__name__.replace('.', '/'), extra) src = inspect.getsource(func) lineno = inspect.getsourcelines(func)[1] doc = docobj.__doc__ origdoc = getattr(docobj, '_origdoc', '') if rstrip: doc = doc.rstrip() origdoc = origdoc.rstrip() if origdoc: lineno += offset(src, origdoc, actualpath, lineno, 1) else: lineno += offset(src, doc, actualpath, lineno, 1) print(poentry(actualpath, lineno, doc)) def rawtext(path): with open(path) as f: src = f.read() print(poentry(path, 1, src)) if __name__ == "__main__": # It is very important that we import the Mercurial modules from # the source tree where hggettext is executed. Otherwise we might # accidentally import and extract strings from a Mercurial # installation mentioned in PYTHONPATH. sys.path.insert(0, os.getcwd()) from mercurial import demandimport; demandimport.enable() for path in sys.argv[1:]: if path.endswith('.txt'): rawtext(path) else: docstrings(path)