(reland) Optimize check_gn_headers.py for speed

- Process the output of 'ninja' live through the pipe rather than
  waiting for it to finish.
- Use multiprocessing.

Benchmark results using pypy:
Before:
17.10user 14.38system 0:20.05elapsed 157%CPU (0avgtext+0avgdata 1708036maxresident)k
18.06user 15.63system 0:21.92elapsed 153%CPU (0avgtext+0avgdata 1710912maxresident)k
17.94user 13.72system 0:19.91elapsed 158%CPU (0avgtext+0avgdata 1172128maxresident)k

After:
15.28user 7.11system 0:06.25elapsed 357%CPU (0avgtext+0avgdata 319236maxresident)k
16.03user 7.74system 0:06.84elapsed 347%CPU (0avgtext+0avgdata 319396maxresident)k
15.24user 8.16system 0:06.36elapsed 367%CPU (0avgtext+0avgdata 318272maxresident)k

BUG=661774

Review-Url: https://codereview.chromium.org/2842513003
Review-Url: https://codereview.chromium.org/2846473002
Cr-Original-Commit-Position: refs/heads/master@{#467604}
Cr-Original-Original-Commit-Position: refs/heads/master@{#466900}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: ef74ec9973e071da2dc5813f0b93662a0f1497bf
This commit is contained in:
wychen 2017-04-26 23:28:25 -07:00 коммит произвёл Commit bot
Родитель 8818505e51
Коммит 7c160a8fdd
2 изменённых файлов: 45 добавлений и 13 удалений

Просмотреть файл

@ -15,12 +15,27 @@ import os
import re
import subprocess
import sys
from multiprocessing import Process, Queue
def GetHeadersFromNinja(out_dir):
def GetHeadersFromNinja(out_dir, q):
"""Return all the header files from ninja_deps"""
ninja_out = subprocess.check_output(['ninja', '-C', out_dir, '-t', 'deps'])
return ParseNinjaDepsOutput(ninja_out)
def NinjaSource():
cmd = ['ninja', '-C', out_dir, '-t', 'deps']
# A negative bufsize means to use the system default, which usually
# means fully buffered.
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=-1)
for line in iter(popen.stdout.readline, ''):
yield line.rstrip()
popen.stdout.close()
return_code = popen.wait()
if return_code:
raise subprocess.CalledProcessError(return_code, cmd)
ninja_out = NinjaSource()
q.put(ParseNinjaDepsOutput(ninja_out))
def ParseNinjaDepsOutput(ninja_out):
@ -30,7 +45,7 @@ def ParseNinjaDepsOutput(ninja_out):
prefix = '..' + os.sep + '..' + os.sep
is_valid = False
for line in ninja_out.split('\n'):
for line in ninja_out:
if line.startswith(' '):
if not is_valid:
continue
@ -49,11 +64,11 @@ def ParseNinjaDepsOutput(ninja_out):
return all_headers
def GetHeadersFromGN(out_dir):
def GetHeadersFromGN(out_dir, q):
"""Return all the header files from GN"""
subprocess.check_call(['gn', 'gen', out_dir, '--ide=json', '-q'])
gn_json = json.load(open(os.path.join(out_dir, 'project.json')))
return ParseGNProjectJSON(gn_json)
q.put(ParseGNProjectJSON(gn_json))
def ParseGNProjectJSON(gn):
@ -70,7 +85,7 @@ def ParseGNProjectJSON(gn):
return all_headers
def GetDepsPrefixes():
def GetDepsPrefixes(q):
"""Return all the folders controlled by DEPS file"""
gclient_out = subprocess.check_output(
['gclient', 'recurse', '--no-progress', '-j1',
@ -80,7 +95,7 @@ def GetDepsPrefixes():
if i.startswith('src/'):
i = i[4:]
prefixes.add(i)
return prefixes
q.put(prefixes)
def ParseWhiteList(whitelist):
@ -101,13 +116,29 @@ def main():
args, _extras = parser.parse_known_args()
d = GetHeadersFromNinja(args.out_dir)
gn = GetHeadersFromGN(args.out_dir)
d_q = Queue()
d_p = Process(target=GetHeadersFromNinja, args=(args.out_dir, d_q,))
d_p.start()
gn_q = Queue()
gn_p = Process(target=GetHeadersFromGN, args=(args.out_dir, gn_q,))
gn_p.start()
deps_q = Queue()
deps_p = Process(target=GetDepsPrefixes, args=(deps_q,))
deps_p.start()
d = d_q.get()
gn = gn_q.get()
missing = d - gn
deps = GetDepsPrefixes()
deps = deps_q.get()
missing = {m for m in missing if not any(m.startswith(d) for d in deps)}
d_p.join()
gn_p.join()
deps_p.join()
if args.whitelist:
whitelist = ParseWhiteList(open(args.whitelist).read())
missing -= whitelist

Просмотреть файл

@ -58,7 +58,7 @@ a/b/c
class CheckGnHeadersTest(unittest.TestCase):
def testNinja(self):
headers = check_gn_headers.ParseNinjaDepsOutput(ninja_input)
headers = check_gn_headers.ParseNinjaDepsOutput(ninja_input.split('\n'))
expected = set([
'dir/path/b.h',
'c.hh',
@ -71,7 +71,8 @@ class CheckGnHeadersTest(unittest.TestCase):
old_sep = os.sep
os.sep = '\\'
headers = check_gn_headers.ParseNinjaDepsOutput(ninja_input_win)
headers = check_gn_headers.ParseNinjaDepsOutput(
ninja_input_win.split('\n'))
expected = set([
'dir\\path\\b.h',
'c.hh',