Android Build Tools
This commit is contained in:
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
desc = """Generate the difference of two YAML files into a new YAML file (works on
|
||||
pair of directories too). A new attribute 'Added' is set to True or False
|
||||
depending whether the entry is added or removed from the first input to the
|
||||
next.
|
||||
|
||||
The tools requires PyYAML."""
|
||||
|
||||
import yaml
|
||||
|
||||
# Try to use the C parser.
|
||||
try:
|
||||
from yaml import CLoader as Loader
|
||||
except ImportError:
|
||||
from yaml import Loader
|
||||
|
||||
import optrecord
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=desc)
|
||||
parser.add_argument(
|
||||
"yaml_dir_or_file_1",
|
||||
help="An optimization record file or a directory searched for optimization "
|
||||
"record files that are used as the old version for the comparison",
|
||||
)
|
||||
parser.add_argument(
|
||||
"yaml_dir_or_file_2",
|
||||
help="An optimization record file or a directory searched for optimization "
|
||||
"record files that are used as the new version for the comparison",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--jobs",
|
||||
"-j",
|
||||
default=None,
|
||||
type=int,
|
||||
help="Max job count (defaults to %(default)s, the current CPU count)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-size",
|
||||
"-m",
|
||||
default=100000,
|
||||
type=int,
|
||||
help="Maximum number of remarks stored in an output file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-progress-indicator",
|
||||
"-n",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Do not display any indicator of how many YAML files were read.",
|
||||
)
|
||||
parser.add_argument("--output", "-o", default="diff{}.opt.yaml")
|
||||
args = parser.parse_args()
|
||||
|
||||
files1 = optrecord.find_opt_files(args.yaml_dir_or_file_1)
|
||||
files2 = optrecord.find_opt_files(args.yaml_dir_or_file_2)
|
||||
|
||||
print_progress = not args.no_progress_indicator
|
||||
all_remarks1, _, _ = optrecord.gather_results(files1, args.jobs, print_progress)
|
||||
all_remarks2, _, _ = optrecord.gather_results(files2, args.jobs, print_progress)
|
||||
|
||||
added = set(all_remarks2.values()) - set(all_remarks1.values())
|
||||
removed = set(all_remarks1.values()) - set(all_remarks2.values())
|
||||
|
||||
for r in added:
|
||||
r.Added = True
|
||||
for r in removed:
|
||||
r.Added = False
|
||||
|
||||
result = list(added | removed)
|
||||
for r in result:
|
||||
r.recover_yaml_structure()
|
||||
|
||||
for i in range(0, len(result), args.max_size):
|
||||
with open(args.output.format(i / args.max_size), "w") as stream:
|
||||
yaml.dump_all(result[i : i + args.max_size], stream)
|
||||
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
desc = """Generate statistics about optimization records from the YAML files
|
||||
generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
|
||||
|
||||
The tools requires PyYAML and Pygments Python packages."""
|
||||
|
||||
import optrecord
|
||||
import argparse
|
||||
import operator
|
||||
from collections import defaultdict
|
||||
from multiprocessing import cpu_count, Pool
|
||||
|
||||
try:
|
||||
from guppy import hpy
|
||||
|
||||
hp = hpy()
|
||||
except ImportError:
|
||||
print("Memory consumption not shown because guppy is not installed")
|
||||
hp = None
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=desc)
|
||||
parser.add_argument(
|
||||
"yaml_dirs_or_files",
|
||||
nargs="+",
|
||||
help="List of optimization record files or directories searched "
|
||||
"for optimization record files.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--jobs",
|
||||
"-j",
|
||||
default=None,
|
||||
type=int,
|
||||
help="Max job count (defaults to %(default)s, the current CPU count)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-progress-indicator",
|
||||
"-n",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Do not display any indicator of how many YAML files were read.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
print_progress = not args.no_progress_indicator
|
||||
|
||||
files = optrecord.find_opt_files(*args.yaml_dirs_or_files)
|
||||
if not files:
|
||||
parser.error("No *.opt.yaml files found")
|
||||
sys.exit(1)
|
||||
|
||||
all_remarks, file_remarks, _ = optrecord.gather_results(
|
||||
files, args.jobs, print_progress
|
||||
)
|
||||
if print_progress:
|
||||
print("\n")
|
||||
|
||||
bypass = defaultdict(int)
|
||||
byname = defaultdict(int)
|
||||
for r in optrecord.itervalues(all_remarks):
|
||||
bypass[r.Pass] += 1
|
||||
byname[r.Pass + "/" + r.Name] += 1
|
||||
|
||||
total = len(all_remarks)
|
||||
print("{:24s} {:10d}".format("Total number of remarks", total))
|
||||
if hp:
|
||||
h = hp.heap()
|
||||
print("{:24s} {:10d}".format("Memory per remark", h.size / len(all_remarks)))
|
||||
print("\n")
|
||||
|
||||
print("Top 10 remarks by pass:")
|
||||
for (passname, count) in sorted(
|
||||
bypass.items(), key=operator.itemgetter(1), reverse=True
|
||||
)[:10]:
|
||||
print(" {:30s} {:2.0f}%".format(passname, count * 100.0 / total))
|
||||
|
||||
print("\nTop 10 remarks:")
|
||||
for (name, count) in sorted(
|
||||
byname.items(), key=operator.itemgetter(1), reverse=True
|
||||
)[:10]:
|
||||
print(" {:30s} {:2.0f}%".format(name, count * 100.0 / total))
|
||||
@ -0,0 +1,471 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import functools
|
||||
import html
|
||||
import io
|
||||
from multiprocessing import cpu_count
|
||||
import os.path
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from pygments import highlight
|
||||
from pygments.lexers.c_cpp import CppLexer
|
||||
from pygments.formatters import HtmlFormatter
|
||||
|
||||
import optpmap
|
||||
import optrecord
|
||||
|
||||
|
||||
desc = """Generate HTML output to visualize optimization records from the YAML files
|
||||
generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
|
||||
|
||||
The tools requires PyYAML and Pygments Python packages."""
|
||||
|
||||
|
||||
# This allows passing the global context to the child processes.
|
||||
class Context:
|
||||
def __init__(self, caller_loc=dict()):
|
||||
# Map function names to their source location for function where inlining happened
|
||||
self.caller_loc = caller_loc
|
||||
|
||||
|
||||
context = Context()
|
||||
|
||||
|
||||
def suppress(remark):
|
||||
if remark.Name == "sil.Specialized":
|
||||
return remark.getArgDict()["Function"][0].startswith('"Swift.')
|
||||
elif remark.Name == "sil.Inlined":
|
||||
return remark.getArgDict()["Callee"][0].startswith(
|
||||
('"Swift.', '"specialized Swift.')
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
class SourceFileRenderer:
|
||||
def __init__(self, source_dir, output_dir, filename, no_highlight):
|
||||
self.filename = filename
|
||||
existing_filename = None
|
||||
if os.path.exists(filename):
|
||||
existing_filename = filename
|
||||
else:
|
||||
fn = os.path.join(source_dir, filename)
|
||||
if os.path.exists(fn):
|
||||
existing_filename = fn
|
||||
|
||||
self.no_highlight = no_highlight
|
||||
self.stream = io.open(
|
||||
os.path.join(output_dir, optrecord.html_file_name(filename)),
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
)
|
||||
if existing_filename:
|
||||
self.source_stream = io.open(existing_filename, encoding="utf-8")
|
||||
else:
|
||||
self.source_stream = None
|
||||
print(
|
||||
"""
|
||||
<html>
|
||||
<h1>Unable to locate file {}</h1>
|
||||
</html>
|
||||
""".format(
|
||||
filename
|
||||
),
|
||||
file=self.stream,
|
||||
)
|
||||
|
||||
self.html_formatter = HtmlFormatter(encoding="utf-8")
|
||||
self.cpp_lexer = CppLexer(stripnl=False)
|
||||
|
||||
def render_source_lines(self, stream, line_remarks):
|
||||
file_text = stream.read()
|
||||
|
||||
if self.no_highlight:
|
||||
html_highlighted = file_text
|
||||
else:
|
||||
html_highlighted = highlight(file_text, self.cpp_lexer, self.html_formatter)
|
||||
|
||||
# Note that the API is different between Python 2 and 3. On
|
||||
# Python 3, pygments.highlight() returns a bytes object, so we
|
||||
# have to decode. On Python 2, the output is str but since we
|
||||
# support unicode characters and the output streams is unicode we
|
||||
# decode too.
|
||||
html_highlighted = html_highlighted.decode("utf-8")
|
||||
|
||||
# Take off the header and footer, these must be
|
||||
# reapplied line-wise, within the page structure
|
||||
html_highlighted = html_highlighted.replace(
|
||||
'<div class="highlight"><pre>', ""
|
||||
)
|
||||
html_highlighted = html_highlighted.replace("</pre></div>", "")
|
||||
|
||||
for (linenum, html_line) in enumerate(html_highlighted.split("\n"), start=1):
|
||||
print(
|
||||
"""
|
||||
<tr>
|
||||
<td><a name=\"L{linenum}\">{linenum}</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><div class="highlight"><pre>{html_line}</pre></div></td>
|
||||
</tr>""".format(
|
||||
**locals()
|
||||
),
|
||||
file=self.stream,
|
||||
)
|
||||
|
||||
for remark in line_remarks.get(linenum, []):
|
||||
if not suppress(remark):
|
||||
self.render_inline_remarks(remark, html_line)
|
||||
|
||||
def render_inline_remarks(self, r, line):
|
||||
inlining_context = r.DemangledFunctionName
|
||||
dl = context.caller_loc.get(r.Function)
|
||||
if dl:
|
||||
dl_dict = dict(list(dl))
|
||||
link = optrecord.make_link(dl_dict["File"], dl_dict["Line"] - 2)
|
||||
inlining_context = "<a href={link}>{r.DemangledFunctionName}</a>".format(
|
||||
**locals()
|
||||
)
|
||||
|
||||
# Column is the number of characters *including* tabs, keep those and
|
||||
# replace everything else with spaces.
|
||||
indent = line[: max(r.Column, 1) - 1]
|
||||
indent = re.sub("\S", " ", indent)
|
||||
|
||||
# Create expanded message and link if we have a multiline message.
|
||||
lines = r.message.split("\n")
|
||||
if len(lines) > 1:
|
||||
expand_link = '<a style="text-decoration: none;" href="" onclick="toggleExpandedMessage(this); return false;">+</a>'
|
||||
message = lines[0]
|
||||
expand_message = """
|
||||
<div class="full-info" style="display:none;">
|
||||
<div class="col-left"><pre style="display:inline">{}</pre></div>
|
||||
<div class="expanded col-left"><pre>{}</pre></div>
|
||||
</div>""".format(
|
||||
indent, "\n".join(lines[1:])
|
||||
)
|
||||
else:
|
||||
expand_link = ""
|
||||
expand_message = ""
|
||||
message = r.message
|
||||
print(
|
||||
"""
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>{r.RelativeHotness}</td>
|
||||
<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td>
|
||||
<td><pre style="display:inline">{indent}</pre><span class=\"column-entry-yellow\">{expand_link} {message} </span>{expand_message}</td>
|
||||
<td class=\"column-entry-yellow\">{inlining_context}</td>
|
||||
</tr>""".format(
|
||||
**locals()
|
||||
),
|
||||
file=self.stream,
|
||||
)
|
||||
|
||||
def render(self, line_remarks):
|
||||
if not self.source_stream:
|
||||
return
|
||||
|
||||
print(
|
||||
"""
|
||||
<html>
|
||||
<title>{}</title>
|
||||
<meta charset="utf-8" />
|
||||
<head>
|
||||
<link rel='stylesheet' type='text/css' href='style.css'>
|
||||
<script type="text/javascript">
|
||||
/* Simple helper to show/hide the expanded message of a remark. */
|
||||
function toggleExpandedMessage(e) {{
|
||||
var FullTextElems = e.parentElement.parentElement.getElementsByClassName("full-info");
|
||||
if (!FullTextElems || FullTextElems.length < 1) {{
|
||||
return false;
|
||||
}}
|
||||
var FullText = FullTextElems[0];
|
||||
if (FullText.style.display == 'none') {{
|
||||
e.innerHTML = '-';
|
||||
FullText.style.display = 'block';
|
||||
}} else {{
|
||||
e.innerHTML = '+';
|
||||
FullText.style.display = 'none';
|
||||
}}
|
||||
}}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="centered">
|
||||
<table class="source">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 2%">Line</td>
|
||||
<th style="width: 3%">Hotness</td>
|
||||
<th style="width: 10%">Optimization</td>
|
||||
<th style="width: 70%">Source</td>
|
||||
<th style="width: 15%">Inline Context</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>""".format(
|
||||
os.path.basename(self.filename)
|
||||
),
|
||||
file=self.stream,
|
||||
)
|
||||
self.render_source_lines(self.source_stream, line_remarks)
|
||||
|
||||
print(
|
||||
"""
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>""",
|
||||
file=self.stream,
|
||||
)
|
||||
|
||||
|
||||
class IndexRenderer:
|
||||
def __init__(
|
||||
self, output_dir, should_display_hotness, max_hottest_remarks_on_index
|
||||
):
|
||||
self.stream = io.open(
|
||||
os.path.join(output_dir, "index.html"), "w", encoding="utf-8"
|
||||
)
|
||||
self.should_display_hotness = should_display_hotness
|
||||
self.max_hottest_remarks_on_index = max_hottest_remarks_on_index
|
||||
|
||||
def render_entry(self, r, odd):
|
||||
escaped_name = html.escape(r.DemangledFunctionName)
|
||||
print(
|
||||
"""
|
||||
<tr>
|
||||
<td class=\"column-entry-{odd}\"><a href={r.Link}>{r.DebugLocString}</a></td>
|
||||
<td class=\"column-entry-{odd}\">{r.RelativeHotness}</td>
|
||||
<td class=\"column-entry-{odd}\">{escaped_name}</td>
|
||||
<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td>
|
||||
</tr>""".format(
|
||||
**locals()
|
||||
),
|
||||
file=self.stream,
|
||||
)
|
||||
|
||||
def render(self, all_remarks):
|
||||
print(
|
||||
"""
|
||||
<html>
|
||||
<meta charset="utf-8" />
|
||||
<head>
|
||||
<link rel='stylesheet' type='text/css' href='style.css'>
|
||||
</head>
|
||||
<body>
|
||||
<div class="centered">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Source Location</td>
|
||||
<td>Hotness</td>
|
||||
<td>Function</td>
|
||||
<td>Pass</td>
|
||||
</tr>""",
|
||||
file=self.stream,
|
||||
)
|
||||
|
||||
max_entries = None
|
||||
if self.should_display_hotness:
|
||||
max_entries = self.max_hottest_remarks_on_index
|
||||
|
||||
for i, remark in enumerate(all_remarks[:max_entries]):
|
||||
if not suppress(remark):
|
||||
self.render_entry(remark, i % 2)
|
||||
print(
|
||||
"""
|
||||
</table>
|
||||
</body>
|
||||
</html>""",
|
||||
file=self.stream,
|
||||
)
|
||||
|
||||
|
||||
def _render_file(source_dir, output_dir, ctx, no_highlight, entry, filter_):
|
||||
global context
|
||||
context = ctx
|
||||
filename, remarks = entry
|
||||
SourceFileRenderer(source_dir, output_dir, filename, no_highlight).render(remarks)
|
||||
|
||||
|
||||
def map_remarks(all_remarks):
|
||||
# Set up a map between function names and their source location for
|
||||
# function where inlining happened
|
||||
for remark in optrecord.itervalues(all_remarks):
|
||||
if (
|
||||
isinstance(remark, optrecord.Passed)
|
||||
and remark.Pass == "inline"
|
||||
and remark.Name == "Inlined"
|
||||
):
|
||||
for arg in remark.Args:
|
||||
arg_dict = dict(list(arg))
|
||||
caller = arg_dict.get("Caller")
|
||||
if caller:
|
||||
try:
|
||||
context.caller_loc[caller] = arg_dict["DebugLoc"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
def generate_report(
|
||||
all_remarks,
|
||||
file_remarks,
|
||||
source_dir,
|
||||
output_dir,
|
||||
no_highlight,
|
||||
should_display_hotness,
|
||||
max_hottest_remarks_on_index,
|
||||
num_jobs,
|
||||
should_print_progress,
|
||||
):
|
||||
try:
|
||||
os.makedirs(output_dir)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST and os.path.isdir(output_dir):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
if should_print_progress:
|
||||
print("Rendering index page...")
|
||||
if should_display_hotness:
|
||||
sorted_remarks = sorted(
|
||||
optrecord.itervalues(all_remarks),
|
||||
key=lambda r: (
|
||||
r.Hotness,
|
||||
r.File,
|
||||
r.Line,
|
||||
r.Column,
|
||||
r.PassWithDiffPrefix,
|
||||
r.yaml_tag,
|
||||
r.Function,
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
else:
|
||||
sorted_remarks = sorted(
|
||||
optrecord.itervalues(all_remarks),
|
||||
key=lambda r: (
|
||||
r.File,
|
||||
r.Line,
|
||||
r.Column,
|
||||
r.PassWithDiffPrefix,
|
||||
r.yaml_tag,
|
||||
r.Function,
|
||||
),
|
||||
)
|
||||
IndexRenderer(
|
||||
output_dir, should_display_hotness, max_hottest_remarks_on_index
|
||||
).render(sorted_remarks)
|
||||
|
||||
shutil.copy(
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "style.css"),
|
||||
output_dir,
|
||||
)
|
||||
|
||||
_render_file_bound = functools.partial(
|
||||
_render_file, source_dir, output_dir, context, no_highlight
|
||||
)
|
||||
if should_print_progress:
|
||||
print("Rendering HTML files...")
|
||||
optpmap.pmap(
|
||||
_render_file_bound, file_remarks.items(), num_jobs, should_print_progress
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=desc)
|
||||
parser.add_argument(
|
||||
"yaml_dirs_or_files",
|
||||
nargs="+",
|
||||
help="List of optimization record files or directories searched "
|
||||
"for optimization record files.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
"-o",
|
||||
default="html",
|
||||
help="Path to a directory where generated HTML files will be output. "
|
||||
"If the directory does not already exist, it will be created. "
|
||||
'"%(default)s" by default.',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--jobs",
|
||||
"-j",
|
||||
default=None,
|
||||
type=int,
|
||||
help="Max job count (defaults to %(default)s, the current CPU count)",
|
||||
)
|
||||
parser.add_argument("--source-dir", "-s", default="", help="set source directory")
|
||||
parser.add_argument(
|
||||
"--no-progress-indicator",
|
||||
"-n",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Do not display any indicator of how many YAML files were read "
|
||||
"or rendered into HTML.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-hottest-remarks-on-index",
|
||||
default=1000,
|
||||
type=int,
|
||||
help="Maximum number of the hottest remarks to appear on the index page",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-highlight",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Do not use a syntax highlighter when rendering the source code",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--demangler",
|
||||
help="Set the demangler to be used (defaults to %s)"
|
||||
% optrecord.Remark.default_demangler,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--filter",
|
||||
default="",
|
||||
help="Only display remarks from passes matching filter expression",
|
||||
)
|
||||
|
||||
# Do not make this a global variable. Values needed to be propagated through
|
||||
# to individual classes and functions to be portable with multiprocessing across
|
||||
# Windows and non-Windows.
|
||||
args = parser.parse_args()
|
||||
|
||||
print_progress = not args.no_progress_indicator
|
||||
if args.demangler:
|
||||
optrecord.Remark.set_demangler(args.demangler)
|
||||
|
||||
files = optrecord.find_opt_files(*args.yaml_dirs_or_files)
|
||||
if not files:
|
||||
parser.error("No *.opt.yaml files found")
|
||||
sys.exit(1)
|
||||
|
||||
all_remarks, file_remarks, should_display_hotness = optrecord.gather_results(
|
||||
files, args.jobs, print_progress, args.filter
|
||||
)
|
||||
|
||||
map_remarks(all_remarks)
|
||||
|
||||
generate_report(
|
||||
all_remarks,
|
||||
file_remarks,
|
||||
args.source_dir,
|
||||
args.output_dir,
|
||||
args.no_highlight,
|
||||
should_display_hotness,
|
||||
args.max_hottest_remarks_on_index,
|
||||
args.jobs,
|
||||
print_progress,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,63 @@
|
||||
import sys
|
||||
import multiprocessing
|
||||
|
||||
|
||||
_current = None
|
||||
_total = None
|
||||
|
||||
|
||||
def _init(current, total):
|
||||
global _current
|
||||
global _total
|
||||
_current = current
|
||||
_total = total
|
||||
|
||||
|
||||
def _wrapped_func(func_and_args):
|
||||
func, argument, should_print_progress, filter_ = func_and_args
|
||||
|
||||
if should_print_progress:
|
||||
with _current.get_lock():
|
||||
_current.value += 1
|
||||
sys.stdout.write("\r\t{} of {}".format(_current.value, _total.value))
|
||||
sys.stdout.flush()
|
||||
|
||||
return func(argument, filter_)
|
||||
|
||||
|
||||
def pmap(
|
||||
func, iterable, processes, should_print_progress, filter_=None, *args, **kwargs
|
||||
):
|
||||
"""
|
||||
A parallel map function that reports on its progress.
|
||||
|
||||
Applies `func` to every item of `iterable` and return a list of the
|
||||
results. If `processes` is greater than one, a process pool is used to run
|
||||
the functions in parallel. `should_print_progress` is a boolean value that
|
||||
indicates whether a string 'N of M' should be printed to indicate how many
|
||||
of the functions have finished being run.
|
||||
"""
|
||||
global _current
|
||||
global _total
|
||||
_current = multiprocessing.Value("i", 0)
|
||||
_total = multiprocessing.Value("i", len(iterable))
|
||||
|
||||
func_and_args = [(func, arg, should_print_progress, filter_) for arg in iterable]
|
||||
if processes == 1:
|
||||
result = list(map(_wrapped_func, func_and_args, *args, **kwargs))
|
||||
else:
|
||||
pool = multiprocessing.Pool(
|
||||
initializer=_init,
|
||||
initargs=(
|
||||
_current,
|
||||
_total,
|
||||
),
|
||||
processes=processes,
|
||||
)
|
||||
result = pool.map(_wrapped_func, func_and_args, *args, **kwargs)
|
||||
pool.close()
|
||||
pool.join()
|
||||
|
||||
if should_print_progress:
|
||||
sys.stdout.write("\r")
|
||||
return result
|
||||
@ -0,0 +1,218 @@
|
||||
.source {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.source td {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.red {
|
||||
background-color: #ffd0d0;
|
||||
}
|
||||
.cyan {
|
||||
background-color: cyan;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, sans-serif;
|
||||
}
|
||||
pre {
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.source-name-title {
|
||||
padding: 5px 10px;
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
background-color: #eee;
|
||||
line-height: 35px;
|
||||
}
|
||||
.centered {
|
||||
display: table;
|
||||
margin-left: left;
|
||||
margin-right: auto;
|
||||
border: 1px solid #dbdbdb;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.expansion-view {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
margin-left: 0px;
|
||||
margin-top: 5px;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
border: 1px solid #dbdbdb;
|
||||
border-radius: 3px;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.light-row {
|
||||
background: #ffffff;
|
||||
border: 1px solid #dbdbdb;
|
||||
}
|
||||
.column-entry {
|
||||
text-align: right;
|
||||
}
|
||||
.column-entry-left {
|
||||
text-align: left;
|
||||
}
|
||||
.column-entry-white {
|
||||
text-align: right;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.column-entry-red {
|
||||
text-align: right;
|
||||
background-color: #ffd0d0;
|
||||
}
|
||||
.column-entry-green {
|
||||
text-align: right;
|
||||
background-color: #d0ffd0;
|
||||
}
|
||||
.column-entry-yellow {
|
||||
text-align: left;
|
||||
background-color: #ffe1a6;
|
||||
}
|
||||
.column-entry-0 {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.column-entry-1 {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
.line-number {
|
||||
text-align: right;
|
||||
color: #aaa;
|
||||
}
|
||||
.covered-line {
|
||||
text-align: right;
|
||||
color: #0080ff;
|
||||
}
|
||||
.uncovered-line {
|
||||
text-align: right;
|
||||
color: #ff3300;
|
||||
}
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline;
|
||||
background-color: #b3e6ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.tooltip span.tooltip-content {
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
margin-left: -50px;
|
||||
color: #FFFFFF;
|
||||
background: #000000;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
visibility: hidden;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.tooltip span.tooltip-content:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -8px;
|
||||
width: 0; height: 0;
|
||||
border-top: 8px solid #000000;
|
||||
border-right: 8px solid transparent;
|
||||
border-left: 8px solid transparent;
|
||||
}
|
||||
:hover.tooltip span.tooltip-content {
|
||||
visibility: visible;
|
||||
opacity: 0.8;
|
||||
bottom: 30px;
|
||||
left: 50%;
|
||||
z-index: 999;
|
||||
}
|
||||
th, td {
|
||||
vertical-align: top;
|
||||
padding: 2px 5px;
|
||||
border-collapse: collapse;
|
||||
border-right: solid 1px #eee;
|
||||
border-left: solid 1px #eee;
|
||||
}
|
||||
td:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.expanded {
|
||||
background-color: #f2f2f2;
|
||||
padding-top: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.col-left {
|
||||
float: left;
|
||||
margin-bottom: -99999px;
|
||||
padding-bottom: 99999px;
|
||||
}
|
||||
|
||||
/* Generated with pygmentize -S colorful -f html >> style.css */
|
||||
|
||||
.hll { background-color: #ffffcc }
|
||||
.c { color: #888888 } /* Comment */
|
||||
.err { color: #FF0000; background-color: #FFAAAA } /* Error */
|
||||
.k { color: #008800; font-weight: bold } /* Keyword */
|
||||
.o { color: #333333 } /* Operator */
|
||||
.ch { color: #888888 } /* Comment.Hashbang */
|
||||
.cm { color: #888888 } /* Comment.Multiline */
|
||||
.cp { color: #557799 } /* Comment.Preproc */
|
||||
.cpf { color: #888888 } /* Comment.PreprocFile */
|
||||
.c1 { color: #888888 } /* Comment.Single */
|
||||
.cs { color: #cc0000; font-weight: bold } /* Comment.Special */
|
||||
.gd { color: #A00000 } /* Generic.Deleted */
|
||||
.ge { font-style: italic } /* Generic.Emph */
|
||||
.gr { color: #FF0000 } /* Generic.Error */
|
||||
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.gi { color: #00A000 } /* Generic.Inserted */
|
||||
.go { color: #888888 } /* Generic.Output */
|
||||
.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
|
||||
.gs { font-weight: bold } /* Generic.Strong */
|
||||
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.gt { color: #0044DD } /* Generic.Traceback */
|
||||
.kc { color: #008800; font-weight: bold } /* Keyword.Constant */
|
||||
.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
|
||||
.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
|
||||
.kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */
|
||||
.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
|
||||
.kt { color: #333399; font-weight: bold } /* Keyword.Type */
|
||||
.m { color: #6600EE; font-weight: bold } /* Literal.Number */
|
||||
.s { background-color: #fff0f0 } /* Literal.String */
|
||||
.na { color: #0000CC } /* Name.Attribute */
|
||||
.nb { color: #007020 } /* Name.Builtin */
|
||||
.nc { color: #BB0066; font-weight: bold } /* Name.Class */
|
||||
.no { color: #003366; font-weight: bold } /* Name.Constant */
|
||||
.nd { color: #555555; font-weight: bold } /* Name.Decorator */
|
||||
.ni { color: #880000; font-weight: bold } /* Name.Entity */
|
||||
.ne { color: #FF0000; font-weight: bold } /* Name.Exception */
|
||||
.nf { color: #0066BB; font-weight: bold } /* Name.Function */
|
||||
.nl { color: #997700; font-weight: bold } /* Name.Label */
|
||||
.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
|
||||
.nt { color: #007700 } /* Name.Tag */
|
||||
.nv { color: #996633 } /* Name.Variable */
|
||||
.ow { color: #000000; font-weight: bold } /* Operator.Word */
|
||||
.w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */
|
||||
.mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */
|
||||
.mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */
|
||||
.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
|
||||
.mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */
|
||||
.sb { background-color: #fff0f0 } /* Literal.String.Backtick */
|
||||
.sc { color: #0044DD } /* Literal.String.Char */
|
||||
.sd { color: #DD4422 } /* Literal.String.Doc */
|
||||
.s2 { background-color: #fff0f0 } /* Literal.String.Double */
|
||||
.se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */
|
||||
.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */
|
||||
.si { background-color: #eeeeee } /* Literal.String.Interpol */
|
||||
.sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */
|
||||
.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */
|
||||
.s1 { background-color: #fff0f0 } /* Literal.String.Single */
|
||||
.ss { color: #AA6600 } /* Literal.String.Symbol */
|
||||
.bp { color: #007020 } /* Name.Builtin.Pseudo */
|
||||
.vc { color: #336699 } /* Name.Variable.Class */
|
||||
.vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */
|
||||
.vi { color: #3333BB } /* Name.Variable.Instance */
|
||||
.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
|
||||
Reference in New Issue
Block a user