Skip to content

Commit 98d562d

Browse files
committed
scripts: dts: optimize gen_driver_kconfig_dts.py
Replace yaml.compose() with a faster event-stream parser that extracts the first top-level "compatible" scalar without constructing the full YAML node tree. This reduces per-file parsing overhead when iterating over thousands of binding files. Binding files are now opened in binary mode and passed directly to SafeLoader/CSafeLoader. Benchmark (Windows, Python 3.12, 2913 bindings): - before: 874ms, 641862 function calls - after: 603ms, 188557 function calls Half of the time is spent opening and closing files, which is quite a bit faster on Linux. Signed-off-by: Guðni Már Gilbert <[email protected]>
1 parent 72360f8 commit 98d562d

File tree

1 file changed

+81
-10
lines changed

1 file changed

+81
-10
lines changed

scripts/dts/gen_driver_kconfig_dts.py

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,99 @@ def parse_args():
5656
return parser.parse_args()
5757

5858

59+
def compatible_from_yaml_file(stream) -> str | None:
60+
"""Extract the top-level ``compatible`` value from a devicetree binding.
61+
62+
This scans the YAML event stream and returns the scalar value associated
63+
with the first top-level ``compatible`` key. It avoids constructing the
64+
full YAML document for performance, and ignores nested mappings and
65+
sequences.
66+
67+
A new PyYAML loader is created from ``stream`` and is always disposed
68+
before this function returns.
69+
70+
:param stream: YAML input stream
71+
:returns: The compatible value if found, otherwise ``None``.
72+
"""
73+
loader = SafeLoader(stream)
74+
mapping_depth = 0
75+
expecting_key = False
76+
pending_compat = False
77+
skip = 0
78+
79+
try:
80+
while loader.check_event():
81+
event = loader.get_event()
82+
83+
if type(event) is yaml.events.MappingStartEvent:
84+
mapping_depth += 1
85+
if skip:
86+
skip += 1
87+
elif mapping_depth == 1:
88+
expecting_key = True
89+
elif mapping_depth == 2 and not expecting_key:
90+
pending_compat = False
91+
skip = 1
92+
continue
93+
94+
if type(event) is yaml.events.MappingEndEvent:
95+
if skip:
96+
skip -= 1
97+
mapping_depth -= 1
98+
if mapping_depth == 1 and skip == 0:
99+
expecting_key = True
100+
continue
101+
102+
if type(event) is yaml.events.SequenceStartEvent:
103+
if skip:
104+
skip += 1
105+
elif mapping_depth == 1 and not expecting_key:
106+
pending_compat = False
107+
skip = 1
108+
continue
109+
110+
if type(event) is yaml.events.SequenceEndEvent:
111+
if skip:
112+
skip -= 1
113+
if mapping_depth == 1 and skip == 0:
114+
expecting_key = True
115+
continue
116+
117+
if skip or mapping_depth != 1:
118+
continue
119+
120+
if type(event) is yaml.events.ScalarEvent:
121+
value = event.value
122+
if expecting_key:
123+
pending_compat = value == "compatible"
124+
expecting_key = False
125+
else:
126+
if pending_compat:
127+
return value
128+
pending_compat = False
129+
expecting_key = True
130+
finally:
131+
loader.dispose()
132+
133+
return None
134+
59135
def main():
60136
args = parse_args()
61137

138+
compat = None
62139
compats = set()
63140

64141
for binding_path in binding_paths(args.bindings_dirs):
65-
with open(binding_path, encoding="utf-8") as f:
142+
with open(binding_path, "rb") as f:
66143
try:
67-
# Parsed PyYAML representation graph. For our purpose,
68-
# we don't need the whole file converted into a dict.
69-
root = yaml.compose(f, Loader=SafeLoader)
144+
compat = compatible_from_yaml_file(f)
70145
except yaml.YAMLError as e:
71146
print(f"WARNING: '{binding_path}' appears in binding "
72147
f"directories but isn't valid YAML: {e}")
73148
continue
74149

75-
if not isinstance(root, yaml.MappingNode):
76-
continue
77-
for key, node in root.value:
78-
if key.value == "compatible" and isinstance(node, yaml.ScalarNode):
79-
compats.add(node.value)
80-
break
150+
if compat is not None:
151+
compats.add(compat)
81152

82153
with open(args.kconfig_out, "w", encoding="utf-8") as kconfig_file:
83154
print(HEADER, file=kconfig_file)

0 commit comments

Comments
 (0)