Skip to content

Commit a65323e

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 a65323e

File tree

1 file changed

+82
-10
lines changed

1 file changed

+82
-10
lines changed

scripts/dts/gen_driver_kconfig_dts.py

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,100 @@ 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+
event_type = event.__class__
83+
84+
if event_type is yaml.events.MappingStartEvent:
85+
mapping_depth += 1
86+
if skip:
87+
skip += 1
88+
elif mapping_depth == 1:
89+
expecting_key = True
90+
elif mapping_depth == 2 and not expecting_key:
91+
pending_compat = False
92+
skip = 1
93+
continue
94+
95+
if event_type is yaml.events.MappingEndEvent:
96+
if skip:
97+
skip -= 1
98+
mapping_depth -= 1
99+
if mapping_depth == 1 and skip == 0:
100+
expecting_key = True
101+
continue
102+
103+
if event_type is yaml.events.SequenceStartEvent:
104+
if skip:
105+
skip += 1
106+
elif mapping_depth == 1 and not expecting_key:
107+
pending_compat = False
108+
skip = 1
109+
continue
110+
111+
if event_type is yaml.events.SequenceEndEvent:
112+
if skip:
113+
skip -= 1
114+
if mapping_depth == 1 and skip == 0:
115+
expecting_key = True
116+
continue
117+
118+
if skip or mapping_depth != 1:
119+
continue
120+
121+
if event_type is yaml.events.ScalarEvent:
122+
value = event.value
123+
if expecting_key:
124+
pending_compat = value == "compatible"
125+
expecting_key = False
126+
else:
127+
if pending_compat:
128+
return value
129+
pending_compat = False
130+
expecting_key = True
131+
finally:
132+
loader.dispose()
133+
134+
return None
135+
59136
def main():
60137
args = parse_args()
61138

139+
compat = None
62140
compats = set()
63141

64142
for binding_path in binding_paths(args.bindings_dirs):
65-
with open(binding_path, encoding="utf-8") as f:
143+
with open(binding_path, "rb") as f:
66144
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)
145+
compat = compatible_from_yaml_file(f)
70146
except yaml.YAMLError as e:
71147
print(f"WARNING: '{binding_path}' appears in binding "
72148
f"directories but isn't valid YAML: {e}")
73149
continue
74150

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
151+
if compat is not None:
152+
compats.add(compat)
81153

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

0 commit comments

Comments
 (0)