##// END OF EJS Templates
diff: do not concatenate immutable bytes while building a/b bodies (issue6445)...
diff: do not concatenate immutable bytes while building a/b bodies (issue6445) Use bytearray instead. I don't know what's changed since Python 2, but bytes concatenation is 100x slow on Python 3. % python2.7 -m timeit -s "s = b''" "for i in range(10000): s += b'line'" 1000 loops, best of 3: 321 usec per loop % python3.9 -m timeit -s "s = b''" "for i in range(10000): s += b'line'" 5 loops, best of 5: 39.2 msec per loop Benchmark using tailwind.css (measuring the fast path, a is empty): % HGRCPATH=/dev/null python2.7 ./hg log -R /tmp/issue6445 -p --time \ --color=always --config diff.word-diff=true >/dev/null (prev) time: real 1.580 secs (user 1.560+0.000 sys 0.020+0.000) (this) time: real 1.610 secs (user 1.570+0.000 sys 0.030+0.000) % HGRCPATH=/dev/null python3.9 ./hg log -R /tmp/issue6445 -p --time \ --color=always --config diff.word-diff=true >/dev/null (prev) time: real 114.500 secs (user 114.460+0.000 sys 0.030+0.000) (this) time: real 2.180 secs (user 2.140+0.000 sys 0.040+0.000) Benchmark using random tabular text data (not the fast path): % dd if=/dev/urandom bs=1k count=1000 | hexdump -v -e '16/1 "%3u," "\n"' > ttf % hg ci -ma % dd if=/dev/urandom bs=1k count=1000 | hexdump -v -e '16/1 "%3u," "\n"' > ttf % hg ci -mb % HGRCPATH=/dev/null python2.7 ./hg log -R /tmp/issue6445 -p --time \ --color=always --config diff.word-diff=true >/dev/null (prev) time: real 3.240 secs (user 3.040+0.000 sys 0.200+0.000 (this) time: real 3.230 secs (user 3.070+0.000 sys 0.160+0.000) % HGRCPATH=/dev/null python3.9 ./hg log -R /tmp/issue6445 -p --time \ --color=always --config diff.word-diff=true >/dev/null (prev) time: real 44.130 secs (user 43.850+0.000 sys 0.270+0.000) (this) time: real 4.170 secs (user 3.850+0.000 sys 0.310+0.000)

File last commit:

r43347:687b865b default
r46624:210f9b8d stable
Show More
loggingutil.py
141 lines | 3.9 KiB | text/x-python | PythonLexer
# loggingutil.py - utility for logging events
#
# Copyright 2010 Nicolas Dumazet
# Copyright 2013 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
import errno
from . import pycompat
from .utils import (
dateutil,
procutil,
stringutil,
)
def openlogfile(ui, vfs, name, maxfiles=0, maxsize=0):
"""Open log file in append mode, with optional rotation
If maxsize > 0, the log file will be rotated up to maxfiles.
"""
def rotate(oldpath, newpath):
try:
vfs.unlink(newpath)
except OSError as err:
if err.errno != errno.ENOENT:
ui.debug(
b"warning: cannot remove '%s': %s\n"
% (newpath, err.strerror)
)
try:
if newpath:
vfs.rename(oldpath, newpath)
except OSError as err:
if err.errno != errno.ENOENT:
ui.debug(
b"warning: cannot rename '%s' to '%s': %s\n"
% (newpath, oldpath, err.strerror)
)
if maxsize > 0:
try:
st = vfs.stat(name)
except OSError:
pass
else:
if st.st_size >= maxsize:
path = vfs.join(name)
for i in pycompat.xrange(maxfiles - 1, 1, -1):
rotate(
oldpath=b'%s.%d' % (path, i - 1),
newpath=b'%s.%d' % (path, i),
)
rotate(oldpath=path, newpath=maxfiles > 0 and path + b'.1')
return vfs(name, b'a', makeparentdirs=False)
def _formatlogline(msg):
date = dateutil.datestr(format=b'%Y/%m/%d %H:%M:%S')
pid = procutil.getpid()
return b'%s (%d)> %s' % (date, pid, msg)
def _matchevent(event, tracked):
return b'*' in tracked or event in tracked
class filelogger(object):
"""Basic logger backed by physical file with optional rotation"""
def __init__(self, vfs, name, tracked, maxfiles=0, maxsize=0):
self._vfs = vfs
self._name = name
self._trackedevents = set(tracked)
self._maxfiles = maxfiles
self._maxsize = maxsize
def tracked(self, event):
return _matchevent(event, self._trackedevents)
def log(self, ui, event, msg, opts):
line = _formatlogline(msg)
try:
with openlogfile(
ui,
self._vfs,
self._name,
maxfiles=self._maxfiles,
maxsize=self._maxsize,
) as fp:
fp.write(line)
except IOError as err:
ui.debug(
b'cannot write to %s: %s\n'
% (self._name, stringutil.forcebytestr(err))
)
class fileobjectlogger(object):
"""Basic logger backed by file-like object"""
def __init__(self, fp, tracked):
self._fp = fp
self._trackedevents = set(tracked)
def tracked(self, event):
return _matchevent(event, self._trackedevents)
def log(self, ui, event, msg, opts):
line = _formatlogline(msg)
try:
self._fp.write(line)
self._fp.flush()
except IOError as err:
ui.debug(
b'cannot write to %s: %s\n'
% (
stringutil.forcebytestr(self._fp.name),
stringutil.forcebytestr(err),
)
)
class proxylogger(object):
"""Forward log events to another logger to be set later"""
def __init__(self):
self.logger = None
def tracked(self, event):
return self.logger is not None and self.logger.tracked(event)
def log(self, ui, event, msg, opts):
assert self.logger is not None
self.logger.log(ui, event, msg, opts)