comparison mercurial/encoding.py @ 22778:80f2b63dd83a

parsers: add a function to efficiently lowercase ASCII strings We need a way to efficiently lowercase ASCII strings. For example, 'hg status' needs to build up the fold map -- a map from a canonical case (for OS X, lowercase) to the actual case of each file and directory in the dirstate. The current way we do that is to try decoding to ASCII and then calling lower() on the string, labeled 'orig' below: str.decode('ascii') return str.lower() This is pretty inefficient, and it turns out we can do much better. I also tested out a condition-based approach, labeled 'cond' below: (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c 'cond' turned out to be slower in all cases. A 256-byte lookup table with invalid values for everything past 127 performed similarly, but this was less verbose. On OS X 10.9 with LLVM version 6.0 (clang-600.0.51), the asciilower function was run against two corpuses. Corpus 1 (list of files from real-world repo, > 100k files): orig: wall 0.428567 comb 0.430000 user 0.430000 sys 0.000000 (best of 24) cond: wall 0.077204 comb 0.070000 user 0.070000 sys 0.000000 (best of 100) lookup: wall 0.060714 comb 0.060000 user 0.060000 sys 0.000000 (best of 100) Corpus 2 (mozilla-central, 113k files): orig: wall 0.238406 comb 0.240000 user 0.240000 sys 0.000000 (best of 42) cond: wall 0.040779 comb 0.040000 user 0.040000 sys 0.000000 (best of 100) lookup: wall 0.037623 comb 0.040000 user 0.040000 sys 0.000000 (best of 100) On a Linux server-class machine with GCC 4.4.6 20120305 (Red Hat 4.4.6-4): Corpus 1 (real-world repo, > 100k files): orig: wall 0.260899 comb 0.260000 user 0.260000 sys 0.000000 (best of 38) cond: wall 0.054818 comb 0.060000 user 0.060000 sys 0.000000 (best of 100) lookup: wall 0.048489 comb 0.050000 user 0.050000 sys 0.000000 (best of 100) Corpus 2 (mozilla-central, 113k files): orig: wall 0.153082 comb 0.150000 user 0.150000 sys 0.000000 (best of 65) cond: wall 0.031007 comb 0.040000 user 0.040000 sys 0.000000 (best of 100) lookup: wall 0.028793 comb 0.030000 user 0.030000 sys 0.000000 (best of 100) SSE instructions might help even more, but I didn't experiment with those.
author Siddharth Agarwal <sid0@fb.com>
date Fri, 03 Oct 2014 18:42:39 -0700
parents f6b533e64ed6
children d9585dda63c3
comparison
equal deleted inserted replaced
22777:bbb2f8b0459e 22778:80f2b63dd83a
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 # 4 #
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 import error 8 import error, parsers
9 import unicodedata, locale, os 9 import unicodedata, locale, os
10 10
11 def _getpreferredencoding(): 11 def _getpreferredencoding():
12 ''' 12 '''
13 On darwin, getpreferredencoding ignores the locale environment and 13 On darwin, getpreferredencoding ignores the locale environment and
256 usub = uslice(i) 256 usub = uslice(i)
257 if ucolwidth(usub) <= width: 257 if ucolwidth(usub) <= width:
258 return concat(usub.encode(encoding)) 258 return concat(usub.encode(encoding))
259 return ellipsis # no enough room for multi-column characters 259 return ellipsis # no enough room for multi-column characters
260 260
261 def asciilower(s):
262 '''convert a string to lowercase if ASCII
263
264 Raises UnicodeDecodeError if non-ASCII characters are found.'''
265 s.decode('ascii')
266 return s.lower()
267
268 asciilower = getattr(parsers, 'asciilower', asciilower)
269
261 def lower(s): 270 def lower(s):
262 "best-effort encoding-aware case-folding of local string s" 271 "best-effort encoding-aware case-folding of local string s"
263 try: 272 try:
264 s.decode('ascii') # throw exception for non-ASCII character 273 s.decode('ascii') # throw exception for non-ASCII character
265 return s.lower() 274 return s.lower()