Commit 96de069f authored by Minh-Duc Nguyen's avatar Minh-Duc Nguyen Committed by Commit Bot

Change autogenerated switchcase converter for all enums

to calculated converter

Currently, converter for enum generate a list of
switch case statements. This is inefficient so we generate
the conversion with mathematical op to reduce the amount of switch
case statement because more switch case statement means longer runtime
and larger binary size.
Gen diff: https://gist.github.com/nguyen-minh-duc/166720ffe3821202093ef00ea83dd028/revisions
The graph represent the runtime of the switch case function (blue line) and the matops 
function (orange line). As we can see as the number of of case statement increase the
runtime increase linearly but the matops function is constant. So changing the switch 
case statement into math operation does generate a gain in runtime.

Graph: https://drive.google.com/a/google.com/file/d/0B-Uw1D6S_sSVN1FwUFVoaXA5NEk/view?usp=sharing
Bug: 
Change-Id: I4a68dd5e7f35c4879a75245b87409b44b11f1e78
Reviewed-on: https://chromium-review.googlesource.com/564903
Commit-Queue: Minh-Duc Nguyen <nmduc@google.com>
Reviewed-by: default avatarDarren Shen <shend@chromium.org>
Reviewed-by: default avatarnainar <nainar@chromium.org>
Reviewed-by: default avatarmeade_UTC10 <meade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#486550}
parent 13f9a4aa
...@@ -10,25 +10,128 @@ import make_style_builder ...@@ -10,25 +10,128 @@ import make_style_builder
from name_utilities import enum_for_css_keyword, enum_value_name from name_utilities import enum_for_css_keyword, enum_value_name
def _find_continuous_segment(numbers):
"""Find the longest continuous segment in a list of numbers.
For example:
input:
1, 2, 3, 4, 5, 6
22,70,23,24,25,26
output:
number_list_sorted:
1, 3, 4, 5, 6, 2
22,23,24,25,26,70
segments:
0, 1, 5, 6
which means there are 3 segment with start and end indices
on the number_list_sorted to be: (0, 1), (1, 5), (5, 6)
Args:
numbers: List of pairs of number
Returns:
segment: a list containing the indices of the segment start point
and end point.
number_list: sorted by the first element version of the input.
"""
segments = [0]
number_list_sorted = sorted(numbers, key=lambda elem: elem[0])
for i in range(len(number_list_sorted) - 1):
# continuous segment is a segment which the number in pair is 1 unit
# more than the previous pair
if (number_list_sorted[i + 1][0] - number_list_sorted[i][0] != 1
or number_list_sorted[i + 1][1] - number_list_sorted[i][1] != 1):
segments.append(i + 1)
segments.append(len(number_list_sorted))
return segments, number_list_sorted
def _find_largest_segment(segments):
"""Find the largest segment given a list of start and end
indices of segments
Args:
segments: a list of start and end indices
Returns:
longest_segment: the start and end indices of the longest segment
"""
segment_list = zip(segments[:-1], segments[1:])
return max(segment_list, key=lambda x: x[1] - x[0])
def _find_enum_longest_continuous_segment(property_, name_to_position_dictionary):
"""Find the longest continuous segment in the list of keywords
Finding the continuous segment will allows us to do the subtraction
between keywords so that the distance between 2 keywords in this
enum is equal to the distance of corresponding keywords in another
enum.
Step 1:
Convert keyword enums into number.
Sort and find all continuous segment in the list of enums.
Step 2:
Get the longest segment.
Step 3:
Compose a list of keyword enums and their respective numbers
in the sorted order.
Step 4:
Build the switch case statements of other enums not in the
segment. Enums in the segment will be computed in default clause.
"""
property_enum_order = range(len(property_['keywords']))
css_enum_order = [name_to_position_dictionary[x] for x in property_['keywords']]
enum_pair_list = zip(css_enum_order, property_enum_order)
enum_segment, enum_pair_list = _find_continuous_segment(enum_pair_list)
longest_segment = _find_largest_segment(enum_segment)
enum_pair_list = [
(enum_value_name(property_['keywords'][x[1]]), x[1],
enum_for_css_keyword(property_['keywords'][x[1]]), x[0]) for x in enum_pair_list
]
return enum_pair_list, enum_segment, longest_segment
class CSSValueIDMappingsWriter(make_style_builder.StyleBuilderWriter): class CSSValueIDMappingsWriter(make_style_builder.StyleBuilderWriter):
def __init__(self, json5_file_path): def __init__(self, json5_file_path):
super(CSSValueIDMappingsWriter, self).__init__(json5_file_path) super(CSSValueIDMappingsWriter, self).__init__([json5_file_path[0]])
self._outputs = { self._outputs = {
'CSSValueIDMappingsGenerated.h': self.generate_css_value_mappings, 'CSSValueIDMappingsGenerated.h': self.generate_css_value_mappings,
} }
self.css_values_dictionary_file = json5_file_path[1]
@template_expander.use_jinja('templates/CSSValueIDMappingsGenerated.h.tmpl') @template_expander.use_jinja('templates/CSSValueIDMappingsGenerated.h.tmpl')
def generate_css_value_mappings(self): def generate_css_value_mappings(self):
mappings = {} mappings = {}
include_paths = set() include_paths = set()
for property_ in self._properties.values(): css_values_dictionary = json5_generator.Json5File.load_from_files(
if property_['field_template'] in ('keyword', 'multi_keyword'): [self.css_values_dictionary_file],
include_paths.update(property_['include_paths']) default_parameters=self.json5_file.parameters
).name_dictionaries
name_to_position_dictionary = dict(zip([x['name'] for x in css_values_dictionary], range(len(css_values_dictionary))))
for property_ in self._properties.values():
include_paths.update(property_['include_paths'])
if property_['field_template'] == 'multi_keyword':
mappings[property_['type_name']] = { mappings[property_['type_name']] = {
'default_value': enum_value_name(property_['default_value']), 'default_value': enum_value_name(property_['default_value']),
'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) for k in property_['keywords']], 'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) for k in property_['keywords']],
} }
elif property_['field_template'] == 'keyword':
enum_pair_list, enum_segment, p_segment = _find_enum_longest_continuous_segment(
property_, name_to_position_dictionary)
mappings[property_['type_name']] = {
'default_value': enum_value_name(property_['default_value']),
'mapping': enum_pair_list,
'segment': enum_segment,
'longest_segment_length': p_segment[1] - p_segment[0],
'start_segment': enum_pair_list[p_segment[0]],
'end_segment': enum_pair_list[p_segment[1] - 1],
}
return { return {
'include_paths': list(sorted(include_paths)), 'include_paths': list(sorted(include_paths)),
......
...@@ -27,6 +27,7 @@ template <class T> ...@@ -27,6 +27,7 @@ template <class T>
T cssValueIDToPlatformEnumGenerated(CSSValueID); T cssValueIDToPlatformEnumGenerated(CSSValueID);
{% for enum_name, mapping in mappings.items() %} {% for enum_name, mapping in mappings.items() %}
{% if 'segment' not in mapping %}
template <> template <>
inline {{enum_name}} cssValueIDToPlatformEnumGenerated(CSSValueID v) { inline {{enum_name}} cssValueIDToPlatformEnumGenerated(CSSValueID v) {
switch (v) { switch (v) {
...@@ -51,7 +52,41 @@ inline CSSValueID platformEnumToCSSValueIDGenerated({{enum_name}} v) { ...@@ -51,7 +52,41 @@ inline CSSValueID platformEnumToCSSValueIDGenerated({{enum_name}} v) {
return CSSValueNone; return CSSValueNone;
} }
} }
{% else %}
template <>
inline {{enum_name}} cssValueIDToPlatformEnumGenerated(CSSValueID v) {
{% if mapping['mapping'] | length > mapping.longest_segment_length %}
switch (v) {
{% for cs_value, cs_num, css_value, css_num in mapping['mapping']: %}
{% if css_num < mapping.start_segment[3] or css_num > mapping.end_segment[3] %}
case {{css_value}}:
return {{enum_name}}::{{cs_value}};
{% endif %}
{% endfor %}
default:
DCHECK(v >= {{mapping.start_segment[2]}} && v <= {{mapping.end_segment[2]}}) ;
return static_cast<{{enum_name}}>(v - {{mapping.start_segment[2]}} + static_cast<int>({{enum_name}}::{{mapping.start_segment[0]}}));
}
{% else %}
DCHECK(v >= {{mapping.start_segment[2]}} && v <= {{mapping.end_segment[2]}}) ;
return static_cast<{{enum_name}}>(v - {{mapping.start_segment[2]}} + static_cast<int>({{enum_name}}::{{mapping.start_segment[0]}}));
{% endif %}
}
inline CSSValueID platformEnumToCSSValueIDGenerated({{enum_name}} v) {
switch (v) {
{% for cs_value, cs_num, css_value, css_num in mapping['mapping']: %}
case {{enum_name}}::{{cs_value}}:
return {{css_value}};
{% endfor %}
default:
NOTREACHED();
return CSSValueNone;
}
}
{% endif %}
{% endfor %} {% endfor %}
} // namespace detail } // namespace detail
......
...@@ -442,8 +442,12 @@ css_properties("make_core_generated_computed_style_base") { ...@@ -442,8 +442,12 @@ css_properties("make_core_generated_computed_style_base") {
css_properties("make_core_generated_css_value_id_mappings") { css_properties("make_core_generated_css_value_id_mappings") {
script = "../build/scripts/make_css_value_id_mappings.py" script = "../build/scripts/make_css_value_id_mappings.py"
other_inputs = in_files = [
[ "../build/scripts/templates/CSSValueIDMappingsGenerated.h.tmpl" ] "css/CSSValueKeywords.json5"
]
other_inputs = [
"../build/scripts/templates/CSSValueIDMappingsGenerated.h.tmpl",
]
outputs = [ outputs = [
"$blink_core_output_dir/CSSValueIDMappingsGenerated.h", "$blink_core_output_dir/CSSValueIDMappingsGenerated.h",
] ]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment